diff options
| author | Christophe Simonis <christophe@tinyerp.com> | 2008-07-22 17:11:28 +0200 |
|---|---|---|
| committer | Christophe Simonis <christophe@tinyerp.com> | 2008-07-22 17:11:28 +0200 |
| commit | d2ff95f66505842995786872924507f077c714dd (patch) | |
| tree | d4b4c59ee9ba927a2869522cad75e1f8cd5d2df2 | |
| parent | 2dd9723b8b3d38df8c8962300ab67afffa5cdaaa (diff) | |
replace tabs with 4 spaces
bzr revid: christophe@tinyerp.com-20080722151128-f5zy75tsn8pgtndd
255 files changed, 23309 insertions, 23309 deletions
diff --git a/addons/account/__terp__.py b/addons/account/__terp__.py index 50b94c9af3b..fba702ee046 100644 --- a/addons/account/__terp__.py +++ b/addons/account/__terp__.py @@ -26,51 +26,51 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Accounting and financial management", - "version" : "1.0", - "depends" : ["product", "base"], - "author" : "Tiny", - "description": """Financial and accounting module that covers: - General accounting - Cost / Analytic accounting - Third party accounting - Taxes management - Budgets - """, - "website" : "http://tinyerp.com/module_account.html", - "category" : "Generic Modules/Accounting", - "init_xml" : [ - - ], - "demo_xml" : [ - "account_demo.xml", - "project/project_demo.xml", - "project/analytic_account_demo.xml", - "account_unit_test.xml", - ], - "update_xml" : [ - "account_wizard.xml", - "account_view.xml", - "account_end_fy.xml", - "account_invoice_view.xml", - "account_report.xml", - "partner_view.xml", - "data/account_invoice.xml", - "data/account_data1.xml", - "data/account_minimal.xml", - "data/account_data2.xml", - "account_invoice_workflow.xml", - "project/project_view.xml", - "project/project_report.xml", - "product_data.xml", - "product_view.xml", - "account_security.xml", - "project/project_security.xml", - "account_assert_test.xml", - ], - "translations" : { - "fr": "i18n/french_fr.csv" - }, - "active": False, - "installable": True + "name" : "Accounting and financial management", + "version" : "1.0", + "depends" : ["product", "base"], + "author" : "Tiny", + "description": """Financial and accounting module that covers: + General accounting + Cost / Analytic accounting + Third party accounting + Taxes management + Budgets + """, + "website" : "http://tinyerp.com/module_account.html", + "category" : "Generic Modules/Accounting", + "init_xml" : [ + + ], + "demo_xml" : [ + "account_demo.xml", + "project/project_demo.xml", + "project/analytic_account_demo.xml", + "account_unit_test.xml", + ], + "update_xml" : [ + "account_wizard.xml", + "account_view.xml", + "account_end_fy.xml", + "account_invoice_view.xml", + "account_report.xml", + "partner_view.xml", + "data/account_invoice.xml", + "data/account_data1.xml", + "data/account_minimal.xml", + "data/account_data2.xml", + "account_invoice_workflow.xml", + "project/project_view.xml", + "project/project_report.xml", + "product_data.xml", + "product_view.xml", + "account_security.xml", + "project/project_security.xml", + "account_assert_test.xml", + ], + "translations" : { + "fr": "i18n/french_fr.csv" + }, + "active": False, + "installable": True } diff --git a/addons/account/account.py b/addons/account/account.py index 2e35a30c2ec..1d0b45baeb0 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -39,901 +39,901 @@ from mx.DateTime import RelativeDateTime, now, DateTime, localtime class account_payment_term(osv.osv): - _name = "account.payment.term" - _description = "Payment Term" - _columns = { - 'name': fields.char('Payment Term', size=32, translate=True), - 'active': fields.boolean('Active'), - 'note': fields.text('Description', translate=True), - 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'), - } - _defaults = { - 'active': lambda *a: 1, - } - _order = "name" - def compute(self, cr, uid, id, value, date_ref=False, context={}): - if not date_ref: - date_ref = now().strftime('%Y-%m-%d') - pt = self.browse(cr, uid, id, context) - amount = value - result = [] - for line in pt.line_ids: - if line.value=='fixed': - amt = round(line.value_amount, 2) - elif line.value=='procent': - amt = round(value * line.value_amount, 2) - elif line.value=='balance': - amt = round(amount, 2) - if amt: - next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days) - if line.condition == 'end of month': - next_date += RelativeDateTime(day=-1) - result.append( (next_date.strftime('%Y-%m-%d'), amt) ) - amount -= amt - return result + _name = "account.payment.term" + _description = "Payment Term" + _columns = { + 'name': fields.char('Payment Term', size=32, translate=True), + 'active': fields.boolean('Active'), + 'note': fields.text('Description', translate=True), + 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'), + } + _defaults = { + 'active': lambda *a: 1, + } + _order = "name" + def compute(self, cr, uid, id, value, date_ref=False, context={}): + if not date_ref: + date_ref = now().strftime('%Y-%m-%d') + pt = self.browse(cr, uid, id, context) + amount = value + result = [] + for line in pt.line_ids: + if line.value=='fixed': + amt = round(line.value_amount, 2) + elif line.value=='procent': + amt = round(value * line.value_amount, 2) + elif line.value=='balance': + amt = round(amount, 2) + if amt: + next_date = mx.DateTime.strptime(date_ref, '%Y-%m-%d') + RelativeDateTime(days=line.days) + if line.condition == 'end of month': + next_date += RelativeDateTime(day=-1) + result.append( (next_date.strftime('%Y-%m-%d'), amt) ) + amount -= amt + return result account_payment_term() class account_payment_term_line(osv.osv): - _name = "account.payment.term.line" - _description = "Payment Term Line" - _columns = { - 'name': fields.char('Line Name', size=32,required=True), - 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"), - 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True), - 'value_amount': fields.float('Value Amount'), - 'days': fields.integer('Number of Days',required=True), - 'condition': fields.selection([('net days','Net Days'),('end of month','End of Month')], 'Condition', required=True, help="The payment delay condition id a number of days expressed in 2 ways: net days or end of the month. The 'net days' condition implies that the paiment arrive after 'Number of Days' calendar days. The 'end of the month' condition requires that the paiement arrives before the end of the month that is that is after 'Number of Days' calendar days."), - 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True), - } - _defaults = { - 'value': lambda *a: 'balance', - 'sequence': lambda *a: 5, - 'condition': lambda *a: 'net days', - } - _order = "sequence" + _name = "account.payment.term.line" + _description = "Payment Term Line" + _columns = { + 'name': fields.char('Line Name', size=32,required=True), + 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"), + 'value': fields.selection([('procent','Percent'),('balance','Balance'),('fixed','Fixed Amount')], 'Value',required=True), + 'value_amount': fields.float('Value Amount'), + 'days': fields.integer('Number of Days',required=True), + 'condition': fields.selection([('net days','Net Days'),('end of month','End of Month')], 'Condition', required=True, help="The payment delay condition id a number of days expressed in 2 ways: net days or end of the month. The 'net days' condition implies that the paiment arrive after 'Number of Days' calendar days. The 'end of the month' condition requires that the paiement arrives before the end of the month that is that is after 'Number of Days' calendar days."), + 'payment_id': fields.many2one('account.payment.term','Payment Term', required=True, select=True), + } + _defaults = { + 'value': lambda *a: 'balance', + 'sequence': lambda *a: 5, + 'condition': lambda *a: 'net days', + } + _order = "sequence" account_payment_term_line() class account_account_type(osv.osv): - _name = "account.account.type" - _description = "Account Type" - _columns = { - 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True), - 'code': fields.char('Code', size=32, required=True), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."), - 'code_from': fields.char('Code From', size=10, help="Gives the range of account code available for this type of account. These fields are given for information and are not used in any constraint."), - 'code_to': fields.char('Code To', size=10, help="Gives the range of account code available for this type of account. These fields are just given for information and are not used in any constraint."), - 'partner_account': fields.boolean('Partner account'), - 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True), - } - _defaults = { - 'close_method': lambda *a: 'none', - 'sequence': lambda *a: 5, - } - _order = "sequence" + _name = "account.account.type" + _description = "Account Type" + _columns = { + 'name': fields.char('Acc. Type Name', size=64, required=True, translate=True), + 'code': fields.char('Code', size=32, required=True), + 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of account types."), + 'code_from': fields.char('Code From', size=10, help="Gives the range of account code available for this type of account. These fields are given for information and are not used in any constraint."), + 'code_to': fields.char('Code To', size=10, help="Gives the range of account code available for this type of account. These fields are just given for information and are not used in any constraint."), + 'partner_account': fields.boolean('Partner account'), + 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True), + } + _defaults = { + 'close_method': lambda *a: 'none', + 'sequence': lambda *a: 5, + } + _order = "sequence" account_account_type() def _code_get(self, cr, uid, context={}): - acc_type_obj = self.pool.get('account.account.type') - ids = acc_type_obj.search(cr, uid, []) - res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) - return [(r['code'], r['name']) for r in res] + acc_type_obj = self.pool.get('account.account.type') + ids = acc_type_obj.search(cr, uid, []) + res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) + return [(r['code'], r['name']) for r in res] #---------------------------------------------------------- # Accounts #---------------------------------------------------------- class account_tax(osv.osv): - _name = 'account.tax' + _name = 'account.tax' account_tax() class account_account(osv.osv): - _order = "code" - _name = "account.account" - _description = "Account" - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - if context is None: - context = {} - pos = 0 - while pos<len(args): - if args[pos][0]=='journal_id': - if not args[pos][2]: - del args[pos] - continue - jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2]) - if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]: - del args[pos] - continue - ids3 = map(lambda x: x.code, jour.type_control_ids) - ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)]) - ids1 += map(lambda x: x.id, jour.account_control_ids) - args[pos] = ('id','in',ids1) - pos+=1 - return super(account_account,self).search(cr, uid, args, offset, limit, - order, context=context, count=count) - - def _credit(self, cr, uid, ids, field_name, arg, context={}): - acc_set = ",".join(map(str, ids)) - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - cr.execute(("SELECT a.id, " \ - "SUM(COALESCE(l.credit * a.sign, 0)) " \ - "FROM account_account a " \ - "LEFT JOIN account_move_line l " \ - "ON (a.id = l.account_id) " \ - "WHERE a.type != 'view' " \ - "AND a.id IN (%s) " \ - "AND " + query + " " \ - "AND a.active " \ - "GROUP BY a.id") % (acc_set, )) - res2 = cr.fetchall() - res = {} - for id in ids: - res[id] = 0.0 - for account_id, sum in res2: - res[account_id] += sum - return res - - def _debit(self, cr, uid, ids, field_name, arg, context={}): - acc_set = ",".join(map(str, ids)) - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - cr.execute(("SELECT a.id, " \ - "SUM(COALESCE(l.debit * a.sign, 0)) " \ - "FROM account_account a " \ - "LEFT JOIN account_move_line l " \ - "ON (a.id = l.account_id) " \ - "WHERE a.type != 'view' " \ - "AND a.id IN (%s) " \ - "AND " + query + " " \ - "AND a.active " \ - "GROUP BY a.id") % (acc_set, )) - res2 = cr.fetchall() - res = {} - for id in ids: - res[id] = 0.0 - for account_id, sum in res2: - res[account_id] += sum - return res - - def _balance(self, cr, uid, ids, field_name, arg, context={}): - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - ids2 = {}.fromkeys(ids + ids2).keys() - acc_set = ",".join(map(str, ids2)) - query = self.pool.get('account.move.line')._query_get(cr, uid, - context=context) - cr.execute(("SELECT a.id, " \ - "SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) " \ - "FROM account_account a " \ - "LEFT JOIN account_move_line l " \ - "ON (a.id=l.account_id) " \ - "WHERE a.type != 'view' " \ - "AND a.id IN (%s) " \ - "AND " + query + " " \ - "AND a.active " \ - "GROUP BY a.id") % (acc_set, )) - res = {} - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - cr.execute("SELECT a.id, a.company_id " \ - "FROM account_account a " \ - "WHERE id IN (%s)" % acc_set) - resc = dict(cr.fetchall()) - cr.execute("SELECT id, currency_id FROM res_company") - rescur = dict(cr.fetchall()) - - for id in ids: - ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) - to_currency_id = rescur[resc[id]] - for idx in ids3: - if idx <> id: - res.setdefault(id, 0.0) - if resc[idx]<>resc[id] and resc[idx] and resc[id]: - from_currency_id = rescur[resc[idx]] - res[id] += self.pool.get('res.currency').compute(cr, - uid, from_currency_id, to_currency_id, - res.get(idx, 0.0), context=context) - else: - res[id] += res.get(idx, 0.0) - for id in ids: - res[id] = round(res.get(id,0.0), 2) - return res - - def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): - result = {} - for rec in self.browse(cr, uid, ids, context): - result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) - return result - - _columns = { - 'name': fields.char('Name', size=128, required=True, select=True), - 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign', required=True, help='Allows to change the displayed amount of the balance to see positive results instead of negative ones in expenses accounts'), - 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."), - 'code': fields.char('Code', size=64), - 'type': fields.selection(_code_get, 'Account Type', required=True), - 'parent_id': fields.many2many('account.account', 'account_account_rel', 'child_id', 'parent_id', 'Parents'), - 'child_id': fields.many2many('account.account', 'account_account_rel', 'parent_id', 'child_id', 'Children'), - 'balance': fields.function(_balance, digits=(16,2), method=True, string='Balance'), - 'credit': fields.function(_credit, digits=(16,2), method=True, string='Credit'), - 'debit': fields.function(_debit, digits=(16,2), method=True, string='Debit'), - 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."), - 'shortcut': fields.char('Shortcut', size=12), - 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True, help="Tell Tiny ERP how to process the entries of this account when you close a fiscal year. None removes all entries to start with an empty account for the new fiscal year. Balance creates only one entry to keep the balance for the new fiscal year. Detail keeps the detail of all entries of the preceeding years. Unreconciled keeps the detail of unreconciled entries only."), - 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel', - 'account_id','tax_id', 'Default Taxes'), - 'note': fields.text('Note'), - 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'active': fields.boolean('Active', select=2), - } - - def _default_company(self, cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - return user.company_id.id - return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - - _defaults = { - 'sign': lambda *a: 1, - 'type' : lambda *a :'view', - 'reconcile': lambda *a: False, - 'close_method': lambda *a: 'balance', - 'company_id': _default_company, - 'active': lambda *a: True, - } - - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from account_account_rel where child_id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) - ] - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context = {} - args = args[:] - ids = [] - try: - if name and str(name).startswith('partner:'): - part_id = int(name.split(':')[1]) - part = self.pool.get('res.partner').browse(cr, user, part_id, context) - args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))] - name = False - if name and str(name).startswith('type:'): - type = name.split(':')[1] - args += [('type','=', type)] - name = False - except: - pass - if name: - ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) - else: - ids = self.search(cr, user, args, context=context, limit=limit) - return self.name_get(cr, user, ids, context=context) - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','code'], context) - res = [] - for record in reads: - name = record['name'] - if record['code']: - name = record['code']+' - '+name - res.append((record['id'],name )) - return res - - def copy(self, cr, uid, id, default=None, context={}): - account = self.browse(cr, uid, id, context=context) - new_child_ids = [] - default['parent_id'] = False - if account: - for child in account.child_id: - new_child_ids.append(self.copy(cr, uid, child.id, default, context=context)) - default['child_id'] = [(6, 0, new_child_ids)] - else: - default['child_id'] = False - return super(account_account, self).copy(cr, uid, id, default, context=context) - - def write(self, cr, uid, ids, vals, context=None): - if not context: - context={} - if 'active' in vals and not vals['active']: - line_obj = self.pool.get('account.move.line') - account_ids = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): - vals=vals.copy() - del vals['active'] - return super(account_account, self).write(cr, uid, ids, vals, context=context) + _order = "code" + _name = "account.account" + _description = "Account" + + def search(self, cr, uid, args, offset=0, limit=None, order=None, + context=None, count=False): + if context is None: + context = {} + pos = 0 + while pos<len(args): + if args[pos][0]=='journal_id': + if not args[pos][2]: + del args[pos] + continue + jour = self.pool.get('account.journal').browse(cr, uid, args[pos][2]) + if (not (jour.account_control_ids or jour.type_control_ids)) or not args[pos][2]: + del args[pos] + continue + ids3 = map(lambda x: x.code, jour.type_control_ids) + ids1 = super(account_account,self).search(cr, uid, [('type','in',ids3)]) + ids1 += map(lambda x: x.id, jour.account_control_ids) + args[pos] = ('id','in',ids1) + pos+=1 + return super(account_account,self).search(cr, uid, args, offset, limit, + order, context=context, count=count) + + def _credit(self, cr, uid, ids, field_name, arg, context={}): + acc_set = ",".join(map(str, ids)) + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + cr.execute(("SELECT a.id, " \ + "SUM(COALESCE(l.credit * a.sign, 0)) " \ + "FROM account_account a " \ + "LEFT JOIN account_move_line l " \ + "ON (a.id = l.account_id) " \ + "WHERE a.type != 'view' " \ + "AND a.id IN (%s) " \ + "AND " + query + " " \ + "AND a.active " \ + "GROUP BY a.id") % (acc_set, )) + res2 = cr.fetchall() + res = {} + for id in ids: + res[id] = 0.0 + for account_id, sum in res2: + res[account_id] += sum + return res + + def _debit(self, cr, uid, ids, field_name, arg, context={}): + acc_set = ",".join(map(str, ids)) + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + cr.execute(("SELECT a.id, " \ + "SUM(COALESCE(l.debit * a.sign, 0)) " \ + "FROM account_account a " \ + "LEFT JOIN account_move_line l " \ + "ON (a.id = l.account_id) " \ + "WHERE a.type != 'view' " \ + "AND a.id IN (%s) " \ + "AND " + query + " " \ + "AND a.active " \ + "GROUP BY a.id") % (acc_set, )) + res2 = cr.fetchall() + res = {} + for id in ids: + res[id] = 0.0 + for account_id, sum in res2: + res[account_id] += sum + return res + + def _balance(self, cr, uid, ids, field_name, arg, context={}): + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + ids2 = {}.fromkeys(ids + ids2).keys() + acc_set = ",".join(map(str, ids2)) + query = self.pool.get('account.move.line')._query_get(cr, uid, + context=context) + cr.execute(("SELECT a.id, " \ + "SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) " \ + "FROM account_account a " \ + "LEFT JOIN account_move_line l " \ + "ON (a.id=l.account_id) " \ + "WHERE a.type != 'view' " \ + "AND a.id IN (%s) " \ + "AND " + query + " " \ + "AND a.active " \ + "GROUP BY a.id") % (acc_set, )) + res = {} + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + cr.execute("SELECT a.id, a.company_id " \ + "FROM account_account a " \ + "WHERE id IN (%s)" % acc_set) + resc = dict(cr.fetchall()) + cr.execute("SELECT id, currency_id FROM res_company") + rescur = dict(cr.fetchall()) + + for id in ids: + ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) + to_currency_id = rescur[resc[id]] + for idx in ids3: + if idx <> id: + res.setdefault(id, 0.0) + if resc[idx]<>resc[id] and resc[idx] and resc[id]: + from_currency_id = rescur[resc[idx]] + res[id] += self.pool.get('res.currency').compute(cr, + uid, from_currency_id, to_currency_id, + res.get(idx, 0.0), context=context) + else: + res[id] += res.get(idx, 0.0) + for id in ids: + res[id] = round(res.get(id,0.0), 2) + return res + + def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): + result = {} + for rec in self.browse(cr, uid, ids, context): + result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) + return result + + _columns = { + 'name': fields.char('Name', size=128, required=True, select=True), + 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign', required=True, help='Allows to change the displayed amount of the balance to see positive results instead of negative ones in expenses accounts'), + 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."), + 'code': fields.char('Code', size=64), + 'type': fields.selection(_code_get, 'Account Type', required=True), + 'parent_id': fields.many2many('account.account', 'account_account_rel', 'child_id', 'parent_id', 'Parents'), + 'child_id': fields.many2many('account.account', 'account_account_rel', 'parent_id', 'child_id', 'Children'), + 'balance': fields.function(_balance, digits=(16,2), method=True, string='Balance'), + 'credit': fields.function(_credit, digits=(16,2), method=True, string='Credit'), + 'debit': fields.function(_debit, digits=(16,2), method=True, string='Debit'), + 'reconcile': fields.boolean('Reconcile', help="Check this account if the user can make a reconciliation of the entries in this account."), + 'shortcut': fields.char('Shortcut', size=12), + 'close_method': fields.selection([('none','None'), ('balance','Balance'), ('detail','Detail'),('unreconciled','Unreconciled')], 'Deferral Method', required=True, help="Tell Tiny ERP how to process the entries of this account when you close a fiscal year. None removes all entries to start with an empty account for the new fiscal year. Balance creates only one entry to keep the balance for the new fiscal year. Detail keeps the detail of all entries of the preceeding years. Unreconciled keeps the detail of unreconciled entries only."), + 'tax_ids': fields.many2many('account.tax', 'account_account_tax_default_rel', + 'account_id','tax_id', 'Default Taxes'), + 'note': fields.text('Note'), + 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Company Currency'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'active': fields.boolean('Active', select=2), + } + + def _default_company(self, cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.id + return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + + _defaults = { + 'sign': lambda *a: 1, + 'type' : lambda *a :'view', + 'reconcile': lambda *a: False, + 'close_method': lambda *a: 'balance', + 'company_id': _default_company, + 'active': lambda *a: True, + } + + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from account_account_rel where child_id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) + ] + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context = {} + args = args[:] + ids = [] + try: + if name and str(name).startswith('partner:'): + part_id = int(name.split(':')[1]) + part = self.pool.get('res.partner').browse(cr, user, part_id, context) + args += [('id','in', (part.property_account_payable.id, part.property_account_receivable.id))] + name = False + if name and str(name).startswith('type:'): + type = name.split(':')[1] + args += [('type','=', type)] + name = False + except: + pass + if name: + ids = self.search(cr, user, [('code','=like',name+"%")]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('shortcut','=',name)]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) + else: + ids = self.search(cr, user, args, context=context, limit=limit) + return self.name_get(cr, user, ids, context=context) + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','code'], context) + res = [] + for record in reads: + name = record['name'] + if record['code']: + name = record['code']+' - '+name + res.append((record['id'],name )) + return res + + def copy(self, cr, uid, id, default=None, context={}): + account = self.browse(cr, uid, id, context=context) + new_child_ids = [] + default['parent_id'] = False + if account: + for child in account.child_id: + new_child_ids.append(self.copy(cr, uid, child.id, default, context=context)) + default['child_id'] = [(6, 0, new_child_ids)] + else: + default['child_id'] = False + return super(account_account, self).copy(cr, uid, id, default, context=context) + + def write(self, cr, uid, ids, vals, context=None): + if not context: + context={} + if 'active' in vals and not vals['active']: + line_obj = self.pool.get('account.move.line') + account_ids = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): + vals=vals.copy() + del vals['active'] + return super(account_account, self).write(cr, uid, ids, vals, context=context) account_account() class account_journal_view(osv.osv): - _name = "account.journal.view" - _description = "Journal View" - _columns = { - 'name': fields.char('Journal View', size=64, required=True), - 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns') - } - _order = "name" + _name = "account.journal.view" + _description = "Journal View" + _columns = { + 'name': fields.char('Journal View', size=64, required=True), + 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns') + } + _order = "name" account_journal_view() class account_journal_column(osv.osv): - def _col_get(self, cr, user, context={}): - result = [] - cols = self.pool.get('account.move.line')._columns - for col in cols: - result.append( (col, cols[col].string) ) - result.sort() - return result - _name = "account.journal.column" - _description = "Journal Column" - _columns = { - 'name': fields.char('Column Name', size=64, required=True), - 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32), - 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True), - 'sequence': fields.integer('Sequence'), - 'required': fields.boolean('Required'), - 'readonly': fields.boolean('Readonly'), - } - _order = "sequence" + def _col_get(self, cr, user, context={}): + result = [] + cols = self.pool.get('account.move.line')._columns + for col in cols: + result.append( (col, cols[col].string) ) + result.sort() + return result + _name = "account.journal.column" + _description = "Journal Column" + _columns = { + 'name': fields.char('Column Name', size=64, required=True), + 'field': fields.selection(_col_get, 'Field Name', method=True, required=True, size=32), + 'view_id': fields.many2one('account.journal.view', 'Journal View', select=True), + 'sequence': fields.integer('Sequence'), + 'required': fields.boolean('Required'), + 'readonly': fields.boolean('Readonly'), + } + _order = "sequence" account_journal_column() class account_journal(osv.osv): - _name = "account.journal" - _description = "Journal" - _columns = { - 'name': fields.char('Journal Name', size=64, required=True, translate=True), - 'code': fields.char('Code', size=16), - 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True), - - 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]), - 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]), - - 'active': fields.boolean('Active'), - 'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell Tiny ERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."), - 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'), - 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'), - 'centralisation': fields.boolean('Centralised counterpart', help="Check this box if you want that each entry doesn't create a counterpart but share the same counterpart for each entry of this journal."), - 'update_posted': fields.boolean('Allow Cancelling Entries'), - 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True), - 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"), - 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'), - 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'), - } - _defaults = { - 'active': lambda *a: 1, - 'user_id': lambda self,cr,uid,context: uid, - } - def create(self, cr, uid, vals, context={}): - journal_id = super(osv.osv, self).create(cr, uid, vals, context) -# journal_name = self.browse(cr, uid, [journal_id])[0].code -# periods = self.pool.get('account.period') -# ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))]) -# for period in periods.browse(cr, uid, ids): -# self.pool.get('account.journal.period').create(cr, uid, { -# 'name': (journal_name or '')+':'+(period.code or ''), -# 'journal_id': journal_id, -# 'period_id': period.id -# }) - return journal_id - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = [] - if name: - ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) - return self.name_get(cr, user, ids, context=context) + _name = "account.journal" + _description = "Journal" + _columns = { + 'name': fields.char('Journal Name', size=64, required=True, translate=True), + 'code': fields.char('Code', size=16), + 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True), + + 'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]), + 'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]), + + 'active': fields.boolean('Active'), + 'view_id': fields.many2one('account.journal.view', 'View', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tell Tiny ERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."), + 'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account'), + 'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account'), + 'centralisation': fields.boolean('Centralised counterpart', help="Check this box if you want that each entry doesn't create a counterpart but share the same counterpart for each entry of this journal."), + 'update_posted': fields.boolean('Allow Cancelling Entries'), + 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="The sequence gives the display order for a list of journals", required=True), + 'user_id': fields.many2one('res.users', 'User', help="The responsible user of this journal"), + 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'), + 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'), + } + _defaults = { + 'active': lambda *a: 1, + 'user_id': lambda self,cr,uid,context: uid, + } + def create(self, cr, uid, vals, context={}): + journal_id = super(osv.osv, self).create(cr, uid, vals, context) +# journal_name = self.browse(cr, uid, [journal_id])[0].code +# periods = self.pool.get('account.period') +# ids = periods.search(cr, uid, [('date_stop','>=',time.strftime('%Y-%m-%d'))]) +# for period in periods.browse(cr, uid, ids): +# self.pool.get('account.journal.period').create(cr, uid, { +# 'name': (journal_name or '')+':'+(period.code or ''), +# 'journal_id': journal_id, +# 'period_id': period.id +# }) + return journal_id + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = [] + if name: + ids = self.search(cr, user, [('code','ilike',name)]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) + return self.name_get(cr, user, ids, context=context) account_journal() class account_fiscalyear(osv.osv): - _name = "account.fiscalyear" - _description = "Fiscal Year" - _columns = { - 'name': fields.char('Fiscal Year', size=64, required=True), - 'code': fields.char('Code', size=6, required=True), - 'date_start': fields.date('Start date', required=True), - 'date_stop': fields.date('End date', required=True), - 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'), - 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', redonly=True), - } - - _defaults = { - 'state': lambda *a: 'draft', - } - _order = "date_start" - def create_period3(self,cr, uid, ids, context={}): - return self.create_period(cr, uid, ids, context, 3) - - def create_period(self,cr, uid, ids, context={}, interval=1): - for fy in self.browse(cr, uid, ids, context): - dt = fy.date_start - ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d') - while ds.strftime('%Y-%m-%d')<fy.date_stop: - de = ds + RelativeDateTime(months=interval, days=-1) - self.pool.get('account.period').create(cr, uid, { - 'name': ds.strftime('%d/%m') + ' - '+de.strftime('%d/%m'), - 'code': ds.strftime('%d/%m') + '-'+de.strftime('%d/%m'), - 'date_start': ds.strftime('%Y-%m-%d'), - 'date_stop': de.strftime('%Y-%m-%d'), - 'fiscalyear_id': fy.id, - }) - ds = ds + RelativeDateTime(months=interval) - return True - - def find(self, cr, uid, dt=None, exception=True, context={}): - if not dt: - dt = time.strftime('%Y-%m-%d') - ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)]) - if not ids: - if exception: - raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.')) - else: - return False - return ids[0] + _name = "account.fiscalyear" + _description = "Fiscal Year" + _columns = { + 'name': fields.char('Fiscal Year', size=64, required=True), + 'code': fields.char('Code', size=6, required=True), + 'date_start': fields.date('Start date', required=True), + 'date_stop': fields.date('End date', required=True), + 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'), + 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', redonly=True), + } + + _defaults = { + 'state': lambda *a: 'draft', + } + _order = "date_start" + def create_period3(self,cr, uid, ids, context={}): + return self.create_period(cr, uid, ids, context, 3) + + def create_period(self,cr, uid, ids, context={}, interval=1): + for fy in self.browse(cr, uid, ids, context): + dt = fy.date_start + ds = mx.DateTime.strptime(fy.date_start, '%Y-%m-%d') + while ds.strftime('%Y-%m-%d')<fy.date_stop: + de = ds + RelativeDateTime(months=interval, days=-1) + self.pool.get('account.period').create(cr, uid, { + 'name': ds.strftime('%d/%m') + ' - '+de.strftime('%d/%m'), + 'code': ds.strftime('%d/%m') + '-'+de.strftime('%d/%m'), + 'date_start': ds.strftime('%Y-%m-%d'), + 'date_stop': de.strftime('%Y-%m-%d'), + 'fiscalyear_id': fy.id, + }) + ds = ds + RelativeDateTime(months=interval) + return True + + def find(self, cr, uid, dt=None, exception=True, context={}): + if not dt: + dt = time.strftime('%Y-%m-%d') + ids = self.search(cr, uid, [('date_start', '<=', dt), ('date_stop', '>=', dt)]) + if not ids: + if exception: + raise osv.except_osv(_('Error !'), _('No fiscal year defined for this date !\nPlease create one.')) + else: + return False + return ids[0] account_fiscalyear() class account_period(osv.osv): - _name = "account.period" - _description = "Account period" - _columns = { - 'name': fields.char('Period Name', size=64, required=True), - 'code': fields.char('Code', size=12), - 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}), - 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}), - 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True), - 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', readonly=True) - } - _defaults = { - 'state': lambda *a: 'draft', - } - _order = "date_start" - def next(self, cr, uid, period, step, context={}): - ids = self.search(cr, uid, [('date_start','>',period.date_start)]) - if len(ids)>=step: - return ids[step-1] - return False - - def find(self, cr, uid, dt=None, context={}): - if not dt: - dt = time.strftime('%Y-%m-%d') + _name = "account.period" + _description = "Account period" + _columns = { + 'name': fields.char('Period Name', size=64, required=True), + 'code': fields.char('Code', size=12), + 'date_start': fields.date('Start of period', required=True, states={'done':[('readonly',True)]}), + 'date_stop': fields.date('End of period', required=True, states={'done':[('readonly',True)]}), + 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True), + 'state': fields.selection([('draft','Draft'), ('done','Done')], 'State', readonly=True) + } + _defaults = { + 'state': lambda *a: 'draft', + } + _order = "date_start" + def next(self, cr, uid, period, step, context={}): + ids = self.search(cr, uid, [('date_start','>',period.date_start)]) + if len(ids)>=step: + return ids[step-1] + return False + + def find(self, cr, uid, dt=None, context={}): + if not dt: + dt = time.strftime('%Y-%m-%d') #CHECKME: shouldn't we check the state of the period? - ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)]) - if not ids: - raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.')) - return ids + ids = self.search(cr, uid, [('date_start','<=',dt),('date_stop','>=',dt)]) + if not ids: + raise osv.except_osv(_('Error !'), _('No period defined for this date !\nPlease create a fiscal year.')) + return ids account_period() class account_journal_period(osv.osv): - _name = "account.journal.period" - _description = "Journal - Period" - - def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}): - result = {}.fromkeys(ids, 'STOCK_NEW') - for r in self.read(cr, uid, ids, ['state']): - result[r['id']] = { - 'draft': 'STOCK_NEW', - 'printed': 'STOCK_PRINT_PREVIEW', - 'done': 'STOCK_DIALOG_AUTHENTICATION', - }.get(r['state'], 'STOCK_NEW') - return result - - _columns = { - 'name': fields.char('Journal-Period Name', size=64, required=True), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"), - 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"), - 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'), - 'active': fields.boolean('Active', required=True), - 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'State', required=True, readonly=True) - } - - def _check(self, cr, uid, ids, context={}): - for obj in self.browse(cr, uid, ids, context): - cr.execute('select * from account_move_line where journal_id=%d and period_id=%d limit 1', (obj.journal_id.id, obj.period_id.id)) - res = cr.fetchall() - if res: - raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !')) - return True - - def write(self, cr, uid, ids, vals, context={}): - self._check(cr, uid, ids, context) - return super(account_journal_period, self).write(cr, uid, ids, vals, context) - - def create(self, cr, uid, vals, context={}): - period_id=vals.get('period_id',False) - if period_id: - period = self.pool.get('account.period').browse(cr, uid,period_id) - vals['state']=period.state - return super(account_journal_period, self).create(cr, uid, vals, context) - - def unlink(self, cr, uid, ids, context={}): - self._check(cr, uid, ids, context) - return super(account_journal_period, self).unlink(cr, uid, ids, context) - - _defaults = { - 'state': lambda *a: 'draft', - 'active': lambda *a: True, - } - _order = "period_id" + _name = "account.journal.period" + _description = "Journal - Period" + + def _icon_get(self, cr, uid, ids, field_name, arg=None, context={}): + result = {}.fromkeys(ids, 'STOCK_NEW') + for r in self.read(cr, uid, ids, ['state']): + result[r['id']] = { + 'draft': 'STOCK_NEW', + 'printed': 'STOCK_PRINT_PREVIEW', + 'done': 'STOCK_DIALOG_AUTHENTICATION', + }.get(r['state'], 'STOCK_NEW') + return result + + _columns = { + 'name': fields.char('Journal-Period Name', size=64, required=True), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"), + 'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"), + 'icon': fields.function(_icon_get, method=True, string='Icon', type='string'), + 'active': fields.boolean('Active', required=True), + 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'State', required=True, readonly=True) + } + + def _check(self, cr, uid, ids, context={}): + for obj in self.browse(cr, uid, ids, context): + cr.execute('select * from account_move_line where journal_id=%d and period_id=%d limit 1', (obj.journal_id.id, obj.period_id.id)) + res = cr.fetchall() + if res: + raise osv.except_osv(_('Error !'), _('You can not modify/delete a journal with entries for this period !')) + return True + + def write(self, cr, uid, ids, vals, context={}): + self._check(cr, uid, ids, context) + return super(account_journal_period, self).write(cr, uid, ids, vals, context) + + def create(self, cr, uid, vals, context={}): + period_id=vals.get('period_id',False) + if period_id: + period = self.pool.get('account.period').browse(cr, uid,period_id) + vals['state']=period.state + return super(account_journal_period, self).create(cr, uid, vals, context) + + def unlink(self, cr, uid, ids, context={}): + self._check(cr, uid, ids, context) + return super(account_journal_period, self).unlink(cr, uid, ids, context) + + _defaults = { + 'state': lambda *a: 'draft', + 'active': lambda *a: True, + } + _order = "period_id" account_journal_period() class account_fiscalyear(osv.osv): - _inherit = "account.fiscalyear" - _description = "Fiscal Year" - _columns = { - 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'), - 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True), - } + _inherit = "account.fiscalyear" + _description = "Fiscal Year" + _columns = { + 'start_journal_period_id':fields.many2one('account.journal.period','New Entries Journal'), + 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True), + } account_fiscalyear() #---------------------------------------------------------- # Entries #---------------------------------------------------------- class account_move(osv.osv): - _name = "account.move" - _description = "Account Entry" - - def name_get(self, cursor, user, ids, context=None): - if not len(ids): - return [] - res=[] - data_move = self.pool.get('account.move').browse(cursor,user,ids) - for move in data_move: - if move.state=='draft': - name = '*' + move.name - else: - name = move.name - res.append((move.id, name)) - return res - - - def _get_period(self, cr, uid, context): - periods = self.pool.get('account.period').find(cr, uid) - if periods: - return periods[0] - else: - return False - _columns = { - 'name': fields.char('Entry Name', size=64, required=True), - 'ref': fields.char('Ref', size=64), - 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}), - 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'State', required=True, readonly=True), - 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}), - } - _defaults = { - 'state': lambda *a: 'draft', - 'period_id': _get_period, - } - - def _check_centralisation(self, cursor, user, ids): - for move in self.browse(cursor, user, ids): - if move.journal_id.centralisation: - move_ids = self.search(cursor, user, [ - ('period_id', '=', move.period_id.id), - ('journal_id', '=', move.journal_id.id), - ]) - if len(move_ids) > 1: - return False - return True - - def _check_period_journal(self, cursor, user, ids): - for move in self.browse(cursor, user, ids): - for line in move.line_id: - if line.period_id.id != move.period_id.id: - return False - if line.journal_id.id != move.journal_id.id: - return False - return True - - _constraints = [ - (_check_centralisation, - 'You can not create more than one move per period on centralized journal', - ['journal_id']), - (_check_period_journal, - 'You can not create entries on different period/journal in the same move', - ['line_id']), - ] - def post(self, cr, uid, ids, context=None): - if self.validate(cr, uid, ids, context) and len(ids): - cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',)) - else: - raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !')) - return True - - def button_validate(self, cursor, user, ids, context=None): - return self.post(cursor, user, ids, context=context) - - def button_cancel(self, cr, uid, ids, context={}): - for line in self.browse(cr, uid, ids, context): - if not line.journal_id.update_posted: - raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !')) - if len(ids): - cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',)) - return True - - def write(self, cr, uid, ids, vals, context={}): - c = context.copy() - c['novalidate'] = True - result = super(osv.osv, self).write(cr, uid, ids, vals, c) - self.validate(cr, uid, ids, context) - return result - - # - # TODO: Check if period is closed ! - # - def create(self, cr, uid, vals, context={}): - if 'line_id' in vals: - if 'journal_id' in vals: - for l in vals['line_id']: - if not l[0]: - l[2]['journal_id'] = vals['journal_id'] - context['journal_id'] = vals['journal_id'] - if 'period_id' in vals: - for l in vals['line_id']: - if not l[0]: - l[2]['period_id'] = vals['period_id'] - context['period_id'] = vals['period_id'] - else: - default_period = self._get_period(cr, uid, context) - for l in vals['line_id']: - if not l[0]: - l[2]['period_id'] = default_period - context['period_id'] = default_period - - if not 'name' in vals: - journal = self.pool.get('account.journal').browse(cr, uid, context.get('journal_id', vals.get('journal_id', False))) - if journal.sequence_id: - vals['name'] = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) - else: - raise osv.except_osv(_('Error'), _('No sequence defined in the journal !')) - if 'line_id' in vals: - c = context.copy() - c['novalidate'] = True - result = super(account_move, self).create(cr, uid, vals, c) - self.validate(cr, uid, [result], context) - else: - result = super(account_move, self).create(cr, uid, vals, context) - return result - - def unlink(self, cr, uid, ids, context={}, check=True): - toremove = [] - for move in self.browse(cr, uid, ids, context): - if move['state'] <> 'draft': - raise osv.except_osv(_('UserError'), - _('You can not delete posted movement: "%s"!') % \ - move['name']) - line_ids = map(lambda x: x.id, move.line_id) - context['journal_id'] = move.journal_id.id - context['period_id'] = move.period_id.id - self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context) - toremove.append(move.id) - result = super(account_move, self).unlink(cr, uid, toremove, context) - return result - - def _compute_balance(self, cr, uid, id, context={}): - move = self.browse(cr, uid, [id])[0] - amount = 0 - for line in move.line_id: - amount+= (line.debit - line.credit) - return amount - - def _centralise(self, cr, uid, move, mode): - if mode=='credit': - account_id = move.journal_id.default_debit_account_id.id - mode2 = 'debit' - if not account_id: - raise osv.except_osv(_('UserError'), - _('There is no default default debit account defined \n' \ - 'on journal "%s"') % move.journal_id.name) - else: - account_id = move.journal_id.default_credit_account_id.id - mode2 = 'credit' - if not account_id: - raise osv.except_osv(_('UserError'), - _('There is no default default credit account defined \n' \ - 'on journal "%s"') % move.journal_id.name) - - # find the first line of this move with the current mode - # or create it if it doesn't exist - cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode)) - res = cr.fetchone() - if res: - line_id = res[0] - else: - line_id = self.pool.get('account.move.line').create(cr, uid, { - 'name': 'Centralisation '+mode, - 'centralisation': mode, - 'account_id': account_id, - 'move_id': move.id, - 'journal_id': move.journal_id.id, - 'period_id': move.period_id.id, - 'date': move.period_id.date_stop, - 'debit': 0.0, - 'credit': 0.0, - }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id}) - - # find the first line of this move with the other mode - # so that we can exclude it from our calculation - cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2)) - res = cr.fetchone() - if res: - line_id2 = res[0] - else: - line_id2 = 0 - - cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2)) - result = cr.fetchone()[0] or 0.0 - cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id)) - return True - - # - # Validate a balanced move. If it is a centralised journal, create a move. - # - def validate(self, cr, uid, ids, context={}): - ok = True - for move in self.browse(cr, uid, ids, context): - journal = move.journal_id - amount = 0 - line_ids = [] - line_draft_ids = [] - company_id=None - for line in move.line_id: - amount += line.debit - line.credit - line_ids.append(line.id) - if line.state=='draft': - line_draft_ids.append(line.id) - - if not company_id: - company_id = line.account_id.company_id.id - if not company_id == line.account_id.company_id.id: - raise osv.except_osv(_('Error'), _('Couldn\'t create move between different companies')) - - if line.account_id.currency_id: - if line.account_id.currency_id.id != line.currency_id.id and (line.account_id.currency_id.id != line.account_id.company_id.currency_id.id or line.currency_id): - raise osv.except_osv(_('Error'), _('Couldn\'t create move with currency different than the secondary currency of the account')) - - if abs(amount) < 0.0001: - if not len(line_draft_ids): - continue - self.pool.get('account.move.line').write(cr, uid, line_draft_ids, { - 'journal_id': move.journal_id.id, - 'period_id': move.period_id.id, - 'state': 'valid' - }, context, check=False) - todo = [] - account = {} - account2 = {} - if journal.type not in ('purchase','sale'): - continue - - for line in move.line_id: - if move.journal_id.type == 'sale': - if line.debit: - field_base = 'ref_' - key = 'account_paid_id' - else: - field_base = '' - key = 'account_collected_id' - else: - if line.debit: - field_base = '' - key = 'account_collected_id' - else: - field_base = 'ref_' - key = 'account_paid_id' - if line.account_id.tax_ids: - code = amount = False - for tax in line.account_id.tax_ids: - if tax.tax_code_id: - acc = getattr(tax, key).id - account[acc] = (getattr(tax, - field_base + 'tax_code_id').id, - getattr(tax, field_base + 'tax_sign')) - account2[(acc,getattr(tax, - field_base + 'tax_code_id').id)] = (getattr(tax, - field_base + 'tax_code_id').id, - getattr(tax, field_base + 'tax_sign')) - code = getattr(tax, field_base + 'base_code_id').id - amount = getattr(tax, field_base+'base_sign') * \ - (line.debit + line.credit) - break - if code and not (line.tax_code_id or line.tax_amount): - self.pool.get('account.move.line').write(cr, uid, - [line.id], { - 'tax_code_id': code, - 'tax_amount': amount - }, context=context, check=False) - else: - todo.append(line) - for line in todo: - code = amount = 0 - key = (line.account_id.id, line.tax_code_id.id) - if key in account2: - code = account2[key][0] - amount = account2[key][1] * (line.debit + line.credit) - elif line.account_id.id in account: - code = account[line.account_id.id][0] - amount = account[line.account_id.id][1] * (line.debit + line.credit) - if (code or amount) and not (line.tax_code_id or line.tax_amount): - self.pool.get('account.move.line').write(cr, uid, [line.id], { - 'tax_code_id': code, - 'tax_amount': amount - }, context, check=False) - # - # Compute VAT - # - continue - if journal.centralisation: - self._centralise(cr, uid, move, 'debit') - self._centralise(cr, uid, move, 'credit') - self.pool.get('account.move.line').write(cr, uid, line_draft_ids, { - 'state': 'valid' - }, context, check=False) - continue - else: - self.pool.get('account.move.line').write(cr, uid, line_ids, { - 'journal_id': move.journal_id.id, - 'period_id': move.period_id.id, - #'tax_code_id': False, - #'tax_amount': False, - 'state': 'draft' - }, context, check=False) - ok = False - return ok + _name = "account.move" + _description = "Account Entry" + + def name_get(self, cursor, user, ids, context=None): + if not len(ids): + return [] + res=[] + data_move = self.pool.get('account.move').browse(cursor,user,ids) + for move in data_move: + if move.state=='draft': + name = '*' + move.name + else: + name = move.name + res.append((move.id, name)) + return res + + + def _get_period(self, cr, uid, context): + periods = self.pool.get('account.period').find(cr, uid) + if periods: + return periods[0] + else: + return False + _columns = { + 'name': fields.char('Entry Name', size=64, required=True), + 'ref': fields.char('Ref', size=64), + 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}), + 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'State', required=True, readonly=True), + 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}), + } + _defaults = { + 'state': lambda *a: 'draft', + 'period_id': _get_period, + } + + def _check_centralisation(self, cursor, user, ids): + for move in self.browse(cursor, user, ids): + if move.journal_id.centralisation: + move_ids = self.search(cursor, user, [ + ('period_id', '=', move.period_id.id), + ('journal_id', '=', move.journal_id.id), + ]) + if len(move_ids) > 1: + return False + return True + + def _check_period_journal(self, cursor, user, ids): + for move in self.browse(cursor, user, ids): + for line in move.line_id: + if line.period_id.id != move.period_id.id: + return False + if line.journal_id.id != move.journal_id.id: + return False + return True + + _constraints = [ + (_check_centralisation, + 'You can not create more than one move per period on centralized journal', + ['journal_id']), + (_check_period_journal, + 'You can not create entries on different period/journal in the same move', + ['line_id']), + ] + def post(self, cr, uid, ids, context=None): + if self.validate(cr, uid, ids, context) and len(ids): + cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('posted',)) + else: + raise osv.except_osv(_('Integrity Error !'), _('You can not validate a non balanced entry !')) + return True + + def button_validate(self, cursor, user, ids, context=None): + return self.post(cursor, user, ids, context=context) + + def button_cancel(self, cr, uid, ids, context={}): + for line in self.browse(cr, uid, ids, context): + if not line.journal_id.update_posted: + raise osv.except_osv(_('Error !'), _('You can not modify a posted entry of this journal !')) + if len(ids): + cr.execute('update account_move set state=%s where id in ('+','.join(map(str,ids))+')', ('draft',)) + return True + + def write(self, cr, uid, ids, vals, context={}): + c = context.copy() + c['novalidate'] = True + result = super(osv.osv, self).write(cr, uid, ids, vals, c) + self.validate(cr, uid, ids, context) + return result + + # + # TODO: Check if period is closed ! + # + def create(self, cr, uid, vals, context={}): + if 'line_id' in vals: + if 'journal_id' in vals: + for l in vals['line_id']: + if not l[0]: + l[2]['journal_id'] = vals['journal_id'] + context['journal_id'] = vals['journal_id'] + if 'period_id' in vals: + for l in vals['line_id']: + if not l[0]: + l[2]['period_id'] = vals['period_id'] + context['period_id'] = vals['period_id'] + else: + default_period = self._get_period(cr, uid, context) + for l in vals['line_id']: + if not l[0]: + l[2]['period_id'] = default_period + context['period_id'] = default_period + + if not 'name' in vals: + journal = self.pool.get('account.journal').browse(cr, uid, context.get('journal_id', vals.get('journal_id', False))) + if journal.sequence_id: + vals['name'] = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) + else: + raise osv.except_osv(_('Error'), _('No sequence defined in the journal !')) + if 'line_id' in vals: + c = context.copy() + c['novalidate'] = True + result = super(account_move, self).create(cr, uid, vals, c) + self.validate(cr, uid, [result], context) + else: + result = super(account_move, self).create(cr, uid, vals, context) + return result + + def unlink(self, cr, uid, ids, context={}, check=True): + toremove = [] + for move in self.browse(cr, uid, ids, context): + if move['state'] <> 'draft': + raise osv.except_osv(_('UserError'), + _('You can not delete posted movement: "%s"!') % \ + move['name']) + line_ids = map(lambda x: x.id, move.line_id) + context['journal_id'] = move.journal_id.id + context['period_id'] = move.period_id.id + self.pool.get('account.move.line')._update_check(cr, uid, line_ids, context) + toremove.append(move.id) + result = super(account_move, self).unlink(cr, uid, toremove, context) + return result + + def _compute_balance(self, cr, uid, id, context={}): + move = self.browse(cr, uid, [id])[0] + amount = 0 + for line in move.line_id: + amount+= (line.debit - line.credit) + return amount + + def _centralise(self, cr, uid, move, mode): + if mode=='credit': + account_id = move.journal_id.default_debit_account_id.id + mode2 = 'debit' + if not account_id: + raise osv.except_osv(_('UserError'), + _('There is no default default debit account defined \n' \ + 'on journal "%s"') % move.journal_id.name) + else: + account_id = move.journal_id.default_credit_account_id.id + mode2 = 'credit' + if not account_id: + raise osv.except_osv(_('UserError'), + _('There is no default default credit account defined \n' \ + 'on journal "%s"') % move.journal_id.name) + + # find the first line of this move with the current mode + # or create it if it doesn't exist + cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode)) + res = cr.fetchone() + if res: + line_id = res[0] + else: + line_id = self.pool.get('account.move.line').create(cr, uid, { + 'name': 'Centralisation '+mode, + 'centralisation': mode, + 'account_id': account_id, + 'move_id': move.id, + 'journal_id': move.journal_id.id, + 'period_id': move.period_id.id, + 'date': move.period_id.date_stop, + 'debit': 0.0, + 'credit': 0.0, + }, {'journal_id': move.journal_id.id, 'period_id': move.period_id.id}) + + # find the first line of this move with the other mode + # so that we can exclude it from our calculation + cr.execute('select id from account_move_line where move_id=%d and centralisation=%s limit 1', (move.id, mode2)) + res = cr.fetchone() + if res: + line_id2 = res[0] + else: + line_id2 = 0 + + cr.execute('select sum('+mode+') from account_move_line where move_id=%d and id<>%d', (move.id, line_id2)) + result = cr.fetchone()[0] or 0.0 + cr.execute('update account_move_line set '+mode2+'=%f where id=%d', (result, line_id)) + return True + + # + # Validate a balanced move. If it is a centralised journal, create a move. + # + def validate(self, cr, uid, ids, context={}): + ok = True + for move in self.browse(cr, uid, ids, context): + journal = move.journal_id + amount = 0 + line_ids = [] + line_draft_ids = [] + company_id=None + for line in move.line_id: + amount += line.debit - line.credit + line_ids.append(line.id) + if line.state=='draft': + line_draft_ids.append(line.id) + + if not company_id: + company_id = line.account_id.company_id.id + if not company_id == line.account_id.company_id.id: + raise osv.except_osv(_('Error'), _('Couldn\'t create move between different companies')) + + if line.account_id.currency_id: + if line.account_id.currency_id.id != line.currency_id.id and (line.account_id.currency_id.id != line.account_id.company_id.currency_id.id or line.currency_id): + raise osv.except_osv(_('Error'), _('Couldn\'t create move with currency different than the secondary currency of the account')) + + if abs(amount) < 0.0001: + if not len(line_draft_ids): + continue + self.pool.get('account.move.line').write(cr, uid, line_draft_ids, { + 'journal_id': move.journal_id.id, + 'period_id': move.period_id.id, + 'state': 'valid' + }, context, check=False) + todo = [] + account = {} + account2 = {} + if journal.type not in ('purchase','sale'): + continue + + for line in move.line_id: + if move.journal_id.type == 'sale': + if line.debit: + field_base = 'ref_' + key = 'account_paid_id' + else: + field_base = '' + key = 'account_collected_id' + else: + if line.debit: + field_base = '' + key = 'account_collected_id' + else: + field_base = 'ref_' + key = 'account_paid_id' + if line.account_id.tax_ids: + code = amount = False + for tax in line.account_id.tax_ids: + if tax.tax_code_id: + acc = getattr(tax, key).id + account[acc] = (getattr(tax, + field_base + 'tax_code_id').id, + getattr(tax, field_base + 'tax_sign')) + account2[(acc,getattr(tax, + field_base + 'tax_code_id').id)] = (getattr(tax, + field_base + 'tax_code_id').id, + getattr(tax, field_base + 'tax_sign')) + code = getattr(tax, field_base + 'base_code_id').id + amount = getattr(tax, field_base+'base_sign') * \ + (line.debit + line.credit) + break + if code and not (line.tax_code_id or line.tax_amount): + self.pool.get('account.move.line').write(cr, uid, + [line.id], { + 'tax_code_id': code, + 'tax_amount': amount + }, context=context, check=False) + else: + todo.append(line) + for line in todo: + code = amount = 0 + key = (line.account_id.id, line.tax_code_id.id) + if key in account2: + code = account2[key][0] + amount = account2[key][1] * (line.debit + line.credit) + elif line.account_id.id in account: + code = account[line.account_id.id][0] + amount = account[line.account_id.id][1] * (line.debit + line.credit) + if (code or amount) and not (line.tax_code_id or line.tax_amount): + self.pool.get('account.move.line').write(cr, uid, [line.id], { + 'tax_code_id': code, + 'tax_amount': amount + }, context, check=False) + # + # Compute VAT + # + continue + if journal.centralisation: + self._centralise(cr, uid, move, 'debit') + self._centralise(cr, uid, move, 'credit') + self.pool.get('account.move.line').write(cr, uid, line_draft_ids, { + 'state': 'valid' + }, context, check=False) + continue + else: + self.pool.get('account.move.line').write(cr, uid, line_ids, { + 'journal_id': move.journal_id.id, + 'period_id': move.period_id.id, + #'tax_code_id': False, + #'tax_amount': False, + 'state': 'draft' + }, context, check=False) + ok = False + return ok account_move() class account_move_reconcile(osv.osv): - _name = "account.move.reconcile" - _description = "Account Reconciliation" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'type': fields.char('Type', size=16, required=True), - 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'), - 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'), - 'create_date': fields.date('Creation date', readonly=True), - } - _defaults = { - 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/', - } - def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}): - for rec in self.pool.get('account.move.reconcile').browse(cr, uid, ids): - total = 0.0 - for line in rec.line_partial_ids: - total += (line.debit or 0.0) - (line.credit or 0.0) - if not total: - self.write(cr,uid, map(lambda x: x.id, rec.line_partial_ids), {'reconcile_id': rec.id }) - for line in rec.line_partial_ids: - total += (line.debit or 0.0) - (line.credit or 0.0) - return True - def name_get(self, cr, uid, ids, context=None): - result = {} - for r in self.browse(cr, uid, ids, context): - total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0) - if total: - result[r.id] = '%s (%.2f)' % (r.name, total) - else: - result[r.id] = r.name - return result + _name = "account.move.reconcile" + _description = "Account Reconciliation" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'type': fields.char('Type', size=16, required=True), + 'line_id': fields.one2many('account.move.line', 'reconcile_id', 'Entry lines'), + 'line_partial_ids': fields.one2many('account.move.line', 'reconcile_partial_id', 'Partial Entry lines'), + 'create_date': fields.date('Creation date', readonly=True), + } + _defaults = { + 'name': lambda self,cr,uid,ctx={}: self.pool.get('ir.sequence').get(cr, uid, 'account.reconcile') or '/', + } + def reconcile_partial_check(self, cr, uid, ids, type='auto', context={}): + for rec in self.pool.get('account.move.reconcile').browse(cr, uid, ids): + total = 0.0 + for line in rec.line_partial_ids: + total += (line.debit or 0.0) - (line.credit or 0.0) + if not total: + self.write(cr,uid, map(lambda x: x.id, rec.line_partial_ids), {'reconcile_id': rec.id }) + for line in rec.line_partial_ids: + total += (line.debit or 0.0) - (line.credit or 0.0) + return True + def name_get(self, cr, uid, ids, context=None): + result = {} + for r in self.browse(cr, uid, ids, context): + total = reduce(lambda y,t: (t.debit or 0.0) - (t.credit or 0.0) + y, r.line_partial_ids, 0.0) + if total: + result[r.id] = '%s (%.2f)' % (r.name, total) + else: + result[r.id] = r.name + return result account_move_reconcile() #---------------------------------------------------------- @@ -944,382 +944,382 @@ a documenter child_depend: la taxe depend des taxes filles """ class account_tax_code(osv.osv): - """ - A code for the tax object. - - This code is used for some tax declarations. - """ - def _sum(self, cr, uid, ids, name, args, context, where =''): - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - acc_set = ",".join(map(str, ids2)) - if context.get('based_on', 'invoices') == 'payments': - cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \ - FROM account_move_line AS line, \ - account_move AS move \ - LEFT JOIN account_invoice invoice ON \ - (invoice.move_id = move.id) \ - WHERE line.tax_code_id in ('+acc_set+') '+where+' \ - AND move.id = line.move_id \ - AND ((invoice.state = \'paid\') \ - OR (invoice.id IS NULL)) \ - GROUP BY line.tax_code_id') - else: - cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \ - FROM account_move_line AS line \ - WHERE line.tax_code_id in ('+acc_set+') '+where+' \ - GROUP BY line.tax_code_id') - res=dict(cr.fetchall()) - for record in self.browse(cr, uid, ids, context): - def _rec_get(record): - amount = res.get(record.id, 0.0) - for rec in record.child_ids: - amount += _rec_get(rec) * rec.sign - return amount - res[record.id] = round(_rec_get(record), 2) - return res - - def _sum_period(self, cr, uid, ids, name, args, context): - if not 'period_id' in context: - period_id = self.pool.get('account.period').find(cr, uid) - if not len(period_id): - return dict.fromkeys(ids, 0.0) - period_id = period_id[0] - else: - period_id = context['period_id'] - return self._sum(cr, uid, ids, name, args, context, - where=' and line.period_id='+str(period_id)) - - _name = 'account.tax.code' - _description = 'Tax Code' - _columns = { - 'name': fields.char('Tax Case Name', size=64, required=True), - 'code': fields.char('Case Code', size=16), - 'info': fields.text('Description'), - 'sum': fields.function(_sum, method=True, string="Year Sum"), - 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"), - 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True), - 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'), - 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'sign': fields.float('Sign for parent', required=True), - } - - def name_get(self, cr, uid, ids, context=None): - if not len(ids): - return [] - if isinstance(ids, (int, long)): - ids = [ids] - reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write') - return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \ - for x in reads] - - def _default_company(self, cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - return user.company_id.id - return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - _defaults = { - 'company_id': _default_company, - 'sign': lambda *args: 1.0, - } - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) - ] - _order = 'code,name' + """ + A code for the tax object. + + This code is used for some tax declarations. + """ + def _sum(self, cr, uid, ids, name, args, context, where =''): + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + if context.get('based_on', 'invoices') == 'payments': + cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \ + FROM account_move_line AS line, \ + account_move AS move \ + LEFT JOIN account_invoice invoice ON \ + (invoice.move_id = move.id) \ + WHERE line.tax_code_id in ('+acc_set+') '+where+' \ + AND move.id = line.move_id \ + AND ((invoice.state = \'paid\') \ + OR (invoice.id IS NULL)) \ + GROUP BY line.tax_code_id') + else: + cr.execute('SELECT line.tax_code_id, sum(line.tax_amount) \ + FROM account_move_line AS line \ + WHERE line.tax_code_id in ('+acc_set+') '+where+' \ + GROUP BY line.tax_code_id') + res=dict(cr.fetchall()) + for record in self.browse(cr, uid, ids, context): + def _rec_get(record): + amount = res.get(record.id, 0.0) + for rec in record.child_ids: + amount += _rec_get(rec) * rec.sign + return amount + res[record.id] = round(_rec_get(record), 2) + return res + + def _sum_period(self, cr, uid, ids, name, args, context): + if not 'period_id' in context: + period_id = self.pool.get('account.period').find(cr, uid) + if not len(period_id): + return dict.fromkeys(ids, 0.0) + period_id = period_id[0] + else: + period_id = context['period_id'] + return self._sum(cr, uid, ids, name, args, context, + where=' and line.period_id='+str(period_id)) + + _name = 'account.tax.code' + _description = 'Tax Code' + _columns = { + 'name': fields.char('Tax Case Name', size=64, required=True), + 'code': fields.char('Case Code', size=16), + 'info': fields.text('Description'), + 'sum': fields.function(_sum, method=True, string="Year Sum"), + 'sum_period': fields.function(_sum_period, method=True, string="Period Sum"), + 'parent_id': fields.many2one('account.tax.code', 'Parent Code', select=True), + 'child_ids': fields.one2many('account.tax.code', 'parent_id', 'Childs Codes'), + 'line_ids': fields.one2many('account.move.line', 'tax_code_id', 'Lines'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'sign': fields.float('Sign for parent', required=True), + } + + def name_get(self, cr, uid, ids, context=None): + if not len(ids): + return [] + if isinstance(ids, (int, long)): + ids = [ids] + reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write') + return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \ + for x in reads] + + def _default_company(self, cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.id + return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + _defaults = { + 'company_id': _default_company, + 'sign': lambda *args: 1.0, + } + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from account_tax_code where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) + ] + _order = 'code,name' account_tax_code() class account_tax(osv.osv): - """ - A tax object. - - Type: percent, fixed, none, code - PERCENT: tax = price * amount - FIXED: tax = price + amount - NONE: no tax line - CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object} - return result in the context - Ex: result=round(price_unit*0.21,4) - """ - _name = 'account.tax' - _description = 'Tax' - _columns = { - 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"), - 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the taxes lines from the lowest sequences to the higher ones. The order is important if you have a tax that have several tax childs. In this case, the evaluation order is important."), - 'amount': fields.float('Amount', required=True, digits=(14,4)), - 'active': fields.boolean('Active'), - 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True), - 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True), - 'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developpers to create specific taxes in a custom domain."), - 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'), - 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'), - 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True), - 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'), - 'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."), - 'python_compute':fields.text('Python Code'), - 'python_compute_inv':fields.text('Python Code (reverse)'), - 'python_applicable':fields.text('Python Code'), - 'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."), - - # - # Fields used for the VAT declaration - # - 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), - 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), - 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."), - 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."), - - # Same fields for refund invoices - - 'ref_base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), - 'ref_tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), - 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."), - 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."), - 'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes"), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'description': fields.char('Internal Name', 32), - } - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - res = [] - for record in self.read(cr, uid, ids, ['description','name'], context): - name = record['description'] and record['description'] or record['name'] - res.append((record['id'],name )) - return res - - def _default_company(self, cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - return user.company_id.id - return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - _defaults = { - 'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''', - 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''', - 'applicable_type': lambda *a: 'true', - 'type': lambda *a: 'percent', - 'amount': lambda *a: 0, - 'active': lambda *a: 1, - 'sequence': lambda *a: 1, - 'tax_group': lambda *a: 'vat', - 'ref_tax_sign': lambda *a: 1, - 'ref_base_sign': lambda *a: 1, - 'tax_sign': lambda *a: 1, - 'base_sign': lambda *a: 1, - 'include_base_amount': lambda *a: False, - 'company_id': _default_company, - } - _order = 'sequence' - - def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): - res = [] - for tax in taxes: - if tax.applicable_type=='code': - localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner} - exec tax.python_applicable in localdict - if localdict.get('result', False): - res.append(tax) - else: - res.append(tax) - return res - - def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): - taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner) - - res = [] - cur_price_unit=price_unit - for tax in taxes: - # we compute the amount for the current tax object and append it to the result - - if tax.type=='percent': - amount = cur_price_unit * tax.amount - res.append({'id':tax.id, - 'name':tax.name, - 'amount':amount, - 'account_collected_id':tax.account_collected_id.id, - 'account_paid_id':tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': cur_price_unit, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id, - }) - - elif tax.type=='fixed': - res.append({'id':tax.id, - 'name':tax.name, - 'amount':tax.amount, - 'account_collected_id':tax.account_collected_id.id, - 'account_paid_id':tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': 1, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id,}) - elif tax.type=='code': - address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None - localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner} - exec tax.python_compute in localdict - amount = localdict['result'] - res.append({ - 'id': tax.id, - 'name': tax.name, - 'amount': amount, - 'account_collected_id': tax.account_collected_id.id, - 'account_paid_id': tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': cur_price_unit, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id, - }) - amount2 = res[-1]['amount'] - if len(tax.child_ids): - if tax.child_depend: - del res[-1] - amount = amount2 - child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner) - res.extend(child_tax) - if tax.include_base_amount: - cur_price_unit+=amount2 - return res - - def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None): - - """ - Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID. - - RETURN: - [ tax ] - tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2} - one tax for each tax id in IDS and their childs - """ - res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner) - for r in res: - r['amount'] *= quantity - return res - - def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): - taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner) - - res = [] - taxes.reverse() - cur_price_unit=price_unit - for tax in taxes: - # we compute the amount for the current tax object and append it to the result - - if tax.type=='percent': - amount = cur_price_unit - (cur_price_unit / (1 + tax.amount)) - res.append({'id':tax.id, - 'name':tax.name, - 'amount':amount, - 'account_collected_id':tax.account_collected_id.id, - 'account_paid_id':tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': cur_price_unit - amount, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id,}) - - elif tax.type=='fixed': - res.append({'id':tax.id, - 'name':tax.name, - 'amount':tax.amount, - 'account_collected_id':tax.account_collected_id.id, - 'account_paid_id':tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': 1, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id,}) - - elif tax.type=='code': - address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None - localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner} - exec tax.python_compute_inv in localdict - amount = localdict['result'] - res.append({ - 'id': tax.id, - 'name': tax.name, - 'amount': amount, - 'account_collected_id': tax.account_collected_id.id, - 'account_paid_id': tax.account_paid_id.id, - 'base_code_id': tax.base_code_id.id, - 'ref_base_code_id': tax.ref_base_code_id.id, - 'sequence': tax.sequence, - 'base_sign': tax.base_sign, - 'tax_sign': tax.tax_sign, - 'ref_base_sign': tax.ref_base_sign, - 'ref_tax_sign': tax.ref_tax_sign, - 'price_unit': cur_price_unit - amount, - 'tax_code_id': tax.tax_code_id.id, - 'ref_tax_code_id': tax.ref_tax_code_id.id, - }) - - amount2 = res[-1]['amount'] - if len(tax.child_ids): - if tax.child_depend: - del res[-1] - amount = price_unit - else: - amount = amount2 - for t in tax.child_ids: - parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner) - res.extend(parent_tax) - if tax.include_base_amount: - cur_price_unit-=amount - taxes.reverse() - return res - - def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None): - """ - Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID. - Price Unit is a VAT included price - - RETURN: - [ tax ] - tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2} - one tax for each tax id in IDS and their childs - """ - res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None) - for r in res: - r['amount'] *= quantity - return res + """ + A tax object. + + Type: percent, fixed, none, code + PERCENT: tax = price * amount + FIXED: tax = price + amount + NONE: no tax line + CODE: execute python code. localcontext = {'price_unit':pu, 'address':address_object} + return result in the context + Ex: result=round(price_unit*0.21,4) + """ + _name = 'account.tax' + _description = 'Tax' + _columns = { + 'name': fields.char('Tax Name', size=64, required=True, help="This name will be used to be displayed on reports"), + 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the taxes lines from the lowest sequences to the higher ones. The order is important if you have a tax that have several tax childs. In this case, the evaluation order is important."), + 'amount': fields.float('Amount', required=True, digits=(14,4)), + 'active': fields.boolean('Active'), + 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True), + 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True), + 'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developpers to create specific taxes in a custom domain."), + 'account_collected_id':fields.many2one('account.account', 'Invoice Tax Account'), + 'account_paid_id':fields.many2one('account.account', 'Refund Tax Account'), + 'parent_id':fields.many2one('account.tax', 'Parent Tax Account', select=True), + 'child_ids':fields.one2many('account.tax', 'parent_id', 'Childs Tax Account'), + 'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."), + 'python_compute':fields.text('Python Code'), + 'python_compute_inv':fields.text('Python Code (reverse)'), + 'python_applicable':fields.text('Python Code'), + 'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."), + + # + # Fields used for the VAT declaration + # + 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), + 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), + 'base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."), + 'tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."), + + # Same fields for refund invoices + + 'ref_base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), + 'ref_tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), + 'ref_base_sign': fields.float('Base Code Sign', help="Usualy 1 or -1."), + 'ref_tax_sign': fields.float('Tax Code Sign', help="Usualy 1 or -1."), + 'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes"), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'description': fields.char('Internal Name', 32), + } + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + res = [] + for record in self.read(cr, uid, ids, ['description','name'], context): + name = record['description'] and record['description'] or record['name'] + res.append((record['id'],name )) + return res + + def _default_company(self, cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.id + return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + _defaults = { + 'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''', + 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''', + 'applicable_type': lambda *a: 'true', + 'type': lambda *a: 'percent', + 'amount': lambda *a: 0, + 'active': lambda *a: 1, + 'sequence': lambda *a: 1, + 'tax_group': lambda *a: 'vat', + 'ref_tax_sign': lambda *a: 1, + 'ref_base_sign': lambda *a: 1, + 'tax_sign': lambda *a: 1, + 'base_sign': lambda *a: 1, + 'include_base_amount': lambda *a: False, + 'company_id': _default_company, + } + _order = 'sequence' + + def _applicable(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): + res = [] + for tax in taxes: + if tax.applicable_type=='code': + localdict = {'price_unit':price_unit, 'address':self.pool.get('res.partner.address').browse(cr, uid, address_id), 'product':product, 'partner':partner} + exec tax.python_applicable in localdict + if localdict.get('result', False): + res.append(tax) + else: + res.append(tax) + return res + + def _unit_compute(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): + taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner) + + res = [] + cur_price_unit=price_unit + for tax in taxes: + # we compute the amount for the current tax object and append it to the result + + if tax.type=='percent': + amount = cur_price_unit * tax.amount + res.append({'id':tax.id, + 'name':tax.name, + 'amount':amount, + 'account_collected_id':tax.account_collected_id.id, + 'account_paid_id':tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': cur_price_unit, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id, + }) + + elif tax.type=='fixed': + res.append({'id':tax.id, + 'name':tax.name, + 'amount':tax.amount, + 'account_collected_id':tax.account_collected_id.id, + 'account_paid_id':tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': 1, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id,}) + elif tax.type=='code': + address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None + localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner} + exec tax.python_compute in localdict + amount = localdict['result'] + res.append({ + 'id': tax.id, + 'name': tax.name, + 'amount': amount, + 'account_collected_id': tax.account_collected_id.id, + 'account_paid_id': tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': cur_price_unit, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id, + }) + amount2 = res[-1]['amount'] + if len(tax.child_ids): + if tax.child_depend: + del res[-1] + amount = amount2 + child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, address_id, product, partner) + res.extend(child_tax) + if tax.include_base_amount: + cur_price_unit+=amount2 + return res + + def compute(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None): + + """ + Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID. + + RETURN: + [ tax ] + tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2} + one tax for each tax id in IDS and their childs + """ + res = self._unit_compute(cr, uid, taxes, price_unit, address_id, product, partner) + for r in res: + r['amount'] *= quantity + return res + + def _unit_compute_inv(self, cr, uid, taxes, price_unit, address_id=None, product=None, partner=None): + taxes = self._applicable(cr, uid, taxes, price_unit, address_id, product, partner) + + res = [] + taxes.reverse() + cur_price_unit=price_unit + for tax in taxes: + # we compute the amount for the current tax object and append it to the result + + if tax.type=='percent': + amount = cur_price_unit - (cur_price_unit / (1 + tax.amount)) + res.append({'id':tax.id, + 'name':tax.name, + 'amount':amount, + 'account_collected_id':tax.account_collected_id.id, + 'account_paid_id':tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': cur_price_unit - amount, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id,}) + + elif tax.type=='fixed': + res.append({'id':tax.id, + 'name':tax.name, + 'amount':tax.amount, + 'account_collected_id':tax.account_collected_id.id, + 'account_paid_id':tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': 1, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id,}) + + elif tax.type=='code': + address = address_id and self.pool.get('res.partner.address').browse(cr, uid, address_id) or None + localdict = {'price_unit':cur_price_unit, 'address':address, 'product':product, 'partner':partner} + exec tax.python_compute_inv in localdict + amount = localdict['result'] + res.append({ + 'id': tax.id, + 'name': tax.name, + 'amount': amount, + 'account_collected_id': tax.account_collected_id.id, + 'account_paid_id': tax.account_paid_id.id, + 'base_code_id': tax.base_code_id.id, + 'ref_base_code_id': tax.ref_base_code_id.id, + 'sequence': tax.sequence, + 'base_sign': tax.base_sign, + 'tax_sign': tax.tax_sign, + 'ref_base_sign': tax.ref_base_sign, + 'ref_tax_sign': tax.ref_tax_sign, + 'price_unit': cur_price_unit - amount, + 'tax_code_id': tax.tax_code_id.id, + 'ref_tax_code_id': tax.ref_tax_code_id.id, + }) + + amount2 = res[-1]['amount'] + if len(tax.child_ids): + if tax.child_depend: + del res[-1] + amount = price_unit + else: + amount = amount2 + for t in tax.child_ids: + parent_tax = self._unit_compute_inv(cr, uid, [t], amount, address_id, product, partner) + res.extend(parent_tax) + if tax.include_base_amount: + cur_price_unit-=amount + taxes.reverse() + return res + + def compute_inv(self, cr, uid, taxes, price_unit, quantity, address_id=None, product=None, partner=None): + """ + Compute tax values for given PRICE_UNIT, QUANTITY and a buyer/seller ADDRESS_ID. + Price Unit is a VAT included price + + RETURN: + [ tax ] + tax = {'name':'', 'amount':0.0, 'account_collected_id':1, 'account_paid_id':2} + one tax for each tax id in IDS and their childs + """ + res = self._unit_compute_inv(cr, uid, taxes, price_unit, address_id, product, partner=None) + for r in res: + r['amount'] *= quantity + return res account_tax() # --------------------------------------------------------- @@ -1327,41 +1327,41 @@ account_tax() # --------------------------------------------------------- class account_budget_post(osv.osv): - _name = 'account.budget.post' - _description = 'Budget item' - _columns = { - 'code': fields.char('Code', size=64, required=True), - 'name': fields.char('Name', size=256, required=True), - 'dotation_ids': fields.one2many('account.budget.post.dotation', 'post_id', 'Expenses'), - 'account_ids': fields.many2many('account.account', 'account_budget_rel', 'budget_id', 'account_id', 'Accounts'), - } - _defaults = { - } - - def spread(self, cr, uid, ids, fiscalyear_id=False, amount=0.0): - dobj = self.pool.get('account.budget.post.dotation') - for o in self.browse(cr, uid, ids): - # delete dotations for this post - dobj.unlink(cr, uid, dobj.search(cr, uid, [('post_id','=',o.id)])) - - # create one dotation per period in the fiscal year, and spread the total amount/quantity over those dotations - fy = self.pool.get('account.fiscalyear').browse(cr, uid, [fiscalyear_id])[0] - num = len(fy.period_ids) - for p in fy.period_ids: - dobj.create(cr, uid, {'post_id': o.id, 'period_id': p.id, 'amount': amount/num}) - return True + _name = 'account.budget.post' + _description = 'Budget item' + _columns = { + 'code': fields.char('Code', size=64, required=True), + 'name': fields.char('Name', size=256, required=True), + 'dotation_ids': fields.one2many('account.budget.post.dotation', 'post_id', 'Expenses'), + 'account_ids': fields.many2many('account.account', 'account_budget_rel', 'budget_id', 'account_id', 'Accounts'), + } + _defaults = { + } + + def spread(self, cr, uid, ids, fiscalyear_id=False, amount=0.0): + dobj = self.pool.get('account.budget.post.dotation') + for o in self.browse(cr, uid, ids): + # delete dotations for this post + dobj.unlink(cr, uid, dobj.search(cr, uid, [('post_id','=',o.id)])) + + # create one dotation per period in the fiscal year, and spread the total amount/quantity over those dotations + fy = self.pool.get('account.fiscalyear').browse(cr, uid, [fiscalyear_id])[0] + num = len(fy.period_ids) + for p in fy.period_ids: + dobj.create(cr, uid, {'post_id': o.id, 'period_id': p.id, 'amount': amount/num}) + return True account_budget_post() class account_budget_post_dotation(osv.osv): - _name = 'account.budget.post.dotation' - _description = "Budget item endowment" - _columns = { - 'name': fields.char('Name', size=64), - 'post_id': fields.many2one('account.budget.post', 'Item', select=True), - 'period_id': fields.many2one('account.period', 'Period'), -# 'quantity': fields.float('Quantity', digits=(16,2)), - 'amount': fields.float('Amount', digits=(16,2)), - } + _name = 'account.budget.post.dotation' + _description = "Budget item endowment" + _columns = { + 'name': fields.char('Name', size=64), + 'post_id': fields.many2one('account.budget.post', 'Item', select=True), + 'period_id': fields.many2one('account.period', 'Period'), +# 'quantity': fields.float('Quantity', digits=(16,2)), + 'amount': fields.float('Amount', digits=(16,2)), + } account_budget_post_dotation() @@ -1370,53 +1370,53 @@ account_budget_post_dotation() # --------------------------------------------------------- class account_model(osv.osv): - _name = "account.model" - _description = "Account Model" - _columns = { - 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"), - 'ref': fields.char('Ref', size=64), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True), - 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'), - 'legend' :fields.text('Legend',readonly=True,size=100), - } - - _defaults = { - 'legend':lambda *a:'''You can specify year, month and date in the name of the model using the following labels:\n\n%(year)s : To Specify Year \n%(month)s : To Specify Month \n%(date)s : Current Date\n\ne.g. My model on %(date)s''', - } + _name = "account.model" + _description = "Account Model" + _columns = { + 'name': fields.char('Model Name', size=64, required=True, help="This is a model for recurring accounting entries"), + 'ref': fields.char('Ref', size=64), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True), + 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'), + 'legend' :fields.text('Legend',readonly=True,size=100), + } + + _defaults = { + 'legend':lambda *a:'''You can specify year, month and date in the name of the model using the following labels:\n\n%(year)s : To Specify Year \n%(month)s : To Specify Month \n%(date)s : Current Date\n\ne.g. My model on %(date)s''', + } account_model() class account_model_line(osv.osv): - _name = "account.model.line" - _description = "Account Model Entries" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"), - 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"), - 'debit': fields.float('Debit', digits=(16,2)), - 'credit': fields.float('Credit', digits=(16,2)), - - 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"), - - 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True), - - 'ref': fields.char('Ref.', size=16), - - 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."), - 'currency_id': fields.many2one('res.currency', 'Currency'), - - 'partner_id': fields.many2one('res.partner', 'Partner Ref.'), - 'date_maturity': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Maturity date', help="The maturity date of the generated entries for this model. You can chosse between the date of the creation action or the the date of the creation of the entries plus the partner payment terms."), - 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"), - } - _defaults = { - 'date': lambda *a: 'today' - } - _order = 'sequence' - _sql_constraints = [ - ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'), - ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'), - ] + _name = "account.model.line" + _description = "Account Model Entries" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the resources from the lowest sequences to the higher ones"), + 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity on entries"), + 'debit': fields.float('Debit', digits=(16,2)), + 'credit': fields.float('Credit', digits=(16,2)), + + 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"), + + 'model_id': fields.many2one('account.model', 'Model', required=True, ondelete="cascade", select=True), + + 'ref': fields.char('Ref.', size=16), + + 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency."), + 'currency_id': fields.many2one('res.currency', 'Currency'), + + 'partner_id': fields.many2one('res.partner', 'Partner Ref.'), + 'date_maturity': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Maturity date', help="The maturity date of the generated entries for this model. You can chosse between the date of the creation action or the the date of the creation of the entries plus the partner payment terms."), + 'date': fields.selection([('today','Date of the day'), ('partner','Partner Payment Term')], 'Current Date', required=True, help="The date of the generated entries"), + } + _defaults = { + 'date': lambda *a: 'today' + } + _order = 'sequence' + _sql_constraints = [ + ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model !'), + ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model !'), + ] account_model_line() # --------------------------------------------------------- @@ -1425,348 +1425,348 @@ account_model_line() class account_subscription(osv.osv): - _name = "account.subscription" - _description = "Account Subscription" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'ref': fields.char('Ref.', size=16), - 'model_id': fields.many2one('account.model', 'Model', required=True), - - 'date_start': fields.date('Starting date', required=True), - 'period_total': fields.integer('Number of period', required=True), - 'period_nbr': fields.integer('Period', required=True), - 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True), - 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'State', required=True, readonly=True), - - 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines') - } - _defaults = { - 'date_start': lambda *a: time.strftime('%Y-%m-%d'), - 'period_type': lambda *a: 'month', - 'period_total': lambda *a: 12, - 'period_nbr': lambda *a: 1, - 'state': lambda *a: 'draft', - } - def state_draft(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state':'draft'}) - return False - - def check(self, cr, uid, ids, context={}): - todone = [] - for sub in self.browse(cr, uid, ids, context): - ok = True - for line in sub.lines_id: - if not line.move_id.id: - ok = False - break - if ok: - todone.append(sub.id) - if len(todone): - self.write(cr, uid, todone, {'state':'done'}) - return False - - def remove_line(self, cr, uid, ids, context={}): - toremove = [] - for sub in self.browse(cr, uid, ids, context): - for line in sub.lines_id: - if not line.move_id.id: - toremove.append(line.id) - if len(toremove): - self.pool.get('account.subscription.line').unlink(cr, uid, toremove) - self.write(cr, uid, ids, {'state':'draft'}) - return False - - def compute(self, cr, uid, ids, context={}): - for sub in self.browse(cr, uid, ids, context): - ds = sub.date_start - for i in range(sub.period_total): - self.pool.get('account.subscription.line').create(cr, uid, { - 'date': ds, - 'subscription_id': sub.id, - }) - if sub.period_type=='day': - ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d') - if sub.period_type=='month': - ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d') - if sub.period_type=='year': - ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d') - self.write(cr, uid, ids, {'state':'running'}) - return True + _name = "account.subscription" + _description = "Account Subscription" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'ref': fields.char('Ref.', size=16), + 'model_id': fields.many2one('account.model', 'Model', required=True), + + 'date_start': fields.date('Starting date', required=True), + 'period_total': fields.integer('Number of period', required=True), + 'period_nbr': fields.integer('Period', required=True), + 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True), + 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'State', required=True, readonly=True), + + 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines') + } + _defaults = { + 'date_start': lambda *a: time.strftime('%Y-%m-%d'), + 'period_type': lambda *a: 'month', + 'period_total': lambda *a: 12, + 'period_nbr': lambda *a: 1, + 'state': lambda *a: 'draft', + } + def state_draft(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state':'draft'}) + return False + + def check(self, cr, uid, ids, context={}): + todone = [] + for sub in self.browse(cr, uid, ids, context): + ok = True + for line in sub.lines_id: + if not line.move_id.id: + ok = False + break + if ok: + todone.append(sub.id) + if len(todone): + self.write(cr, uid, todone, {'state':'done'}) + return False + + def remove_line(self, cr, uid, ids, context={}): + toremove = [] + for sub in self.browse(cr, uid, ids, context): + for line in sub.lines_id: + if not line.move_id.id: + toremove.append(line.id) + if len(toremove): + self.pool.get('account.subscription.line').unlink(cr, uid, toremove) + self.write(cr, uid, ids, {'state':'draft'}) + return False + + def compute(self, cr, uid, ids, context={}): + for sub in self.browse(cr, uid, ids, context): + ds = sub.date_start + for i in range(sub.period_total): + self.pool.get('account.subscription.line').create(cr, uid, { + 'date': ds, + 'subscription_id': sub.id, + }) + if sub.period_type=='day': + ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(days=sub.period_nbr)).strftime('%Y-%m-%d') + if sub.period_type=='month': + ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(months=sub.period_nbr)).strftime('%Y-%m-%d') + if sub.period_type=='year': + ds = (mx.DateTime.strptime(ds, '%Y-%m-%d') + RelativeDateTime(years=sub.period_nbr)).strftime('%Y-%m-%d') + self.write(cr, uid, ids, {'state':'running'}) + return True account_subscription() class account_subscription_line(osv.osv): - _name = "account.subscription.line" - _description = "Account Subscription Line" - _columns = { - 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True), - 'date': fields.date('Date', required=True), - 'move_id': fields.many2one('account.move', 'Entry'), - } - _defaults = { - } - def move_create(self, cr, uid, ids, context={}): - tocheck = {} - for line in self.browse(cr, uid, ids, context): - datas = { - 'date': line.date, - } - ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context) - tocheck[line.subscription_id.id] = True - self.write(cr, uid, [line.id], {'move_id':ids[0]}) - if tocheck: - self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context) - return True - _rec_name = 'date' + _name = "account.subscription.line" + _description = "Account Subscription Line" + _columns = { + 'subscription_id': fields.many2one('account.subscription', 'Subscription', required=True, select=True), + 'date': fields.date('Date', required=True), + 'move_id': fields.many2one('account.move', 'Entry'), + } + _defaults = { + } + def move_create(self, cr, uid, ids, context={}): + tocheck = {} + for line in self.browse(cr, uid, ids, context): + datas = { + 'date': line.date, + } + ids = self.pool.get('account.model').generate(cr, uid, [line.subscription_id.model_id.id], datas, context) + tocheck[line.subscription_id.id] = True + self.write(cr, uid, [line.id], {'move_id':ids[0]}) + if tocheck: + self.pool.get('account.subscription').check(cr, uid, tocheck.keys(), context) + return True + _rec_name = 'date' account_subscription_line() class account_config_fiscalyear(osv.osv_memory): - _name = 'account.config.fiscalyear' - _columns = { - 'name':fields.char('Name', required=True,size=64), - 'code':fields.char('Code', required=True,size=64), - 'date1': fields.date('Starting Date', required=True), - 'date2': fields.date('Ending Date', required=True), - } - _defaults = { - 'code': lambda *a: time.strftime('%Y'), - 'date1': lambda *a: time.strftime('%Y-01-01'), - 'date2': lambda *a: time.strftime('%Y-12-31'), - } - def action_cancel(self,cr,uid,ids,conect=None): - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } - def action_create(self, cr, uid,ids, context=None): - res=self.read(cr,uid,ids)[0] - if 'date1' in res and 'date2' in res: - res_obj = self.pool.get('account.fiscalyear') - start_date=res['date1'] - end_date=res['date2'] - name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y') - vals={ - 'name':name, - 'code':name, - 'date_start':start_date, - 'date_stop':end_date, - } - new_id=res_obj.create(cr, uid, vals, context=context) - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } + _name = 'account.config.fiscalyear' + _columns = { + 'name':fields.char('Name', required=True,size=64), + 'code':fields.char('Code', required=True,size=64), + 'date1': fields.date('Starting Date', required=True), + 'date2': fields.date('Ending Date', required=True), + } + _defaults = { + 'code': lambda *a: time.strftime('%Y'), + 'date1': lambda *a: time.strftime('%Y-01-01'), + 'date2': lambda *a: time.strftime('%Y-12-31'), + } + def action_cancel(self,cr,uid,ids,conect=None): + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } + def action_create(self, cr, uid,ids, context=None): + res=self.read(cr,uid,ids)[0] + if 'date1' in res and 'date2' in res: + res_obj = self.pool.get('account.fiscalyear') + start_date=res['date1'] + end_date=res['date2'] + name=res['name']#DateTime.strptime(start_date, '%Y-%m-%d').strftime('%m.%Y') + '-' + DateTime.strptime(end_date, '%Y-%m-%d').strftime('%m.%Y') + vals={ + 'name':name, + 'code':name, + 'date_start':start_date, + 'date_stop':end_date, + } + new_id=res_obj.create(cr, uid, vals, context=context) + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } account_config_fiscalyear() class account_config_journal_bank_accounts(osv.osv_memory): - _name='account.config.journal.bank.account' - _columns = { - 'name':fields.char('Journal Name', size=64), - 'lines_id': fields.one2many('account.config.journal.bank.account.line', 'journal_id', 'Journal Lines'), - } - - def action_cancel(self,cr,uid,ids,conect=None): - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } - - def action_create(self, cr, uid, ids, context=None): - config_res=self.read(cr,uid,ids)[0] - res_obj = self.pool.get('account.journal') - line_obj=self.pool.get('account.config.journal.bank.account.line') - if 'lines_id' in config_res and config_res['lines_id']: - lines=line_obj.read(cr,uid,config_res['lines_id']) - for res in lines: - sequence_ids=self.pool.get('ir.sequence').search(cr,uid,[('name','=','Account Journal')]) - if 'name' in res and 'bank_account_id' in res and 'view_id' in res and sequence_ids and len(sequence_ids): - vals={ - 'name':res['name'], - 'type':'cash', - 'view_id':res['view_id'], - 'default_credit_account_id':res['bank_account_id'], - 'default_debit_account_id':res['bank_account_id'], - 'sequence_id':sequence_ids[0] - } - res_obj.create(cr, uid, vals, context=context) - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } + _name='account.config.journal.bank.account' + _columns = { + 'name':fields.char('Journal Name', size=64), + 'lines_id': fields.one2many('account.config.journal.bank.account.line', 'journal_id', 'Journal Lines'), + } + + def action_cancel(self,cr,uid,ids,conect=None): + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } + + def action_create(self, cr, uid, ids, context=None): + config_res=self.read(cr,uid,ids)[0] + res_obj = self.pool.get('account.journal') + line_obj=self.pool.get('account.config.journal.bank.account.line') + if 'lines_id' in config_res and config_res['lines_id']: + lines=line_obj.read(cr,uid,config_res['lines_id']) + for res in lines: + sequence_ids=self.pool.get('ir.sequence').search(cr,uid,[('name','=','Account Journal')]) + if 'name' in res and 'bank_account_id' in res and 'view_id' in res and sequence_ids and len(sequence_ids): + vals={ + 'name':res['name'], + 'type':'cash', + 'view_id':res['view_id'], + 'default_credit_account_id':res['bank_account_id'], + 'default_debit_account_id':res['bank_account_id'], + 'sequence_id':sequence_ids[0] + } + res_obj.create(cr, uid, vals, context=context) + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } account_config_journal_bank_accounts() class account_config_journal_bank_accounts_line(osv.osv_memory): - _name='account.config.journal.bank.account.line' - def _journal_view_get(self, cr, uid, context={}): - journal_obj = self.pool.get('account.journal.view') - ids = journal_obj.search(cr, uid, []) - res = journal_obj.read(cr, uid, ids, ['id', 'name'], context) - return [(r['id'], r['name']) for r in res] - _columns = { - 'name':fields.char('Journal Name', size=64,required=True), - 'bank_account_id':fields.many2one('account.account', 'Bank Account', required=True, domain=[('type','=','cash')]), - 'view_id':fields.selection(_journal_view_get, 'Journal View', required=True), - 'journal_id':fields.many2one('account.config.journal.bank.account', 'Journal', required=True), - } + _name='account.config.journal.bank.account.line' + def _journal_view_get(self, cr, uid, context={}): + journal_obj = self.pool.get('account.journal.view') + ids = journal_obj.search(cr, uid, []) + res = journal_obj.read(cr, uid, ids, ['id', 'name'], context) + return [(r['id'], r['name']) for r in res] + _columns = { + 'name':fields.char('Journal Name', size=64,required=True), + 'bank_account_id':fields.many2one('account.account', 'Bank Account', required=True, domain=[('type','=','cash')]), + 'view_id':fields.selection(_journal_view_get, 'Journal View', required=True), + 'journal_id':fields.many2one('account.config.journal.bank.account', 'Journal', required=True), + } account_config_journal_bank_accounts_line() # ---------------------------------------------- -# Account Templates : Account, Tax and charts. +# Account Templates : Account, Tax and charts. # ---------------------------------------------- class account_tax_template(osv.osv): - _name = 'account.tax.template' + _name = 'account.tax.template' account_tax() class account_account_template(osv.osv): - _name='account.account.template' - _description ='Templates for Accounts' - _order = "code" - _columns = { - 'name': fields.char('Name', size=128, required=True, select=True), - 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign', required=True, help='Allows to change the displayed amount of the balance to see positive results instead of negative ones in expenses accounts.'), - 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."), - 'code': fields.char('Code', size=64), - 'type': fields.selection(_code_get, 'Account Type', required=True), - 'reconcile': fields.boolean('Reconcile', help="Check this option if the user can make a reconciliation of the entries in this account."), - 'shortcut': fields.char('Shortcut', size=12), - 'note': fields.text('Note'), - } - - _defaults = { - 'sign': lambda *a: 1, - 'reconcile': lambda *a: False, - 'type' : lambda *a :'view', - } - - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from account_account_template_rel where child_id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) - ] - - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','code'], context) - res = [] - for record in reads: - name = record['name'] - if record['code']: - name = record['code']+' - '+name - res.append((record['id'],name )) - return res + _name='account.account.template' + _description ='Templates for Accounts' + _order = "code" + _columns = { + 'name': fields.char('Name', size=128, required=True, select=True), + 'sign': fields.selection([(-1, 'Negative'), (1, 'Positive')], 'Sign', required=True, help='Allows to change the displayed amount of the balance to see positive results instead of negative ones in expenses accounts.'), + 'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Force all moves for this account to have this secondary currency."), + 'code': fields.char('Code', size=64), + 'type': fields.selection(_code_get, 'Account Type', required=True), + 'reconcile': fields.boolean('Reconcile', help="Check this option if the user can make a reconciliation of the entries in this account."), + 'shortcut': fields.char('Shortcut', size=12), + 'note': fields.text('Note'), + } + + _defaults = { + 'sign': lambda *a: 1, + 'reconcile': lambda *a: False, + 'type' : lambda *a :'view', + } + + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from account_account_template_rel where child_id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive accounts.', ['parent_id']) + ] + + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','code'], context) + res = [] + for record in reads: + name = record['name'] + if record['code']: + name = record['code']+' - '+name + res.append((record['id'],name )) + return res account_account_template() class account_tax_template(osv.osv): - _name = 'account.tax.template' - _description = 'Templates for Taxes' - - _columns = { - 'name': fields.char('Tax Name', size=64, required=True), - 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the taxes lines from the lowest sequences to the higher ones. The order is important if you have a tax that have several tax children. In this case, the evaluation order is important."), -# 'amount': fields.float('Amount', required=True, digits=(14,4)), -# 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True), - 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None')],'Tax Type', required=True), -# 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True), - 'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developers to create specific taxes in a custom domain."), -# 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'), -# 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'), -# 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True), -# 'child_ids':fields.one2many('account.tax.template', 'parent_id', 'Childs Tax Account'), -# 'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."), -# 'python_compute':fields.text('Python Code'), -# 'python_compute_inv':fields.text('Python Code (reverse)'), -# 'python_applicable':fields.text('Python Code'), - 'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."), - - # - # Fields used for the VAT declaration - # -# 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), -# 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), -# 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), -# 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), - - # Same fields for refund invoices - -# 'ref_base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), -# 'ref_tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), -# 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), -# 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), -# 'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes."), - 'description': fields.char('Internal Name', size=32), - } - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - res = [] - for record in self.read(cr, uid, ids, ['description','name'], context): - name = record['description'] and record['description'] or record['name'] - res.append((record['id'],name )) - return res - - _defaults = { -# 'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''', -# 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''', -# 'applicable_type': lambda *a: 'true', - 'type': lambda *a: 'percent', -# 'amount': lambda *a: 0, - 'sequence': lambda *a: 1, - 'tax_group': lambda *a: 'vat', -# 'ref_tax_sign': lambda *a: 1, -# 'ref_base_sign': lambda *a: 1, -# 'tax_sign': lambda *a: 1, -# 'base_sign': lambda *a: 1, -# 'include_base_amount': lambda *a: False, -# 'company_id': _default_company, - } - _order = 'sequence' + _name = 'account.tax.template' + _description = 'Templates for Taxes' + + _columns = { + 'name': fields.char('Tax Name', size=64, required=True), + 'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the taxes lines from the lowest sequences to the higher ones. The order is important if you have a tax that have several tax children. In this case, the evaluation order is important."), +# 'amount': fields.float('Amount', required=True, digits=(14,4)), +# 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None'), ('code','Python Code')], 'Tax Type', required=True), + 'type': fields.selection( [('percent','Percent'), ('fixed','Fixed'), ('none','None')],'Tax Type', required=True), +# 'applicable_type': fields.selection( [('true','True'), ('code','Python Code')], 'Applicable Type', required=True), + 'domain':fields.char('Domain', size=32, help="This field is only used if you develop your own module allowing developers to create specific taxes in a custom domain."), +# 'account_collected_id':fields.many2one('account.account.template', 'Invoice Tax Account'), +# 'account_paid_id':fields.many2one('account.account.template', 'Refund Tax Account'), +# 'parent_id':fields.many2one('account.tax.template', 'Parent Tax Account', select=True), +# 'child_ids':fields.one2many('account.tax.template', 'parent_id', 'Childs Tax Account'), +# 'child_depend':fields.boolean('Tax on Childs', help="Indicate if the tax computation is based on the value computed for the computation of child taxes or based on the total amount."), +# 'python_compute':fields.text('Python Code'), +# 'python_compute_inv':fields.text('Python Code (reverse)'), +# 'python_applicable':fields.text('Python Code'), + 'tax_group': fields.selection([('vat','VAT'),('other','Other')], 'Tax Group', help="If a default tax if given in the partner it only override taxes from account (or product) of the same group."), + + # + # Fields used for the VAT declaration + # +# 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), +# 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), +# 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), +# 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), + + # Same fields for refund invoices + +# 'ref_base_code_id': fields.many2one('account.tax.code', 'Base Code', help="Use this code for the VAT declaration."), +# 'ref_tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="Use this code for the VAT declaration."), +# 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), +# 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), +# 'include_base_amount': fields.boolean('Include in base amount', help="Indicate if the amount of tax must be included in the base amount for the computation of the next taxes."), + 'description': fields.char('Internal Name', size=32), + } + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + res = [] + for record in self.read(cr, uid, ids, ['description','name'], context): + name = record['description'] and record['description'] or record['name'] + res.append((record['id'],name )) + return res + + _defaults = { +# 'python_compute': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or None\n# partner : res.partner object or None\n\nresult = price_unit * 0.10''', +# 'python_compute_inv': lambda *a: '''# price_unit\n# address : res.partner.address object or False\n# product : product.product object or False\n\nresult = price_unit * 0.10''', +# 'applicable_type': lambda *a: 'true', + 'type': lambda *a: 'percent', +# 'amount': lambda *a: 0, + 'sequence': lambda *a: 1, + 'tax_group': lambda *a: 'vat', +# 'ref_tax_sign': lambda *a: 1, +# 'ref_base_sign': lambda *a: 1, +# 'tax_sign': lambda *a: 1, +# 'base_sign': lambda *a: 1, +# 'include_base_amount': lambda *a: False, +# 'company_id': _default_company, + } + _order = 'sequence' account_tax_template() class account_chart_template(osv.osv): - _name="account.chart.template" - _description= "Templates for Account Chart" - - _columns={ - 'name': fields.char('Name', size=64, required=True), - 'account_root_id': fields.many2one('account.account.template','Root Account',required=True), - 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True), - 'property_receivable_id': fields.many2one('account.account.template','Receivable Account'), - 'property_payable_id': fields.many2one('account.account.template','Payable Account'), - 'property_account_expense_categ_id': fields.many2one('account.account.template','Expense Category Account'), - 'property_account_income_categ_id': fields.many2one('account.account.template','Income Category Account'), - } + _name="account.chart.template" + _description= "Templates for Account Chart" + + _columns={ + 'name': fields.char('Name', size=64, required=True), + 'account_root_id': fields.many2one('account.account.template','Root Account',required=True), + 'bank_account_view_id': fields.many2one('account.account.template','Bank Account',required=True), + 'property_receivable_id': fields.many2one('account.account.template','Receivable Account'), + 'property_payable_id': fields.many2one('account.account.template','Payable Account'), + 'property_account_expense_categ_id': fields.many2one('account.account.template','Expense Category Account'), + 'property_account_income_categ_id': fields.many2one('account.account.template','Income Category Account'), + } account_chart_template() diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 3dd06187bf2..c3cd2c6aa5f 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -35,97 +35,97 @@ from osv import osv from tools.translate import _ class account_analytic_line(osv.osv): - _name = 'account.analytic.line' - _description = 'Analytic lines' - _columns = { - 'name' : fields.char('Description', size=256, required=True), - 'date' : fields.date('Date', required=True), - 'amount' : fields.float('Amount', required=True), - 'unit_amount' : fields.float('Quantity'), - 'product_uom_id' : fields.many2one('product.uom', 'UoM'), - 'product_id' : fields.many2one('product.product', 'Product'), - 'account_id' : fields.many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='cascade', select=True), - 'general_account_id' : fields.many2one('account.account', 'General account', required=True, ondelete='cascade'), - 'move_id' : fields.many2one('account.move.line', 'General entry', ondelete='cascade', select=True), - 'journal_id' : fields.many2one('account.analytic.journal', 'Analytic journal', required=True, ondelete='cascade', select=True), - 'code' : fields.char('Code', size=8), - 'user_id' : fields.many2one('res.users', 'User',), - 'ref': fields.char('Ref.', size=32), - } - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d'), - } - _order = 'date' - def _check_company(self, cr, uid, ids): - lines = self.browse(cr, uid, ids) - for l in lines: - if l.move_id and not l.account_id.company_id.id == l.move_id.account_id.company_id.id: - return False - return True - _constraints = [ - (_check_company, 'You can not create analytic line that is not in the same company than the account line', ['account_id']) - ] - - def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, - unit=False, context=None): - uom_obj = self.pool.get('product.uom') - product_obj = self.pool.get('product.product') - if unit_amount and prod_id: - prod = product_obj.browse(cr, uid, prod_id) - a = prod.product_tmpl_id.property_account_expense.id - if not a: - a = prod.categ_id.property_account_expense_categ.id - if not a: - raise osv.except_osv(_('Error !'), - _('There is no expense account define ' \ - 'for this product: "%s" (id:%d)') % \ - (prod.name, prod.id,)) - amount = unit_amount * uom_obj._compute_price(cr, uid, - prod.uom_id.id, prod.standard_price, unit) - return {'value': { - 'amount': - round(amount, 2), - 'general_account_id': a, - }} - return {} + _name = 'account.analytic.line' + _description = 'Analytic lines' + _columns = { + 'name' : fields.char('Description', size=256, required=True), + 'date' : fields.date('Date', required=True), + 'amount' : fields.float('Amount', required=True), + 'unit_amount' : fields.float('Quantity'), + 'product_uom_id' : fields.many2one('product.uom', 'UoM'), + 'product_id' : fields.many2one('product.product', 'Product'), + 'account_id' : fields.many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='cascade', select=True), + 'general_account_id' : fields.many2one('account.account', 'General account', required=True, ondelete='cascade'), + 'move_id' : fields.many2one('account.move.line', 'General entry', ondelete='cascade', select=True), + 'journal_id' : fields.many2one('account.analytic.journal', 'Analytic journal', required=True, ondelete='cascade', select=True), + 'code' : fields.char('Code', size=8), + 'user_id' : fields.many2one('res.users', 'User',), + 'ref': fields.char('Ref.', size=32), + } + _defaults = { + 'date': lambda *a: time.strftime('%Y-%m-%d'), + } + _order = 'date' + def _check_company(self, cr, uid, ids): + lines = self.browse(cr, uid, ids) + for l in lines: + if l.move_id and not l.account_id.company_id.id == l.move_id.account_id.company_id.id: + return False + return True + _constraints = [ + (_check_company, 'You can not create analytic line that is not in the same company than the account line', ['account_id']) + ] + + def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, + unit=False, context=None): + uom_obj = self.pool.get('product.uom') + product_obj = self.pool.get('product.product') + if unit_amount and prod_id: + prod = product_obj.browse(cr, uid, prod_id) + a = prod.product_tmpl_id.property_account_expense.id + if not a: + a = prod.categ_id.property_account_expense_categ.id + if not a: + raise osv.except_osv(_('Error !'), + _('There is no expense account define ' \ + 'for this product: "%s" (id:%d)') % \ + (prod.name, prod.id,)) + amount = unit_amount * uom_obj._compute_price(cr, uid, + prod.uom_id.id, prod.standard_price, unit) + return {'value': { + 'amount': - round(amount, 2), + 'general_account_id': a, + }} + return {} account_analytic_line() class timesheet_invoice(osv.osv): - _name = "report.hr.timesheet.invoice.journal" - _description = "Analytic account costs and revenues" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True, select=True), - 'journal_id': fields.many2one('account.analytic.journal', 'Journal', readonly=True), - 'quantity': fields.float('Quantities', readonly=True), - 'cost': fields.float('Credit', readonly=True), - 'revenue': fields.float('Debit', readonly=True) - } - _order = 'name desc, account_id' - def init(self, cr): - cr.execute(""" - create or replace view report_hr_timesheet_invoice_journal as ( - select - min(l.id) as id, - date_trunc('month', l.date)::date as name, - sum( - CASE WHEN l.amount>0 THEN 0 ELSE l.amount - END - ) as cost, - sum( - CASE WHEN l.amount>0 THEN l.amount ELSE 0 - END - ) as revenue, - sum(l.unit_amount* COALESCE(u.factor, 1)) as quantity, - journal_id, - account_id - from account_analytic_line l - LEFT OUTER join product_uom u on (u.id=l.product_uom_id) - group by - date_trunc('month', l.date), - journal_id, - account_id - )""") + _name = "report.hr.timesheet.invoice.journal" + _description = "Analytic account costs and revenues" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True, select=True), + 'journal_id': fields.many2one('account.analytic.journal', 'Journal', readonly=True), + 'quantity': fields.float('Quantities', readonly=True), + 'cost': fields.float('Credit', readonly=True), + 'revenue': fields.float('Debit', readonly=True) + } + _order = 'name desc, account_id' + def init(self, cr): + cr.execute(""" + create or replace view report_hr_timesheet_invoice_journal as ( + select + min(l.id) as id, + date_trunc('month', l.date)::date as name, + sum( + CASE WHEN l.amount>0 THEN 0 ELSE l.amount + END + ) as cost, + sum( + CASE WHEN l.amount>0 THEN l.amount ELSE 0 + END + ) as revenue, + sum(l.unit_amount* COALESCE(u.factor, 1)) as quantity, + journal_id, + account_id + from account_analytic_line l + LEFT OUTER join product_uom u on (u.id=l.product_uom_id) + group by + date_trunc('month', l.date), + journal_id, + account_id + )""") timesheet_invoice() diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 3f50255c416..ad01d223fa5 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -39,545 +39,545 @@ from mx.DateTime import RelativeDateTime, now, DateTime, localtime class account_bank_statement(osv.osv): - def _default_journal_id(self, cr, uid, context={}): - if context.get('journal_id', False): - return context['journal_id'] - return False - - def _default_balance_start(self, cr, uid, context={}): - cr.execute('select id from account_bank_statement where journal_id=%d order by date desc limit 1', (1,)) - res = cr.fetchone() - if res: - return self.browse(cr, uid, [res[0]], context)[0].balance_end - return 0.0 - - def _end_balance(self, cursor, user, ids, name, attr, context=None): - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - res = {} - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - statements = self.browse(cursor, user, ids, context=context) - for statement in statements: - res[statement.id] = statement.balance_start - currency_id = statement.currency.id - for line in statement.move_line_ids: - if line.debit > 0: - if line.account_id.id == \ - statement.journal_id.default_debit_account_id.id: - res[statement.id] += res_currency_obj.compute(cursor, - user, company_currency_id, currency_id, - line.debit, context=context) - else: - if line.account_id.id == \ - statement.journal_id.default_credit_account_id.id: - res[statement.id] += res_currency_obj.compute(cursor, - user, company_currency_id, currency_id, - line.credit, context=context) - if statement.state == 'draft': - for line in statement.line_ids: - res[statement.id] += line.amount - for r in res: - res[r] = round(res[r], 2) - return res - - def _get_period(self, cr, uid, context={}): - periods = self.pool.get('account.period').find(cr, uid) - if periods: - return periods[0] - else: - return False - - def _currency(self, cursor, user, ids, name, args, context=None): - res = {} - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - default_currency = res_users_obj.browse(cursor, user, - user, context=context).company_id.currency_id - for statement in self.browse(cursor, user, ids, context=context): - currency = statement.journal_id.currency - if not currency: - currency = default_currency - res[statement.id] = currency.id - currency_names = {} - for currency_id, currency_name in res_currency_obj.name_get(cursor, - user, [x for x in res.values()], context=context): - currency_names[currency_id] = currency_name - for statement_id in res.keys(): - currency_id = res[statement_id] - res[statement_id] = (currency_id, currency_names[currency_id]) - return res - - _order = "date desc" - _name = "account.bank.statement" - _description = "Bank Statement" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'date': fields.date('Date', required=True, - states={'confirm': [('readonly', True)]}), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True, - states={'confirm': [('readonly', True)]}, domain=[('type', '=', 'cash')]), - 'period_id': fields.many2one('account.period', 'Period', required=True, - states={'confirm':[('readonly', True)]}), - 'balance_start': fields.float('Starting Balance', digits=(16,2), - states={'confirm':[('readonly',True)]}), - 'balance_end_real': fields.float('Ending Balance', digits=(16,2), - states={'confirm':[('readonly', True)]}), - 'balance_end': fields.function(_end_balance, method=True, string='Balance'), - 'line_ids': fields.one2many('account.bank.statement.line', - 'statement_id', 'Statement lines', - states={'confirm':[('readonly', True)]}), - 'move_line_ids': fields.one2many('account.move.line', 'statement_id', - 'Entry lines', states={'confirm':[('readonly',True)]}), - 'state': fields.selection([('draft', 'Draft'),('confirm', 'Confirm')], - 'State', required=True, - states={'confirm': [('readonly', True)]}, readonly="1"), - 'currency': fields.function(_currency, method=True, string='Currency', - type='many2one', relation='res.currency'), - } - - _defaults = { - 'name': lambda self, cr, uid, context=None: \ - self.pool.get('ir.sequence').get(cr, uid, 'account.bank.statement'), - 'date': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'draft', - 'balance_start': _default_balance_start, - 'journal_id': _default_journal_id, - 'period_id': _get_period, - } - - def button_confirm(self, cr, uid, ids, context=None): - done = [] - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - account_move_obj = self.pool.get('account.move') - account_move_line_obj = self.pool.get('account.move.line') - account_bank_statement_line_obj = \ - self.pool.get('account.bank.statement.line') - - company_currency_id = res_users_obj.browse(cr, uid, uid, - context=context).company_id.currency_id.id - - for st in self.browse(cr, uid, ids, context): - if not st.state=='draft': - continue - - if not (abs(st.balance_end - st.balance_end_real) < 0.0001): - raise osv.except_osv(_('Error !'), - _('The statement balance is incorrect !\n' - 'Check that the ending balance equals the computed one.')) - if (not st.journal_id.default_credit_account_id) \ - or (not st.journal_id.default_debit_account_id): - raise osv.except_osv(_('Configration Error !'), - _('Please verify that an account is defined in the journal.')) - - for line in st.move_line_ids: - if line.state <> 'valid': - raise osv.except_osv(_('Error !'), - _('The account entries lines are not valid.')) - - for move in st.line_ids: - move_id = account_move_obj.create(cr, uid, { - 'journal_id': st.journal_id.id, - 'period_id': st.period_id.id, - }, context=context) - account_bank_statement_line_obj.write(cr, uid, [move.id], { - 'move_ids': [(4,move_id, False)] - }) - if not move.amount: - continue - - torec = [] - amount = res_currency_obj.compute(cr, uid, st.currency.id, - company_currency_id, move.amount, context=context) - if move.reconcile_id and move.reconcile_id.line_new_ids: - for newline in move.reconcile_id.line_new_ids: - amount += newline.amount - - val = { - 'name': move.name, - 'date': move.date, - 'ref': move.ref, - 'move_id': move_id, - 'partner_id': ((move.partner_id) and move.partner_id.id) or False, - 'account_id': (move.account_id) and move.account_id.id, - 'credit': ((amount>0) and amount) or 0.0, - 'debit': ((amount<0) and -amount) or 0.0, - 'statement_id': st.id, - 'journal_id': st.journal_id.id, - 'period_id': st.period_id.id, - 'currency_id': st.currency.id, - } - - amount = res_currency_obj.compute(cr, uid, st.currency.id, - company_currency_id, move.amount, context=context) - - if move.account_id and move.account_id.currency_id: - val['currency_id'] = move.account_id.currency_id.id - if company_currency_id==move.account_id.currency_id.id: - amount_cur = move.amount - else: - amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, - move.account_id.currency_id.id, amount, context=context) - val['amount_currency'] = amount_cur - - torec.append(account_move_line_obj.create(cr, uid, val , context=context)) - - if move.reconcile_id and move.reconcile_id.line_new_ids: - for newline in move.reconcile_id.line_new_ids: - account_move_line_obj.create(cr, uid, { - 'name': newline.name or move.name, - 'date': move.date, - 'ref': move.ref, - 'move_id': move_id, - 'partner_id': ((move.partner_id) and move.partner_id.id) or False, - 'account_id': (newline.account_id) and newline.account_id.id, - 'debit': newline.amount>0 and newline.amount or 0.0, - 'credit': newline.amount<0 and -newline.amount or 0.0, - 'statement_id': st.id, - 'journal_id': st.journal_id.id, - 'period_id': st.period_id.id, - }, context=context) - - if amount >= 0: - account_id = st.journal_id.default_credit_account_id.id - else: - account_id = st.journal_id.default_debit_account_id.id - - # Fill the secondary amount/currency - # if currency is not the same than the company - amount_currency = False - currency_id = False - if st.currency.id <> company_currency_id: - amount_currency = move.amount - currency_id = st.currency.id - - account_move_line_obj.create(cr, uid, { - 'name': move.name, - 'date': move.date, - 'ref': move.ref, - 'move_id': move_id, - 'partner_id': ((move.partner_id) and move.partner_id.id) or False, - 'account_id': account_id, - 'credit': ((amount < 0) and -amount) or 0.0, - 'debit': ((amount > 0) and amount) or 0.0, - 'statement_id': st.id, - 'journal_id': st.journal_id.id, - 'period_id': st.period_id.id, - 'amount_currency': amount_currency, - 'currency_id': currency_id, - }, context=context) - - for line in account_move_line_obj.browse(cr, uid, [x.id for x in - account_move_obj.browse(cr, uid, move_id, - context=context).line_id], - context=context): - if line.state <> 'valid': - raise osv.except_osv(_('Error !'), - _('Account move line "%s" is not valid') % line.name) - - if move.reconcile_id and move.reconcile_id.line_ids: - torec += map(lambda x: x.id, move.reconcile_id.line_ids) - try: - if abs(move.reconcile_amount-move.amount)<0.0001: - account_move_line_obj.reconcile(cr, uid, torec, 'statement', context) - else: - account_move_line_obj.reconcile_partial(cr, uid, torec, 'statement', context) - except: - raise osv.except_osv(_('Error !'), _('Unable to reconcile entry "%s": %.2f') % (move.name, move.amount)) - - done.append(st.id) - self.write(cr, uid, done, {'state':'confirm'}, context=context) - return True - - def button_cancel(self, cr, uid, ids, context={}): - done = [] - for st in self.browse(cr, uid, ids, context): - if st.state=='draft': - continue - ids = [] - for line in st.line_ids: - ids += [x.id for x in line.move_ids] - self.pool.get('account.move').unlink(cr, uid, ids, context) - done.append(st.id) - self.write(cr, uid, done, {'state':'draft'}, context=context) - return True - - def onchange_journal_id(self, cursor, user, statement_id, journal_id, context=None): - if not journal_id: - return {'value': {'currency': False}} - - account_journal_obj = self.pool.get('account.journal') - res_users_obj = self.pool.get('res.users') - res_currency_obj = self.pool.get('res.currency') - - cursor.execute('SELECT balance_end_real \ - FROM account_bank_statement \ - WHERE journal_id = %d \ - ORDER BY date DESC LIMIT 1', (journal_id,)) - res = cursor.fetchone() - balance_start = res and res[0] or 0.0 - - currency_id = account_journal_obj.browse(cursor, user, journal_id, - context=context).currency.id - if not currency_id: - currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - currency = res_currency_obj.name_get(cursor, user, [currency_id], - context=context)[0] - return {'value': {'balance_start': balance_start, 'currency': currency}} + def _default_journal_id(self, cr, uid, context={}): + if context.get('journal_id', False): + return context['journal_id'] + return False + + def _default_balance_start(self, cr, uid, context={}): + cr.execute('select id from account_bank_statement where journal_id=%d order by date desc limit 1', (1,)) + res = cr.fetchone() + if res: + return self.browse(cr, uid, [res[0]], context)[0].balance_end + return 0.0 + + def _end_balance(self, cursor, user, ids, name, attr, context=None): + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + res = {} + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + statements = self.browse(cursor, user, ids, context=context) + for statement in statements: + res[statement.id] = statement.balance_start + currency_id = statement.currency.id + for line in statement.move_line_ids: + if line.debit > 0: + if line.account_id.id == \ + statement.journal_id.default_debit_account_id.id: + res[statement.id] += res_currency_obj.compute(cursor, + user, company_currency_id, currency_id, + line.debit, context=context) + else: + if line.account_id.id == \ + statement.journal_id.default_credit_account_id.id: + res[statement.id] += res_currency_obj.compute(cursor, + user, company_currency_id, currency_id, + line.credit, context=context) + if statement.state == 'draft': + for line in statement.line_ids: + res[statement.id] += line.amount + for r in res: + res[r] = round(res[r], 2) + return res + + def _get_period(self, cr, uid, context={}): + periods = self.pool.get('account.period').find(cr, uid) + if periods: + return periods[0] + else: + return False + + def _currency(self, cursor, user, ids, name, args, context=None): + res = {} + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + default_currency = res_users_obj.browse(cursor, user, + user, context=context).company_id.currency_id + for statement in self.browse(cursor, user, ids, context=context): + currency = statement.journal_id.currency + if not currency: + currency = default_currency + res[statement.id] = currency.id + currency_names = {} + for currency_id, currency_name in res_currency_obj.name_get(cursor, + user, [x for x in res.values()], context=context): + currency_names[currency_id] = currency_name + for statement_id in res.keys(): + currency_id = res[statement_id] + res[statement_id] = (currency_id, currency_names[currency_id]) + return res + + _order = "date desc" + _name = "account.bank.statement" + _description = "Bank Statement" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'date': fields.date('Date', required=True, + states={'confirm': [('readonly', True)]}), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True, + states={'confirm': [('readonly', True)]}, domain=[('type', '=', 'cash')]), + 'period_id': fields.many2one('account.period', 'Period', required=True, + states={'confirm':[('readonly', True)]}), + 'balance_start': fields.float('Starting Balance', digits=(16,2), + states={'confirm':[('readonly',True)]}), + 'balance_end_real': fields.float('Ending Balance', digits=(16,2), + states={'confirm':[('readonly', True)]}), + 'balance_end': fields.function(_end_balance, method=True, string='Balance'), + 'line_ids': fields.one2many('account.bank.statement.line', + 'statement_id', 'Statement lines', + states={'confirm':[('readonly', True)]}), + 'move_line_ids': fields.one2many('account.move.line', 'statement_id', + 'Entry lines', states={'confirm':[('readonly',True)]}), + 'state': fields.selection([('draft', 'Draft'),('confirm', 'Confirm')], + 'State', required=True, + states={'confirm': [('readonly', True)]}, readonly="1"), + 'currency': fields.function(_currency, method=True, string='Currency', + type='many2one', relation='res.currency'), + } + + _defaults = { + 'name': lambda self, cr, uid, context=None: \ + self.pool.get('ir.sequence').get(cr, uid, 'account.bank.statement'), + 'date': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'balance_start': _default_balance_start, + 'journal_id': _default_journal_id, + 'period_id': _get_period, + } + + def button_confirm(self, cr, uid, ids, context=None): + done = [] + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + account_move_obj = self.pool.get('account.move') + account_move_line_obj = self.pool.get('account.move.line') + account_bank_statement_line_obj = \ + self.pool.get('account.bank.statement.line') + + company_currency_id = res_users_obj.browse(cr, uid, uid, + context=context).company_id.currency_id.id + + for st in self.browse(cr, uid, ids, context): + if not st.state=='draft': + continue + + if not (abs(st.balance_end - st.balance_end_real) < 0.0001): + raise osv.except_osv(_('Error !'), + _('The statement balance is incorrect !\n' + 'Check that the ending balance equals the computed one.')) + if (not st.journal_id.default_credit_account_id) \ + or (not st.journal_id.default_debit_account_id): + raise osv.except_osv(_('Configration Error !'), + _('Please verify that an account is defined in the journal.')) + + for line in st.move_line_ids: + if line.state <> 'valid': + raise osv.except_osv(_('Error !'), + _('The account entries lines are not valid.')) + + for move in st.line_ids: + move_id = account_move_obj.create(cr, uid, { + 'journal_id': st.journal_id.id, + 'period_id': st.period_id.id, + }, context=context) + account_bank_statement_line_obj.write(cr, uid, [move.id], { + 'move_ids': [(4,move_id, False)] + }) + if not move.amount: + continue + + torec = [] + amount = res_currency_obj.compute(cr, uid, st.currency.id, + company_currency_id, move.amount, context=context) + if move.reconcile_id and move.reconcile_id.line_new_ids: + for newline in move.reconcile_id.line_new_ids: + amount += newline.amount + + val = { + 'name': move.name, + 'date': move.date, + 'ref': move.ref, + 'move_id': move_id, + 'partner_id': ((move.partner_id) and move.partner_id.id) or False, + 'account_id': (move.account_id) and move.account_id.id, + 'credit': ((amount>0) and amount) or 0.0, + 'debit': ((amount<0) and -amount) or 0.0, + 'statement_id': st.id, + 'journal_id': st.journal_id.id, + 'period_id': st.period_id.id, + 'currency_id': st.currency.id, + } + + amount = res_currency_obj.compute(cr, uid, st.currency.id, + company_currency_id, move.amount, context=context) + + if move.account_id and move.account_id.currency_id: + val['currency_id'] = move.account_id.currency_id.id + if company_currency_id==move.account_id.currency_id.id: + amount_cur = move.amount + else: + amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, + move.account_id.currency_id.id, amount, context=context) + val['amount_currency'] = amount_cur + + torec.append(account_move_line_obj.create(cr, uid, val , context=context)) + + if move.reconcile_id and move.reconcile_id.line_new_ids: + for newline in move.reconcile_id.line_new_ids: + account_move_line_obj.create(cr, uid, { + 'name': newline.name or move.name, + 'date': move.date, + 'ref': move.ref, + 'move_id': move_id, + 'partner_id': ((move.partner_id) and move.partner_id.id) or False, + 'account_id': (newline.account_id) and newline.account_id.id, + 'debit': newline.amount>0 and newline.amount or 0.0, + 'credit': newline.amount<0 and -newline.amount or 0.0, + 'statement_id': st.id, + 'journal_id': st.journal_id.id, + 'period_id': st.period_id.id, + }, context=context) + + if amount >= 0: + account_id = st.journal_id.default_credit_account_id.id + else: + account_id = st.journal_id.default_debit_account_id.id + + # Fill the secondary amount/currency + # if currency is not the same than the company + amount_currency = False + currency_id = False + if st.currency.id <> company_currency_id: + amount_currency = move.amount + currency_id = st.currency.id + + account_move_line_obj.create(cr, uid, { + 'name': move.name, + 'date': move.date, + 'ref': move.ref, + 'move_id': move_id, + 'partner_id': ((move.partner_id) and move.partner_id.id) or False, + 'account_id': account_id, + 'credit': ((amount < 0) and -amount) or 0.0, + 'debit': ((amount > 0) and amount) or 0.0, + 'statement_id': st.id, + 'journal_id': st.journal_id.id, + 'period_id': st.period_id.id, + 'amount_currency': amount_currency, + 'currency_id': currency_id, + }, context=context) + + for line in account_move_line_obj.browse(cr, uid, [x.id for x in + account_move_obj.browse(cr, uid, move_id, + context=context).line_id], + context=context): + if line.state <> 'valid': + raise osv.except_osv(_('Error !'), + _('Account move line "%s" is not valid') % line.name) + + if move.reconcile_id and move.reconcile_id.line_ids: + torec += map(lambda x: x.id, move.reconcile_id.line_ids) + try: + if abs(move.reconcile_amount-move.amount)<0.0001: + account_move_line_obj.reconcile(cr, uid, torec, 'statement', context) + else: + account_move_line_obj.reconcile_partial(cr, uid, torec, 'statement', context) + except: + raise osv.except_osv(_('Error !'), _('Unable to reconcile entry "%s": %.2f') % (move.name, move.amount)) + + done.append(st.id) + self.write(cr, uid, done, {'state':'confirm'}, context=context) + return True + + def button_cancel(self, cr, uid, ids, context={}): + done = [] + for st in self.browse(cr, uid, ids, context): + if st.state=='draft': + continue + ids = [] + for line in st.line_ids: + ids += [x.id for x in line.move_ids] + self.pool.get('account.move').unlink(cr, uid, ids, context) + done.append(st.id) + self.write(cr, uid, done, {'state':'draft'}, context=context) + return True + + def onchange_journal_id(self, cursor, user, statement_id, journal_id, context=None): + if not journal_id: + return {'value': {'currency': False}} + + account_journal_obj = self.pool.get('account.journal') + res_users_obj = self.pool.get('res.users') + res_currency_obj = self.pool.get('res.currency') + + cursor.execute('SELECT balance_end_real \ + FROM account_bank_statement \ + WHERE journal_id = %d \ + ORDER BY date DESC LIMIT 1', (journal_id,)) + res = cursor.fetchone() + balance_start = res and res[0] or 0.0 + + currency_id = account_journal_obj.browse(cursor, user, journal_id, + context=context).currency.id + if not currency_id: + currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + currency = res_currency_obj.name_get(cursor, user, [currency_id], + context=context)[0] + return {'value': {'balance_start': balance_start, 'currency': currency}} account_bank_statement() class account_bank_statement_reconcile(osv.osv): - _name = "account.bank.statement.reconcile" - _description = "Statement reconcile" - - def _total_entry(self, cursor, user, ids, name, attr, context=None): - result = {} - for o in self.browse(cursor, user, ids, context=context): - result[o.id] = 0.0 - for line in o.line_ids: - result[o.id] += line.debit - line.credit - return result - - def _total_new(self, cursor, user, ids, name, attr, context=None): - result = {} - for o in self.browse(cursor, user, ids, context=context): - result[o.id] = 0.0 - for line in o.line_new_ids: - result[o.id] += line.amount - return result - - def _total_balance(self, cursor, user, ids, name, attr, context=None): - result = {} - for o in self.browse(cursor, user, ids, context=context): - result[o.id] = o.total_new - o.total_entry + o.total_amount - return result - - def _total_amount(self, cursor, user, ids, name, attr, context=None): - res = {} - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - currency_id = context.get('currency_id', company_currency_id) - - for reconcile_id in ids: - res[reconcile_id] = res_currency_obj.compute(cursor, user, - currency_id, company_currency_id, - context.get('amount', 0.0), context=context) - return res - - def _default_amount(self, cursor, user, context=None): - if context is None: - context = {} - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - currency_id = context.get('currency_id', company_currency_id) - - return res_currency_obj.compute(cursor, user, - currency_id, company_currency_id, - context.get('amount', 0.0), context=context) - - def _total_currency(self, cursor, user, ids, name, attrs, context=None): - res = {} - res_users_obj = self.pool.get('res.users') - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - for reconcile_id in ids: - res[reconcile_id] = company_currency_id - return res - - def _default_currency(self, cursor, user, context=None): - res_users_obj = self.pool.get('res.users') - - return res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - - def _total_second_amount(self, cursor, user, ids, name, attr, - context=None): - res = {} - for reconcile_id in ids: - res[reconcile_id] = context.get('amount', 0.0) - return res - - def _total_second_currency(self, cursor, user, ids, name, attr, context=None): - res = {} - for reconcile_id in ids: - res[reconcile_id] = context.get('currency_id', False) - return res - - def name_get(self, cursor, user, ids, context=None): - res= [] - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - for o in self.browse(cursor, user, ids, context=context): - td = '' - if o.statement_line: - currency_id = o.statement_line[0].statement_id.currency.id - if abs(o.statement_line[0].amount - (o.total_entry - o.total_new))>0.01: - td = 'P ' - else: - currency_id = company_currency_id - res.append((o.id, '%s[%.2f/%.2f]' % (td, - res_currency_obj.compute(cursor, user, company_currency_id, - currency_id, o.total_entry, context=context), - res_currency_obj.compute(cursor, user, company_currency_id, - currency_id, o.total_new, context=context)))) - return res - - _columns = { - 'name': fields.char('Date', size=64, required=True), - 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), - 'line_new_ids': fields.one2many('account.bank.statement.reconcile.line', - 'line_id', 'Write-Off'), - 'total_entry': fields.function(_total_entry, method=True, - string='Total entries'), - 'total_new': fields.function(_total_new, method=True, - string='Total write-off'), - 'total_second_amount': fields.function(_total_second_amount, - method=True, string='Payment amount', - help='The amount in the currency of the journal'), - 'total_second_currency': fields.function(_total_second_currency, method=True, - string='Currency', type='many2one', relation='res.currency', - help='The currency of the journal'), - 'total_amount': fields.function(_total_amount, method=True, - string='Payment amount'), - 'total_currency': fields.function(_total_currency, method=True, - string='Currency', type='many2one', relation='res.currency'), - 'total_balance': fields.function(_total_balance, method=True, - string='Balance'), - #line_ids define in account.py - 'statement_line': fields.one2many('account.bank.statement.line', - 'reconcile_id', 'Bank Statement Line'), - } - _defaults = { - 'name': lambda *a: time.strftime('%Y-%m-%d'), - 'partner_id': lambda obj, cursor, user, context=None: \ - context.get('partner', False), - 'total_amount': _default_amount, - 'total_currency': _default_currency, - 'total_second_amount': lambda obj, cursor, user, context=None: \ - context.get('amount', 0.0), - 'total_second_currency': lambda obj, cursor, user, context=None: \ - context.get('currency_id', False), - 'total_balance': _default_amount, - } + _name = "account.bank.statement.reconcile" + _description = "Statement reconcile" + + def _total_entry(self, cursor, user, ids, name, attr, context=None): + result = {} + for o in self.browse(cursor, user, ids, context=context): + result[o.id] = 0.0 + for line in o.line_ids: + result[o.id] += line.debit - line.credit + return result + + def _total_new(self, cursor, user, ids, name, attr, context=None): + result = {} + for o in self.browse(cursor, user, ids, context=context): + result[o.id] = 0.0 + for line in o.line_new_ids: + result[o.id] += line.amount + return result + + def _total_balance(self, cursor, user, ids, name, attr, context=None): + result = {} + for o in self.browse(cursor, user, ids, context=context): + result[o.id] = o.total_new - o.total_entry + o.total_amount + return result + + def _total_amount(self, cursor, user, ids, name, attr, context=None): + res = {} + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + currency_id = context.get('currency_id', company_currency_id) + + for reconcile_id in ids: + res[reconcile_id] = res_currency_obj.compute(cursor, user, + currency_id, company_currency_id, + context.get('amount', 0.0), context=context) + return res + + def _default_amount(self, cursor, user, context=None): + if context is None: + context = {} + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + currency_id = context.get('currency_id', company_currency_id) + + return res_currency_obj.compute(cursor, user, + currency_id, company_currency_id, + context.get('amount', 0.0), context=context) + + def _total_currency(self, cursor, user, ids, name, attrs, context=None): + res = {} + res_users_obj = self.pool.get('res.users') + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + for reconcile_id in ids: + res[reconcile_id] = company_currency_id + return res + + def _default_currency(self, cursor, user, context=None): + res_users_obj = self.pool.get('res.users') + + return res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + + def _total_second_amount(self, cursor, user, ids, name, attr, + context=None): + res = {} + for reconcile_id in ids: + res[reconcile_id] = context.get('amount', 0.0) + return res + + def _total_second_currency(self, cursor, user, ids, name, attr, context=None): + res = {} + for reconcile_id in ids: + res[reconcile_id] = context.get('currency_id', False) + return res + + def name_get(self, cursor, user, ids, context=None): + res= [] + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + for o in self.browse(cursor, user, ids, context=context): + td = '' + if o.statement_line: + currency_id = o.statement_line[0].statement_id.currency.id + if abs(o.statement_line[0].amount - (o.total_entry - o.total_new))>0.01: + td = 'P ' + else: + currency_id = company_currency_id + res.append((o.id, '%s[%.2f/%.2f]' % (td, + res_currency_obj.compute(cursor, user, company_currency_id, + currency_id, o.total_entry, context=context), + res_currency_obj.compute(cursor, user, company_currency_id, + currency_id, o.total_new, context=context)))) + return res + + _columns = { + 'name': fields.char('Date', size=64, required=True), + 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), + 'line_new_ids': fields.one2many('account.bank.statement.reconcile.line', + 'line_id', 'Write-Off'), + 'total_entry': fields.function(_total_entry, method=True, + string='Total entries'), + 'total_new': fields.function(_total_new, method=True, + string='Total write-off'), + 'total_second_amount': fields.function(_total_second_amount, + method=True, string='Payment amount', + help='The amount in the currency of the journal'), + 'total_second_currency': fields.function(_total_second_currency, method=True, + string='Currency', type='many2one', relation='res.currency', + help='The currency of the journal'), + 'total_amount': fields.function(_total_amount, method=True, + string='Payment amount'), + 'total_currency': fields.function(_total_currency, method=True, + string='Currency', type='many2one', relation='res.currency'), + 'total_balance': fields.function(_total_balance, method=True, + string='Balance'), + #line_ids define in account.py + 'statement_line': fields.one2many('account.bank.statement.line', + 'reconcile_id', 'Bank Statement Line'), + } + _defaults = { + 'name': lambda *a: time.strftime('%Y-%m-%d'), + 'partner_id': lambda obj, cursor, user, context=None: \ + context.get('partner', False), + 'total_amount': _default_amount, + 'total_currency': _default_currency, + 'total_second_amount': lambda obj, cursor, user, context=None: \ + context.get('amount', 0.0), + 'total_second_currency': lambda obj, cursor, user, context=None: \ + context.get('currency_id', False), + 'total_balance': _default_amount, + } account_bank_statement_reconcile() class account_bank_statement_reconcile_line(osv.osv): - _name = "account.bank.statement.reconcile.line" - _description = "Statement reconcile line" - _columns = { - 'name': fields.char('Description', size=64), - 'account_id': fields.many2one('account.account', 'Account', required=True), - 'line_id': fields.many2one('account.bank.statement.reconcile', 'Reconcile'), - 'amount': fields.float('Amount', required=True), - } + _name = "account.bank.statement.reconcile.line" + _description = "Statement reconcile line" + _columns = { + 'name': fields.char('Description', size=64), + 'account_id': fields.many2one('account.account', 'Account', required=True), + 'line_id': fields.many2one('account.bank.statement.reconcile', 'Reconcile'), + 'amount': fields.float('Amount', required=True), + } account_bank_statement_reconcile_line() class account_bank_statement_line(osv.osv): - def onchange_partner_id(self, cursor, user, line_id, partner_id, type, currency_id, - context={}): - if not partner_id: - return {} - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - if not currency_id: - currency_id = company_currency_id - - part = self.pool.get('res.partner').browse(cursor, user, partner_id, - context=context) - if type == 'supplier': - account_id = part.property_account_payable.id - else: - account_id = part.property_account_receivable.id - - cursor.execute('SELECT sum(debit-credit) \ - FROM account_move_line \ - WHERE (reconcile_id is null) \ - AND partner_id = %d \ - AND account_id=%d', (partner_id, account_id)) - res = cursor.fetchone() - balance = res and res[0] or 0.0 - - balance = res_currency_obj.compute(cursor, user, company_currency_id, - currency_id, balance, context=context) - return {'value': {'amount': balance, 'account_id': account_id}} - - def _reconcile_amount(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - res_currency_obj = self.pool.get('res.currency') - res_users_obj = self.pool.get('res.users') - - res = {} - company_currency_id = res_users_obj.browse(cursor, user, user, - context=context).company_id.currency_id.id - - for line in self.browse(cursor, user, ids, context=context): - if line.reconcile_id: - res[line.id] = res_currency_obj.compute(cursor, user, - company_currency_id, line.statement_id.currency.id, - line.reconcile_id.total_entry, context=context) - else: - res[line.id] = 0.0 - return res - - _order = "date,name desc" - _name = "account.bank.statement.line" - _description = "Bank Statement Line" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'date': fields.date('Date', required=True), - 'amount': fields.float('Amount'), - 'type': fields.selection([ - ('supplier','Supplier'), - ('customer','Customer'), - ('general','General') - ], 'Type', required=True), - 'partner_id': fields.many2one('res.partner', 'Partner'), - 'account_id': fields.many2one('account.account','Account', - required=True), - 'statement_id': fields.many2one('account.bank.statement', 'Statement', - select=True, required=True), - 'reconcile_id': fields.many2one('account.bank.statement.reconcile', - 'Reconcile', states={'confirm':[('readonly',True)]}), - 'move_ids': fields.many2many('account.move', - 'account_bank_statement_line_move_rel', 'move_id','statement_id', - 'Moves'), - 'ref': fields.char('Ref.', size=32), - 'note': fields.text('Notes'), - 'reconcile_amount': fields.function(_reconcile_amount, - string='Amount reconciled', method=True, type='float'), - } - _defaults = { - 'name': lambda self,cr,uid,context={}: self.pool.get('ir.sequence').get(cr, uid, 'account.bank.statement.line'), - 'date': lambda *a: time.strftime('%Y-%m-%d'), - 'type': lambda *a: 'general', - } + def onchange_partner_id(self, cursor, user, line_id, partner_id, type, currency_id, + context={}): + if not partner_id: + return {} + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + if not currency_id: + currency_id = company_currency_id + + part = self.pool.get('res.partner').browse(cursor, user, partner_id, + context=context) + if type == 'supplier': + account_id = part.property_account_payable.id + else: + account_id = part.property_account_receivable.id + + cursor.execute('SELECT sum(debit-credit) \ + FROM account_move_line \ + WHERE (reconcile_id is null) \ + AND partner_id = %d \ + AND account_id=%d', (partner_id, account_id)) + res = cursor.fetchone() + balance = res and res[0] or 0.0 + + balance = res_currency_obj.compute(cursor, user, company_currency_id, + currency_id, balance, context=context) + return {'value': {'amount': balance, 'account_id': account_id}} + + def _reconcile_amount(self, cursor, user, ids, name, args, context=None): + if not ids: + return {} + res_currency_obj = self.pool.get('res.currency') + res_users_obj = self.pool.get('res.users') + + res = {} + company_currency_id = res_users_obj.browse(cursor, user, user, + context=context).company_id.currency_id.id + + for line in self.browse(cursor, user, ids, context=context): + if line.reconcile_id: + res[line.id] = res_currency_obj.compute(cursor, user, + company_currency_id, line.statement_id.currency.id, + line.reconcile_id.total_entry, context=context) + else: + res[line.id] = 0.0 + return res + + _order = "date,name desc" + _name = "account.bank.statement.line" + _description = "Bank Statement Line" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'date': fields.date('Date', required=True), + 'amount': fields.float('Amount'), + 'type': fields.selection([ + ('supplier','Supplier'), + ('customer','Customer'), + ('general','General') + ], 'Type', required=True), + 'partner_id': fields.many2one('res.partner', 'Partner'), + 'account_id': fields.many2one('account.account','Account', + required=True), + 'statement_id': fields.many2one('account.bank.statement', 'Statement', + select=True, required=True), + 'reconcile_id': fields.many2one('account.bank.statement.reconcile', + 'Reconcile', states={'confirm':[('readonly',True)]}), + 'move_ids': fields.many2many('account.move', + 'account_bank_statement_line_move_rel', 'move_id','statement_id', + 'Moves'), + 'ref': fields.char('Ref.', size=32), + 'note': fields.text('Notes'), + 'reconcile_amount': fields.function(_reconcile_amount, + string='Amount reconciled', method=True, type='float'), + } + _defaults = { + 'name': lambda self,cr,uid,context={}: self.pool.get('ir.sequence').get(cr, uid, 'account.bank.statement.line'), + 'date': lambda *a: time.strftime('%Y-%m-%d'), + 'type': lambda *a: 'general', + } account_bank_statement_line() diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 418ff1f56a9..616750dd5c2 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -34,721 +34,721 @@ from osv import fields, osv from tools.translate import _ class account_move_line(osv.osv): - _name = "account.move.line" - _description = "Entry lines" - - def _query_get(self, cr, uid, obj='l', context={}): - fiscalyear_obj = self.pool.get('account.fiscalyear') - if not context.get('fiscalyear', False): - fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draft')]) - fiscalyear_clause = (','.join([str(x) for x in fiscalyear_ids])) or '0' - else: - fiscalyear_clause = '%s' % context['fiscalyear'] - if context.get('periods', False): - ids = ','.join([str(x) for x in context['periods']]) - return obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s) AND id in (%s))" % (fiscalyear_clause, ids) - else: - return obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s))" % (fiscalyear_clause,) - - def default_get(self, cr, uid, fields, context={}): - data = self._default_get(cr, uid, fields, context) - for f in data.keys(): - if f not in fields: - del data[f] - return data - - def _default_get(self, cr, uid, fields, context={}): - # Compute simple values - data = super(account_move_line, self).default_get(cr, uid, fields, context) - - if not 'move_id' in fields: #we are not in manual entry - return data - - period_obj = self.pool.get('account.period') - - # Compute the current move - move_id = False - partner_id = False - if context.get('journal_id',False) and context.get('period_id',False): - if 'move_id' in fields: - cr.execute('select move_id \ - from \ - account_move_line \ - where \ - journal_id=%d and period_id=%d and create_uid=%d and state=%s \ - order by id desc limit 1', - (context['journal_id'], context['period_id'], uid, 'draft')) - res = cr.fetchone() - move_id = (res and res[0]) or False - if not move_id: - return data - else: - data['move_id'] = move_id - if 'date' in fields: - cr.execute('select date \ - from \ - account_move_line \ - where \ - journal_id=%d and period_id=%d and create_uid=%d \ - order by id desc', - (context['journal_id'], context['period_id'], uid)) - res = cr.fetchone() - if res: - data['date'] = res[0] - else: - period = period_obj.browse(cr, uid, context['period_id'], - context=context) - data['date'] = period.date_start - - if not move_id: - return data - - total = 0 - ref_id = False - taxes = {} - move = self.pool.get('account.move').browse(cr, uid, move_id, context) - for l in move.line_id: - partner_id = partner_id or l.partner_id.id - ref_id = ref_id or l.ref - total += (l.debit - l.credit) - for tax in l.account_id.tax_ids: - if move.journal_id.type == 'sale': - if l.debit: - code = tax.ref_tax_code_id.id - acc = tax.account_paid_id.id - else: - code = tax.tax_code_id.id - acc = tax.account_collected_id.id - else: - if l.debit: - code = tax.tax_code_id.id - acc = tax.account_collected_id.id - else: - code = tax.ref_tax_code_id.id - acc = tax.account_paid_id.id - taxes.setdefault((acc, code), False) - taxes[(l.account_id.id, l.tax_code_id.id)] = True - if 'name' in fields: - data.setdefault('name', l.name) - - if 'ref' in fields: - data['ref'] = ref_id - if 'partner_id' in fields: - data['partner_id'] = partner_id - - if move.journal_id.type in ('purchase', 'sale'): - for t in taxes: - if not taxes[t] and t[0]: - s = 0 - tax_amount = 0 - for l in move.line_id: - if move.journal_id.type == 'sale': - if l.debit: - field_base = 'ref_' - key = 'account_paid_id' - else: - field_base = '' - key = 'account_collected_id' - else: - if l.debit: - field_base = '' - key = 'account_collected_id' - else: - field_base = 'ref_' - key = 'account_paid_id' - for tax in self.pool.get('account.tax').compute(cr, uid, - l.account_id.tax_ids, l.debit or l.credit, 1, False): - if (tax[key] == t[0]) \ - and (tax[field_base + 'tax_code_id'] == t[1]): - if l.debit: - s += tax['amount'] - else: - s -= tax['amount'] - tax_amount += tax['amount'] * \ - tax[field_base + 'tax_sign'] - if ('debit' in fields) or ('credit' in fields): - data['debit'] = s>0 and s or 0.0 - data['credit'] = s<0 and -s or 0.0 - - if 'tax_code_id' in fields: - data['tax_code_id'] = t[1] - if 'account_id' in fields: - data['account_id'] = t[0] - if 'tax_amount' in fields: - data['tax_amount'] = tax_amount - # - # Compute line for tax T - # - return data - - # - # Compute latest line - # - if ('debit' in fields) or ('credit' in fields): - data['credit'] = total>0 and total - data['debit'] = total<0 and -total - if 'account_id' in fields: - if total >= 0: - data['account_id'] = move.journal_id.default_credit_account_id.id or False - else: - data['account_id'] = move.journal_id.default_debit_account_id.id or False - if 'account_id' in fields and data['account_id']: - account = self.pool.get('account.account').browse(cr, uid, data['account_id']) - data['tax_code_id'] = self._default_get_tax(cr, uid, account ) - return data - - def _default_get_tax(self, cr, uid, account, debit=0, credit=0, context={}): - if account.tax_ids: - return account.tax_ids[0].base_code_id.id - return False - - def _on_create_write(self, cr, uid, id, context={}): - ml = self.browse(cr, uid, id, context) - return map(lambda x: x.id, ml.move_id.line_id) - - def _balance(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res={} - # TODO group the foreach in sql - for id in ids: - cr.execute('SELECT date,account_id FROM account_move_line WHERE id=%d', (id,)) - dt, acc = cr.fetchone() - cr.execute('SELECT SUM(debit-credit) FROM account_move_line WHERE account_id=%d AND (date<%s OR (date=%s AND id<=%d)) and active', (acc,dt,dt,id)) - res[id] = cr.fetchone()[0] - return res - - def _invoice(self, cursor, user, ids, name, arg, context=None): - invoice_obj = self.pool.get('account.invoice') - res = {} - for line_id in ids: - res[line_id] = False - cursor.execute('SELECT l.id, i.id ' \ - 'FROM account_move_line l, account_invoice i ' \ - 'WHERE l.move_id = i.move_id ' \ - 'AND l.id in (' + ','.join([str(x) for x in ids]) + ')') - invoice_ids = [] - for line_id, invoice_id in cursor.fetchall(): - res[line_id] = invoice_id - invoice_ids.append(invoice_id) - invoice_names = {False: ''} - for invoice_id, name in invoice_obj.name_get(cursor, user, - invoice_ids, context=context): - invoice_names[invoice_id] = name - for line_id in res.keys(): - invoice_id = res[line_id] - res[line_id] = (invoice_id, invoice_names[invoice_id]) - return res - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - result = [] - for line in self.browse(cr, uid, ids, context): - if line.ref: - result.append((line.id, (line.name or '')+' ('+line.ref+')')) - else: - result.append((line.id, line.name)) - return result - - def _invoice_search(self, cursor, user, obj, name, args): - if not len(args): - return [] - invoice_obj = self.pool.get('account.invoice') - - i = 0 - while i < len(args): - fargs = args[i][0].split('.', 1) - if len(fargs) > 1: - args[i] = (frags[0], 'in', invoice_obj.search(cursor, user, - [(fargs[1], args[i][1], args[i][2])])) - i += 1 - continue - if isinstance(args[i][2], basestring): - res_ids = invoice_obj.name_search(cursor, user, args[i][2], [], - args[i][1]) - args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) - i += 1 - qu1, qu2 = [], [] - for x in args: - if x[1] != 'in': - if (x[2] is False) and (x[1] == '='): - qu1.append('(i.id IS NULL)') - elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): - qu1.append('(i.id IS NOT NULL)') - else: - qu1.append('(i.id %s %s)' % (x[1], '%d')) - qu2.append(x[2]) - elif x[1] == 'in': - if len(x[2]) > 0: - qu1.append('(i.id in (%s))' % (','.join(['%d'] * len(x[2])))) - qu2 += x[2] - else: - qu1.append(' (False)') - if len(qu1): - qu1 = ' AND' + ' AND'.join(qu1) - else: - qu1 = '' - cursor.execute('SELECT l.id ' \ - 'FROM account_move_line l, account_invoice i ' \ - 'WHERE l.move_id = i.move_id ' + qu1, qu2) - res = cursor.fetchall() - if not len(res): - return [('id', '=', '0')] - return [('id', 'in', [x[0] for x in res])] - - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity expressed by this line, eg: number of product sold. The quantity is not a legal requirement but is very usefull for some reports."), - 'debit': fields.float('Debit', digits=(16,2)), - 'credit': fields.float('Credit', digits=(16,2)), - 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade", domain=[('type','<>','view'), ('type', '<>', 'closed')], select=2), - - 'move_id': fields.many2one('account.move', 'Move', ondelete="cascade", states={'valid':[('readonly',True)]}, help="The move of this entry line.", select=2), - - 'ref': fields.char('Ref.', size=32), - 'statement_id': fields.many2one('account.bank.statement', 'Statement', help="The bank statement used for bank reconciliation", select=1), - 'reconcile_id': fields.many2one('account.move.reconcile', 'Reconcile', readonly=True, ondelete='set null', select=2), - 'reconcile_partial_id': fields.many2one('account.move.reconcile', 'Partial Reconcile', readonly=True, ondelete='set null', select=2), - 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency if it is a multi-currency entry."), - 'currency_id': fields.many2one('res.currency', 'Currency', help="The optionnal other currency if it is a multi-currency entry."), - - 'period_id': fields.many2one('account.period', 'Period', required=True), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True), - 'blocked': fields.boolean('Litigation', help="You can check this box to mark the entry line as a litigation with the associated partner"), - - 'partner_id': fields.many2one('res.partner', 'Partner Ref.'), - 'date_maturity': fields.date('Maturity date', help="This field is used for payable and receivable entries. You can put the limit date for the payment of this entry line."), - 'date': fields.date('Effective date', required=True), - 'date_created': fields.date('Creation date'), - 'analytic_lines': fields.one2many('account.analytic.line', 'move_id', 'Analytic lines'), - 'centralisation': fields.selection([('normal','Normal'),('credit','Credit Centralisation'),('debit','Debit Centralisation')], 'Centralisation', size=6), - 'balance': fields.function(_balance, method=True, string='Balance'), - 'active': fields.boolean('Active'), - 'state': fields.selection([('draft','Draft'), ('valid','Valid')], 'State', readonly=True), - 'tax_code_id': fields.many2one('account.tax.code', 'Tax Account'), - 'tax_amount': fields.float('Tax/Base Amount', digits=(16,2), select=True), - 'invoice': fields.function(_invoice, method=True, string='Invoice', - type='many2one', relation='account.invoice', fnct_search=_invoice_search), - } - - def _get_date(self, cr, uid, context): - period_obj = self.pool.get('account.period') - dt = time.strftime('%Y-%m-%d') - if ('journal_id' in context) and ('period_id' in context): - cr.execute('select date from account_move_line ' \ - 'where journal_id=%d and period_id=%d ' \ - 'order by id desc limit 1', - (context['journal_id'], context['period_id'])) - res = cr.fetchone() - if res: - dt = res[0] - else: - period = period_obj.browse(cr, uid, context['period_id'], - context=context) - dt = period.date_start - return dt - _defaults = { - 'blocked': lambda *a: False, - 'active': lambda *a: True, - 'centralisation': lambda *a: 'normal', - 'date': _get_date, - 'date_created': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'draft', - 'journal_id': lambda self, cr, uid, c: c.get('journal_id', False), - 'period_id': lambda self, cr, uid, c: c.get('period_id', False), - } - _order = "date desc,id desc" - _sql_constraints = [ - ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in accounting entry !'), - ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in accounting entry !'), - ] - - def _auto_init(self, cr, context={}): - super(account_move_line, self)._auto_init(cr, context) - cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'account_move_line_journal_id_period_id_index\'') - if not cr.fetchone(): - cr.execute('CREATE INDEX account_move_line_journal_id_period_id_index ON account_move_line (journal_id, period_id)') - cr.commit() - - def _check_no_view(self, cr, uid, ids): - lines = self.browse(cr, uid, ids) - for l in lines: - if l.account_id.type == 'view': - return False - return True - - def _check_no_closed(self, cr, uid, ids): - lines = self.browse(cr, uid, ids) - for l in lines: - if l.account_id.type == 'closed': - return False - return True - - _constraints = [ - (_check_no_view, 'You can not create move line on view account.', ['account_id']), - (_check_no_closed, 'You can not create move line on closed account.', ['account_id']), - ] - - def onchange_partner_id(self, cr, uid, ids, move_id, partner_id, account_id=None, debit=0, credit=0, journal=False): - if (not partner_id) or account_id: - return {} - part = self.pool.get('res.partner').browse(cr, uid, partner_id) - id1 = part.property_account_payable.id - id2 = part.property_account_receivable.id - cr.execute('select sum(debit-credit) from account_move_line where (reconcile_id is null) and partner_id=%d and account_id=%d', (partner_id, id2)) - balance = cr.fetchone()[0] or 0.0 - val = {} - if (not debit) and (not credit): - if abs(balance)>0.01: - val['credit'] = ((balance>0) and balance) or 0 - val['debit'] = ((balance<0) and -balance) or 0 - val['account_id'] = id2 - else: - cr.execute('select sum(debit-credit) from account_move_line where (reconcile_id is null) and partner_id=%d and account_id=%d', (partner_id, id1)) - balance = cr.fetchone()[0] or 0.0 - val['credit'] = ((balance>0) and balance) or 0 - val['debit'] = ((balance<0) and -balance) or 0 - val['account_id'] = id1 - else: - val['account_id'] = (debit>0) and id2 or id1 - if journal: - jt = self.pool.get('account.journal').browse(cr, uid, journal).type - if jt=='sale': - val['account_id'] = id2 - elif jt=='purchase': - val['account_id'] = id1 - return {'value':val} - - # - # type: the type if reconciliation (no logic behind this field, for info) - # - # writeoff; entry generated for the difference between the lines - # - - def reconcile_partial(self, cr, uid, ids, type='auto', context={}): - merges = [] - unmerge = [] - total = 0.0 - merges_rec = [] - for line in self.browse(cr, uid, ids, context): - if line.reconcile_id: - raise _('Already Reconciled') - if line.reconcile_partial_id: - for line2 in line.reconcile_partial_id.line_partial_ids: - if not line2.reconcile_id: - merges.append(line2.id) - total += (line2.debit or 0.0) - (line2.credit or 0.0) - merges_rec.append(line.reconcile_partial_id.id) - else: - unmerge.append(line.id) - total += (line.debit or 0.0) - (line.credit or 0.0) - if not total: - return self.reconcile(cr, uid, merges+unmerge, context=context) - r_id = self.pool.get('account.move.reconcile').create(cr, uid, { - 'type': type, - 'line_partial_ids': map(lambda x: (4,x,False), merges+unmerge) - }) - self.pool.get('account.move.reconcile').reconcile_partial_check(cr, uid, [r_id] + merges_rec, context=context) - return True - - def reconcile(self, cr, uid, ids, type='auto', writeoff_acc_id=False, writeoff_period_id=False, writeoff_journal_id=False, context={}): - id_set = ','.join(map(str, ids)) - lines = self.browse(cr, uid, ids, context=context) - unrec_lines = filter(lambda x: not x['reconcile_id'], lines) - credit = debit = 0.0 - currency = 0.0 - account_id = False - partner_id = False - for line in unrec_lines: - if line.state <> 'valid': - raise osv.except_osv(_('Error'), - _('Entry "%s" is not valid !') % line.name) - credit += line['credit'] - debit += line['debit'] - currency += line['amount_currency'] or 0.0 - account_id = line['account_id']['id'] - partner_id = (line['partner_id'] and line['partner_id']['id']) or False - writeoff = debit - credit - date = time.strftime('%Y-%m-%d') - - cr.execute('SELECT account_id, reconcile_id \ - FROM account_move_line \ - WHERE id IN ('+id_set+') \ - GROUP BY account_id,reconcile_id') - r = cr.fetchall() + _name = "account.move.line" + _description = "Entry lines" + + def _query_get(self, cr, uid, obj='l', context={}): + fiscalyear_obj = self.pool.get('account.fiscalyear') + if not context.get('fiscalyear', False): + fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draft')]) + fiscalyear_clause = (','.join([str(x) for x in fiscalyear_ids])) or '0' + else: + fiscalyear_clause = '%s' % context['fiscalyear'] + if context.get('periods', False): + ids = ','.join([str(x) for x in context['periods']]) + return obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s) AND id in (%s))" % (fiscalyear_clause, ids) + else: + return obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id in (%s))" % (fiscalyear_clause,) + + def default_get(self, cr, uid, fields, context={}): + data = self._default_get(cr, uid, fields, context) + for f in data.keys(): + if f not in fields: + del data[f] + return data + + def _default_get(self, cr, uid, fields, context={}): + # Compute simple values + data = super(account_move_line, self).default_get(cr, uid, fields, context) + + if not 'move_id' in fields: #we are not in manual entry + return data + + period_obj = self.pool.get('account.period') + + # Compute the current move + move_id = False + partner_id = False + if context.get('journal_id',False) and context.get('period_id',False): + if 'move_id' in fields: + cr.execute('select move_id \ + from \ + account_move_line \ + where \ + journal_id=%d and period_id=%d and create_uid=%d and state=%s \ + order by id desc limit 1', + (context['journal_id'], context['period_id'], uid, 'draft')) + res = cr.fetchone() + move_id = (res and res[0]) or False + if not move_id: + return data + else: + data['move_id'] = move_id + if 'date' in fields: + cr.execute('select date \ + from \ + account_move_line \ + where \ + journal_id=%d and period_id=%d and create_uid=%d \ + order by id desc', + (context['journal_id'], context['period_id'], uid)) + res = cr.fetchone() + if res: + data['date'] = res[0] + else: + period = period_obj.browse(cr, uid, context['period_id'], + context=context) + data['date'] = period.date_start + + if not move_id: + return data + + total = 0 + ref_id = False + taxes = {} + move = self.pool.get('account.move').browse(cr, uid, move_id, context) + for l in move.line_id: + partner_id = partner_id or l.partner_id.id + ref_id = ref_id or l.ref + total += (l.debit - l.credit) + for tax in l.account_id.tax_ids: + if move.journal_id.type == 'sale': + if l.debit: + code = tax.ref_tax_code_id.id + acc = tax.account_paid_id.id + else: + code = tax.tax_code_id.id + acc = tax.account_collected_id.id + else: + if l.debit: + code = tax.tax_code_id.id + acc = tax.account_collected_id.id + else: + code = tax.ref_tax_code_id.id + acc = tax.account_paid_id.id + taxes.setdefault((acc, code), False) + taxes[(l.account_id.id, l.tax_code_id.id)] = True + if 'name' in fields: + data.setdefault('name', l.name) + + if 'ref' in fields: + data['ref'] = ref_id + if 'partner_id' in fields: + data['partner_id'] = partner_id + + if move.journal_id.type in ('purchase', 'sale'): + for t in taxes: + if not taxes[t] and t[0]: + s = 0 + tax_amount = 0 + for l in move.line_id: + if move.journal_id.type == 'sale': + if l.debit: + field_base = 'ref_' + key = 'account_paid_id' + else: + field_base = '' + key = 'account_collected_id' + else: + if l.debit: + field_base = '' + key = 'account_collected_id' + else: + field_base = 'ref_' + key = 'account_paid_id' + for tax in self.pool.get('account.tax').compute(cr, uid, + l.account_id.tax_ids, l.debit or l.credit, 1, False): + if (tax[key] == t[0]) \ + and (tax[field_base + 'tax_code_id'] == t[1]): + if l.debit: + s += tax['amount'] + else: + s -= tax['amount'] + tax_amount += tax['amount'] * \ + tax[field_base + 'tax_sign'] + if ('debit' in fields) or ('credit' in fields): + data['debit'] = s>0 and s or 0.0 + data['credit'] = s<0 and -s or 0.0 + + if 'tax_code_id' in fields: + data['tax_code_id'] = t[1] + if 'account_id' in fields: + data['account_id'] = t[0] + if 'tax_amount' in fields: + data['tax_amount'] = tax_amount + # + # Compute line for tax T + # + return data + + # + # Compute latest line + # + if ('debit' in fields) or ('credit' in fields): + data['credit'] = total>0 and total + data['debit'] = total<0 and -total + if 'account_id' in fields: + if total >= 0: + data['account_id'] = move.journal_id.default_credit_account_id.id or False + else: + data['account_id'] = move.journal_id.default_debit_account_id.id or False + if 'account_id' in fields and data['account_id']: + account = self.pool.get('account.account').browse(cr, uid, data['account_id']) + data['tax_code_id'] = self._default_get_tax(cr, uid, account ) + return data + + def _default_get_tax(self, cr, uid, account, debit=0, credit=0, context={}): + if account.tax_ids: + return account.tax_ids[0].base_code_id.id + return False + + def _on_create_write(self, cr, uid, id, context={}): + ml = self.browse(cr, uid, id, context) + return map(lambda x: x.id, ml.move_id.line_id) + + def _balance(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res={} + # TODO group the foreach in sql + for id in ids: + cr.execute('SELECT date,account_id FROM account_move_line WHERE id=%d', (id,)) + dt, acc = cr.fetchone() + cr.execute('SELECT SUM(debit-credit) FROM account_move_line WHERE account_id=%d AND (date<%s OR (date=%s AND id<=%d)) and active', (acc,dt,dt,id)) + res[id] = cr.fetchone()[0] + return res + + def _invoice(self, cursor, user, ids, name, arg, context=None): + invoice_obj = self.pool.get('account.invoice') + res = {} + for line_id in ids: + res[line_id] = False + cursor.execute('SELECT l.id, i.id ' \ + 'FROM account_move_line l, account_invoice i ' \ + 'WHERE l.move_id = i.move_id ' \ + 'AND l.id in (' + ','.join([str(x) for x in ids]) + ')') + invoice_ids = [] + for line_id, invoice_id in cursor.fetchall(): + res[line_id] = invoice_id + invoice_ids.append(invoice_id) + invoice_names = {False: ''} + for invoice_id, name in invoice_obj.name_get(cursor, user, + invoice_ids, context=context): + invoice_names[invoice_id] = name + for line_id in res.keys(): + invoice_id = res[line_id] + res[line_id] = (invoice_id, invoice_names[invoice_id]) + return res + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + result = [] + for line in self.browse(cr, uid, ids, context): + if line.ref: + result.append((line.id, (line.name or '')+' ('+line.ref+')')) + else: + result.append((line.id, line.name)) + return result + + def _invoice_search(self, cursor, user, obj, name, args): + if not len(args): + return [] + invoice_obj = self.pool.get('account.invoice') + + i = 0 + while i < len(args): + fargs = args[i][0].split('.', 1) + if len(fargs) > 1: + args[i] = (frags[0], 'in', invoice_obj.search(cursor, user, + [(fargs[1], args[i][1], args[i][2])])) + i += 1 + continue + if isinstance(args[i][2], basestring): + res_ids = invoice_obj.name_search(cursor, user, args[i][2], [], + args[i][1]) + args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) + i += 1 + qu1, qu2 = [], [] + for x in args: + if x[1] != 'in': + if (x[2] is False) and (x[1] == '='): + qu1.append('(i.id IS NULL)') + elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): + qu1.append('(i.id IS NOT NULL)') + else: + qu1.append('(i.id %s %s)' % (x[1], '%d')) + qu2.append(x[2]) + elif x[1] == 'in': + if len(x[2]) > 0: + qu1.append('(i.id in (%s))' % (','.join(['%d'] * len(x[2])))) + qu2 += x[2] + else: + qu1.append(' (False)') + if len(qu1): + qu1 = ' AND' + ' AND'.join(qu1) + else: + qu1 = '' + cursor.execute('SELECT l.id ' \ + 'FROM account_move_line l, account_invoice i ' \ + 'WHERE l.move_id = i.move_id ' + qu1, qu2) + res = cursor.fetchall() + if not len(res): + return [('id', '=', '0')] + return [('id', 'in', [x[0] for x in res])] + + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'quantity': fields.float('Quantity', digits=(16,2), help="The optionnal quantity expressed by this line, eg: number of product sold. The quantity is not a legal requirement but is very usefull for some reports."), + 'debit': fields.float('Debit', digits=(16,2)), + 'credit': fields.float('Credit', digits=(16,2)), + 'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade", domain=[('type','<>','view'), ('type', '<>', 'closed')], select=2), + + 'move_id': fields.many2one('account.move', 'Move', ondelete="cascade", states={'valid':[('readonly',True)]}, help="The move of this entry line.", select=2), + + 'ref': fields.char('Ref.', size=32), + 'statement_id': fields.many2one('account.bank.statement', 'Statement', help="The bank statement used for bank reconciliation", select=1), + 'reconcile_id': fields.many2one('account.move.reconcile', 'Reconcile', readonly=True, ondelete='set null', select=2), + 'reconcile_partial_id': fields.many2one('account.move.reconcile', 'Partial Reconcile', readonly=True, ondelete='set null', select=2), + 'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optionnal other currency if it is a multi-currency entry."), + 'currency_id': fields.many2one('res.currency', 'Currency', help="The optionnal other currency if it is a multi-currency entry."), + + 'period_id': fields.many2one('account.period', 'Period', required=True), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True), + 'blocked': fields.boolean('Litigation', help="You can check this box to mark the entry line as a litigation with the associated partner"), + + 'partner_id': fields.many2one('res.partner', 'Partner Ref.'), + 'date_maturity': fields.date('Maturity date', help="This field is used for payable and receivable entries. You can put the limit date for the payment of this entry line."), + 'date': fields.date('Effective date', required=True), + 'date_created': fields.date('Creation date'), + 'analytic_lines': fields.one2many('account.analytic.line', 'move_id', 'Analytic lines'), + 'centralisation': fields.selection([('normal','Normal'),('credit','Credit Centralisation'),('debit','Debit Centralisation')], 'Centralisation', size=6), + 'balance': fields.function(_balance, method=True, string='Balance'), + 'active': fields.boolean('Active'), + 'state': fields.selection([('draft','Draft'), ('valid','Valid')], 'State', readonly=True), + 'tax_code_id': fields.many2one('account.tax.code', 'Tax Account'), + 'tax_amount': fields.float('Tax/Base Amount', digits=(16,2), select=True), + 'invoice': fields.function(_invoice, method=True, string='Invoice', + type='many2one', relation='account.invoice', fnct_search=_invoice_search), + } + + def _get_date(self, cr, uid, context): + period_obj = self.pool.get('account.period') + dt = time.strftime('%Y-%m-%d') + if ('journal_id' in context) and ('period_id' in context): + cr.execute('select date from account_move_line ' \ + 'where journal_id=%d and period_id=%d ' \ + 'order by id desc limit 1', + (context['journal_id'], context['period_id'])) + res = cr.fetchone() + if res: + dt = res[0] + else: + period = period_obj.browse(cr, uid, context['period_id'], + context=context) + dt = period.date_start + return dt + _defaults = { + 'blocked': lambda *a: False, + 'active': lambda *a: True, + 'centralisation': lambda *a: 'normal', + 'date': _get_date, + 'date_created': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'journal_id': lambda self, cr, uid, c: c.get('journal_id', False), + 'period_id': lambda self, cr, uid, c: c.get('period_id', False), + } + _order = "date desc,id desc" + _sql_constraints = [ + ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in accounting entry !'), + ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in accounting entry !'), + ] + + def _auto_init(self, cr, context={}): + super(account_move_line, self)._auto_init(cr, context) + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'account_move_line_journal_id_period_id_index\'') + if not cr.fetchone(): + cr.execute('CREATE INDEX account_move_line_journal_id_period_id_index ON account_move_line (journal_id, period_id)') + cr.commit() + + def _check_no_view(self, cr, uid, ids): + lines = self.browse(cr, uid, ids) + for l in lines: + if l.account_id.type == 'view': + return False + return True + + def _check_no_closed(self, cr, uid, ids): + lines = self.browse(cr, uid, ids) + for l in lines: + if l.account_id.type == 'closed': + return False + return True + + _constraints = [ + (_check_no_view, 'You can not create move line on view account.', ['account_id']), + (_check_no_closed, 'You can not create move line on closed account.', ['account_id']), + ] + + def onchange_partner_id(self, cr, uid, ids, move_id, partner_id, account_id=None, debit=0, credit=0, journal=False): + if (not partner_id) or account_id: + return {} + part = self.pool.get('res.partner').browse(cr, uid, partner_id) + id1 = part.property_account_payable.id + id2 = part.property_account_receivable.id + cr.execute('select sum(debit-credit) from account_move_line where (reconcile_id is null) and partner_id=%d and account_id=%d', (partner_id, id2)) + balance = cr.fetchone()[0] or 0.0 + val = {} + if (not debit) and (not credit): + if abs(balance)>0.01: + val['credit'] = ((balance>0) and balance) or 0 + val['debit'] = ((balance<0) and -balance) or 0 + val['account_id'] = id2 + else: + cr.execute('select sum(debit-credit) from account_move_line where (reconcile_id is null) and partner_id=%d and account_id=%d', (partner_id, id1)) + balance = cr.fetchone()[0] or 0.0 + val['credit'] = ((balance>0) and balance) or 0 + val['debit'] = ((balance<0) and -balance) or 0 + val['account_id'] = id1 + else: + val['account_id'] = (debit>0) and id2 or id1 + if journal: + jt = self.pool.get('account.journal').browse(cr, uid, journal).type + if jt=='sale': + val['account_id'] = id2 + elif jt=='purchase': + val['account_id'] = id1 + return {'value':val} + + # + # type: the type if reconciliation (no logic behind this field, for info) + # + # writeoff; entry generated for the difference between the lines + # + + def reconcile_partial(self, cr, uid, ids, type='auto', context={}): + merges = [] + unmerge = [] + total = 0.0 + merges_rec = [] + for line in self.browse(cr, uid, ids, context): + if line.reconcile_id: + raise _('Already Reconciled') + if line.reconcile_partial_id: + for line2 in line.reconcile_partial_id.line_partial_ids: + if not line2.reconcile_id: + merges.append(line2.id) + total += (line2.debit or 0.0) - (line2.credit or 0.0) + merges_rec.append(line.reconcile_partial_id.id) + else: + unmerge.append(line.id) + total += (line.debit or 0.0) - (line.credit or 0.0) + if not total: + return self.reconcile(cr, uid, merges+unmerge, context=context) + r_id = self.pool.get('account.move.reconcile').create(cr, uid, { + 'type': type, + 'line_partial_ids': map(lambda x: (4,x,False), merges+unmerge) + }) + self.pool.get('account.move.reconcile').reconcile_partial_check(cr, uid, [r_id] + merges_rec, context=context) + return True + + def reconcile(self, cr, uid, ids, type='auto', writeoff_acc_id=False, writeoff_period_id=False, writeoff_journal_id=False, context={}): + id_set = ','.join(map(str, ids)) + lines = self.browse(cr, uid, ids, context=context) + unrec_lines = filter(lambda x: not x['reconcile_id'], lines) + credit = debit = 0.0 + currency = 0.0 + account_id = False + partner_id = False + for line in unrec_lines: + if line.state <> 'valid': + raise osv.except_osv(_('Error'), + _('Entry "%s" is not valid !') % line.name) + credit += line['credit'] + debit += line['debit'] + currency += line['amount_currency'] or 0.0 + account_id = line['account_id']['id'] + partner_id = (line['partner_id'] and line['partner_id']['id']) or False + writeoff = debit - credit + date = time.strftime('%Y-%m-%d') + + cr.execute('SELECT account_id, reconcile_id \ + FROM account_move_line \ + WHERE id IN ('+id_set+') \ + GROUP BY account_id,reconcile_id') + r = cr.fetchall() #TODO: move this check to a constraint in the account_move_reconcile object - if len(r) != 1: - raise osv.except_osv(_('Error'), _('Entries are not of the same account or already reconciled ! ')) - account = self.pool.get('account.account').browse(cr, uid, account_id, context=context) - if not account.reconcile: - raise osv.except_osv(_('Error'), _('The account is not defined to be reconcile !')) - if r[0][1] != None: - raise osv.except_osv(_('Error'), _('Some entries are already reconciled !')) - - if (not self.pool.get('res.currency').is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \ - (account.currency_id and (not self.pool.get('res.currency').is_zero(cr, uid, account.currency_id, currency))): - if not writeoff_acc_id: - raise osv.except_osv(_('Warning'), _('You have to provide an account for the write off entry !')) - if writeoff > 0: - debit = writeoff - credit = 0.0 - self_credit = writeoff - self_debit = 0.0 - else: - debit = 0.0 - credit = -writeoff - self_credit = 0.0 - self_debit = -writeoff - - writeoff_lines = [ - (0, 0, { - 'name':'Write-Off', - 'debit':self_debit, - 'credit':self_credit, - 'account_id':account_id, - 'date':date, - 'partner_id':partner_id, - 'currency_id': account.currency_id.id or False, - 'amount_currency': account.currency_id.id and -currency or 0.0 - }), - (0, 0, { - 'name':'Write-Off', - 'debit':debit, - 'credit':credit, - 'account_id':writeoff_acc_id, - 'date':date, - 'partner_id':partner_id - }) - ] - - name = 'Write-Off' - if writeoff_journal_id: - journal = self.pool.get('account.journal').browse(cr, uid, writeoff_journal_id) - if journal.sequence_id: - name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) - - writeoff_move_id = self.pool.get('account.move').create(cr, uid, { - 'name': name, - 'period_id': writeoff_period_id, - 'journal_id': writeoff_journal_id, - - 'state': 'draft', - 'line_id': writeoff_lines - }) - - writeoff_line_ids = self.search(cr, uid, [('move_id', '=', writeoff_move_id), ('account_id', '=', account_id)]) - ids += writeoff_line_ids - - r_id = self.pool.get('account.move.reconcile').create(cr, uid, { - #'name': date, - 'type': type, - 'line_id': map(lambda x: (4,x,False), ids), - 'line_partial_ids': map(lambda x: (3,x,False), ids) - }) - # the id of the move.reconcile is written in the move.line (self) by the create method above - # because of the way the line_id are defined: (4, x, False) - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_trigger(uid, 'account.move.line', id, cr) - return r_id - - def view_header_get(self, cr, user, view_id, view_type, context): - if (not context.get('journal_id', False)) or (not context.get('period_id', False)): - return False - cr.execute('select code from account_journal where id=%d', (context['journal_id'],)) - j = cr.fetchone()[0] or '' - cr.execute('select code from account_period where id=%d', (context['period_id'],)) - p = cr.fetchone()[0] or '' - if j or p: - return j+':'+p - return 'Journal' - - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={}, toolbar=False): - result = super(osv.osv, self).fields_view_get(cr, uid, view_id,view_type,context) - if view_type=='tree' and 'journal_id' in context: - title = self.view_header_get(cr, uid, view_id, view_type, context) - journal = self.pool.get('account.journal').browse(cr, uid, context['journal_id']) - - # if the journal view has a state field, color lines depending on - # its value - state = '' - for field in journal.view_id.columns_id: - if field.field=='state': - state = ' colors="red:state==\'draft\'"' - - #xml = '''<?xml version="1.0"?>\n<tree string="%s" editable="top" refresh="5"%s>\n\t''' % (title, state) - xml = '''<?xml version="1.0"?>\n<tree string="%s" editable="top" refresh="5" on_write="_on_create_write"%s>\n\t''' % (title, state) - fields = [] - - widths = { - 'ref': 50, - 'statement_id': 50, - 'state': 60, - 'tax_code_id': 50, - 'move_id': 40, - } - for field in journal.view_id.columns_id: - fields.append(field.field) - attrs = [] - if field.field=='debit': - attrs.append('sum="Total debit"') - elif field.field=='credit': - attrs.append('sum="Total credit"') - elif field.field=='account_id' and journal.id: - attrs.append('domain="[(\'journal_id\', \'=\', '+str(journal.id)+'),(\'type\',\'<>\',\'view\'), (\'type\',\'<>\',\'closed\')]"') - if field.readonly: - attrs.append('readonly="1"') - if field.required: - attrs.append('required="1"') - else: - attrs.append('required="0"') - if field.field == 'partner_id': - attrs.append('on_change="onchange_partner_id(move_id,partner_id,account_id,debit,credit,((\'journal_id\' in context) and context[\'journal_id\']) or {})"') - if field.field in widths: - attrs.append('width="'+str(widths[field.field])+'"') - xml += '''<field name="%s" %s/>\n''' % (field.field,' '.join(attrs)) - - xml += '''</tree>''' - result['arch'] = xml - result['fields'] = self.fields_get(cr, uid, fields, context) - return result - - def unlink(self, cr, uid, ids, context={}, check=True): - self._update_check(cr, uid, ids, context) - result = False - for line in self.browse(cr, uid, ids, context): - context['journal_id']=line.journal_id.id - context['period_id']=line.period_id.id - result = super(account_move_line, self).unlink(cr, uid, [line.id], context=context) - if check: - self.pool.get('account.move').validate(cr, uid, [line.move_id.id], context=context) - return result - - def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): - if not context: - context={} - account_obj = self.pool.get('account.account') - - if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']: - raise osv.except_osv(_('Bad account!'), _('You can not use an inactive account!')) - if update_check: - if ('account_id' in vals) or ('journal_id' in vals) or ('period_id' in vals) or ('move_id' in vals) or ('debit' in vals) or ('credit' in vals) or ('date' in vals): - self._update_check(cr, uid, ids, context) - result = super(osv.osv, self).write(cr, uid, ids, vals, context) - if check: - done = [] - for line in self.browse(cr, uid, ids): - if line.move_id.id not in done: - done.append(line.move_id.id) - self.pool.get('account.move').validate(cr, uid, [line.move_id.id], context) - return result - - def _update_journal_check(self, cr, uid, journal_id, period_id, context={}): - cr.execute('select state from account_journal_period where journal_id=%d and period_id=%d', (journal_id, period_id)) - result = cr.fetchall() - for (state,) in result: - if state=='done': - raise osv.except_osv(_('Error !'), _('You can not add/modify entries in a closed journal.')) - if not result: - journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context) - period = self.pool.get('account.period').browse(cr, uid, period_id, context) - self.pool.get('account.journal.period').create(cr, uid, { - 'name': (journal.code or journal.name)+':'+(period.name or ''), - 'journal_id': journal.id, - 'period_id': period.id - }) - return True - - def _update_check(self, cr, uid, ids, context={}): - done = {} - for line in self.browse(cr, uid, ids, context): - if line.move_id.state<>'draft': - raise osv.except_osv(_('Error !'), _('You can not do this modification on a confirmed entry ! Please note that you can just change some non important fields !')) - if line.reconcile_id: - raise osv.except_osv(_('Error !'), _('You can not do this modification on a reconciled entry ! Please note that you can just change some non important fields !')) - t = (line.journal_id.id, line.period_id.id) - if t not in done: - self._update_journal_check(cr, uid, line.journal_id.id, line.period_id.id, context) - done[t] = True - return True - - def create(self, cr, uid, vals, context=None, check=True): - if not context: - context={} - account_obj = self.pool.get('account.account') - - if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']: - raise osv.except_osv(_('Bad account!'), _('You can not use an inactive account!')) - if 'journal_id' in vals and 'journal_id' not in context: - context['journal_id'] = vals['journal_id'] - if 'period_id' in vals and 'period_id' not in context: - context['period_id'] = vals['period_id'] - if 'journal_id' not in context and 'move_id' in vals: - m = self.pool.get('account.move').browse(cr, uid, vals['move_id']) - context['journal_id'] = m.journal_id.id - context['period_id'] = m.period_id.id - self._update_journal_check(cr, uid, context['journal_id'], context['period_id'], context) - move_id = vals.get('move_id', False) - journal = self.pool.get('account.journal').browse(cr, uid, context['journal_id']) - if not move_id: - if journal.centralisation: - # use the first move ever created for this journal and period - cr.execute('select id, state, name from account_move where journal_id=%d and period_id=%d order by id limit 1', (context['journal_id'],context['period_id'])) - res = cr.fetchone() - if res: - if res[1] != 'draft': - raise osv.except_osv(_('UserError'), - _('The account move (%s) for centralisation ' \ - 'has been confirmed!') % res[2]) - vals['move_id'] = res[0] - - if not vals.get('move_id', False): - if journal.sequence_id: - name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) - v = { - 'name': name, - 'period_id': context['period_id'], - 'journal_id': context['journal_id'] - } - move_id = self.pool.get('account.move').create(cr, uid, v, context) - vals['move_id'] = move_id - else: - raise osv.except_osv(_('No piece number !'), _('Can not create an automatic sequence for this piece !\n\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.')) - - ok = not (journal.type_control_ids or journal.account_control_ids) - if ('account_id' in vals): - account = account_obj.browse(cr, uid, vals['account_id']) - if journal.type_control_ids: - type = account.type - for t in journal.type_control_ids: - if type==t.code: - ok = True - break - if journal.account_control_ids and not ok: - for a in journal.account_control_ids: - if a.id==vals['account_id']: - ok = True - break - if (account.currency_id) and 'amount_currency' not in vals: - vals['currency_id'] = account.currency_id.id - cur_obj = self.pool.get('res.currency') - ctx = {} - if 'date' in vals: - ctx['date'] = vals['date'] - vals['amount_currency'] = cur_obj.compute(cr, uid, account.company_id.currency_id.id, account.currency_id.id, vals.get('debit', 0.0)-vals.get('credit', 0.0), context=ctx) - if not ok: - raise osv.except_osv(_('Bad account !'), _('You can not use this general account in this journal !')) - result = super(osv.osv, self).create(cr, uid, vals, context) - if check: - self.pool.get('account.move').validate(cr, uid, [vals['move_id']], context) - return result + if len(r) != 1: + raise osv.except_osv(_('Error'), _('Entries are not of the same account or already reconciled ! ')) + account = self.pool.get('account.account').browse(cr, uid, account_id, context=context) + if not account.reconcile: + raise osv.except_osv(_('Error'), _('The account is not defined to be reconcile !')) + if r[0][1] != None: + raise osv.except_osv(_('Error'), _('Some entries are already reconciled !')) + + if (not self.pool.get('res.currency').is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \ + (account.currency_id and (not self.pool.get('res.currency').is_zero(cr, uid, account.currency_id, currency))): + if not writeoff_acc_id: + raise osv.except_osv(_('Warning'), _('You have to provide an account for the write off entry !')) + if writeoff > 0: + debit = writeoff + credit = 0.0 + self_credit = writeoff + self_debit = 0.0 + else: + debit = 0.0 + credit = -writeoff + self_credit = 0.0 + self_debit = -writeoff + + writeoff_lines = [ + (0, 0, { + 'name':'Write-Off', + 'debit':self_debit, + 'credit':self_credit, + 'account_id':account_id, + 'date':date, + 'partner_id':partner_id, + 'currency_id': account.currency_id.id or False, + 'amount_currency': account.currency_id.id and -currency or 0.0 + }), + (0, 0, { + 'name':'Write-Off', + 'debit':debit, + 'credit':credit, + 'account_id':writeoff_acc_id, + 'date':date, + 'partner_id':partner_id + }) + ] + + name = 'Write-Off' + if writeoff_journal_id: + journal = self.pool.get('account.journal').browse(cr, uid, writeoff_journal_id) + if journal.sequence_id: + name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) + + writeoff_move_id = self.pool.get('account.move').create(cr, uid, { + 'name': name, + 'period_id': writeoff_period_id, + 'journal_id': writeoff_journal_id, + + 'state': 'draft', + 'line_id': writeoff_lines + }) + + writeoff_line_ids = self.search(cr, uid, [('move_id', '=', writeoff_move_id), ('account_id', '=', account_id)]) + ids += writeoff_line_ids + + r_id = self.pool.get('account.move.reconcile').create(cr, uid, { + #'name': date, + 'type': type, + 'line_id': map(lambda x: (4,x,False), ids), + 'line_partial_ids': map(lambda x: (3,x,False), ids) + }) + # the id of the move.reconcile is written in the move.line (self) by the create method above + # because of the way the line_id are defined: (4, x, False) + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_trigger(uid, 'account.move.line', id, cr) + return r_id + + def view_header_get(self, cr, user, view_id, view_type, context): + if (not context.get('journal_id', False)) or (not context.get('period_id', False)): + return False + cr.execute('select code from account_journal where id=%d', (context['journal_id'],)) + j = cr.fetchone()[0] or '' + cr.execute('select code from account_period where id=%d', (context['period_id'],)) + p = cr.fetchone()[0] or '' + if j or p: + return j+':'+p + return 'Journal' + + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={}, toolbar=False): + result = super(osv.osv, self).fields_view_get(cr, uid, view_id,view_type,context) + if view_type=='tree' and 'journal_id' in context: + title = self.view_header_get(cr, uid, view_id, view_type, context) + journal = self.pool.get('account.journal').browse(cr, uid, context['journal_id']) + + # if the journal view has a state field, color lines depending on + # its value + state = '' + for field in journal.view_id.columns_id: + if field.field=='state': + state = ' colors="red:state==\'draft\'"' + + #xml = '''<?xml version="1.0"?>\n<tree string="%s" editable="top" refresh="5"%s>\n\t''' % (title, state) + xml = '''<?xml version="1.0"?>\n<tree string="%s" editable="top" refresh="5" on_write="_on_create_write"%s>\n\t''' % (title, state) + fields = [] + + widths = { + 'ref': 50, + 'statement_id': 50, + 'state': 60, + 'tax_code_id': 50, + 'move_id': 40, + } + for field in journal.view_id.columns_id: + fields.append(field.field) + attrs = [] + if field.field=='debit': + attrs.append('sum="Total debit"') + elif field.field=='credit': + attrs.append('sum="Total credit"') + elif field.field=='account_id' and journal.id: + attrs.append('domain="[(\'journal_id\', \'=\', '+str(journal.id)+'),(\'type\',\'<>\',\'view\'), (\'type\',\'<>\',\'closed\')]"') + if field.readonly: + attrs.append('readonly="1"') + if field.required: + attrs.append('required="1"') + else: + attrs.append('required="0"') + if field.field == 'partner_id': + attrs.append('on_change="onchange_partner_id(move_id,partner_id,account_id,debit,credit,((\'journal_id\' in context) and context[\'journal_id\']) or {})"') + if field.field in widths: + attrs.append('width="'+str(widths[field.field])+'"') + xml += '''<field name="%s" %s/>\n''' % (field.field,' '.join(attrs)) + + xml += '''</tree>''' + result['arch'] = xml + result['fields'] = self.fields_get(cr, uid, fields, context) + return result + + def unlink(self, cr, uid, ids, context={}, check=True): + self._update_check(cr, uid, ids, context) + result = False + for line in self.browse(cr, uid, ids, context): + context['journal_id']=line.journal_id.id + context['period_id']=line.period_id.id + result = super(account_move_line, self).unlink(cr, uid, [line.id], context=context) + if check: + self.pool.get('account.move').validate(cr, uid, [line.move_id.id], context=context) + return result + + def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True): + if not context: + context={} + account_obj = self.pool.get('account.account') + + if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']: + raise osv.except_osv(_('Bad account!'), _('You can not use an inactive account!')) + if update_check: + if ('account_id' in vals) or ('journal_id' in vals) or ('period_id' in vals) or ('move_id' in vals) or ('debit' in vals) or ('credit' in vals) or ('date' in vals): + self._update_check(cr, uid, ids, context) + result = super(osv.osv, self).write(cr, uid, ids, vals, context) + if check: + done = [] + for line in self.browse(cr, uid, ids): + if line.move_id.id not in done: + done.append(line.move_id.id) + self.pool.get('account.move').validate(cr, uid, [line.move_id.id], context) + return result + + def _update_journal_check(self, cr, uid, journal_id, period_id, context={}): + cr.execute('select state from account_journal_period where journal_id=%d and period_id=%d', (journal_id, period_id)) + result = cr.fetchall() + for (state,) in result: + if state=='done': + raise osv.except_osv(_('Error !'), _('You can not add/modify entries in a closed journal.')) + if not result: + journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context) + period = self.pool.get('account.period').browse(cr, uid, period_id, context) + self.pool.get('account.journal.period').create(cr, uid, { + 'name': (journal.code or journal.name)+':'+(period.name or ''), + 'journal_id': journal.id, + 'period_id': period.id + }) + return True + + def _update_check(self, cr, uid, ids, context={}): + done = {} + for line in self.browse(cr, uid, ids, context): + if line.move_id.state<>'draft': + raise osv.except_osv(_('Error !'), _('You can not do this modification on a confirmed entry ! Please note that you can just change some non important fields !')) + if line.reconcile_id: + raise osv.except_osv(_('Error !'), _('You can not do this modification on a reconciled entry ! Please note that you can just change some non important fields !')) + t = (line.journal_id.id, line.period_id.id) + if t not in done: + self._update_journal_check(cr, uid, line.journal_id.id, line.period_id.id, context) + done[t] = True + return True + + def create(self, cr, uid, vals, context=None, check=True): + if not context: + context={} + account_obj = self.pool.get('account.account') + + if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']: + raise osv.except_osv(_('Bad account!'), _('You can not use an inactive account!')) + if 'journal_id' in vals and 'journal_id' not in context: + context['journal_id'] = vals['journal_id'] + if 'period_id' in vals and 'period_id' not in context: + context['period_id'] = vals['period_id'] + if 'journal_id' not in context and 'move_id' in vals: + m = self.pool.get('account.move').browse(cr, uid, vals['move_id']) + context['journal_id'] = m.journal_id.id + context['period_id'] = m.period_id.id + self._update_journal_check(cr, uid, context['journal_id'], context['period_id'], context) + move_id = vals.get('move_id', False) + journal = self.pool.get('account.journal').browse(cr, uid, context['journal_id']) + if not move_id: + if journal.centralisation: + # use the first move ever created for this journal and period + cr.execute('select id, state, name from account_move where journal_id=%d and period_id=%d order by id limit 1', (context['journal_id'],context['period_id'])) + res = cr.fetchone() + if res: + if res[1] != 'draft': + raise osv.except_osv(_('UserError'), + _('The account move (%s) for centralisation ' \ + 'has been confirmed!') % res[2]) + vals['move_id'] = res[0] + + if not vals.get('move_id', False): + if journal.sequence_id: + name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) + v = { + 'name': name, + 'period_id': context['period_id'], + 'journal_id': context['journal_id'] + } + move_id = self.pool.get('account.move').create(cr, uid, v, context) + vals['move_id'] = move_id + else: + raise osv.except_osv(_('No piece number !'), _('Can not create an automatic sequence for this piece !\n\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.')) + + ok = not (journal.type_control_ids or journal.account_control_ids) + if ('account_id' in vals): + account = account_obj.browse(cr, uid, vals['account_id']) + if journal.type_control_ids: + type = account.type + for t in journal.type_control_ids: + if type==t.code: + ok = True + break + if journal.account_control_ids and not ok: + for a in journal.account_control_ids: + if a.id==vals['account_id']: + ok = True + break + if (account.currency_id) and 'amount_currency' not in vals: + vals['currency_id'] = account.currency_id.id + cur_obj = self.pool.get('res.currency') + ctx = {} + if 'date' in vals: + ctx['date'] = vals['date'] + vals['amount_currency'] = cur_obj.compute(cr, uid, account.company_id.currency_id.id, account.currency_id.id, vals.get('debit', 0.0)-vals.get('credit', 0.0), context=ctx) + if not ok: + raise osv.except_osv(_('Bad account !'), _('You can not use this general account in this journal !')) + result = super(osv.osv, self).create(cr, uid, vals, context) + if check: + self.pool.get('account.move').validate(cr, uid, [vals['move_id']], context) + return result account_move_line() class account_bank_statement_reconcile(osv.osv): - _inherit = "account.bank.statement.reconcile" - _columns = { - 'line_ids': fields.many2many('account.move.line', 'account_bank_statement_line_rel', 'statement_id', 'line_id', 'Entries'), - } + _inherit = "account.bank.statement.reconcile" + _columns = { + 'line_ids': fields.many2many('account.move.line', 'account_bank_statement_line_rel', 'statement_id', 'line_id', 'Entries'), + } account_bank_statement_reconcile() diff --git a/addons/account/invoice.py b/addons/account/invoice.py index 3ec87e737d3..fea8fcccdfd 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -40,959 +40,959 @@ from tools import config from tools.translate import _ class account_invoice(osv.osv): - def _amount_untaxed(self, cr, uid, ids, name, args, context={}): - id_set=",".join(map(str,ids)) - cr.execute("SELECT s.id,COALESCE(SUM(l.price_subtotal),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_line l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") - res=dict(cr.fetchall()) - return res - - def _amount_tax(self, cr, uid, ids, name, args, context={}): - id_set=",".join(map(str,ids)) - cr.execute("SELECT s.id,COALESCE(SUM(l.amount),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_tax l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") - res=dict(cr.fetchall()) - return res - - def _amount_total(self, cr, uid, ids, name, args, context={}): - untax = self._amount_untaxed(cr, uid, ids, name, args, context) - tax = self._amount_tax(cr, uid, ids, name, args, context) - res = {} - for id in ids: - res[id] = untax.get(id,0.0) + tax.get(id,0.0) - return res - - def _get_journal(self, cr, uid, context): - type_inv = context.get('type', 'out_invoice') - type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} - journal_obj = self.pool.get('account.journal') - res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale'))], limit=1) - if res: - return res[0] - else: - return False - - def _get_currency(self, cr, uid, context): - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0] - if user.company_id: - return user.company_id.currency_id.id - else: - return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] - - def _get_journal_analytic(self, cr, uid, type_inv, context={}): - type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} - tt = type2journal.get(type_inv, 'sale') - cr.execute("select id from account_analytic_journal where type=%s limit 1", (tt,)) - result = cr.fetchone() - if not result: - raise osv.except_osv(_('No Analytic Journal !'),("You have to define an analytic journal of type '%s' !") % (tt,)) - return result[0] - - def _get_type(self, cr, uid, context={}): - type = context.get('type', 'out_invoice') - return type - - def _reconciled(self, cr, uid, ids, name, args, context): - res = {} - for id in ids: - res[id] = self.test_paid(cr, uid, [id]) - return res - - def _get_reference_type(self, cursor, user, context=None): - return [('none', 'Free Reference')] - - _name = "account.invoice" - _description = 'Invoice' - _order = "number" - _columns = { - 'name': fields.char('Description', size=64, select=True,readonly=True, states={'draft':[('readonly',False)]}), - 'origin': fields.char('Origin', size=64), - 'type': fields.selection([ - ('out_invoice','Customer Invoice'), - ('in_invoice','Supplier Invoice'), - ('out_refund','Customer Refund'), - ('in_refund','Supplier Refund'), - ],'Type', readonly=True, select=True), - - 'number': fields.char('Invoice Number', size=32, readonly=True), - 'reference': fields.char('Invoice Reference', size=64), - 'reference_type': fields.selection(_get_reference_type, 'Reference Type', - required=True), - 'comment': fields.text('Additionnal Information'), - - 'state': fields.selection([ - ('draft','Draft'), - ('proforma','Pro-forma'), - ('open','Open'), - ('paid','Paid'), - ('cancel','Canceled') - ],'State', select=True, readonly=True), - - 'date_invoice': fields.date('Date Invoiced', required=True, states={'open':[('readonly',True)],'close':[('readonly',True)]}), - 'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]}), - - 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}), - 'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}), - 'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), - - 'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]} ), - - 'period_id': fields.many2one('account.period', 'Force Period', help="Keep empty to use the period of the validation date."), - - 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}), - 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), - - 'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True), - 'amount_untaxed': fields.function(_amount_untaxed, method=True, digits=(16,2),string='Untaxed', store=True), - 'amount_tax': fields.function(_amount_tax, method=True, digits=(16,2), string='Tax', store=True), - 'amount_total': fields.function(_amount_total, method=True, digits=(16,2), string='Total', store=True), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'check_total': fields.float('Total', digits=(16,2), states={'open':[('readonly',True)],'close':[('readonly',True)]}), - 'reconciled': fields.function(_reconciled, method=True, string='Reconciled', type='boolean'), - 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account', - help='The bank account to pay or to be paid'), - } - _defaults = { - 'type': _get_type, - 'date_invoice': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'draft', - 'journal_id': _get_journal, - 'currency_id': _get_currency, - 'company_id': lambda self, cr, uid, context: \ - self.pool.get('res.users').browse(cr, uid, uid, - context=context).company_id.id, - 'reference_type': lambda *a: 'none', - } - - def unlink(self, cr, uid, ids): - invoices = self.read(cr, uid, ids, ['state']) - unlink_ids = [] - for t in invoices: - if t['state'] in ('draft', 'cancel'): - unlink_ids.append(t['id']) - else: - raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) which are already opened or paid !')) - osv.osv.unlink(self, cr, uid, unlink_ids) - return True - -# def get_invoice_address(self, cr, uid, ids): -# res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice']) -# return [{}] - - def onchange_partner_id(self, cr, uid, ids, type, partner_id, - date_invoice=False, payment_term=False, partner_bank_id=False): - invoice_addr_id = False - contact_addr_id = False - partner_payment_term = False - acc_id = False - bank_id = False - - opt = [('uid', str(uid))] - if partner_id: - - opt.insert(0, ('id', partner_id)) - res = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['contact', 'invoice']) - contact_addr_id = res['contact'] - invoice_addr_id = res['invoice'] - p = self.pool.get('res.partner').browse(cr, uid, partner_id) - if type in ('out_invoice', 'out_refund'): - acc_id = p.property_account_receivable.id - else: - acc_id = p.property_account_payable.id - - partner_payment_term = p.property_payment_term and p.property_payment_term.id or False - if p.bank_ids: - bank_id = p.bank_ids[0].id - - result = {'value': { - 'address_contact_id': contact_addr_id, - 'address_invoice_id': invoice_addr_id, - 'account_id': acc_id, - 'payment_term': partner_payment_term, - } - } - - if type in ('in_invoice', 'in_refund'): - result['value']['partner_bank'] = bank_id - - if payment_term != partner_payment_term: - if partner_payment_term: - to_update = self.onchange_payment_term_date_invoice( - cr,uid,ids,partner_payment_term,date_invoice) - result['value'].update(to_update['value']) - else: - result['value']['date_due'] = False - - if partner_bank_id != bank_id: - to_update = self.onchange_partner_bank(cr, uid, ids, bank_id) - result['value'].update(to_update['value']) - return result - - def onchange_currency_id(self, cr, uid, ids, curr_id): - return {} - - def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice): - if not payment_term_id: - return {} - res={} - pt_obj= self.pool.get('account.payment.term') - - if not date_invoice : - date_invoice = self._defaults["date_invoice"](cr,uid,{}) - - pterm_list= pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice) - - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - res= {'value':{'date_due': pterm_list[-1]}} - - return res - - def onchange_invoice_line(self, cr, uid, ids, lines): - return {} - - def onchange_partner_bank(self, cursor, user, ids, partner_bank_id): - return {'value': {}} - - # go from canceled state to draft state - def action_cancel_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'draft'}) - wf_service = netsvc.LocalService("workflow") - for inv_id in ids: - wf_service.trg_create(uid, 'account.invoice', inv_id, cr) - return True - - # Workflow stuff - ################# - - # return the ids of the move lines which has the same account than the invoice - # whose id is in ids - def move_line_id_payment_get(self, cr, uid, ids, *args): - ml = self.pool.get('account.move.line') - res = [] - for inv in self.read(cr, uid, ids, ['move_id','account_id']): - if inv['move_id']: - move_line_ids = ml.search(cr, uid, [('move_id', '=', inv['move_id'][0])]) - for line in ml.read(cr, uid, move_line_ids, ['account_id']): - if line['account_id']==inv['account_id']: - res.append(line['id']) - return res - - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default = default.copy() - default.update({'state':'draft', 'number':False, 'move_id':False,}) - if 'date_invoice' not in default: - default['date_invoice'] = time.strftime('%Y-%m-%d') - if 'date_due' not in default: - default['date_due'] = False - return super(account_invoice, self).copy(cr, uid, id, default, context) - - def test_paid(self, cr, uid, ids, *args): - res = self.move_line_id_payment_get(cr, uid, ids) - if not res: - return False - ok = True - for id in res: - cr.execute('select reconcile_id from account_move_line where id=%d', (id,)) - ok = ok and bool(cr.fetchone()[0]) - return ok - - def button_reset_taxes(self, cr, uid, ids, context={}): - ait_obj = self.pool.get('account.invoice.tax') - for id in ids: - cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%d", (id,)) - for taxe in ait_obj.compute(cr, uid, id).values(): - ait_obj.create(cr, uid, taxe) - return True - - def button_compute(self, cr, uid, ids, context={}, set_total=False): - ait_obj = self.pool.get('account.invoice.tax') - cur_obj = self.pool.get('res.currency') - for inv in self.browse(cr, uid, ids): - company_currency = inv.company_id.currency_id.id - compute_taxes = ait_obj.compute(cr, uid, inv.id) - if not inv.tax_line: - for tax in compute_taxes.values(): - ait_obj.create(cr, uid, tax) - else: - tax_key = [] - for tax in inv.tax_line: - if tax.manual: - continue - key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) - tax_key.append(key) - if not key in compute_taxes: - ait_obj.unlink(cr, uid, [tax.id]) - continue - compute_taxes[key]['base'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, compute_taxes[key]['base'], context={'date': inv.date_invoice}) - if abs(compute_taxes[key]['base'] - tax.base) > inv.company_id.currency_id.rounding: - ait_obj.write(cr, uid, [tax.id], compute_taxes[key]) - for key in compute_taxes: - if not key in tax_key: - ait_obj.create(cr, uid, compute_taxes[key]) - if set_total: - self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total}) - return True - - def _convert_ref(self, cr, uid, ref): - return (ref or '').replace('/','') - - def _get_analityc_lines(self, cr, uid, id): - inv = self.browse(cr, uid, [id])[0] - cur_obj = self.pool.get('res.currency') - - company_currency = inv.company_id.currency_id.id - if inv.type in ('out_invoice', 'in_refund'): - sign = 1 - else: - sign = -1 - - iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id) - for il in iml: - if il['account_analytic_id']: - if inv.type in ('in_invoice', 'in_refund'): - ref = inv.reference - else: - ref = self._convert_ref(cr, uid, inv.number) - il['analytic_lines'] = [(0,0, { - 'name': il['name'], - 'date': inv['date_invoice'], - 'account_id': il['account_analytic_id'], - 'unit_amount': il['quantity'], - 'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign, - 'product_id': il['product_id'], - 'product_uom_id': il['uos_id'], - 'general_account_id': il['account_id'], - 'journal_id': self._get_journal_analytic(cr, uid, inv.type), - 'ref': ref, - })] - return iml - - def action_move_create(self, cr, uid, ids, *args): - ait_obj = self.pool.get('account.invoice.tax') - cur_obj = self.pool.get('res.currency') - for inv in self.browse(cr, uid, ids): - if inv.move_id: - continue - if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0): - raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.')) - company_currency = inv.company_id.currency_id.id - # create the analytical lines - line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line'] - ils = self.pool.get('account.invoice.line').read(cr, uid, line_ids) - # one move line per invoice line - iml = self._get_analityc_lines(cr, uid, inv.id) - # check if taxes are all computed - compute_taxes = ait_obj.compute(cr, uid, inv.id) - if not inv.tax_line: - for tax in compute_taxes.values(): - ait_obj.create(cr, uid, tax) - else: - tax_key = [] - for tax in inv.tax_line: - if tax.manual: - continue - key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) - tax_key.append(key) - if not key in compute_taxes: - raise osv.except_osv(_('Warning !'), _('Global taxes defined, but not in invoice lines !')) - base = compute_taxes[key]['base'] - if abs(base - tax.base) > inv.company_id.currency_id.rounding: - raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base')) - for key in compute_taxes: - if not key in tax_key: - raise osv.except_osv(_('Warning !'), _('Taxes missing !')) - - # one move line per tax line - iml += ait_obj.move_line_get(cr, uid, inv.id) - - if inv.type in ('in_invoice', 'in_refund'): - ref = inv.reference - else: - ref = self._convert_ref(cr, uid, inv.number) - - diff_currency_p = inv.currency_id.id <> company_currency - # create one move line for the total and possibly adjust the other lines amount - total = 0 - total_currency = 0 - for i in iml: - if inv.currency_id.id != company_currency: - i['currency_id'] = inv.currency_id.id - i['amount_currency'] = i['price'] - i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, - company_currency, i['price'], - context={'date': inv.date_invoice}) - else: - i['amount_currency'] = False - i['currency_id'] = False - i['ref'] = ref - if inv.type in ('out_invoice','in_refund'): - total += i['price'] - total_currency += i['amount_currency'] or i['price'] - i['price'] = - i['price'] - else: - total -= i['price'] - total_currency -= i['amount_currency'] or i['price'] - acc_id = inv.account_id.id - - name = inv['name'] or '/' - totlines = False - if inv.payment_term: - totlines = self.pool.get('account.payment.term').compute(cr, - uid, inv.payment_term.id, total, inv.date_invoice or False) - if totlines: - res_amount_currency = total_currency - i = 0 - for t in totlines: - if inv.currency_id.id != company_currency: - amount_currency = cur_obj.compute(cr, uid, - company_currency, inv.currency_id.id, t[1]) - else: - amount_currency = False - - # last line add the diff - res_amount_currency -= amount_currency or 0 - i += 1 - if i == len(totlines): - amount_currency += res_amount_currency - - iml.append({ - 'type': 'dest', - 'name': name, - 'price': t[1], - 'account_id': acc_id, - 'date_maturity': t[0], - 'amount_currency': diff_currency_p \ - and amount_currency or False, - 'currency_id': diff_currency_p \ - and inv.currency_id.id or False, - 'ref': ref, - }) - else: - iml.append({ - 'type': 'dest', - 'name': name, - 'price': total, - 'account_id': acc_id, - 'date_maturity' : inv.date_due or False, - 'amount_currency': diff_currency_p \ - and total_currency or False, - 'currency_id': diff_currency_p \ - and inv.currency_id.id or False, - 'ref': ref - }) - - date = inv.date_invoice - part = inv.partner_id.id - line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml) - - journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']}) - journal = self.pool.get('account.journal').browse(cr, uid, journal_id) - if journal.sequence_id: - name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) - if journal.centralisation: - raise osv.except_osv(_('UserError'), - _('Can not create invoice move on centralized journal')) - - move = {'name': name, 'line_id': line, 'journal_id': journal_id} - if inv.period_id: - move['period_id'] = inv.period_id.id - for i in line: - i[2]['period_id'] = inv.period_id.id - - move_id = self.pool.get('account.move').create(cr, uid, move) - # make the invoice point to that move - self.write(cr, uid, [inv.id], {'move_id': move_id}) - self.pool.get('account.move').post(cr, uid, [move_id]) - self._log_event(cr, uid, ids) - return True - - def line_get_convert(self, cr, uid, x, part, date, context={}): - return { - 'date':date, - 'date_maturity': x.get('date_maturity', False), - 'partner_id':part, - 'name':x['name'][:64], - 'debit':x['price']>0 and x['price'], - 'credit':x['price']<0 and -x['price'], - 'account_id':x['account_id'], - 'analytic_lines':x.get('analytic_lines', []), - 'amount_currency':x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)), - 'currency_id':x.get('currency_id', False), - 'tax_code_id': x.get('tax_code_id', False), - 'tax_amount': x.get('tax_amount', False), - 'ref':x.get('ref',False) - } - - def action_number(self, cr, uid, ids, *args): - cr.execute('SELECT id, type, number, move_id, reference ' \ - 'FROM account_invoice ' \ - 'WHERE id IN ('+','.join(map(str,ids))+')') - for (id, invtype, number, move_id, reference) in cr.fetchall(): - if not number: - number = self.pool.get('ir.sequence').get(cr, uid, - 'account.invoice.' + invtype) - if invtype in ('in_invoice', 'in_refund'): - ref = reference - else: - ref = self._convert_ref(cr, uid, number) - cr.execute('UPDATE account_invoice SET number=%s ' \ - 'WHERE id=%d', (number, id)) - cr.execute('UPDATE account_move_line SET ref=%s ' \ - 'WHERE move_id=%d AND (ref is null OR ref = \'\')', - (ref, move_id)) - cr.execute('UPDATE account_analytic_line SET ref=%s ' \ - 'FROM account_move_line ' \ - 'WHERE account_move_line.move_id = %d ' \ - 'AND account_analytic_line.move_id = account_move_line.id', - (ref, move_id)) - return True - - def action_cancel(self, cr, uid, ids, *args): - account_move_obj = self.pool.get('account.move') - invoices = self.read(cr, uid, ids, ['move_id']) - for i in invoices: - if i['move_id']: - account_move_obj.button_cancel(cr, uid, [i['move_id'][0]]) - # delete the move this invoice was pointing to - # Note that the corresponding move_lines and move_reconciles - # will be automatically deleted too - account_move_obj.unlink(cr, uid, [i['move_id'][0]]) - self.write(cr, uid, ids, {'state':'cancel', 'move_id':False}) - self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice') - return True - - ################### - - def list_distinct_taxes(self, cr, uid, ids): - invoices = self.browse(cr, uid, ids) - taxes = {} - for inv in invoices: - for tax in inv.tax_line: - if not tax['name'] in taxes: - taxes[tax['name']] = {'name': tax['name']} - return taxes.values() - - def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'): - invs = self.read(cr, uid, ids, ['type','partner_id','amount_untaxed']) - for inv in invs: - part=inv['partner_id'] and inv['partner_id'][0] - pc = pr = 0.0 - cr.execute('select sum(quantity*price_unit) from account_invoice_line where invoice_id=%d', (inv['id'],)) - total = inv['amount_untaxed'] - if inv['type'] in ('in_invoice','in_refund'): - partnertype='supplier' - eventtype = 'purchase' - pc = total*factor - else: - partnertype = 'customer' - eventtype = 'sale' - pr = total*factor - if self.pool.get('res.partner.event.type').check(cr, uid, 'invoice_open'): - self.pool.get('res.partner.event').create(cr, uid, {'name':'Invoice: '+name, 'som':False, 'description':name+' '+str(inv['id']), 'document':name, 'partner_id':part, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':pc, 'type':eventtype}) - return len(invs) - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - types = { - 'out_invoice': 'CI: ', - 'in_invoice': 'SI: ', - 'out_refund': 'OR: ', - 'in_refund': 'SR: ', - } - return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')] - - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = [] - if name: - ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) - return self.name_get(cr, user, ids, context) - - def _refund_cleanup_lines(self, lines): - for line in lines: - del line['id'] - del line['invoice_id'] - if 'account_id' in line: - line['account_id'] = line.get('account_id', False) and line['account_id'][0] - if 'product_id' in line: - line['product_id'] = line.get('product_id', False) and line['product_id'][0] - if 'uos_id' in line: - line['uos_id'] = line.get('uos_id', False) and line['uos_id'][0] - if 'invoice_line_tax_id' in line: - line['invoice_line_tax_id'] = [(6,0, line.get('invoice_line_tax_id', [])) ] - if 'account_analytic_id' in line: - line['account_analytic_id'] = line.get('account_analytic_id', False) and line['account_analytic_id'][0] - return map(lambda x: (0,0,x), lines) - - def refund(self, cr, uid, ids): - invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'address_contact_id', 'address_invoice_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id']) - - new_ids = [] - for invoice in invoices: - del invoice['id'] - - type_dict = { - 'out_invoice': 'out_refund', # Customer Invoice - 'in_invoice': 'in_refund', # Supplier Invoice - 'out_refund': 'out_invoice', # Customer Refund - 'in_refund': 'in_invoice', # Supplier Refund - } - - - invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line']) - invoice_lines = self._refund_cleanup_lines(invoice_lines) - - tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line']) - tax_lines = filter(lambda l: l['manual'], tax_lines) - tax_lines = self._refund_cleanup_lines(tax_lines) - - invoice.update({ - 'type': type_dict[invoice['type']], - 'date_invoice': time.strftime('%Y-%m-%d'), - 'state': 'draft', - 'number': False, - 'invoice_line': invoice_lines, - 'tax_line': tax_lines - }) - - # take the id part of the tuple returned for many2one fields - for field in ('address_contact_id', 'address_invoice_id', 'partner_id', - 'account_id', 'currency_id', 'payment_term', 'journal_id'): - invoice[field] = invoice[field] and invoice[field][0] - - # create the new invoice - new_ids.append(self.create(cr, uid, invoice)) - return new_ids - - def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context={}, name=''): - #TODO check if we can use different period for payment and the writeoff line - assert len(ids)==1, "Can only pay one invoice at a time" - invoice = self.browse(cr, uid, ids[0]) - src_account_id = invoice.account_id.id - journal = self.pool.get('account.journal').browse(cr, uid, pay_journal_id) - if not name: - if journal.sequence_id: - name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) - else: - raise osv.except_osv(_('No piece number !'), _('Can not create an automatic sequence for this piece !\n\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.')) - types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1} - direction = types[invoice.type] - l1 = { - 'name': name, - 'debit': direction * pay_amount>0 and direction * pay_amount, - 'credit': direction * pay_amount<0 and - direction * pay_amount, - 'account_id': src_account_id, - 'partner_id': invoice.partner_id.id, - 'date': time.strftime('%Y-%m-%d'), - } - l2 = { - 'name':name, - 'debit': direction * pay_amount<0 and - direction * pay_amount, - 'credit': direction * pay_amount>0 and direction * pay_amount, - 'account_id': pay_account_id, - 'partner_id': invoice.partner_id.id, - 'date': time.strftime('%Y-%m-%d'), - } - - lines = [(0, 0, l1), (0, 0, l2)] - move = {'name': name, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id} - move_id = self.pool.get('account.move').create(cr, uid, move) - - line_ids = [] - total = 0.0 - line = self.pool.get('account.move.line') - cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')') - lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) ) - for l in lines: - if l.account_id.id==src_account_id: - line_ids.append(l.id) - total += (l.debit or 0.0) - (l.credit or 0.0) - if (not total) or writeoff_acc_id: - self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context) - else: - self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context) - return True + def _amount_untaxed(self, cr, uid, ids, name, args, context={}): + id_set=",".join(map(str,ids)) + cr.execute("SELECT s.id,COALESCE(SUM(l.price_subtotal),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_line l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") + res=dict(cr.fetchall()) + return res + + def _amount_tax(self, cr, uid, ids, name, args, context={}): + id_set=",".join(map(str,ids)) + cr.execute("SELECT s.id,COALESCE(SUM(l.amount),0)::decimal(16,2) AS amount FROM account_invoice s LEFT OUTER JOIN account_invoice_tax l ON (s.id=l.invoice_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") + res=dict(cr.fetchall()) + return res + + def _amount_total(self, cr, uid, ids, name, args, context={}): + untax = self._amount_untaxed(cr, uid, ids, name, args, context) + tax = self._amount_tax(cr, uid, ids, name, args, context) + res = {} + for id in ids: + res[id] = untax.get(id,0.0) + tax.get(id,0.0) + return res + + def _get_journal(self, cr, uid, context): + type_inv = context.get('type', 'out_invoice') + type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} + journal_obj = self.pool.get('account.journal') + res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale'))], limit=1) + if res: + return res[0] + else: + return False + + def _get_currency(self, cr, uid, context): + user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0] + if user.company_id: + return user.company_id.currency_id.id + else: + return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] + + def _get_journal_analytic(self, cr, uid, type_inv, context={}): + type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} + tt = type2journal.get(type_inv, 'sale') + cr.execute("select id from account_analytic_journal where type=%s limit 1", (tt,)) + result = cr.fetchone() + if not result: + raise osv.except_osv(_('No Analytic Journal !'),("You have to define an analytic journal of type '%s' !") % (tt,)) + return result[0] + + def _get_type(self, cr, uid, context={}): + type = context.get('type', 'out_invoice') + return type + + def _reconciled(self, cr, uid, ids, name, args, context): + res = {} + for id in ids: + res[id] = self.test_paid(cr, uid, [id]) + return res + + def _get_reference_type(self, cursor, user, context=None): + return [('none', 'Free Reference')] + + _name = "account.invoice" + _description = 'Invoice' + _order = "number" + _columns = { + 'name': fields.char('Description', size=64, select=True,readonly=True, states={'draft':[('readonly',False)]}), + 'origin': fields.char('Origin', size=64), + 'type': fields.selection([ + ('out_invoice','Customer Invoice'), + ('in_invoice','Supplier Invoice'), + ('out_refund','Customer Refund'), + ('in_refund','Supplier Refund'), + ],'Type', readonly=True, select=True), + + 'number': fields.char('Invoice Number', size=32, readonly=True), + 'reference': fields.char('Invoice Reference', size=64), + 'reference_type': fields.selection(_get_reference_type, 'Reference Type', + required=True), + 'comment': fields.text('Additionnal Information'), + + 'state': fields.selection([ + ('draft','Draft'), + ('proforma','Pro-forma'), + ('open','Open'), + ('paid','Paid'), + ('cancel','Canceled') + ],'State', select=True, readonly=True), + + 'date_invoice': fields.date('Date Invoiced', required=True, states={'open':[('readonly',True)],'close':[('readonly',True)]}), + 'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',True)]}), + + 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}), + 'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}), + 'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), + + 'payment_term': fields.many2one('account.payment.term', 'Payment Term',readonly=True, states={'draft':[('readonly',False)]} ), + + 'period_id': fields.many2one('account.period', 'Force Period', help="Keep empty to use the period of the validation date."), + + 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}), + 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), + + 'move_id': fields.many2one('account.move', 'Invoice Movement', readonly=True), + 'amount_untaxed': fields.function(_amount_untaxed, method=True, digits=(16,2),string='Untaxed', store=True), + 'amount_tax': fields.function(_amount_tax, method=True, digits=(16,2), string='Tax', store=True), + 'amount_total': fields.function(_amount_total, method=True, digits=(16,2), string='Total', store=True), + 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'check_total': fields.float('Total', digits=(16,2), states={'open':[('readonly',True)],'close':[('readonly',True)]}), + 'reconciled': fields.function(_reconciled, method=True, string='Reconciled', type='boolean'), + 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account', + help='The bank account to pay or to be paid'), + } + _defaults = { + 'type': _get_type, + 'date_invoice': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'journal_id': _get_journal, + 'currency_id': _get_currency, + 'company_id': lambda self, cr, uid, context: \ + self.pool.get('res.users').browse(cr, uid, uid, + context=context).company_id.id, + 'reference_type': lambda *a: 'none', + } + + def unlink(self, cr, uid, ids): + invoices = self.read(cr, uid, ids, ['state']) + unlink_ids = [] + for t in invoices: + if t['state'] in ('draft', 'cancel'): + unlink_ids.append(t['id']) + else: + raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) which are already opened or paid !')) + osv.osv.unlink(self, cr, uid, unlink_ids) + return True + +# def get_invoice_address(self, cr, uid, ids): +# res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice']) +# return [{}] + + def onchange_partner_id(self, cr, uid, ids, type, partner_id, + date_invoice=False, payment_term=False, partner_bank_id=False): + invoice_addr_id = False + contact_addr_id = False + partner_payment_term = False + acc_id = False + bank_id = False + + opt = [('uid', str(uid))] + if partner_id: + + opt.insert(0, ('id', partner_id)) + res = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['contact', 'invoice']) + contact_addr_id = res['contact'] + invoice_addr_id = res['invoice'] + p = self.pool.get('res.partner').browse(cr, uid, partner_id) + if type in ('out_invoice', 'out_refund'): + acc_id = p.property_account_receivable.id + else: + acc_id = p.property_account_payable.id + + partner_payment_term = p.property_payment_term and p.property_payment_term.id or False + if p.bank_ids: + bank_id = p.bank_ids[0].id + + result = {'value': { + 'address_contact_id': contact_addr_id, + 'address_invoice_id': invoice_addr_id, + 'account_id': acc_id, + 'payment_term': partner_payment_term, + } + } + + if type in ('in_invoice', 'in_refund'): + result['value']['partner_bank'] = bank_id + + if payment_term != partner_payment_term: + if partner_payment_term: + to_update = self.onchange_payment_term_date_invoice( + cr,uid,ids,partner_payment_term,date_invoice) + result['value'].update(to_update['value']) + else: + result['value']['date_due'] = False + + if partner_bank_id != bank_id: + to_update = self.onchange_partner_bank(cr, uid, ids, bank_id) + result['value'].update(to_update['value']) + return result + + def onchange_currency_id(self, cr, uid, ids, curr_id): + return {} + + def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice): + if not payment_term_id: + return {} + res={} + pt_obj= self.pool.get('account.payment.term') + + if not date_invoice : + date_invoice = self._defaults["date_invoice"](cr,uid,{}) + + pterm_list= pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice) + + if pterm_list: + pterm_list = [line[0] for line in pterm_list] + pterm_list.sort() + res= {'value':{'date_due': pterm_list[-1]}} + + return res + + def onchange_invoice_line(self, cr, uid, ids, lines): + return {} + + def onchange_partner_bank(self, cursor, user, ids, partner_bank_id): + return {'value': {}} + + # go from canceled state to draft state + def action_cancel_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'draft'}) + wf_service = netsvc.LocalService("workflow") + for inv_id in ids: + wf_service.trg_create(uid, 'account.invoice', inv_id, cr) + return True + + # Workflow stuff + ################# + + # return the ids of the move lines which has the same account than the invoice + # whose id is in ids + def move_line_id_payment_get(self, cr, uid, ids, *args): + ml = self.pool.get('account.move.line') + res = [] + for inv in self.read(cr, uid, ids, ['move_id','account_id']): + if inv['move_id']: + move_line_ids = ml.search(cr, uid, [('move_id', '=', inv['move_id'][0])]) + for line in ml.read(cr, uid, move_line_ids, ['account_id']): + if line['account_id']==inv['account_id']: + res.append(line['id']) + return res + + def copy(self, cr, uid, id, default=None, context=None): + if default is None: + default = {} + default = default.copy() + default.update({'state':'draft', 'number':False, 'move_id':False,}) + if 'date_invoice' not in default: + default['date_invoice'] = time.strftime('%Y-%m-%d') + if 'date_due' not in default: + default['date_due'] = False + return super(account_invoice, self).copy(cr, uid, id, default, context) + + def test_paid(self, cr, uid, ids, *args): + res = self.move_line_id_payment_get(cr, uid, ids) + if not res: + return False + ok = True + for id in res: + cr.execute('select reconcile_id from account_move_line where id=%d', (id,)) + ok = ok and bool(cr.fetchone()[0]) + return ok + + def button_reset_taxes(self, cr, uid, ids, context={}): + ait_obj = self.pool.get('account.invoice.tax') + for id in ids: + cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%d", (id,)) + for taxe in ait_obj.compute(cr, uid, id).values(): + ait_obj.create(cr, uid, taxe) + return True + + def button_compute(self, cr, uid, ids, context={}, set_total=False): + ait_obj = self.pool.get('account.invoice.tax') + cur_obj = self.pool.get('res.currency') + for inv in self.browse(cr, uid, ids): + company_currency = inv.company_id.currency_id.id + compute_taxes = ait_obj.compute(cr, uid, inv.id) + if not inv.tax_line: + for tax in compute_taxes.values(): + ait_obj.create(cr, uid, tax) + else: + tax_key = [] + for tax in inv.tax_line: + if tax.manual: + continue + key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) + tax_key.append(key) + if not key in compute_taxes: + ait_obj.unlink(cr, uid, [tax.id]) + continue + compute_taxes[key]['base'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, compute_taxes[key]['base'], context={'date': inv.date_invoice}) + if abs(compute_taxes[key]['base'] - tax.base) > inv.company_id.currency_id.rounding: + ait_obj.write(cr, uid, [tax.id], compute_taxes[key]) + for key in compute_taxes: + if not key in tax_key: + ait_obj.create(cr, uid, compute_taxes[key]) + if set_total: + self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total}) + return True + + def _convert_ref(self, cr, uid, ref): + return (ref or '').replace('/','') + + def _get_analityc_lines(self, cr, uid, id): + inv = self.browse(cr, uid, [id])[0] + cur_obj = self.pool.get('res.currency') + + company_currency = inv.company_id.currency_id.id + if inv.type in ('out_invoice', 'in_refund'): + sign = 1 + else: + sign = -1 + + iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id) + for il in iml: + if il['account_analytic_id']: + if inv.type in ('in_invoice', 'in_refund'): + ref = inv.reference + else: + ref = self._convert_ref(cr, uid, inv.number) + il['analytic_lines'] = [(0,0, { + 'name': il['name'], + 'date': inv['date_invoice'], + 'account_id': il['account_analytic_id'], + 'unit_amount': il['quantity'], + 'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign, + 'product_id': il['product_id'], + 'product_uom_id': il['uos_id'], + 'general_account_id': il['account_id'], + 'journal_id': self._get_journal_analytic(cr, uid, inv.type), + 'ref': ref, + })] + return iml + + def action_move_create(self, cr, uid, ids, *args): + ait_obj = self.pool.get('account.invoice.tax') + cur_obj = self.pool.get('res.currency') + for inv in self.browse(cr, uid, ids): + if inv.move_id: + continue + if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0): + raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.')) + company_currency = inv.company_id.currency_id.id + # create the analytical lines + line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line'] + ils = self.pool.get('account.invoice.line').read(cr, uid, line_ids) + # one move line per invoice line + iml = self._get_analityc_lines(cr, uid, inv.id) + # check if taxes are all computed + compute_taxes = ait_obj.compute(cr, uid, inv.id) + if not inv.tax_line: + for tax in compute_taxes.values(): + ait_obj.create(cr, uid, tax) + else: + tax_key = [] + for tax in inv.tax_line: + if tax.manual: + continue + key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) + tax_key.append(key) + if not key in compute_taxes: + raise osv.except_osv(_('Warning !'), _('Global taxes defined, but not in invoice lines !')) + base = compute_taxes[key]['base'] + if abs(base - tax.base) > inv.company_id.currency_id.rounding: + raise osv.except_osv(_('Warning !'), _('Tax base different !\nClick on compute to update tax base')) + for key in compute_taxes: + if not key in tax_key: + raise osv.except_osv(_('Warning !'), _('Taxes missing !')) + + # one move line per tax line + iml += ait_obj.move_line_get(cr, uid, inv.id) + + if inv.type in ('in_invoice', 'in_refund'): + ref = inv.reference + else: + ref = self._convert_ref(cr, uid, inv.number) + + diff_currency_p = inv.currency_id.id <> company_currency + # create one move line for the total and possibly adjust the other lines amount + total = 0 + total_currency = 0 + for i in iml: + if inv.currency_id.id != company_currency: + i['currency_id'] = inv.currency_id.id + i['amount_currency'] = i['price'] + i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, + company_currency, i['price'], + context={'date': inv.date_invoice}) + else: + i['amount_currency'] = False + i['currency_id'] = False + i['ref'] = ref + if inv.type in ('out_invoice','in_refund'): + total += i['price'] + total_currency += i['amount_currency'] or i['price'] + i['price'] = - i['price'] + else: + total -= i['price'] + total_currency -= i['amount_currency'] or i['price'] + acc_id = inv.account_id.id + + name = inv['name'] or '/' + totlines = False + if inv.payment_term: + totlines = self.pool.get('account.payment.term').compute(cr, + uid, inv.payment_term.id, total, inv.date_invoice or False) + if totlines: + res_amount_currency = total_currency + i = 0 + for t in totlines: + if inv.currency_id.id != company_currency: + amount_currency = cur_obj.compute(cr, uid, + company_currency, inv.currency_id.id, t[1]) + else: + amount_currency = False + + # last line add the diff + res_amount_currency -= amount_currency or 0 + i += 1 + if i == len(totlines): + amount_currency += res_amount_currency + + iml.append({ + 'type': 'dest', + 'name': name, + 'price': t[1], + 'account_id': acc_id, + 'date_maturity': t[0], + 'amount_currency': diff_currency_p \ + and amount_currency or False, + 'currency_id': diff_currency_p \ + and inv.currency_id.id or False, + 'ref': ref, + }) + else: + iml.append({ + 'type': 'dest', + 'name': name, + 'price': total, + 'account_id': acc_id, + 'date_maturity' : inv.date_due or False, + 'amount_currency': diff_currency_p \ + and total_currency or False, + 'currency_id': diff_currency_p \ + and inv.currency_id.id or False, + 'ref': ref + }) + + date = inv.date_invoice + part = inv.partner_id.id + line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml) + + journal_id = inv.journal_id.id #self._get_journal(cr, uid, {'type': inv['type']}) + journal = self.pool.get('account.journal').browse(cr, uid, journal_id) + if journal.sequence_id: + name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) + if journal.centralisation: + raise osv.except_osv(_('UserError'), + _('Can not create invoice move on centralized journal')) + + move = {'name': name, 'line_id': line, 'journal_id': journal_id} + if inv.period_id: + move['period_id'] = inv.period_id.id + for i in line: + i[2]['period_id'] = inv.period_id.id + + move_id = self.pool.get('account.move').create(cr, uid, move) + # make the invoice point to that move + self.write(cr, uid, [inv.id], {'move_id': move_id}) + self.pool.get('account.move').post(cr, uid, [move_id]) + self._log_event(cr, uid, ids) + return True + + def line_get_convert(self, cr, uid, x, part, date, context={}): + return { + 'date':date, + 'date_maturity': x.get('date_maturity', False), + 'partner_id':part, + 'name':x['name'][:64], + 'debit':x['price']>0 and x['price'], + 'credit':x['price']<0 and -x['price'], + 'account_id':x['account_id'], + 'analytic_lines':x.get('analytic_lines', []), + 'amount_currency':x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)), + 'currency_id':x.get('currency_id', False), + 'tax_code_id': x.get('tax_code_id', False), + 'tax_amount': x.get('tax_amount', False), + 'ref':x.get('ref',False) + } + + def action_number(self, cr, uid, ids, *args): + cr.execute('SELECT id, type, number, move_id, reference ' \ + 'FROM account_invoice ' \ + 'WHERE id IN ('+','.join(map(str,ids))+')') + for (id, invtype, number, move_id, reference) in cr.fetchall(): + if not number: + number = self.pool.get('ir.sequence').get(cr, uid, + 'account.invoice.' + invtype) + if invtype in ('in_invoice', 'in_refund'): + ref = reference + else: + ref = self._convert_ref(cr, uid, number) + cr.execute('UPDATE account_invoice SET number=%s ' \ + 'WHERE id=%d', (number, id)) + cr.execute('UPDATE account_move_line SET ref=%s ' \ + 'WHERE move_id=%d AND (ref is null OR ref = \'\')', + (ref, move_id)) + cr.execute('UPDATE account_analytic_line SET ref=%s ' \ + 'FROM account_move_line ' \ + 'WHERE account_move_line.move_id = %d ' \ + 'AND account_analytic_line.move_id = account_move_line.id', + (ref, move_id)) + return True + + def action_cancel(self, cr, uid, ids, *args): + account_move_obj = self.pool.get('account.move') + invoices = self.read(cr, uid, ids, ['move_id']) + for i in invoices: + if i['move_id']: + account_move_obj.button_cancel(cr, uid, [i['move_id'][0]]) + # delete the move this invoice was pointing to + # Note that the corresponding move_lines and move_reconciles + # will be automatically deleted too + account_move_obj.unlink(cr, uid, [i['move_id'][0]]) + self.write(cr, uid, ids, {'state':'cancel', 'move_id':False}) + self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice') + return True + + ################### + + def list_distinct_taxes(self, cr, uid, ids): + invoices = self.browse(cr, uid, ids) + taxes = {} + for inv in invoices: + for tax in inv.tax_line: + if not tax['name'] in taxes: + taxes[tax['name']] = {'name': tax['name']} + return taxes.values() + + def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'): + invs = self.read(cr, uid, ids, ['type','partner_id','amount_untaxed']) + for inv in invs: + part=inv['partner_id'] and inv['partner_id'][0] + pc = pr = 0.0 + cr.execute('select sum(quantity*price_unit) from account_invoice_line where invoice_id=%d', (inv['id'],)) + total = inv['amount_untaxed'] + if inv['type'] in ('in_invoice','in_refund'): + partnertype='supplier' + eventtype = 'purchase' + pc = total*factor + else: + partnertype = 'customer' + eventtype = 'sale' + pr = total*factor + if self.pool.get('res.partner.event.type').check(cr, uid, 'invoice_open'): + self.pool.get('res.partner.event').create(cr, uid, {'name':'Invoice: '+name, 'som':False, 'description':name+' '+str(inv['id']), 'document':name, 'partner_id':part, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':pc, 'type':eventtype}) + return len(invs) + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + types = { + 'out_invoice': 'CI: ', + 'in_invoice': 'SI: ', + 'out_refund': 'OR: ', + 'in_refund': 'SR: ', + } + return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')] + + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = [] + if name: + ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) + return self.name_get(cr, user, ids, context) + + def _refund_cleanup_lines(self, lines): + for line in lines: + del line['id'] + del line['invoice_id'] + if 'account_id' in line: + line['account_id'] = line.get('account_id', False) and line['account_id'][0] + if 'product_id' in line: + line['product_id'] = line.get('product_id', False) and line['product_id'][0] + if 'uos_id' in line: + line['uos_id'] = line.get('uos_id', False) and line['uos_id'][0] + if 'invoice_line_tax_id' in line: + line['invoice_line_tax_id'] = [(6,0, line.get('invoice_line_tax_id', [])) ] + if 'account_analytic_id' in line: + line['account_analytic_id'] = line.get('account_analytic_id', False) and line['account_analytic_id'][0] + return map(lambda x: (0,0,x), lines) + + def refund(self, cr, uid, ids): + invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'address_contact_id', 'address_invoice_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id']) + + new_ids = [] + for invoice in invoices: + del invoice['id'] + + type_dict = { + 'out_invoice': 'out_refund', # Customer Invoice + 'in_invoice': 'in_refund', # Supplier Invoice + 'out_refund': 'out_invoice', # Customer Refund + 'in_refund': 'in_invoice', # Supplier Refund + } + + + invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line']) + invoice_lines = self._refund_cleanup_lines(invoice_lines) + + tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line']) + tax_lines = filter(lambda l: l['manual'], tax_lines) + tax_lines = self._refund_cleanup_lines(tax_lines) + + invoice.update({ + 'type': type_dict[invoice['type']], + 'date_invoice': time.strftime('%Y-%m-%d'), + 'state': 'draft', + 'number': False, + 'invoice_line': invoice_lines, + 'tax_line': tax_lines + }) + + # take the id part of the tuple returned for many2one fields + for field in ('address_contact_id', 'address_invoice_id', 'partner_id', + 'account_id', 'currency_id', 'payment_term', 'journal_id'): + invoice[field] = invoice[field] and invoice[field][0] + + # create the new invoice + new_ids.append(self.create(cr, uid, invoice)) + return new_ids + + def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context={}, name=''): + #TODO check if we can use different period for payment and the writeoff line + assert len(ids)==1, "Can only pay one invoice at a time" + invoice = self.browse(cr, uid, ids[0]) + src_account_id = invoice.account_id.id + journal = self.pool.get('account.journal').browse(cr, uid, pay_journal_id) + if not name: + if journal.sequence_id: + name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id) + else: + raise osv.except_osv(_('No piece number !'), _('Can not create an automatic sequence for this piece !\n\nPut a sequence in the journal definition for automatic numbering or create a sequence manually for this piece.')) + types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1} + direction = types[invoice.type] + l1 = { + 'name': name, + 'debit': direction * pay_amount>0 and direction * pay_amount, + 'credit': direction * pay_amount<0 and - direction * pay_amount, + 'account_id': src_account_id, + 'partner_id': invoice.partner_id.id, + 'date': time.strftime('%Y-%m-%d'), + } + l2 = { + 'name':name, + 'debit': direction * pay_amount<0 and - direction * pay_amount, + 'credit': direction * pay_amount>0 and direction * pay_amount, + 'account_id': pay_account_id, + 'partner_id': invoice.partner_id.id, + 'date': time.strftime('%Y-%m-%d'), + } + + lines = [(0, 0, l1), (0, 0, l2)] + move = {'name': name, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id} + move_id = self.pool.get('account.move').create(cr, uid, move) + + line_ids = [] + total = 0.0 + line = self.pool.get('account.move.line') + cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')') + lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) ) + for l in lines: + if l.account_id.id==src_account_id: + line_ids.append(l.id) + total += (l.debit or 0.0) - (l.credit or 0.0) + if (not total) or writeoff_acc_id: + self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context) + else: + self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context) + return True account_invoice() class account_invoice_line(osv.osv): - def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): - res = {} - for line in self.browse(cr, uid, ids): - res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),2) - return res - - def _price_unit_default(self, cr, uid, context={}): - if 'check_total' in context: - t = context['check_total'] - for l in context.get('invoice_line', {}): - if len(l) >= 3 and l[2]: - tax_obj = self.pool.get('account.tax') - p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0) - t = t - (p * l[2].get('quantity')) - taxes = l[2].get('invoice_line_tax_id') - if len(taxes[0]) >= 3 and taxes[0][2]: - taxes=tax_obj.browse(cr, uid, taxes[0][2]) - for tax in tax_obj.compute(cr, uid, taxes, p,l[2].get('quantity'), context.get('address_invoice_id', False), l[2].get('product_id', False), context.get('partner_id', False)): - t = t - tax['amount'] - return t - return 0 - - _name = "account.invoice.line" - _description = "Invoice line" - _columns = { - 'name': fields.char('Description', size=256, required=True), - 'invoice_id': fields.many2one('account.invoice', 'Invoice Ref', ondelete='cascade', select=True), - 'uos_id': fields.many2one('product.uom', 'Unit', ondelete='set null'), - 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'), - 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')]), - 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), - 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True), - 'quantity': fields.float('Quantity', required=True), - 'discount': fields.float('Discount (%)', digits=(16,2)), - 'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]), - 'note': fields.text('Notes'), - 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), - } - _defaults = { - 'quantity': lambda *a: 1, - 'discount': lambda *a: 0.0, - 'price_unit': _price_unit_default, - } - - def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}): - tax_obj = self.pool.get('account.tax') - if price_unit: - taxes = tax_obj.browse(cr, uid, tax_id) - for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id): - price_unit = price_unit - tax['amount'] - return {'price_unit': price_unit,'invoice_line_tax_id': tax_id} - - def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, context={}): - if not product: - if type in ('in_invoice', 'in_refund'): - return {'domain':{'product_uom':[]}} - else: - return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} - lang=False - context.update({'lang': lang}) - res = self.pool.get('product.product').browse(cr, uid, product, context=context) - taxep=None - if partner_id: - lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang'] - taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax - tax_obj = self.pool.get('account.tax') - if type in ('out_invoice', 'out_refund'): - if not taxep or not taxep.id: - tax_id = map(lambda x: x.id, res.taxes_id) - else: - tax_id = [taxep.id] - for t in res.taxes_id: - if not t.tax_group==taxep.tax_group: - tax_id.append(t.id) - else: - if not taxep or not taxep.id: - tax_id = map(lambda x: x.id, res.supplier_taxes_id) - else: - tax_id = [taxep.id] - for t in res.supplier_taxes_id: - if not t.tax_group==taxep.tax_group: - tax_id.append(t.id) - if type in ('in_invoice', 'in_refund'): - result = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context) - else: - result = {'price_unit': res.list_price, 'invoice_line_tax_id': tax_id} - - if not name: - result['name'] = res.name - - if type in ('out_invoice','out_refund'): - a = res.product_tmpl_id.property_account_income.id - if not a: - a = res.categ_id.property_account_income_categ.id - else: - a = res.product_tmpl_id.property_account_expense.id - if not a: - a = res.categ_id.property_account_expense_categ.id - if a: - result['account_id'] = a - - domain = {} - result['uos_id'] = uom or res.uom_id.id or False - if result['uos_id']: - res2 = res.uom_id.category_id.id - if res2 : - domain = {'uos_id':[('category_id','=',res2 )]} - return {'value':result, 'domain':domain} - - def move_line_get(self, cr, uid, invoice_id, context={}): - res = [] - tax_grouped = {} - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - ait_obj = self.pool.get('account.invoice.tax') - inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) - company_currency = inv.company_id.currency_id.id - cur = inv.currency_id - - for line in inv.invoice_line: - mres = self.move_line_get_item(cr, uid, line, context) - if not mres: - continue - res.append(mres) - tax_code_found= False - for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, - (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)), - line.quantity, inv.address_invoice_id.id, line.product_id, - inv.partner_id): - - if inv.type in ('out_invoice', 'in_invoice'): - tax_code_id = tax['base_code_id'] - tax_amount = line.price_subtotal * tax['base_sign'] - else: - tax_code_id = tax['ref_base_code_id'] - tax_amount = line.price_subtotal * tax['ref_base_sign'] - - if tax_code_found: - if not tax_code_id: - continue - res.append(self.move_line_get_item(cr, uid, line, context)) - res[-1]['price'] = 0.0 - res[-1]['account_analytic_id'] = False - elif not tax_code_id: - continue - tax_code_found = True - - res[-1]['tax_code_id'] = tax_code_id - res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice}) - return res - - def move_line_get_item(self, cr, uid, line, context={}): - return { - 'type':'src', - 'name': line.name[:64], - 'price_unit':line.price_unit, - 'quantity':line.quantity, - 'price':line.price_subtotal, - 'account_id':line.account_id.id, - 'product_id':line.product_id.id, - 'uos_id':line.uos_id.id, - 'account_analytic_id':line.account_analytic_id.id, - } - # - # Set the tax field according to the account and the partner - # - def onchange_account_id(self, cr, uid, ids, partner_id,account_id): - if not (partner_id and account_id): - return {} - taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids - taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax - if not taxep.id: - return {'value': {'invoice_line_tax_id': map(lambda x: x.id, taxes or []) }} - res = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - res.append(t.id) - r = {'value':{'invoice_line_tax_id': res}} - return r + def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): + res = {} + for line in self.browse(cr, uid, ids): + res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),2) + return res + + def _price_unit_default(self, cr, uid, context={}): + if 'check_total' in context: + t = context['check_total'] + for l in context.get('invoice_line', {}): + if len(l) >= 3 and l[2]: + tax_obj = self.pool.get('account.tax') + p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0) + t = t - (p * l[2].get('quantity')) + taxes = l[2].get('invoice_line_tax_id') + if len(taxes[0]) >= 3 and taxes[0][2]: + taxes=tax_obj.browse(cr, uid, taxes[0][2]) + for tax in tax_obj.compute(cr, uid, taxes, p,l[2].get('quantity'), context.get('address_invoice_id', False), l[2].get('product_id', False), context.get('partner_id', False)): + t = t - tax['amount'] + return t + return 0 + + _name = "account.invoice.line" + _description = "Invoice line" + _columns = { + 'name': fields.char('Description', size=256, required=True), + 'invoice_id': fields.many2one('account.invoice', 'Invoice Ref', ondelete='cascade', select=True), + 'uos_id': fields.many2one('product.uom', 'Unit', ondelete='set null'), + 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'), + 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')]), + 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), + 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True), + 'quantity': fields.float('Quantity', required=True), + 'discount': fields.float('Discount (%)', digits=(16,2)), + 'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]), + 'note': fields.text('Notes'), + 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), + } + _defaults = { + 'quantity': lambda *a: 1, + 'discount': lambda *a: 0.0, + 'price_unit': _price_unit_default, + } + + def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}): + tax_obj = self.pool.get('account.tax') + if price_unit: + taxes = tax_obj.browse(cr, uid, tax_id) + for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id): + price_unit = price_unit - tax['amount'] + return {'price_unit': price_unit,'invoice_line_tax_id': tax_id} + + def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, context={}): + if not product: + if type in ('in_invoice', 'in_refund'): + return {'domain':{'product_uom':[]}} + else: + return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} + lang=False + context.update({'lang': lang}) + res = self.pool.get('product.product').browse(cr, uid, product, context=context) + taxep=None + if partner_id: + lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang'] + taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax + tax_obj = self.pool.get('account.tax') + if type in ('out_invoice', 'out_refund'): + if not taxep or not taxep.id: + tax_id = map(lambda x: x.id, res.taxes_id) + else: + tax_id = [taxep.id] + for t in res.taxes_id: + if not t.tax_group==taxep.tax_group: + tax_id.append(t.id) + else: + if not taxep or not taxep.id: + tax_id = map(lambda x: x.id, res.supplier_taxes_id) + else: + tax_id = [taxep.id] + for t in res.supplier_taxes_id: + if not t.tax_group==taxep.tax_group: + tax_id.append(t.id) + if type in ('in_invoice', 'in_refund'): + result = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context) + else: + result = {'price_unit': res.list_price, 'invoice_line_tax_id': tax_id} + + if not name: + result['name'] = res.name + + if type in ('out_invoice','out_refund'): + a = res.product_tmpl_id.property_account_income.id + if not a: + a = res.categ_id.property_account_income_categ.id + else: + a = res.product_tmpl_id.property_account_expense.id + if not a: + a = res.categ_id.property_account_expense_categ.id + if a: + result['account_id'] = a + + domain = {} + result['uos_id'] = uom or res.uom_id.id or False + if result['uos_id']: + res2 = res.uom_id.category_id.id + if res2 : + domain = {'uos_id':[('category_id','=',res2 )]} + return {'value':result, 'domain':domain} + + def move_line_get(self, cr, uid, invoice_id, context={}): + res = [] + tax_grouped = {} + tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') + ait_obj = self.pool.get('account.invoice.tax') + inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) + company_currency = inv.company_id.currency_id.id + cur = inv.currency_id + + for line in inv.invoice_line: + mres = self.move_line_get_item(cr, uid, line, context) + if not mres: + continue + res.append(mres) + tax_code_found= False + for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, + (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)), + line.quantity, inv.address_invoice_id.id, line.product_id, + inv.partner_id): + + if inv.type in ('out_invoice', 'in_invoice'): + tax_code_id = tax['base_code_id'] + tax_amount = line.price_subtotal * tax['base_sign'] + else: + tax_code_id = tax['ref_base_code_id'] + tax_amount = line.price_subtotal * tax['ref_base_sign'] + + if tax_code_found: + if not tax_code_id: + continue + res.append(self.move_line_get_item(cr, uid, line, context)) + res[-1]['price'] = 0.0 + res[-1]['account_analytic_id'] = False + elif not tax_code_id: + continue + tax_code_found = True + + res[-1]['tax_code_id'] = tax_code_id + res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice}) + return res + + def move_line_get_item(self, cr, uid, line, context={}): + return { + 'type':'src', + 'name': line.name[:64], + 'price_unit':line.price_unit, + 'quantity':line.quantity, + 'price':line.price_subtotal, + 'account_id':line.account_id.id, + 'product_id':line.product_id.id, + 'uos_id':line.uos_id.id, + 'account_analytic_id':line.account_analytic_id.id, + } + # + # Set the tax field according to the account and the partner + # + def onchange_account_id(self, cr, uid, ids, partner_id,account_id): + if not (partner_id and account_id): + return {} + taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids + taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax + if not taxep.id: + return {'value': {'invoice_line_tax_id': map(lambda x: x.id, taxes or []) }} + res = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + res.append(t.id) + r = {'value':{'invoice_line_tax_id': res}} + return r account_invoice_line() class account_invoice_tax(osv.osv): - _name = "account.invoice.tax" - _description = "Invoice Tax" - _columns = { - 'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True), - 'name': fields.char('Tax Description', size=64, required=True), - 'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]), - 'base': fields.float('Base', digits=(16,2)), - 'amount': fields.float('Amount', digits=(16,2)), - 'manual': fields.boolean('Manual'), - 'sequence': fields.integer('Sequence'), - - 'base_code_id': fields.many2one('account.tax.code', 'Base Code'), - 'base_amount': fields.float('Base Code Amount', digits=(16,2)), - 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code'), - 'tax_amount': fields.float('Tax Code Amount', digits=(16,2)), - } - def base_change(self, cr, uid, ids, base): - return {'value': {'base_amount':base}} - def amount_change(self, cr, uid, ids, amount): - return {'value': {'tax_amount':amount}} - _order = 'sequence' - _defaults = { - 'manual': lambda *a: 1, - 'base_amount': lambda *a: 0.0, - 'tax_amount': lambda *a: 0.0, - } - def compute(self, cr, uid, invoice_id): - tax_grouped = {} - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) - cur = inv.currency_id - company_currency = inv.company_id.currency_id.id - - for line in inv.invoice_line: - for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id): - val={} - val['invoice_id'] = inv.id - val['name'] = tax['name'] - val['amount'] = cur_obj.round(cr, uid, cur, tax['amount']) - val['manual'] = False - val['sequence'] = tax['sequence'] - val['base'] = tax['price_unit'] * line['quantity'] - - if inv.type in ('out_invoice','in_invoice'): - val['base_code_id'] = tax['base_code_id'] - val['tax_code_id'] = tax['tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice}) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice}) - val['account_id'] = tax['account_collected_id'] or line.account_id.id - else: - val['base_code_id'] = tax['ref_base_code_id'] - val['tax_code_id'] = tax['ref_tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice}) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice}) - val['account_id'] = tax['account_paid_id'] or line.account_id.id - - key = (val['tax_code_id'], val['base_code_id'], val['account_id']) - if not key in tax_grouped: - tax_grouped[key] = val - else: - tax_grouped[key]['amount'] += val['amount'] - tax_grouped[key]['base'] += val['base'] - tax_grouped[key]['base_amount'] += val['base_amount'] - tax_grouped[key]['tax_amount'] += val['tax_amount'] - - return tax_grouped - - def move_line_get(self, cr, uid, invoice_id): - res = [] - cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%d', (invoice_id,)) - for t in cr.dictfetchall(): - if not t['amount'] \ - and not t['tax_code_id'] \ - and not t['tax_amount']: - continue - res.append({ - 'type':'tax', - 'name':t['name'], - 'price_unit': t['amount'], - 'quantity': 1, - 'price': t['amount'] or 0.0, - 'account_id': t['account_id'], - 'tax_code_id': t['tax_code_id'], - 'tax_amount': t['tax_amount'] - }) - return res + _name = "account.invoice.tax" + _description = "Invoice Tax" + _columns = { + 'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True), + 'name': fields.char('Tax Description', size=64, required=True), + 'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]), + 'base': fields.float('Base', digits=(16,2)), + 'amount': fields.float('Amount', digits=(16,2)), + 'manual': fields.boolean('Manual'), + 'sequence': fields.integer('Sequence'), + + 'base_code_id': fields.many2one('account.tax.code', 'Base Code'), + 'base_amount': fields.float('Base Code Amount', digits=(16,2)), + 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code'), + 'tax_amount': fields.float('Tax Code Amount', digits=(16,2)), + } + def base_change(self, cr, uid, ids, base): + return {'value': {'base_amount':base}} + def amount_change(self, cr, uid, ids, amount): + return {'value': {'tax_amount':amount}} + _order = 'sequence' + _defaults = { + 'manual': lambda *a: 1, + 'base_amount': lambda *a: 0.0, + 'tax_amount': lambda *a: 0.0, + } + def compute(self, cr, uid, invoice_id): + tax_grouped = {} + tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') + inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) + cur = inv.currency_id + company_currency = inv.company_id.currency_id.id + + for line in inv.invoice_line: + for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id): + val={} + val['invoice_id'] = inv.id + val['name'] = tax['name'] + val['amount'] = cur_obj.round(cr, uid, cur, tax['amount']) + val['manual'] = False + val['sequence'] = tax['sequence'] + val['base'] = tax['price_unit'] * line['quantity'] + + if inv.type in ('out_invoice','in_invoice'): + val['base_code_id'] = tax['base_code_id'] + val['tax_code_id'] = tax['tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice}) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice}) + val['account_id'] = tax['account_collected_id'] or line.account_id.id + else: + val['base_code_id'] = tax['ref_base_code_id'] + val['tax_code_id'] = tax['ref_tax_code_id'] + val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice}) + val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice}) + val['account_id'] = tax['account_paid_id'] or line.account_id.id + + key = (val['tax_code_id'], val['base_code_id'], val['account_id']) + if not key in tax_grouped: + tax_grouped[key] = val + else: + tax_grouped[key]['amount'] += val['amount'] + tax_grouped[key]['base'] += val['base'] + tax_grouped[key]['base_amount'] += val['base_amount'] + tax_grouped[key]['tax_amount'] += val['tax_amount'] + + return tax_grouped + + def move_line_get(self, cr, uid, invoice_id): + res = [] + cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%d', (invoice_id,)) + for t in cr.dictfetchall(): + if not t['amount'] \ + and not t['tax_code_id'] \ + and not t['tax_amount']: + continue + res.append({ + 'type':'tax', + 'name':t['name'], + 'price_unit': t['amount'], + 'quantity': 1, + 'price': t['amount'] or 0.0, + 'account_id': t['account_id'], + 'tax_code_id': t['tax_code_id'], + 'tax_amount': t['tax_amount'] + }) + return res account_invoice_tax() diff --git a/addons/account/partner.py b/addons/account/partner.py index 539a8ef2d2f..97d0cf9544d 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -32,94 +32,94 @@ from osv import fields, osv import ir class res_partner(osv.osv): - _name = 'res.partner' - _inherit = 'res.partner' - _description = 'Partner' - def _credit_get(self, cr, uid, ids, name, arg, context): - res={} - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - for id in ids: - cr.execute("select sum(debit-credit) from account_move_line as l where account_id in (select id from account_account where type = %s and active) and partner_id=%d and reconcile_id is null and "+query, ('receivable', id)) - res[id]=cr.fetchone()[0] or 0.0 - return res + _name = 'res.partner' + _inherit = 'res.partner' + _description = 'Partner' + def _credit_get(self, cr, uid, ids, name, arg, context): + res={} + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + for id in ids: + cr.execute("select sum(debit-credit) from account_move_line as l where account_id in (select id from account_account where type = %s and active) and partner_id=%d and reconcile_id is null and "+query, ('receivable', id)) + res[id]=cr.fetchone()[0] or 0.0 + return res - def _debit_get(self, cr, uid, ids, name, arg, context): - res={} - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) - for id in ids: - cr.execute("select sum(debit-credit) from account_move_line as l where account_id in (select id from account_account where type = %s and active) and partner_id=%d and reconcile_id is null and "+query, ('payable', id)) - res[id]=cr.fetchone()[0] or 0.0 - return res + def _debit_get(self, cr, uid, ids, name, arg, context): + res={} + query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + for id in ids: + cr.execute("select sum(debit-credit) from account_move_line as l where account_id in (select id from account_account where type = %s and active) and partner_id=%d and reconcile_id is null and "+query, ('payable', id)) + res[id]=cr.fetchone()[0] or 0.0 + return res - def _credit_search(self, cr, uid, obj, name, args): - if not len(args): - return [] - where = ' and '.join(map(lambda x: '(sum(debit-credit)'+x[1]+str(x[2])+')',args)) - query = self.pool.get('account.move.line')._query_get(cr, uid, context={}) - cr.execute(('select partner_id from account_move_line l where account_id in (select id from account_account where type=%s and active) and reconcile_id is null and '+query+' and partner_id is not null group by partner_id having '+where), ('receivable',) ) - res = cr.fetchall() - if not len(res): - return [('id','=','0')] - return [('id','in',map(lambda x:x[0], res))] + def _credit_search(self, cr, uid, obj, name, args): + if not len(args): + return [] + where = ' and '.join(map(lambda x: '(sum(debit-credit)'+x[1]+str(x[2])+')',args)) + query = self.pool.get('account.move.line')._query_get(cr, uid, context={}) + cr.execute(('select partner_id from account_move_line l where account_id in (select id from account_account where type=%s and active) and reconcile_id is null and '+query+' and partner_id is not null group by partner_id having '+where), ('receivable',) ) + res = cr.fetchall() + if not len(res): + return [('id','=','0')] + return [('id','in',map(lambda x:x[0], res))] - def _debit_search(self, cr, uid, obj, name, args): - if not len(args): - return [] - query = self.pool.get('account.move.line')._query_get(cr, uid, context={}) - where = ' and '.join(map(lambda x: '(sum(debit-credit)'+x[1]+str(x[2])+')',args)) - cr.execute(('select partner_id from account_move_line l where account_id in (select id from account_account where type=%s and active) and reconcile_id is null and '+query+' and partner_id is not null group by partner_id having '+where), ('payable',) ) - res = cr.fetchall() - if not len(res): - return [('id','=','0')] - return [('id','in',map(lambda x:x[0], res))] + def _debit_search(self, cr, uid, obj, name, args): + if not len(args): + return [] + query = self.pool.get('account.move.line')._query_get(cr, uid, context={}) + where = ' and '.join(map(lambda x: '(sum(debit-credit)'+x[1]+str(x[2])+')',args)) + cr.execute(('select partner_id from account_move_line l where account_id in (select id from account_account where type=%s and active) and reconcile_id is null and '+query+' and partner_id is not null group by partner_id having '+where), ('payable',) ) + res = cr.fetchall() + if not len(res): + return [('id','=','0')] + return [('id','in',map(lambda x:x[0], res))] - _columns = { - 'credit': fields.function(_credit_get, fnct_search=_credit_search, method=True, string='Total Receivable'), - 'debit': fields.function(_debit_get, fnct_search=_debit_search, method=True, string='Total Payable'), - 'debit_limit': fields.float('Payable Limit'), - 'property_account_payable': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Account Payable", - method=True, - view_load=True, - group_name="Accounting Properties", - domain="[('type', '=', 'payable')]", - help="This account will be used, instead of the default one, as the payable account for the current partner", - required=True), - 'property_account_receivable': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Account Receivable", - method=True, - view_load=True, - group_name="Accounting Properties", - domain="[('type', '=', 'receivable')]", - help="This account will be used, instead of the default one, as the receivable account for the current partner", - required=True), - 'property_account_tax': fields.property( - 'account.tax', - type='many2one', - relation='account.tax', - string="Default Tax", - method=True, - view_load=True, - group_name="Accounting Properties", - help="This tax will be used, instead of the default one."), - 'property_payment_term': fields.property( - 'account.payment.term', - type='many2one', - relation='account.payment.term', - string ='Payment Term', - method=True, - view_load=True, - group_name="Accounting Properties", - help="This payment term will be used, instead of the default one, for the current partner"), - 'ref_companies': fields.one2many('res.company', 'partner_id', - 'Companies that refers to partner'), - } + _columns = { + 'credit': fields.function(_credit_get, fnct_search=_credit_search, method=True, string='Total Receivable'), + 'debit': fields.function(_debit_get, fnct_search=_debit_search, method=True, string='Total Payable'), + 'debit_limit': fields.float('Payable Limit'), + 'property_account_payable': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Account Payable", + method=True, + view_load=True, + group_name="Accounting Properties", + domain="[('type', '=', 'payable')]", + help="This account will be used, instead of the default one, as the payable account for the current partner", + required=True), + 'property_account_receivable': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Account Receivable", + method=True, + view_load=True, + group_name="Accounting Properties", + domain="[('type', '=', 'receivable')]", + help="This account will be used, instead of the default one, as the receivable account for the current partner", + required=True), + 'property_account_tax': fields.property( + 'account.tax', + type='many2one', + relation='account.tax', + string="Default Tax", + method=True, + view_load=True, + group_name="Accounting Properties", + help="This tax will be used, instead of the default one."), + 'property_payment_term': fields.property( + 'account.payment.term', + type='many2one', + relation='account.payment.term', + string ='Payment Term', + method=True, + view_load=True, + group_name="Accounting Properties", + help="This payment term will be used, instead of the default one, for the current partner"), + 'ref_companies': fields.one2many('res.company', 'partner_id', + 'Companies that refers to partner'), + } res_partner() diff --git a/addons/account/product.py b/addons/account/product.py index 5c4d2ffa26f..151d6218c69 100644 --- a/addons/account/product.py +++ b/addons/account/product.py @@ -33,58 +33,58 @@ import netsvc from osv import fields, osv class product_category(osv.osv): - _inherit = "product.category" - _columns = { - 'property_account_income_categ': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Income Account", - method=True, - view_load=True, - group_name="Accounting Properties", - help="This account will be used, instead of the default one, to value incoming stock for the current product category"), - 'property_account_expense_categ': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Expense Account", - method=True, - view_load=True, - group_name="Accounting Properties", - help="This account will be used, instead of the default one, to value outgoing stock for the current product category"), - } + _inherit = "product.category" + _columns = { + 'property_account_income_categ': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Income Account", + method=True, + view_load=True, + group_name="Accounting Properties", + help="This account will be used, instead of the default one, to value incoming stock for the current product category"), + 'property_account_expense_categ': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Expense Account", + method=True, + view_load=True, + group_name="Accounting Properties", + help="This account will be used, instead of the default one, to value outgoing stock for the current product category"), + } product_category() #---------------------------------------------------------- # Products #---------------------------------------------------------- class product_template(osv.osv): - _inherit = "product.template" - _columns = { - 'taxes_id': fields.many2many('account.tax', 'product_taxes_rel', - 'prod_id', 'tax_id', 'Customer Taxes', - domain=[('parent_id','=',False)]), - 'supplier_taxes_id': fields.many2many('account.tax', - 'product_supplier_taxes_rel', 'prod_id', 'tax_id', - 'Supplier Taxes', domain=[('parent_id', '=', False)]), - 'property_account_income': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Income Account", - method=True, - view_load=True, - group_name="Accounting Properties", - help="This account will be used, instead of the default one, to value incoming stock for the current product"), - 'property_account_expense': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Expense Account", - method=True, - view_load=True, - group_name="Accounting Properties", - help="This account will be used, instead of the default one, to value outgoing stock for the current product"), - } + _inherit = "product.template" + _columns = { + 'taxes_id': fields.many2many('account.tax', 'product_taxes_rel', + 'prod_id', 'tax_id', 'Customer Taxes', + domain=[('parent_id','=',False)]), + 'supplier_taxes_id': fields.many2many('account.tax', + 'product_supplier_taxes_rel', 'prod_id', 'tax_id', + 'Supplier Taxes', domain=[('parent_id', '=', False)]), + 'property_account_income': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Income Account", + method=True, + view_load=True, + group_name="Accounting Properties", + help="This account will be used, instead of the default one, to value incoming stock for the current product"), + 'property_account_expense': fields.property( + 'account.account', + type='many2one', + relation='account.account', + string="Expense Account", + method=True, + view_load=True, + group_name="Accounting Properties", + help="This account will be used, instead of the default one, to value outgoing stock for the current product"), + } product_template() diff --git a/addons/account/project/project.py b/addons/account/project/project.py index 1bf9bf0a4c4..c2ca84fb738 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -38,202 +38,202 @@ from osv import osv # class account_analytic_account(osv.osv): - _name = 'account.analytic.account' - _description = 'Analytic Accounts' - - def _credit_calc(self, cr, uid, ids, name, arg, context={}): - acc_set = ",".join(map(str, ids)) - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount<0 and a.id IN (%s) GROUP BY a.id" % acc_set) - r= dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - - def _debit_calc(self, cr, uid, ids, name, arg, context={}): - - acc_set = ",".join(map(str, ids)) - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount>0 and a.id IN (%s) GROUP BY a.id" % acc_set) - r= dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - - - - def _balance_calc(self, cr, uid, ids, name, arg, context={}): - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - acc_set = ",".join(map(str, ids2)) - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE a.id IN (%s) GROUP BY a.id" % acc_set) - res = {} - for account_id, sum in cr.fetchall(): - res[account_id] = sum - - cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id in (%s)" % acc_set) - - currency= dict(cr.fetchall()) - - res_currency= self.pool.get('res.currency') - for id in ids: - for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): - if child <> id: - res.setdefault(id, 0.0) - if currency[child]<>currency[id] : - res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) - else: - res[id] += res.get(child, 0.0) - - cur_obj = res_currency.browse(cr,uid,currency.values(),context) - cur_obj = dict([(o.id, o) for o in cur_obj]) - for id in ids: - res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) - - return dict([(i, res[i]) for i in ids ]) - - def _quantity_calc(self, cr, uid, ids, name, arg, context={}): - #XXX must convert into one uom - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - acc_set = ",".join(map(str, ids2)) - cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \ - FROM account_analytic_account a \ - LEFT JOIN account_analytic_line l ON (a.id = l.account_id) \ - WHERE a.id IN ('+acc_set+') GROUP BY a.id') - res = {} - for account_id, sum in cr.fetchall(): - res[account_id] = sum - - for id in ids: - for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): - if child <> id: - res.setdefault(id, 0.0) - res[id] += res.get(child, 0.0) - return dict([(i, res[i]) for i in ids]) - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','parent_id'], context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res - - def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res = self.name_get(cr, uid, ids) - return dict(res) - - def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): - result = {} - for rec in self.browse(cr, uid, ids, context): - result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False - return result - - _columns = { - 'name' : fields.char('Account name', size=64, required=True), - 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Account Name'), - 'code' : fields.char('Account code', size=24), - 'active' : fields.boolean('Active'), - 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account type'), - 'description' : fields.text('Description'), - 'parent_id': fields.many2one('account.analytic.account', 'Parent analytic account', select=2), - 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Childs Accounts'), - 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic entries'), - 'balance' : fields.function(_balance_calc, method=True, type='float', string='Balance'), - 'debit' : fields.function(_debit_calc, method=True, type='float', string='Debit'), - 'credit' : fields.function(_credit_calc, method=True, type='float', string='Credit'), - 'quantity': fields.function(_quantity_calc, method=True, type='float', string='Quantity'), - 'quantity_max': fields.float('Maximal quantity'), - 'partner_id' : fields.many2one('res.partner', 'Associated partner'), - 'contact_id' : fields.many2one('res.partner.address', 'Contact'), - 'user_id' : fields.many2one('res.users', 'Account Manager'), - 'date_start': fields.date('Date Start'), - 'date': fields.date('Date End'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'), - 'state': fields.selection([('draft','Draft'), ('open','Open'), ('pending','Pending'), ('close','Close'),], 'State', required=True), - } - - def _default_company(self, cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - return user.company_id.id - return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - _defaults = { - 'active' : lambda *a : True, - 'type' : lambda *a : 'normal', - 'company_id': _default_company, - 'state' : lambda *a : 'draft', - 'user_id' : lambda self,cr,uid,ctx : uid - } - - def check_recursion(self, cr, uid, ids, parent=None): - return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent) - - _order = 'parent_id desc,code' - _constraints = [ - (check_recursion, 'Error! You can not create recursive account.', ['parent_id']) - ] - - def create(self, cr, uid, vals, context=None): - parent_id = vals.get('parent_id', 0) - if ('code' not in vals or not vals['code']) and not parent_id: - vals['code'] = self.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account') - return super(account_analytic_account, self).create(cr, uid, vals, context=context) - - def copy(self, cr, uid, id, default=None, context={}): - if not default: - default = {} - default['code'] = False - return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) - - - def on_change_parent(self, cr, uid, id, parent_id): - if not parent_id: - return {} - parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0] - childs = self.search(cr, uid, [('parent_id', '=', parent_id), ('active', 'in', [True, False])]) - numchild = len(childs) - if parent['partner_id']: - partner = parent['partner_id'][0] - else: - partner = False - res = {'value' : {'code' : '%s - %03d' % (parent['code'] or '', numchild + 1),}} - if partner: - res['value']['partner_id'] = partner - return res - - def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - account = self.search(cr, uid, [('code', '=', name)]+args, limit=limit, context=context) - if not account: - account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)]+args, limit=limit, context=context) - newacc = account - while newacc: - newacc = self.search(cr, uid, [('parent_id', 'in', newacc)]+args, limit=limit, context=context) - account+=newacc - return self.name_get(cr, uid, account, context=context) + _name = 'account.analytic.account' + _description = 'Analytic Accounts' + + def _credit_calc(self, cr, uid, ids, name, arg, context={}): + acc_set = ",".join(map(str, ids)) + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount<0 and a.id IN (%s) GROUP BY a.id" % acc_set) + r= dict(cr.fetchall()) + for i in ids: + r.setdefault(i,0.0) + return r + + def _debit_calc(self, cr, uid, ids, name, arg, context={}): + + acc_set = ",".join(map(str, ids)) + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount>0 and a.id IN (%s) GROUP BY a.id" % acc_set) + r= dict(cr.fetchall()) + for i in ids: + r.setdefault(i,0.0) + return r + + + + def _balance_calc(self, cr, uid, ids, name, arg, context={}): + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE a.id IN (%s) GROUP BY a.id" % acc_set) + res = {} + for account_id, sum in cr.fetchall(): + res[account_id] = sum + + cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id in (%s)" % acc_set) + + currency= dict(cr.fetchall()) + + res_currency= self.pool.get('res.currency') + for id in ids: + for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): + if child <> id: + res.setdefault(id, 0.0) + if currency[child]<>currency[id] : + res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) + else: + res[id] += res.get(child, 0.0) + + cur_obj = res_currency.browse(cr,uid,currency.values(),context) + cur_obj = dict([(o.id, o) for o in cur_obj]) + for id in ids: + res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) + + return dict([(i, res[i]) for i in ids ]) + + def _quantity_calc(self, cr, uid, ids, name, arg, context={}): + #XXX must convert into one uom + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \ + FROM account_analytic_account a \ + LEFT JOIN account_analytic_line l ON (a.id = l.account_id) \ + WHERE a.id IN ('+acc_set+') GROUP BY a.id') + res = {} + for account_id, sum in cr.fetchall(): + res[account_id] = sum + + for id in ids: + for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): + if child <> id: + res.setdefault(id, 0.0) + res[id] += res.get(child, 0.0) + return dict([(i, res[i]) for i in ids]) + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','parent_id'], context) + res = [] + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1]+' / '+name + res.append((record['id'], name)) + return res + + def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res = self.name_get(cr, uid, ids) + return dict(res) + + def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): + result = {} + for rec in self.browse(cr, uid, ids, context): + result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False + return result + + _columns = { + 'name' : fields.char('Account name', size=64, required=True), + 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Account Name'), + 'code' : fields.char('Account code', size=24), + 'active' : fields.boolean('Active'), + 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account type'), + 'description' : fields.text('Description'), + 'parent_id': fields.many2one('account.analytic.account', 'Parent analytic account', select=2), + 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Childs Accounts'), + 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic entries'), + 'balance' : fields.function(_balance_calc, method=True, type='float', string='Balance'), + 'debit' : fields.function(_debit_calc, method=True, type='float', string='Debit'), + 'credit' : fields.function(_credit_calc, method=True, type='float', string='Credit'), + 'quantity': fields.function(_quantity_calc, method=True, type='float', string='Quantity'), + 'quantity_max': fields.float('Maximal quantity'), + 'partner_id' : fields.many2one('res.partner', 'Associated partner'), + 'contact_id' : fields.many2one('res.partner.address', 'Contact'), + 'user_id' : fields.many2one('res.users', 'Account Manager'), + 'date_start': fields.date('Date Start'), + 'date': fields.date('Date End'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'), + 'state': fields.selection([('draft','Draft'), ('open','Open'), ('pending','Pending'), ('close','Close'),], 'State', required=True), + } + + def _default_company(self, cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.id + return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + _defaults = { + 'active' : lambda *a : True, + 'type' : lambda *a : 'normal', + 'company_id': _default_company, + 'state' : lambda *a : 'draft', + 'user_id' : lambda self,cr,uid,ctx : uid + } + + def check_recursion(self, cr, uid, ids, parent=None): + return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent) + + _order = 'parent_id desc,code' + _constraints = [ + (check_recursion, 'Error! You can not create recursive account.', ['parent_id']) + ] + + def create(self, cr, uid, vals, context=None): + parent_id = vals.get('parent_id', 0) + if ('code' not in vals or not vals['code']) and not parent_id: + vals['code'] = self.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account') + return super(account_analytic_account, self).create(cr, uid, vals, context=context) + + def copy(self, cr, uid, id, default=None, context={}): + if not default: + default = {} + default['code'] = False + return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) + + + def on_change_parent(self, cr, uid, id, parent_id): + if not parent_id: + return {} + parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0] + childs = self.search(cr, uid, [('parent_id', '=', parent_id), ('active', 'in', [True, False])]) + numchild = len(childs) + if parent['partner_id']: + partner = parent['partner_id'][0] + else: + partner = False + res = {'value' : {'code' : '%s - %03d' % (parent['code'] or '', numchild + 1),}} + if partner: + res['value']['partner_id'] = partner + return res + + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + account = self.search(cr, uid, [('code', '=', name)]+args, limit=limit, context=context) + if not account: + account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)]+args, limit=limit, context=context) + newacc = account + while newacc: + newacc = self.search(cr, uid, [('parent_id', 'in', newacc)]+args, limit=limit, context=context) + account+=newacc + return self.name_get(cr, uid, account, context=context) account_analytic_account() class account_analytic_journal(osv.osv): - _name = 'account.analytic.journal' - _columns = { - 'name' : fields.char('Journal name', size=64, required=True), - 'code' : fields.char('Journal code', size=8), - 'active' : fields.boolean('Active'), - 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True, help="Gives the type of the analytic journal. When a document (eg: an invoice) needs to create analytic entries, Tiny ERP will look for a matching journal of the same type."), - 'line_ids' : fields.one2many('account.analytic.line', 'journal_id', 'Lines'), - } - _defaults = { - 'active': lambda *a: True, - 'type': lambda *a: 'general', - } + _name = 'account.analytic.journal' + _columns = { + 'name' : fields.char('Journal name', size=64, required=True), + 'code' : fields.char('Journal code', size=8), + 'active' : fields.boolean('Active'), + 'type': fields.selection([('sale','Sale'), ('purchase','Purchase'), ('cash','Cash'), ('general','General'), ('situation','Situation')], 'Type', size=32, required=True, help="Gives the type of the analytic journal. When a document (eg: an invoice) needs to create analytic entries, Tiny ERP will look for a matching journal of the same type."), + 'line_ids' : fields.one2many('account.analytic.line', 'journal_id', 'Lines'), + } + _defaults = { + 'active': lambda *a: True, + 'type': lambda *a: 'general', + } account_analytic_journal() @@ -242,42 +242,42 @@ account_analytic_journal() # --------------------------------------------------------- #class account_analytic_budget_post(osv.osv): -# _name = 'account.analytic.budget.post' -# _description = 'Budget item' -# _columns = { -# 'code': fields.char('Code', size=64, required=True), -# 'name': fields.char('Name', size=256, required=True), -# 'sens': fields.selection( [('charge','Charge'), ('produit','Product')], 'Direction', required=True), -# 'dotation_ids': fields.one2many('account.analytic.budget.post.dotation', 'post_id', 'Expenses'), -# 'account_ids': fields.many2many('account.analytic.account', 'account_analytic_budget_rel', 'budget_id', 'account_id', 'Accounts'), -# } -# _defaults = { -# 'sens': lambda *a: 'produit', -# } +# _name = 'account.analytic.budget.post' +# _description = 'Budget item' +# _columns = { +# 'code': fields.char('Code', size=64, required=True), +# 'name': fields.char('Name', size=256, required=True), +# 'sens': fields.selection( [('charge','Charge'), ('produit','Product')], 'Direction', required=True), +# 'dotation_ids': fields.one2many('account.analytic.budget.post.dotation', 'post_id', 'Expenses'), +# 'account_ids': fields.many2many('account.analytic.account', 'account_analytic_budget_rel', 'budget_id', 'account_id', 'Accounts'), +# } +# _defaults = { +# 'sens': lambda *a: 'produit', +# } # -# def spread(self, cr, uid, ids, fiscalyear_id=False, quantity=0.0, amount=0.0): +# def spread(self, cr, uid, ids, fiscalyear_id=False, quantity=0.0, amount=0.0): # -# dobj = self.pool.get('account.analytic.budget.post.dotation') -# for o in self.browse(cr, uid, ids): -# # delete dotations for this post -# dobj.unlink(cr, uid, dobj.search(cr, uid, [('post_id','=',o.id)])) +# dobj = self.pool.get('account.analytic.budget.post.dotation') +# for o in self.browse(cr, uid, ids): +# # delete dotations for this post +# dobj.unlink(cr, uid, dobj.search(cr, uid, [('post_id','=',o.id)])) # -# # create one dotation per period in the fiscal year, and spread the total amount/quantity over those dotations -# fy = self.pool.get('account.fiscalyear').browse(cr, uid, [fiscalyear_id])[0] -# num = len(fy.period_ids) -# for p in fy.period_ids: -# dobj.create(cr, uid, {'post_id': o.id, 'period_id': p.id, 'quantity': quantity/num, 'amount': amount/num}) -# return True +# # create one dotation per period in the fiscal year, and spread the total amount/quantity over those dotations +# fy = self.pool.get('account.fiscalyear').browse(cr, uid, [fiscalyear_id])[0] +# num = len(fy.period_ids) +# for p in fy.period_ids: +# dobj.create(cr, uid, {'post_id': o.id, 'period_id': p.id, 'quantity': quantity/num, 'amount': amount/num}) +# return True #account_analytic_budget_post() # #class account_analytic_budget_post_dotation(osv.osv): -# _name = 'account.analytic.budget.post.dotation' -# _description = "Budget item endowment" -# _columns = { -# 'name': fields.char('Name', size=64), -# 'post_id': fields.many2one('account.analytic.budget.post', 'Item', select=True), -# 'period_id': fields.many2one('account.period', 'Period'), -# 'quantity': fields.float('Quantity', digits=(16,2)), -# 'amount': fields.float('Amount', digits=(16,2)), -# } +# _name = 'account.analytic.budget.post.dotation' +# _description = "Budget item endowment" +# _columns = { +# 'name': fields.char('Name', size=64), +# 'post_id': fields.many2one('account.analytic.budget.post', 'Item', select=True), +# 'period_id': fields.many2one('account.period', 'Period'), +# 'quantity': fields.float('Quantity', digits=(16,2)), +# 'amount': fields.float('Amount', digits=(16,2)), +# } #account_analytic_budget_post_dotation() diff --git a/addons/account/project/report/account_journal.py b/addons/account/project/report/account_journal.py index 1ed0bb9f2a7..92d7d983f56 100644 --- a/addons/account/project/report/account_journal.py +++ b/addons/account/project/report/account_journal.py @@ -35,20 +35,20 @@ from report import report_sxw # Use period and Journal for selection or resources # class journal_print(report_sxw.rml_parse): - def lines(self, journal_id, *args): - self.cr.execute('select id from account_analytic_line where journal_id=%d order by date,id', (journal_id,)) - ids = map(lambda x: x[0], self.cr.fetchall()) - res = self.pool.get('account.analytic.line').browse(self.cr, self.uid, ids) - return res - def _sum_lines(self, journal_id): - self.cr.execute('select sum(amount) from account_analytic_line where journal_id=%d', (journal_id,)) - return self.cr.fetchone()[0] or 0.0 - def __init__(self, cr, uid, name, context): - super(journal_print, self).__init__(cr, uid, name, context) - self.localcontext = { - 'time': time, - 'lines': self.lines, - 'sum_lines': self._sum_lines, - } + def lines(self, journal_id, *args): + self.cr.execute('select id from account_analytic_line where journal_id=%d order by date,id', (journal_id,)) + ids = map(lambda x: x[0], self.cr.fetchall()) + res = self.pool.get('account.analytic.line').browse(self.cr, self.uid, ids) + return res + def _sum_lines(self, journal_id): + self.cr.execute('select sum(amount) from account_analytic_line where journal_id=%d', (journal_id,)) + return self.cr.fetchone()[0] or 0.0 + def __init__(self, cr, uid, name, context): + super(journal_print, self).__init__(cr, uid, name, context) + self.localcontext = { + 'time': time, + 'lines': self.lines, + 'sum_lines': self._sum_lines, + } report_sxw.report_sxw('report.account.analytic.journal.print', 'account.analytic.journal', 'addons/account/project/report/analytic_journal.rml',parser=journal_print) diff --git a/addons/account/project/report/analytic_balance.py b/addons/account/project/report/analytic_balance.py index c8038bdb667..4884db9c86c 100644 --- a/addons/account/project/report/analytic_balance.py +++ b/addons/account/project/report/analytic_balance.py @@ -32,137 +32,137 @@ import time from report import report_sxw class account_analytic_balance(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_balance, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines_g': self._lines_g, - 'move_sum_debit': self._move_sum_debit, - 'move_sum_credit': self._move_sum_credit, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_balance': self._sum_balance, - 'sum_quantity': self._sum_quantity, - 'move_sum_balance': self._move_sum_balance, - 'move_sum_quantity': self._move_sum_quantity, - }) - - def _lines_g(self, account_id, date1, date2): - account_analytic_obj = self.pool.get('account.analytic.account') - ids = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', [account_id])]) - self.cr.execute("SELECT aa.name AS name, aa.code AS code, \ - sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity \ - FROM account_analytic_line AS aal, account_account AS aa \ - WHERE (aal.general_account_id=aa.id) \ - AND (aal.account_id in (" + ','.join(map(str, ids)) + "))\ - AND (date>=%s) AND (date<=%s) AND aa.active \ - GROUP BY aal.general_account_id, aa.name, aa.code, aal.code \ - ORDER BY aal.code", (date1, date2)) - res = self.cr.dictfetchall() - - for r in res: - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['balance'] == 0 - r['debit'] = 0.0 - r['credit'] = 0.0 - return res - + def __init__(self, cr, uid, name, context): + super(account_analytic_balance, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines_g': self._lines_g, + 'move_sum_debit': self._move_sum_debit, + 'move_sum_credit': self._move_sum_credit, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'sum_balance': self._sum_balance, + 'sum_quantity': self._sum_quantity, + 'move_sum_balance': self._move_sum_balance, + 'move_sum_quantity': self._move_sum_quantity, + }) + + def _lines_g(self, account_id, date1, date2): + account_analytic_obj = self.pool.get('account.analytic.account') + ids = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', [account_id])]) + self.cr.execute("SELECT aa.name AS name, aa.code AS code, \ + sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity \ + FROM account_analytic_line AS aal, account_account AS aa \ + WHERE (aal.general_account_id=aa.id) \ + AND (aal.account_id in (" + ','.join(map(str, ids)) + "))\ + AND (date>=%s) AND (date<=%s) AND aa.active \ + GROUP BY aal.general_account_id, aa.name, aa.code, aal.code \ + ORDER BY aal.code", (date1, date2)) + res = self.cr.dictfetchall() + + for r in res: + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['balance'] == 0 + r['debit'] = 0.0 + r['credit'] = 0.0 + return res + - def _move_sum_debit(self, account_id, date1, date2): - account_analytic_obj = self.pool.get('account.analytic.account') - ids = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', [account_id])]) - self.cr.execute("SELECT sum(amount) \ - FROM account_analytic_line \ - WHERE account_id in ("+ ','.join(map(str, ids)) +") \ - AND date>=%s AND date<=%s AND amount>0", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _move_sum_debit(self, account_id, date1, date2): + account_analytic_obj = self.pool.get('account.analytic.account') + ids = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', [account_id])]) + self.cr.execute("SELECT sum(amount) \ + FROM account_analytic_line \ + WHERE account_id in ("+ ','.join(map(str, ids)) +") \ + AND date>=%s AND date<=%s AND amount>0", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _move_sum_credit(self, account_id, date1, date2): - account_analytic_obj = self.pool.get('account.analytic.account') - ids = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', [account_id])]) - self.cr.execute("SELECT -sum(amount) \ - FROM account_analytic_line \ - WHERE account_id in ("+ ','.join(map(str, ids)) +") \ - AND date>=%s AND date<=%s AND amount<0", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _move_sum_balance(self, account_id, date1, date2): - debit = self._move_sum_debit(account_id, date1, date2) - credit = self._move_sum_credit(account_id, date1, date2) - return (debit-credit) - - def _move_sum_quantity(self, account_id, date1, date2): - account_analytic_obj = self.pool.get('account.analytic.account') - ids = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', [account_id])]) - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id in ("+ ','.join(map(str, ids)) +") \ - AND date>=%s AND date<=%s", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _move_sum_credit(self, account_id, date1, date2): + account_analytic_obj = self.pool.get('account.analytic.account') + ids = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', [account_id])]) + self.cr.execute("SELECT -sum(amount) \ + FROM account_analytic_line \ + WHERE account_id in ("+ ','.join(map(str, ids)) +") \ + AND date>=%s AND date<=%s AND amount<0", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _move_sum_balance(self, account_id, date1, date2): + debit = self._move_sum_debit(account_id, date1, date2) + credit = self._move_sum_credit(account_id, date1, date2) + return (debit-credit) + + def _move_sum_quantity(self, account_id, date1, date2): + account_analytic_obj = self.pool.get('account.analytic.account') + ids = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', [account_id])]) + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id in ("+ ','.join(map(str, ids)) +") \ + AND date>=%s AND date<=%s", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - - def _sum_debit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - account_analytic_obj = self.pool.get('account.analytic.account') - ids2 = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', ids)]) - self.cr.execute("SELECT sum(amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids2))+") \ - AND date>=%s AND date<=%s AND amount>0", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - ids = map(lambda x: x.id, accounts) - account_analytic_obj = self.pool.get('account.analytic.account') - ids2 = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', ids)]) - self.cr.execute("SELECT -sum(amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids2))+") \ - AND date>=%s AND date<=%s AND amount<0", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + + def _sum_debit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + account_analytic_obj = self.pool.get('account.analytic.account') + ids2 = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', ids)]) + self.cr.execute("SELECT sum(amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids2))+") \ + AND date>=%s AND date<=%s AND amount>0", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + ids = map(lambda x: x.id, accounts) + account_analytic_obj = self.pool.get('account.analytic.account') + ids2 = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', ids)]) + self.cr.execute("SELECT -sum(amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids2))+") \ + AND date>=%s AND date<=%s AND amount<0", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _sum_balance(self, accounts, date1, date2): - debit = self._sum_debit(accounts, date1, date2) or 0.0 - credit = self._sum_credit(accounts, date1, date2) or 0.0 - return (debit-credit) + def _sum_balance(self, accounts, date1, date2): + debit = self._sum_debit(accounts, date1, date2) or 0.0 + credit = self._sum_credit(accounts, date1, date2) or 0.0 + return (debit-credit) - def _sum_quantity(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - account_analytic_obj = self.pool.get('account.analytic.account') - ids2 = account_analytic_obj.search(self.cr, self.uid, - [('parent_id', 'child_of', ids)]) - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids2))+") \ - AND date>=%s AND date<=%s", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_quantity(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + account_analytic_obj = self.pool.get('account.analytic.account') + ids2 = account_analytic_obj.search(self.cr, self.uid, + [('parent_id', 'child_of', ids)]) + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids2))+") \ + AND date>=%s AND date<=%s", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.analytic.account.balance', - 'account.analytic.account', 'addons/account/project/report/analytic_balance.rml', - parser=account_analytic_balance, header=False) + 'account.analytic.account', 'addons/account/project/report/analytic_balance.rml', + parser=account_analytic_balance, header=False) diff --git a/addons/account/project/report/analytic_check.py b/addons/account/project/report/analytic_check.py index 5d849beb82f..6c062a6467f 100644 --- a/addons/account/project/report/analytic_check.py +++ b/addons/account/project/report/analytic_check.py @@ -32,75 +32,75 @@ import time from report import report_sxw class account_analytic_analytic_check(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_analytic_check, self).__init__(cr, uid, name, context) - self.sum_gen_deb = 0.0 - self.sum_gen_cred = 0.0 - self.sum_ana_deb = 0.0 - self.sum_ana_cred = 0.0 - self.localcontext.update( { - 'time': time, - 'lines_p': self._lines_p, - 'general_debit': self._gen_deb, - 'general_credit': self._gen_cred, - 'analytic_debit': self._ana_deb, - 'analytic_credit': self._ana_cred, - 'delta_debit': self._delta_deb, - 'delta_credit': self._delta_cred, - }) + def __init__(self, cr, uid, name, context): + super(account_analytic_analytic_check, self).__init__(cr, uid, name, context) + self.sum_gen_deb = 0.0 + self.sum_gen_cred = 0.0 + self.sum_ana_deb = 0.0 + self.sum_ana_cred = 0.0 + self.localcontext.update( { + 'time': time, + 'lines_p': self._lines_p, + 'general_debit': self._gen_deb, + 'general_credit': self._gen_cred, + 'analytic_debit': self._ana_deb, + 'analytic_credit': self._ana_cred, + 'delta_debit': self._delta_deb, + 'delta_credit': self._delta_cred, + }) - def _lines_p(self, date1, date2): - res = [] - acc_obj = self.pool.get('account.account') - for a in acc_obj.read(self.cr, self.uid, self.ids, ['name', 'code', 'sign']): - self.cr.execute("SELECT sum(debit), sum(credit) \ - FROM account_move_line \ - WHERE date>=%s AND date<=%s AND state<>'draft' AND account_id = %d", (date1, date2, a['id'])) - (gd, gc) = self.cr.fetchone() - gd = gd or 0.0 - gc = gc or 0.0 + def _lines_p(self, date1, date2): + res = [] + acc_obj = self.pool.get('account.account') + for a in acc_obj.read(self.cr, self.uid, self.ids, ['name', 'code', 'sign']): + self.cr.execute("SELECT sum(debit), sum(credit) \ + FROM account_move_line \ + WHERE date>=%s AND date<=%s AND state<>'draft' AND account_id = %d", (date1, date2, a['id'])) + (gd, gc) = self.cr.fetchone() + gd = gd or 0.0 + gc = gc or 0.0 - self.cr.execute("SELECT abs(sum(amount)) AS balance \ - FROM account_analytic_line \ - WHERE date>=%s AND date<=%s AND amount*%d>0 AND general_account_id = %d", (date1, date2, a['sign'], a['id'])) - (ad,) = self.cr.fetchone() - ad = ad or 0.0 - self.cr.execute("SELECT abs(sum(amount)) AS balance \ - FROM account_analytic_line \ - WHERE date>=%s AND date<=%s AND amount*%d<0 AND general_account_id = %d", (date1, date2, a['sign'], a['id'])) - (ac,) = self.cr.fetchone() - ac = ac or 0.0 + self.cr.execute("SELECT abs(sum(amount)) AS balance \ + FROM account_analytic_line \ + WHERE date>=%s AND date<=%s AND amount*%d>0 AND general_account_id = %d", (date1, date2, a['sign'], a['id'])) + (ad,) = self.cr.fetchone() + ad = ad or 0.0 + self.cr.execute("SELECT abs(sum(amount)) AS balance \ + FROM account_analytic_line \ + WHERE date>=%s AND date<=%s AND amount*%d<0 AND general_account_id = %d", (date1, date2, a['sign'], a['id'])) + (ac,) = self.cr.fetchone() + ac = ac or 0.0 - res.append({'code': a['code'], 'name': a['name'], - 'gen_debit': gd, - 'gen_credit': gc, - 'ana_debit': ad, - 'ana_credit': ac, - 'delta_debit': gd - ad, - 'delta_credit': gc - ac,}) - self.sum_gen_deb += gd - self.sum_gen_cred += gc - self.sum_ana_deb += ad - self.sum_ana_cred += ac - return res + res.append({'code': a['code'], 'name': a['name'], + 'gen_debit': gd, + 'gen_credit': gc, + 'ana_debit': ad, + 'ana_credit': ac, + 'delta_debit': gd - ad, + 'delta_credit': gc - ac,}) + self.sum_gen_deb += gd + self.sum_gen_cred += gc + self.sum_ana_deb += ad + self.sum_ana_cred += ac + return res - def _gen_deb(self, date1, date2): - return self.sum_gen_deb + def _gen_deb(self, date1, date2): + return self.sum_gen_deb - def _gen_cred(self, date1, date2): - return self.sum_gen_cred + def _gen_cred(self, date1, date2): + return self.sum_gen_cred - def _ana_deb(self, date1, date2): - return self.sum_ana_deb + def _ana_deb(self, date1, date2): + return self.sum_ana_deb - def _ana_cred(self, date1, date2): - return self.sum_ana_cred + def _ana_cred(self, date1, date2): + return self.sum_ana_cred - def _delta_deb(self, date1, date2): - return (self._gen_deb(date1,date2)-self._ana_deb(date1,date2)) - - def _delta_cred(self, date1, date2): - return (self._gen_cred(date1,date2)-self._ana_cred(date1,date2)) + def _delta_deb(self, date1, date2): + return (self._gen_deb(date1,date2)-self._ana_deb(date1,date2)) + + def _delta_cred(self, date1, date2): + return (self._gen_cred(date1,date2)-self._ana_cred(date1,date2)) report_sxw.report_sxw('report.account.analytic.account.analytic.check', 'account.analytic.account', 'addons/account/project/report/analytic_check.rml',parser=account_analytic_analytic_check, header=False) diff --git a/addons/account/project/report/analytic_journal.py b/addons/account/project/report/analytic_journal.py index e11be742fad..2cab00c8fd2 100644 --- a/addons/account/project/report/analytic_journal.py +++ b/addons/account/project/report/analytic_journal.py @@ -34,35 +34,35 @@ from report import report_sxw # Use period and Journal for selection or resources # class account_analytic_journal(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_journal, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self._lines, - 'lines_a': self._lines_a, - 'sum_general': self._sum_general, - 'sum_analytic': self._sum_analytic, - }) + def __init__(self, cr, uid, name, context): + super(account_analytic_journal, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self._lines, + 'lines_a': self._lines_a, + 'sum_general': self._sum_general, + 'sum_analytic': self._sum_analytic, + }) - def _lines(self, journal_id, date1, date2): - self.cr.execute('SELECT DISTINCT move_id FROM account_analytic_line WHERE (date>=%s) AND (date<=%s) AND (journal_id=%d) AND (move_id is not null)', (date1, date2, journal_id,)) - ids = map(lambda x: x[0], self.cr.fetchall()) - return self.pool.get('account.move.line').browse(self.cr, self.uid, ids) + def _lines(self, journal_id, date1, date2): + self.cr.execute('SELECT DISTINCT move_id FROM account_analytic_line WHERE (date>=%s) AND (date<=%s) AND (journal_id=%d) AND (move_id is not null)', (date1, date2, journal_id,)) + ids = map(lambda x: x[0], self.cr.fetchall()) + return self.pool.get('account.move.line').browse(self.cr, self.uid, ids) - def _lines_a(self, move_id, journal_id, date1, date2): - ids = self.pool.get('account.analytic.line').search(self.cr, self.uid, [('move_id','=',move_id), ('journal_id','=',journal_id), ('date','>=',date1), ('date','<=',date2)]) - if not ids: - return [] - return self.pool.get('account.analytic.line').browse(self.cr, self.uid, ids) - - def _sum_general(self, journal_id, date1, date2): - self.cr.execute('SELECT SUM(debit-credit) FROM account_move_line WHERE id IN (SELECT move_id FROM account_analytic_line WHERE (date>=%s) AND (date<=%s) AND (journal_id=%d) AND (move_id is not null))', (date1, date2, journal_id,)) - return self.cr.fetchall()[0][0] or 0 + def _lines_a(self, move_id, journal_id, date1, date2): + ids = self.pool.get('account.analytic.line').search(self.cr, self.uid, [('move_id','=',move_id), ('journal_id','=',journal_id), ('date','>=',date1), ('date','<=',date2)]) + if not ids: + return [] + return self.pool.get('account.analytic.line').browse(self.cr, self.uid, ids) + + def _sum_general(self, journal_id, date1, date2): + self.cr.execute('SELECT SUM(debit-credit) FROM account_move_line WHERE id IN (SELECT move_id FROM account_analytic_line WHERE (date>=%s) AND (date<=%s) AND (journal_id=%d) AND (move_id is not null))', (date1, date2, journal_id,)) + return self.cr.fetchall()[0][0] or 0 - def _sum_analytic(self, journal_id, date1, date2): - self.cr.execute("SELECT SUM(amount) FROM account_analytic_line WHERE date>=%s AND date<=%s AND journal_id=%d", (date1, date2, journal_id)) - res = self.cr.dictfetchone() - return res['sum'] or 0 + def _sum_analytic(self, journal_id, date1, date2): + self.cr.execute("SELECT SUM(amount) FROM account_analytic_line WHERE date>=%s AND date<=%s AND journal_id=%d", (date1, date2, journal_id)) + res = self.cr.dictfetchone() + return res['sum'] or 0 report_sxw.report_sxw('report.account.analytic.journal', 'account.analytic.journal', 'addons/account/project/report/analytic_journal.rml',parser=account_analytic_journal) diff --git a/addons/account/project/report/cost_ledger.py b/addons/account/project/report/cost_ledger.py index fd5208fef01..fca21404b76 100644 --- a/addons/account/project/report/cost_ledger.py +++ b/addons/account/project/report/cost_ledger.py @@ -32,90 +32,90 @@ import time from report import report_sxw class account_analytic_cost_ledger(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_cost_ledger, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines_g': self._lines_g, - 'lines_a': self._lines_a, - 'account_sum_debit': self._account_sum_debit, - 'account_sum_credit': self._account_sum_credit, - 'account_sum_balance': self._account_sum_balance, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_balance': self._sum_balance, - }) + def __init__(self, cr, uid, name, context): + super(account_analytic_cost_ledger, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines_g': self._lines_g, + 'lines_a': self._lines_a, + 'account_sum_debit': self._account_sum_debit, + 'account_sum_credit': self._account_sum_credit, + 'account_sum_balance': self._account_sum_balance, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'sum_balance': self._sum_balance, + }) - def _lines_g(self, account_id, date1, date2): - self.cr.execute("SELECT sum(aal.amount) AS balance, aa.code AS code, aa.name AS name, aa.id AS id \ - FROM account_account AS aa, account_analytic_line AS aal \ - WHERE (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) AND (aal.general_account_id=aa.id) AND aa.active \ - GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", (account_id, date1, date2)) - res = self.cr.dictfetchall() + def _lines_g(self, account_id, date1, date2): + self.cr.execute("SELECT sum(aal.amount) AS balance, aa.code AS code, aa.name AS name, aa.id AS id \ + FROM account_account AS aa, account_analytic_line AS aal \ + WHERE (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) AND (aal.general_account_id=aa.id) AND aa.active \ + GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", (account_id, date1, date2)) + res = self.cr.dictfetchall() - for r in res: - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res + for r in res: + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res - def _lines_a(self, general_account_id, account_id, date1, date2): - self.cr.execute("SELECT aal.name AS name, aal.code AS code, aal.amount AS balance, aal.date AS date, aaj.code AS cj FROM account_analytic_line AS aal, account_analytic_journal AS aaj \ - WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) \ - AND (aal.journal_id=aaj.id) \ - ORDER BY aal.date, aaj.code, aal.code", (general_account_id, account_id, date1, date2)) - res = self.cr.dictfetchall() + def _lines_a(self, general_account_id, account_id, date1, date2): + self.cr.execute("SELECT aal.name AS name, aal.code AS code, aal.amount AS balance, aal.date AS date, aaj.code AS cj FROM account_analytic_line AS aal, account_analytic_journal AS aaj \ + WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) \ + AND (aal.journal_id=aaj.id) \ + ORDER BY aal.date, aaj.code, aal.code", (general_account_id, account_id, date1, date2)) + res = self.cr.dictfetchall() - for r in res: - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res + for r in res: + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res - def _account_sum_debit(self, account_id, date1, date2): - self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount>0", (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _account_sum_debit(self, account_id, date1, date2): + self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount>0", (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _account_sum_credit(self, account_id, date1, date2): - self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount<0", (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _account_sum_credit(self, account_id, date1, date2): + self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount<0", (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _account_sum_balance(self, account_id, date1, date2): - debit = self._account_sum_debit(account_id, date1, date2) - credit = self._account_sum_credit(account_id, date1, date2) - return (debit-credit) + def _account_sum_balance(self, account_id, date1, date2): + debit = self._account_sum_debit(account_id, date1, date2) + credit = self._account_sum_credit(account_id, date1, date2) + return (debit-credit) - def _sum_debit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_debit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _sum_credit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_credit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _sum_balance(self, accounts, date1, date2): - debit = self._sum_debit(accounts, date1, date2) or 0.0 - credit = self._sum_credit(accounts, date1, date2) or 0.0 - return (debit-credit) + def _sum_balance(self, accounts, date1, date2): + debit = self._sum_debit(accounts, date1, date2) or 0.0 + credit = self._sum_credit(accounts, date1, date2) or 0.0 + return (debit-credit) report_sxw.report_sxw('report.account.analytic.account.cost_ledger', 'account.analytic.account', 'addons/account/project/report/cost_ledger.rml',parser=account_analytic_cost_ledger, header=False) diff --git a/addons/account/project/report/inverted_analytic_balance.py b/addons/account/project/report/inverted_analytic_balance.py index 32613fac807..90b38294ee6 100644 --- a/addons/account/project/report/inverted_analytic_balance.py +++ b/addons/account/project/report/inverted_analytic_balance.py @@ -32,89 +32,89 @@ import time from report import report_sxw class account_inverted_analytic_balance(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_inverted_analytic_balance, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines_g': self._lines_g, - 'lines_a': self._lines_a, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_balance': self._sum_balance, - 'sum_quantity': self._sum_quantity, - }) + def __init__(self, cr, uid, name, context): + super(account_inverted_analytic_balance, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines_g': self._lines_g, + 'lines_a': self._lines_a, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'sum_balance': self._sum_balance, + 'sum_quantity': self._sum_quantity, + }) - def _lines_g(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT aa.name AS name, aa.code AS code, sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity, aa.id AS id \ - FROM account_analytic_line AS aal, account_account AS aa \ - WHERE (aal.general_account_id=aa.id) AND (aal.account_id IN ("+','.join(map(str, ids))+")) AND (date>=%s) AND (date<=%s) AND aa.active \ - GROUP BY aal.general_account_id, aa.name, aa.code, aal.code, aa.id ORDER BY aal.code", (date1, date2)) - res = self.cr.dictfetchall() + def _lines_g(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT aa.name AS name, aa.code AS code, sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity, aa.id AS id \ + FROM account_analytic_line AS aal, account_account AS aa \ + WHERE (aal.general_account_id=aa.id) AND (aal.account_id IN ("+','.join(map(str, ids))+")) AND (date>=%s) AND (date<=%s) AND aa.active \ + GROUP BY aal.general_account_id, aa.name, aa.code, aal.code, aa.id ORDER BY aal.code", (date1, date2)) + res = self.cr.dictfetchall() - for r in res: - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res + for r in res: + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res - def _lines_a(self, accounts, general_account_id, date1, date2): - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity, aaa.code AS code, aaa.name AS name, account_id \ - FROM account_analytic_line AS aal, account_analytic_account AS aaa \ - WHERE aal.account_id=aaa.id AND aal.account_id IN ("+','.join(map(str, ids))+") AND aal.general_account_id=%d AND aal.date>=%s AND aal.date<=%s \ - GROUP BY aal.account_id, general_account_id, aaa.code, aaa.name ORDER BY aal.account_id", (general_account_id, date1, date2)) - res = self.cr.dictfetchall() + def _lines_a(self, accounts, general_account_id, date1, date2): + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT sum(aal.amount) AS balance, sum(aal.unit_amount) AS quantity, aaa.code AS code, aaa.name AS name, account_id \ + FROM account_analytic_line AS aal, account_analytic_account AS aaa \ + WHERE aal.account_id=aaa.id AND aal.account_id IN ("+','.join(map(str, ids))+") AND aal.general_account_id=%d AND aal.date>=%s AND aal.date<=%s \ + GROUP BY aal.account_id, general_account_id, aaa.code, aaa.name ORDER BY aal.account_id", (general_account_id, date1, date2)) + res = self.cr.dictfetchall() - aaa_obj = self.pool.get('account.analytic.account') - res2 = aaa_obj.read(self.cr, self.uid, ids, ['complete_name']) - complete_name = {} - for r in res2: - complete_name[r['id']] = r['complete_name'] - for r in res: - r['complete_name'] = complete_name[r['account_id']] - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res + aaa_obj = self.pool.get('account.analytic.account') + res2 = aaa_obj.read(self.cr, self.uid, ids, ['complete_name']) + complete_name = {} + for r in res2: + complete_name[r['id']] = r['complete_name'] + for r in res: + r['complete_name'] = complete_name[r['account_id']] + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res - def _sum_debit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT sum(amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT -sum(amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_debit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT sum(amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT -sum(amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _sum_balance(self, accounts, date1, date2): - debit = self._sum_debit(accounts, date1, date2) - credit = self._sum_credit(accounts, date1, date2) - return (debit-credit) + def _sum_balance(self, accounts, date1, date2): + debit = self._sum_debit(accounts, date1, date2) + credit = self._sum_credit(accounts, date1, date2) + return (debit-credit) - def _sum_quantity(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_quantity(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.analytic.account.inverted.balance', 'account.analytic.account', 'addons/account/project/report/inverted_analytic_balance.rml',parser=account_inverted_analytic_balance, header=False) diff --git a/addons/account/project/report/quantity_cost_ledger.py b/addons/account/project/report/quantity_cost_ledger.py index 9b5cfaf61fa..bb870a40c82 100644 --- a/addons/account/project/report/quantity_cost_ledger.py +++ b/addons/account/project/report/quantity_cost_ledger.py @@ -32,109 +32,109 @@ import time from report import report_sxw class account_analytic_quantity_cost_ledger(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_quantity_cost_ledger, self).__init__(cr, uid, - name, context) - self.localcontext.update( { - 'time': time, - 'lines_g': self._lines_g, - 'lines_a': self._lines_a, - 'sum_quantity': self._sum_quantity, - 'account_sum_quantity': self._account_sum_quantity, - }) + def __init__(self, cr, uid, name, context): + super(account_analytic_quantity_cost_ledger, self).__init__(cr, uid, + name, context) + self.localcontext.update( { + 'time': time, + 'lines_g': self._lines_g, + 'lines_a': self._lines_a, + 'sum_quantity': self._sum_quantity, + 'account_sum_quantity': self._account_sum_quantity, + }) - def _lines_g(self, account_id, date1, date2, journals): - if not journals or not journals[0][2]: - self.cr.execute("SELECT sum(aal.unit_amount) AS quantity, \ - aa.code AS code, aa.name AS name, aa.id AS id \ - FROM account_account AS aa, account_analytic_line AS aal \ - WHERE (aal.account_id=%d) AND (aal.date>=%s) \ - AND (aal.date<=%s) AND (aal.general_account_id=aa.id) \ - AND aa.active \ - GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", - (account_id, date1, date2)) - else: - journal_ids = journals[0][2] - self.cr.execute("SELECT sum(aal.unit_amount) AS quantity, \ - aa.code AS code, aa.name AS name, aa.id AS id \ - FROM account_account AS aa, account_analytic_line AS aal \ - WHERE (aal.account_id=%d) AND (aal.date>=%s) \ - AND (aal.date<=%s) AND (aal.general_account_id=aa.id) \ - AND aa.active \ - AND (aal.journal_id IN (" + - ','.join(map(str, journal_ids)) + ")) \ - GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", - (account_id, date1, date2)) - res = self.cr.dictfetchall() - return res + def _lines_g(self, account_id, date1, date2, journals): + if not journals or not journals[0][2]: + self.cr.execute("SELECT sum(aal.unit_amount) AS quantity, \ + aa.code AS code, aa.name AS name, aa.id AS id \ + FROM account_account AS aa, account_analytic_line AS aal \ + WHERE (aal.account_id=%d) AND (aal.date>=%s) \ + AND (aal.date<=%s) AND (aal.general_account_id=aa.id) \ + AND aa.active \ + GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", + (account_id, date1, date2)) + else: + journal_ids = journals[0][2] + self.cr.execute("SELECT sum(aal.unit_amount) AS quantity, \ + aa.code AS code, aa.name AS name, aa.id AS id \ + FROM account_account AS aa, account_analytic_line AS aal \ + WHERE (aal.account_id=%d) AND (aal.date>=%s) \ + AND (aal.date<=%s) AND (aal.general_account_id=aa.id) \ + AND aa.active \ + AND (aal.journal_id IN (" + + ','.join(map(str, journal_ids)) + ")) \ + GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", + (account_id, date1, date2)) + res = self.cr.dictfetchall() + return res - def _lines_a(self, general_account_id, account_id, date1, date2, journals): - if not journals or not journals[0][2]: - self.cr.execute("SELECT aal.name AS name, aal.code AS code, \ - aal.unit_amount AS quantity, aal.date AS date, \ - aaj.code AS cj \ - FROM account_analytic_line AS aal, \ - account_analytic_journal AS aaj \ - WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) \ - AND (aal.date>=%s) AND (aal.date<=%s) \ - AND (aal.journal_id=aaj.id) \ - ORDER BY aal.date, aaj.code, aal.code", - (general_account_id, account_id, date1, date2)) - else: - journal_ids = journals[0][2] - self.cr.execute("SELECT aal.name AS name, aal.code AS code, \ - aal.unit_amount AS quantity, aal.date AS date, \ - aaj.code AS cj \ - FROM account_analytic_line AS aal, \ - account_analytic_journal AS aaj \ - WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) \ - AND (aal.date>=%s) AND (aal.date<=%s) \ - AND (aal.journal_id=aaj.id) AND (aaj.id IN (" + - ','.join(map(str, journal_ids)) + ")) \ - ORDER BY aal.date, aaj.code, aal.code", - (general_account_id, account_id, date1, date2)) - res = self.cr.dictfetchall() - return res + def _lines_a(self, general_account_id, account_id, date1, date2, journals): + if not journals or not journals[0][2]: + self.cr.execute("SELECT aal.name AS name, aal.code AS code, \ + aal.unit_amount AS quantity, aal.date AS date, \ + aaj.code AS cj \ + FROM account_analytic_line AS aal, \ + account_analytic_journal AS aaj \ + WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) \ + AND (aal.date>=%s) AND (aal.date<=%s) \ + AND (aal.journal_id=aaj.id) \ + ORDER BY aal.date, aaj.code, aal.code", + (general_account_id, account_id, date1, date2)) + else: + journal_ids = journals[0][2] + self.cr.execute("SELECT aal.name AS name, aal.code AS code, \ + aal.unit_amount AS quantity, aal.date AS date, \ + aaj.code AS cj \ + FROM account_analytic_line AS aal, \ + account_analytic_journal AS aaj \ + WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) \ + AND (aal.date>=%s) AND (aal.date<=%s) \ + AND (aal.journal_id=aaj.id) AND (aaj.id IN (" + + ','.join(map(str, journal_ids)) + ")) \ + ORDER BY aal.date, aaj.code, aal.code", + (general_account_id, account_id, date1, date2)) + res = self.cr.dictfetchall() + return res - def _account_sum_quantity(self, account_id, date1, date2, journals): - if not journals or not journals[0][2]: - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id=%d AND date>=%s AND date<=%s", - (account_id, date1, date2)) - else: - journal_ids = journals[0][2] - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id = %d AND date >= %s AND date <= %s \ - AND journal_id IN (" + - ','.join(map(str, journal_ids)) + ")", - (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _account_sum_quantity(self, account_id, date1, date2, journals): + if not journals or not journals[0][2]: + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id=%d AND date>=%s AND date<=%s", + (account_id, date1, date2)) + else: + journal_ids = journals[0][2] + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id = %d AND date >= %s AND date <= %s \ + AND journal_id IN (" + + ','.join(map(str, journal_ids)) + ")", + (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 - def _sum_quantity(self, accounts, date1, date2, journals): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - if not journals or not journals[0][2]: - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id IN (" + - ','.join(map(str, ids)) + ") AND date>=%s AND date<=%s", - (date1, date2)) - else: - journal_ids = journals[0][2] - self.cr.execute("SELECT sum(unit_amount) \ - FROM account_analytic_line \ - WHERE account_id IN (" + - ','.join(map(str, ids)) + ") AND date >= %s AND date <= %s \ - AND journal_id IN (" + - ','.join(map(str, journal_ids)) + ")", - (date1, date2)) - return self.cr.fetchone()[0] or 0.0 + def _sum_quantity(self, accounts, date1, date2, journals): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + if not journals or not journals[0][2]: + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id IN (" + + ','.join(map(str, ids)) + ") AND date>=%s AND date<=%s", + (date1, date2)) + else: + journal_ids = journals[0][2] + self.cr.execute("SELECT sum(unit_amount) \ + FROM account_analytic_line \ + WHERE account_id IN (" + + ','.join(map(str, ids)) + ") AND date >= %s AND date <= %s \ + AND journal_id IN (" + + ','.join(map(str, journal_ids)) + ")", + (date1, date2)) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.analytic.account.quantity_cost_ledger', - 'account.analytic.account', - 'addons/account/project/report/quantity_cost_ledger.rml', - parser=account_analytic_quantity_cost_ledger, header=False) + 'account.analytic.account', + 'addons/account/project/report/quantity_cost_ledger.rml', + parser=account_analytic_quantity_cost_ledger, header=False) diff --git a/addons/account/project/wizard/wizard_account_analytic_analytic_check.py b/addons/account/project/wizard/wizard_account_analytic_analytic_check.py index a2537c8dd0d..43cfa6ad332 100644 --- a/addons/account/project/wizard/wizard_account_analytic_analytic_check.py +++ b/addons/account/project/wizard/wizard_account_analytic_analytic_check.py @@ -32,26 +32,26 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.analytic.account.analytic.check', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.analytic.account.analytic.check', 'state':'end'} + } + } wizard_report('account.analytic.account.analytic.check.report') diff --git a/addons/account/project/wizard/wizard_account_analytic_balance_report.py b/addons/account/project/wizard/wizard_account_analytic_balance_report.py index 9c1439e63f9..dab606b3f4f 100644 --- a/addons/account/project/wizard/wizard_account_analytic_balance_report.py +++ b/addons/account/project/wizard/wizard_account_analytic_balance_report.py @@ -32,27 +32,27 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.analytic.account.balance', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.analytic.account.balance', 'state':'end'} + } + } wizard_report('account.analytic.account.balance.report') diff --git a/addons/account/project/wizard/wizard_account_analytic_cost_ledger_for_journal_report.py b/addons/account/project/wizard/wizard_account_analytic_cost_ledger_for_journal_report.py index df4de9bc29e..7958209553c 100644 --- a/addons/account/project/wizard/wizard_account_analytic_cost_ledger_for_journal_report.py +++ b/addons/account/project/wizard/wizard_account_analytic_cost_ledger_for_journal_report.py @@ -32,42 +32,42 @@ import wizard _form = '''<?xml version="1.0"?> <form string="Select period"> - <separator string="Cost Legder for period" colspan="4"/> - <field name="date1"/> - <field name="date2"/> - <separator string="and Journals" colspan="4"/> - <field name="journal" colspan="4"/> + <separator string="Cost Legder for period" colspan="4"/> + <field name="date1"/> + <field name="date2"/> + <separator string="and Journals" colspan="4"/> + <field name="journal" colspan="4"/> </form>''' _fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, - 'journal': {'string':'Journals','type':'many2many', 'relation':'account.analytic.journal'}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'journal': {'string':'Journals','type':'many2many', 'relation':'account.analytic.journal'}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': _form, - 'fields': _fields, - 'state': [ - ('end','Cancel'), - ('report','Print') - ] - } - }, - 'report': { - 'actions': [], - 'result': { - 'type': 'print', - 'report': 'account.analytic.account.quantity_cost_ledger', - 'state': 'end' - } - }, - } + states = { + 'init': { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': _form, + 'fields': _fields, + 'state': [ + ('end','Cancel'), + ('report','Print') + ] + } + }, + 'report': { + 'actions': [], + 'result': { + 'type': 'print', + 'report': 'account.analytic.account.quantity_cost_ledger', + 'state': 'end' + } + }, + } wizard_report('account.analytic.account.quantity_cost_ledger.report') diff --git a/addons/account/project/wizard/wizard_account_analytic_cost_ledger_report.py b/addons/account/project/wizard/wizard_account_analytic_cost_ledger_report.py index d72a088e88c..b07e11d5a8a 100644 --- a/addons/account/project/wizard/wizard_account_analytic_cost_ledger_report.py +++ b/addons/account/project/wizard/wizard_account_analytic_cost_ledger_report.py @@ -32,26 +32,26 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.analytic.account.cost_ledger', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.analytic.account.cost_ledger', 'state':'end'} + } + } wizard_report('account.analytic.account.cost_ledger.report') diff --git a/addons/account/project/wizard/wizard_account_analytic_inverted_balance_report.py b/addons/account/project/wizard/wizard_account_analytic_inverted_balance_report.py index e5f00cb12f7..5213c91bc91 100644 --- a/addons/account/project/wizard/wizard_account_analytic_inverted_balance_report.py +++ b/addons/account/project/wizard/wizard_account_analytic_inverted_balance_report.py @@ -32,26 +32,26 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.analytic.account.inverted.balance', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.analytic.account.inverted.balance', 'state':'end'} + } + } wizard_report('account.analytic.account.inverted.balance.report') diff --git a/addons/account/project/wizard/wizard_account_analytic_journal_report.py b/addons/account/project/wizard/wizard_account_analytic_journal_report.py index f70ebeef978..a57d9d0c95c 100644 --- a/addons/account/project/wizard/wizard_account_analytic_journal_report.py +++ b/addons/account/project/wizard/wizard_account_analytic_journal_report.py @@ -32,25 +32,25 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Analytic Journal Report"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.analytic.journal', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'), ('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.analytic.journal', 'state':'end'} + } + } wizard_report('account.analytic.account.journal.report') diff --git a/addons/account/report/account_balance.py b/addons/account/report/account_balance.py index 5debaf7b751..4167cb09c69 100644 --- a/addons/account/report/account_balance.py +++ b/addons/account/report/account_balance.py @@ -31,93 +31,93 @@ import time from report import report_sxw class account_balance(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_balance, self).__init__(cr, uid, name, context) - self.sum_debit = 0.0 - self.sum_credit = 0.0 - self.localcontext.update({ - 'time': time, - 'lines': self.lines, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'get_fiscalyear':self.get_fiscalyear, - 'get_periods':self.get_periods, - }) - self.context = context - - def get_fiscalyear(self, form): - fisc_id = form['fiscalyear'] - if not(fisc_id): - return '' - self.cr.execute("select name from account_fiscalyear where id = %d" %(int(fisc_id))) - res=self.cr.fetchone() - return res and res[0] or '' + def __init__(self, cr, uid, name, context): + super(account_balance, self).__init__(cr, uid, name, context) + self.sum_debit = 0.0 + self.sum_credit = 0.0 + self.localcontext.update({ + 'time': time, + 'lines': self.lines, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'get_fiscalyear':self.get_fiscalyear, + 'get_periods':self.get_periods, + }) + self.context = context + + def get_fiscalyear(self, form): + fisc_id = form['fiscalyear'] + if not(fisc_id): + return '' + self.cr.execute("select name from account_fiscalyear where id = %d" %(int(fisc_id))) + res=self.cr.fetchone() + return res and res[0] or '' - def get_periods(self, form): - period_ids = ",".join([str(x) for x in form['periods'][0][2] if x]) - self.cr.execute("select name from account_period where id in (%s)" % (period_ids)) - res=self.cr.fetchall() - result='' - for r in res: - result+=r[0]+"," - return str(result and result[:-1]) or '' + def get_periods(self, form): + period_ids = ",".join([str(x) for x in form['periods'][0][2] if x]) + self.cr.execute("select name from account_period where id in (%s)" % (period_ids)) + res=self.cr.fetchall() + result='' + for r in res: + result+=r[0]+"," + return str(result and result[:-1]) or '' - def lines(self, form, ids={}, done=None, level=1): - if not ids: - ids = self.ids - if not ids: - return [] - if not done: - done={} - result = [] - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - ctx['target_move'] = form['target_move'] - accounts = self.pool.get('account.account').browse(self.cr, self.uid, ids, ctx) - def cmp_code(x, y): - return cmp(x.code, y.code) - accounts.sort(cmp_code) - for account in accounts: - if account.id in done: - continue - done[account.id] = 1 - res = { - 'code': account.code, - 'name': account.name, - 'level': level, - 'debit': account.debit, - 'credit': account.credit, - 'balance': account.balance, - 'leef': not bool(account.child_id) - } - self.sum_debit += account.debit - self.sum_credit += account.credit - if not (res['credit'] or res['debit']) and not account.child_id: - continue - if account.child_id: - def _check_rec(account): - if not account.child_id: - return bool(account.credit or account.debit) - for c in account.child_id: - if _check_rec(c): - return True - return False - if not _check_rec(account): - continue + def lines(self, form, ids={}, done=None, level=1): + if not ids: + ids = self.ids + if not ids: + return [] + if not done: + done={} + result = [] + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + ctx['target_move'] = form['target_move'] + accounts = self.pool.get('account.account').browse(self.cr, self.uid, ids, ctx) + def cmp_code(x, y): + return cmp(x.code, y.code) + accounts.sort(cmp_code) + for account in accounts: + if account.id in done: + continue + done[account.id] = 1 + res = { + 'code': account.code, + 'name': account.name, + 'level': level, + 'debit': account.debit, + 'credit': account.credit, + 'balance': account.balance, + 'leef': not bool(account.child_id) + } + self.sum_debit += account.debit + self.sum_credit += account.credit + if not (res['credit'] or res['debit']) and not account.child_id: + continue + if account.child_id: + def _check_rec(account): + if not account.child_id: + return bool(account.credit or account.debit) + for c in account.child_id: + if _check_rec(c): + return True + return False + if not _check_rec(account): + continue - result.append(res) - if account.child_id: - ids2 = [(x.code,x.id) for x in account.child_id] - ids2.sort() - result += self.lines(form, [x[1] for x in ids2], done, level+1) - return result + result.append(res) + if account.child_id: + ids2 = [(x.code,x.id) for x in account.child_id] + ids2.sort() + result += self.lines(form, [x[1] for x in ids2], done, level+1) + return result - def _sum_credit(self): - return self.sum_credit + def _sum_credit(self): + return self.sum_credit - def _sum_debit(self): - return self.sum_debit + def _sum_debit(self): + return self.sum_debit report_sxw.report_sxw('report.account.account.balance', 'account.account', 'addons/account/report/account_balance.rml', parser=account_balance, header=2) diff --git a/addons/account/report/account_journal.py b/addons/account/report/account_journal.py index 74e28d698f6..87a6e43ebcf 100644 --- a/addons/account/report/account_journal.py +++ b/addons/account/report/account_journal.py @@ -35,28 +35,28 @@ from report import report_sxw # Use period and Journal for selection or resources # class journal_print(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(journal_print, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self.lines, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit - }) + def __init__(self, cr, uid, name, context): + super(journal_print, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self.lines, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit + }) - def lines(self, period_id, journal_id, *args): - self.cr.execute('update account_journal_period set state=%s where journal_id=%d and period_id=%d and state=%s', ('printed',journal_id,period_id,'draft')) - self.cr.commit() - self.cr.execute('select id from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\' order by date,id', (period_id, journal_id)) - ids = map(lambda x: x[0], self.cr.fetchall()) - return self.pool.get('account.move.line').browse(self.cr, self.uid, ids) + def lines(self, period_id, journal_id, *args): + self.cr.execute('update account_journal_period set state=%s where journal_id=%d and period_id=%d and state=%s', ('printed',journal_id,period_id,'draft')) + self.cr.commit() + self.cr.execute('select id from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\' order by date,id', (period_id, journal_id)) + ids = map(lambda x: x[0], self.cr.fetchall()) + return self.pool.get('account.move.line').browse(self.cr, self.uid, ids) - def _sum_debit(self, period_id, journal_id): - self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) - return self.cr.fetchone()[0] or 0.0 + def _sum_debit(self, period_id, journal_id): + self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) + return self.cr.fetchone()[0] or 0.0 - def _sum_credit(self, period_id, journal_id): - self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) - return self.cr.fetchone()[0] or 0.0 + def _sum_credit(self, period_id, journal_id): + self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.journal.period.print', 'account.journal.period', 'addons/account/report/account_journal.rml', parser=journal_print) diff --git a/addons/account/report/aged_trial_balance.py b/addons/account/report/aged_trial_balance.py index f5737a673d9..7c4710c9710 100644 --- a/addons/account/report/aged_trial_balance.py +++ b/addons/account/report/aged_trial_balance.py @@ -32,160 +32,160 @@ import pooler from report import report_sxw class aged_trial_report(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(aged_trial_report, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'get_lines': self._get_lines, - 'get_total': self._get_total, - 'get_before': self._get_before, - 'get_for_period': self._get_for_period, - 'get_company': self._get_company, - 'get_currency': self._get_currency, - }) + def __init__(self, cr, uid, name, context): + super(aged_trial_report, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'get_lines': self._get_lines, + 'get_total': self._get_total, + 'get_before': self._get_before, + 'get_for_period': self._get_for_period, + 'get_company': self._get_company, + 'get_currency': self._get_currency, + }) - def _add_header(self, node): - return True + def _add_header(self, node): + return True - def _get_lines(self, form): - self.form=form - res = [] - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': form['fiscalyear']}) - self.cr.execute("SELECT DISTINCT res_partner.id AS id, " \ - "res_partner.name AS name, res_partner.ref AS code " \ - "FROM res_partner, account_move_line AS line, account_account " \ - "WHERE (line.account_id=account_account.id) " \ - "AND (line.reconcile_id IS NULL) " \ - "AND (line.partner_id=res_partner.id) " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active " \ - "ORDER BY res_partner.name", (form['company_id'],)) - partners = self.cr.dictfetchall() - for partner in partners: - values = {} - self.cr.execute("SELECT SUM(debit-credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id=account_account.id) " \ - "AND (account_account.type IN (" + form['computation'] +")) " \ - "AND (date < %s) AND (partner_id=%d) " \ - "AND (reconcile_id IS NULL) " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (form['0']['start'], partner['id'], form['company_id'])) - before = self.cr.fetchone() - values['before'] = before and before[0] or "" - for i in range(5): - self.cr.execute("SELECT SUM(debit-credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id=account_account.id) " \ - "AND (account_account.type IN (" + form['computation'] + ")) " \ - "AND (date >= %s) AND (date <= %s) " \ - "AND (partner_id = %d) " \ - "AND (reconcile_id IS NULL) " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (form[str(i)]['start'], form[str(i)]['stop'], - partner['id'], form['company_id'])) - during = self.cr.fetchone() - values[str(i)] = during and during[0] or "" + def _get_lines(self, form): + self.form=form + res = [] + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': form['fiscalyear']}) + self.cr.execute("SELECT DISTINCT res_partner.id AS id, " \ + "res_partner.name AS name, res_partner.ref AS code " \ + "FROM res_partner, account_move_line AS line, account_account " \ + "WHERE (line.account_id=account_account.id) " \ + "AND (line.reconcile_id IS NULL) " \ + "AND (line.partner_id=res_partner.id) " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active " \ + "ORDER BY res_partner.name", (form['company_id'],)) + partners = self.cr.dictfetchall() + for partner in partners: + values = {} + self.cr.execute("SELECT SUM(debit-credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id=account_account.id) " \ + "AND (account_account.type IN (" + form['computation'] +")) " \ + "AND (date < %s) AND (partner_id=%d) " \ + "AND (reconcile_id IS NULL) " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (form['0']['start'], partner['id'], form['company_id'])) + before = self.cr.fetchone() + values['before'] = before and before[0] or "" + for i in range(5): + self.cr.execute("SELECT SUM(debit-credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id=account_account.id) " \ + "AND (account_account.type IN (" + form['computation'] + ")) " \ + "AND (date >= %s) AND (date <= %s) " \ + "AND (partner_id = %d) " \ + "AND (reconcile_id IS NULL) " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (form[str(i)]['start'], form[str(i)]['stop'], + partner['id'], form['company_id'])) + during = self.cr.fetchone() + values[str(i)] = during and during[0] or "" - self.cr.execute("SELECT SUM(debit-credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id = account_account.id) " \ - "AND (account_account.type IN (" + form['computation'] + ")) " \ - "AND (partner_id = %d) " \ - "AND (reconcile_id IS NULL) " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (partner['id'], form['company_id'])) - total = self.cr.fetchone() - values['total'] = total and total[0] or 0.0 - values['name'] = partner['name'] - values['ref'] = partner['code'] - t = 0.0 - for i in range(5)+['before']: - t+= float(values.get(str(i), 0.0) or 0.0) - if values['total']: - res.append(values) - total = 0.0 - totals = {} - for r in res: - total += float(r['total'] or 0.0) - for i in range(5)+['before']: - totals.setdefault(str(i), 0.0) - totals[str(i)] += float(r[str(i)] or 0.0) - if form['sorting_on']=='amount': - res.sort(lambda x,y:cmp(y['total'],x['total'])) - return res + self.cr.execute("SELECT SUM(debit-credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id = account_account.id) " \ + "AND (account_account.type IN (" + form['computation'] + ")) " \ + "AND (partner_id = %d) " \ + "AND (reconcile_id IS NULL) " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (partner['id'], form['company_id'])) + total = self.cr.fetchone() + values['total'] = total and total[0] or 0.0 + values['name'] = partner['name'] + values['ref'] = partner['code'] + t = 0.0 + for i in range(5)+['before']: + t+= float(values.get(str(i), 0.0) or 0.0) + if values['total']: + res.append(values) + total = 0.0 + totals = {} + for r in res: + total += float(r['total'] or 0.0) + for i in range(5)+['before']: + totals.setdefault(str(i), 0.0) + totals[str(i)] += float(r[str(i)] or 0.0) + if form['sorting_on']=='amount': + res.sort(lambda x,y:cmp(y['total'],x['total'])) + return res - def _get_total(self, fiscalyear, company_id): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': fiscalyear}) - self.cr.execute("SELECT SUM(debit - credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id = account_account.id) " \ - "AND (account_account.type IN (" + self.form['computation'] + ")) "\ - "AND reconcile_id IS NULL " \ - "AND partner_id is NOT NULL " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (company_id,)) - total = self.cr.fetchone() - return total and total[0] or 0.0 + def _get_total(self, fiscalyear, company_id): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': fiscalyear}) + self.cr.execute("SELECT SUM(debit - credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id = account_account.id) " \ + "AND (account_account.type IN (" + self.form['computation'] + ")) "\ + "AND reconcile_id IS NULL " \ + "AND partner_id is NOT NULL " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (company_id,)) + total = self.cr.fetchone() + return total and total[0] or 0.0 - def _get_before(self, date, fiscalyear, company_id): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': fiscalyear}) - self.cr.execute("SELECT SUM(debit - credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id = account_account.id) " \ - "AND (account_account.type IN (" + self.form['computation'] + ")) " \ - "AND reconcile_id IS NULL " \ - "AND (date < %s) " \ - "AND partner_id IS NOT NULL " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (date, company_id)) - before = self.cr.fetchone() - return before and before[0] or 0.0 + def _get_before(self, date, fiscalyear, company_id): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': fiscalyear}) + self.cr.execute("SELECT SUM(debit - credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id = account_account.id) " \ + "AND (account_account.type IN (" + self.form['computation'] + ")) " \ + "AND reconcile_id IS NULL " \ + "AND (date < %s) " \ + "AND partner_id IS NOT NULL " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (date, company_id)) + before = self.cr.fetchone() + return before and before[0] or 0.0 - def _get_for_period(self, period, fiscalyear, company_id): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': fiscalyear}) - self.cr.execute("SELECT SUM(debit - credit) " \ - "FROM account_move_line AS line, account_account " \ - "WHERE (line.account_id = account_account.id) " \ - "AND (account_account.type IN (" + self.form['computation'] + ")) " \ - "AND reconcile_id IS NULL " \ - "AND (date >= %s) " \ - "AND (date <= %s) " \ - "AND partner_id IS NOT NULL " \ - "AND " + line_query + " " \ - "AND (account_account.company_id = %d) " \ - "AND account_account.active", - (period['start'], period['stop'], company_id)) - period = self.cr.fetchone() - return period and period[0] or 0.0 + def _get_for_period(self, period, fiscalyear, company_id): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': fiscalyear}) + self.cr.execute("SELECT SUM(debit - credit) " \ + "FROM account_move_line AS line, account_account " \ + "WHERE (line.account_id = account_account.id) " \ + "AND (account_account.type IN (" + self.form['computation'] + ")) " \ + "AND reconcile_id IS NULL " \ + "AND (date >= %s) " \ + "AND (date <= %s) " \ + "AND partner_id IS NOT NULL " \ + "AND " + line_query + " " \ + "AND (account_account.company_id = %d) " \ + "AND account_account.active", + (period['start'], period['stop'], company_id)) + period = self.cr.fetchone() + return period and period[0] or 0.0 - def _get_company(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name + def _get_company(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name - def _get_currency(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name + def _get_currency(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name report_sxw.report_sxw( - 'report.account.aged.trial.balance', - 'res.partner', - 'addons/account/report/aged_trial_balance.rml', - parser=aged_trial_report, header=False) + 'report.account.aged.trial.balance', + 'res.partner', + 'addons/account/report/aged_trial_balance.rml', + parser=aged_trial_report, header=False) diff --git a/addons/account/report/budget_report.py b/addons/account/report/budget_report.py index 699142455ac..e2a19276d02 100644 --- a/addons/account/report/budget_report.py +++ b/addons/account/report/budget_report.py @@ -33,42 +33,42 @@ import datetime import operator class budget_report(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(budget_report, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'lines': self.lines, - 'budget_total': self.budget_total, - 'post_total': self.post_total, - 'time': time, - }) + def __init__(self, cr, uid, name, context): + super(budget_report, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'lines': self.lines, + 'budget_total': self.budget_total, + 'post_total': self.post_total, + 'time': time, + }) - def post_total(self, post_obj, date1, date2): - def str2date(date_str): - return datetime.date.fromtimestamp(time.mktime(time.strptime(date_str, '%Y-%m-%d'))) - def interval(d1str, d2str): - return (str2date(d2str) - str2date(d1str) + datetime.timedelta(days=1)).days - prev = reduce(lambda x,d: x + d.amount, post_obj.dotation_ids, 0.0) - period_days = interval(date1, date2) - for d in post_obj.dotation_ids: - i = interval(d.period_id.date_start, d.period_id.date_stop) - total_days = reduce(lambda x,d: x+interval(d.period_id.date_start, d.period_id.date_stop), post_obj.dotation_ids, 0) - achievements = reduce(lambda x,l: x+l['achievements'], self.lines(post_obj, date1, date2), 0.0) - return [{'prev': prev, 'prev_period': prev * period_days / total_days, 'achievements': achievements}] + def post_total(self, post_obj, date1, date2): + def str2date(date_str): + return datetime.date.fromtimestamp(time.mktime(time.strptime(date_str, '%Y-%m-%d'))) + def interval(d1str, d2str): + return (str2date(d2str) - str2date(d1str) + datetime.timedelta(days=1)).days + prev = reduce(lambda x,d: x + d.amount, post_obj.dotation_ids, 0.0) + period_days = interval(date1, date2) + for d in post_obj.dotation_ids: + i = interval(d.period_id.date_start, d.period_id.date_stop) + total_days = reduce(lambda x,d: x+interval(d.period_id.date_start, d.period_id.date_stop), post_obj.dotation_ids, 0) + achievements = reduce(lambda x,l: x+l['achievements'], self.lines(post_obj, date1, date2), 0.0) + return [{'prev': prev, 'prev_period': prev * period_days / total_days, 'achievements': achievements}] - def budget_total(self, post_objs, date1, date2): - res = {'prev': 0.0, 'prev_period': 0.0, 'achievements': 0.0} - for post_obj in post_objs: - r = self.post_total(post_obj, date1, date2)[0] - for k in r: - res[k] += r[k] - return [res] - - def lines(self, post_obj, date1, date2): - res = [] - for a in post_obj.account_ids: - self.cr.execute("SELECT COALESCE(SUM(debit-credit), 0) FROM account_move_line WHERE account_id=%d AND date>=%s AND date<=%s and state<>'draft'", (a.id, date1, date2)) - achievements = float(self.cr.fetchone()[0]) * (post_obj.sens=='produit' and -1 or 1) - res.append({'name': a.name, 'code': a.code, 'achievements': achievements}) - return res + def budget_total(self, post_objs, date1, date2): + res = {'prev': 0.0, 'prev_period': 0.0, 'achievements': 0.0} + for post_obj in post_objs: + r = self.post_total(post_obj, date1, date2)[0] + for k in r: + res[k] += r[k] + return [res] + + def lines(self, post_obj, date1, date2): + res = [] + for a in post_obj.account_ids: + self.cr.execute("SELECT COALESCE(SUM(debit-credit), 0) FROM account_move_line WHERE account_id=%d AND date>=%s AND date<=%s and state<>'draft'", (a.id, date1, date2)) + achievements = float(self.cr.fetchone()[0]) * (post_obj.sens=='produit' and -1 or 1) + res.append({'name': a.name, 'code': a.code, 'achievements': achievements}) + return res report_sxw.report_sxw('report.account.budget', 'account.budget.post', 'addons/account/report/budget_report.rml',parser=budget_report) diff --git a/addons/account/report/central_journal.py b/addons/account/report/central_journal.py index 7a198b1c524..7cff84e6ef6 100644 --- a/addons/account/report/central_journal.py +++ b/addons/account/report/central_journal.py @@ -34,25 +34,25 @@ from report import report_sxw # Use period and Journal for selection or resources # class journal_print(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(journal_print, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'lines': self.lines, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit - }) - - def lines(self, period_id, journal_id, *args): - self.cr.execute('select a.code, a.name, sum(debit) as debit, sum(credit) as credit from account_move_line l left join account_account a on (l.account_id=a.id) where l.period_id=%d and l.journal_id=%d and l.state<>\'draft\' group by a.id, a.code, a.name', (period_id, journal_id)) - return self.cr.dictfetchall() - - def _sum_debit(self, period_id, journal_id): - self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self, period_id, journal_id): - self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) - return self.cr.fetchone()[0] or 0.0 + def __init__(self, cr, uid, name, context): + super(journal_print, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'lines': self.lines, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit + }) + + def lines(self, period_id, journal_id, *args): + self.cr.execute('select a.code, a.name, sum(debit) as debit, sum(credit) as credit from account_move_line l left join account_account a on (l.account_id=a.id) where l.period_id=%d and l.journal_id=%d and l.state<>\'draft\' group by a.id, a.code, a.name', (period_id, journal_id)) + return self.cr.dictfetchall() + + def _sum_debit(self, period_id, journal_id): + self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, period_id, journal_id): + self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id=%d and state<>\'draft\'', (period_id, journal_id)) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.central.journal', 'account.journal.period', 'addons/account/report/central_journal.rml',parser=journal_print) diff --git a/addons/account/report/general_journal.py b/addons/account/report/general_journal.py index cc7b60d7b93..ef697496a39 100644 --- a/addons/account/report/general_journal.py +++ b/addons/account/report/general_journal.py @@ -35,65 +35,65 @@ from report import report_sxw # Use period and Journal for selection or resources # class journal_print(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(journal_print, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self.lines, - 'periods': self.periods, - 'sum_debit_period': self._sum_debit_period, - 'sum_credit_period': self._sum_credit_period, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit - }) + def __init__(self, cr, uid, name, context): + super(journal_print, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self.lines, + 'periods': self.periods, + 'sum_debit_period': self._sum_debit_period, + 'sum_credit_period': self._sum_credit_period, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit + }) - def preprocess(self, objects, datas, ids): - super(journal_print, self).preprocess(objects, datas, ids) - self.cr.execute('select period_id, journal_id from account_journal_period where id in (' + ','.join([str(id) for id in ids]) + ')') - res = self.cr.fetchall() - self.period_ids = ','.join([str(x[0]) for x in res]) - self.journal_ids = ','.join([str(x[1]) for x in res]) + def preprocess(self, objects, datas, ids): + super(journal_print, self).preprocess(objects, datas, ids) + self.cr.execute('select period_id, journal_id from account_journal_period where id in (' + ','.join([str(id) for id in ids]) + ')') + res = self.cr.fetchall() + self.period_ids = ','.join([str(x[0]) for x in res]) + self.journal_ids = ','.join([str(x[1]) for x in res]) - # returns a list of period objs - def periods(self, journal_period_objs): - dic = {} - def filter_unique(o): - key = o.period_id.id - res = key in dic - if not res: - dic[key] = True - return not res - filtered_objs = filter(filter_unique, journal_period_objs) - return map(lambda x: x.period_id, filtered_objs) - - def lines(self, period_id): - if not self.journal_ids: - return [] - self.cr.execute('select j.code, j.name, sum(l.debit) as debit, sum(l.credit) as credit from account_move_line l left join account_journal j on (l.journal_id=j.id) where period_id=%d and journal_id in (' + self.journal_ids + ') and l.state<>\'draft\' group by j.id, j.code, j.name', (period_id,)) - return self.cr.dictfetchall() - - def _sum_debit_period(self, period_id): - if not self.journal_ids: - return 0.0 - self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id in (' + self.journal_ids + ') and state<>\'draft\'', (period_id,)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit_period(self, period_id): - if not self.journal_ids: - return 0.0 - self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id in (' + self.journal_ids + ') and state<>\'draft\'', (period_id,)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_debit(self): - if not self.journal_ids or not self.period_ids: - return 0.0 - self.cr.execute('select sum(debit) from account_move_line where period_id in (' + self.period_ids + ') and journal_id in (' + self.journal_ids + ') and state<>\'draft\'') - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self): - if not self.journal_ids or not self.period_ids: - return 0.0 - self.cr.execute('select sum(credit) from account_move_line where period_id in (' + self.period_ids + ') and journal_id in (' + self.journal_ids + ') and state<>\'draft\'') - return self.cr.fetchone()[0] or 0.0 + # returns a list of period objs + def periods(self, journal_period_objs): + dic = {} + def filter_unique(o): + key = o.period_id.id + res = key in dic + if not res: + dic[key] = True + return not res + filtered_objs = filter(filter_unique, journal_period_objs) + return map(lambda x: x.period_id, filtered_objs) + + def lines(self, period_id): + if not self.journal_ids: + return [] + self.cr.execute('select j.code, j.name, sum(l.debit) as debit, sum(l.credit) as credit from account_move_line l left join account_journal j on (l.journal_id=j.id) where period_id=%d and journal_id in (' + self.journal_ids + ') and l.state<>\'draft\' group by j.id, j.code, j.name', (period_id,)) + return self.cr.dictfetchall() + + def _sum_debit_period(self, period_id): + if not self.journal_ids: + return 0.0 + self.cr.execute('select sum(debit) from account_move_line where period_id=%d and journal_id in (' + self.journal_ids + ') and state<>\'draft\'', (period_id,)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit_period(self, period_id): + if not self.journal_ids: + return 0.0 + self.cr.execute('select sum(credit) from account_move_line where period_id=%d and journal_id in (' + self.journal_ids + ') and state<>\'draft\'', (period_id,)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_debit(self): + if not self.journal_ids or not self.period_ids: + return 0.0 + self.cr.execute('select sum(debit) from account_move_line where period_id in (' + self.period_ids + ') and journal_id in (' + self.journal_ids + ') and state<>\'draft\'') + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self): + if not self.journal_ids or not self.period_ids: + return 0.0 + self.cr.execute('select sum(credit) from account_move_line where period_id in (' + self.period_ids + ') and journal_id in (' + self.journal_ids + ') and state<>\'draft\'') + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.general.journal', 'account.journal.period', 'addons/account/report/general_journal.rml',parser=journal_print) diff --git a/addons/account/report/general_ledger.py b/addons/account/report/general_ledger.py index 7360de69153..be005b64de7 100644 --- a/addons/account/report/general_ledger.py +++ b/addons/account/report/general_ledger.py @@ -32,139 +32,139 @@ from report import report_sxw import pooler class general_ledger(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(general_ledger, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self.lines, - 'sum_debit_account': self._sum_debit_account, - 'sum_credit_account': self._sum_credit_account, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - - }) - self.context = context - self.tmp_list2=[] - self.final_list=[] - - - def recur(self,list1): - tmp_list3 =list1 - self.tmp_list2 =list1 -# print "self list",self.tmp_list2 - for i in range(0,len(list1)): - if list1[i] in self.final_list: - continue - self.final_list.append(list1[i]) -# print "finallly",self.final_list - if list1[i].child_id: - - tmp_list4=(hasattr(list1[i],'child_id') and list(list1[i].child_id) or []) - - self.tmp_list2 +=tmp_list4 - - self.tmp_list2+=self.recur(tmp_list4) - - return self.final_list - - def repeatIn(self, lst, name,nodes_parent=False): - - if name=='o': - list_final=[] - if not lst: - return super(general_ledger,self).repeatIn(lst, name,nodes_parent) - try: - tmp_list = list(lst) - if tmp_list: - tmp_list = self.recur(tmp_list) - else: - return super(general_ledger,self).repeatIn(lst, name,nodes_parent) - - lst = list(set([x for x in tmp_list])) - - final={} -# for x in lst: -# final[x]=x.id -# final1=sorted(final.items(), lambda x, y: cmp(x[1], y[1])) + def __init__(self, cr, uid, name, context): + super(general_ledger, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self.lines, + 'sum_debit_account': self._sum_debit_account, + 'sum_credit_account': self._sum_credit_account, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + + }) + self.context = context + self.tmp_list2=[] + self.final_list=[] + + + def recur(self,list1): + tmp_list3 =list1 + self.tmp_list2 =list1 +# print "self list",self.tmp_list2 + for i in range(0,len(list1)): + if list1[i] in self.final_list: + continue + self.final_list.append(list1[i]) +# print "finallly",self.final_list + if list1[i].child_id: + + tmp_list4=(hasattr(list1[i],'child_id') and list(list1[i].child_id) or []) + + self.tmp_list2 +=tmp_list4 + + self.tmp_list2+=self.recur(tmp_list4) + + return self.final_list + + def repeatIn(self, lst, name,nodes_parent=False): + + if name=='o': + list_final=[] + if not lst: + return super(general_ledger,self).repeatIn(lst, name,nodes_parent) + try: + tmp_list = list(lst) + if tmp_list: + tmp_list = self.recur(tmp_list) + else: + return super(general_ledger,self).repeatIn(lst, name,nodes_parent) + + lst = list(set([x for x in tmp_list])) + + final={} +# for x in lst: +# final[x]=x.id +# final1=sorted(final.items(), lambda x, y: cmp(x[1], y[1])) # -# for a in final1: -# list_final.append(a[0]) - list_final=tmp_list - - except: - pass - else: - - list_final=lst - ret_data = super(general_ledger,self).repeatIn(list_final, name,nodes_parent) - - return ret_data - - - def lines(self, account, form): - - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) - self.cr.execute("SELECT l.date, j.code, l.ref, l.name, l.debit, l.credit "\ - "FROM account_move_line l, account_journal j "\ - "WHERE l.journal_id = j.id "\ - "AND account_id = %d AND "+query+" "\ - "ORDER by l.id", (account.id,)) - res = self.cr.dictfetchall() - sum = 0.0 - - for l in res: - sum += l['debit'] - l ['credit'] - l['progress'] = sum - self.ids +=[account.id] - return res - - def _sum_debit_account(self, account, form): - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) - self.cr.execute("SELECT sum(debit) "\ - "FROM account_move_line l "\ - "WHERE l.account_id = %d AND "+query, (account.id,)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit_account(self, account, form): - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) - self.cr.execute("SELECT sum(credit) "\ - "FROM account_move_line l "\ - "WHERE l.account_id = %d AND "+query, (account.id,)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_debit(self, form): - if not self.ids: - return 0.0 - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) - self.cr.execute("SELECT sum(debit) "\ - "FROM account_move_line l "\ - "WHERE l.account_id in ("+','.join(map(str, self.ids))+") AND "+query) - - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self, form): - if not self.ids: - return 0.0 - ctx = self.context.copy() - ctx['fiscalyear'] = form['fiscalyear'] - ctx['periods'] = form['periods'][0][2] - query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) - self.cr.execute("SELECT sum(credit) "\ - "FROM account_move_line l "\ - "WHERE l.account_id in ("+','.join(map(str, self.ids))+") AND "+query) - return self.cr.fetchone()[0] or 0.0 +# for a in final1: +# list_final.append(a[0]) + list_final=tmp_list + + except: + pass + else: + + list_final=lst + ret_data = super(general_ledger,self).repeatIn(list_final, name,nodes_parent) + + return ret_data + + + def lines(self, account, form): + + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) + self.cr.execute("SELECT l.date, j.code, l.ref, l.name, l.debit, l.credit "\ + "FROM account_move_line l, account_journal j "\ + "WHERE l.journal_id = j.id "\ + "AND account_id = %d AND "+query+" "\ + "ORDER by l.id", (account.id,)) + res = self.cr.dictfetchall() + sum = 0.0 + + for l in res: + sum += l['debit'] - l ['credit'] + l['progress'] = sum + self.ids +=[account.id] + return res + + def _sum_debit_account(self, account, form): + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) + self.cr.execute("SELECT sum(debit) "\ + "FROM account_move_line l "\ + "WHERE l.account_id = %d AND "+query, (account.id,)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit_account(self, account, form): + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) + self.cr.execute("SELECT sum(credit) "\ + "FROM account_move_line l "\ + "WHERE l.account_id = %d AND "+query, (account.id,)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_debit(self, form): + if not self.ids: + return 0.0 + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) + self.cr.execute("SELECT sum(debit) "\ + "FROM account_move_line l "\ + "WHERE l.account_id in ("+','.join(map(str, self.ids))+") AND "+query) + + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, form): + if not self.ids: + return 0.0 + ctx = self.context.copy() + ctx['fiscalyear'] = form['fiscalyear'] + ctx['periods'] = form['periods'][0][2] + query = self.pool.get('account.move.line')._query_get(self.cr, self.uid, context=ctx) + self.cr.execute("SELECT sum(credit) "\ + "FROM account_move_line l "\ + "WHERE l.account_id in ("+','.join(map(str, self.ids))+") AND "+query) + return self.cr.fetchone()[0] or 0.0 report_sxw.report_sxw('report.account.general.ledger', 'account.account', 'addons/account/report/general_ledger.rml', parser=general_ledger, header=False) diff --git a/addons/account/report/invoice.py b/addons/account/report/invoice.py index f8d1bbc036c..786ea6153ca 100644 --- a/addons/account/report/invoice.py +++ b/addons/account/report/invoice.py @@ -31,15 +31,15 @@ import time from report import report_sxw class account_invoice(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_invoice, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - }) + def __init__(self, cr, uid, name, context): + super(account_invoice, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + }) report_sxw.report_sxw( - 'report.account.invoice', - 'account.invoice', - 'addons/account/report/invoice.rml', - parser=account_invoice, - ) + 'report.account.invoice', + 'account.invoice', + 'addons/account/report/invoice.rml', + parser=account_invoice, + ) diff --git a/addons/account/report/overdue.py b/addons/account/report/overdue.py index afd0c32794c..f551811de60 100644 --- a/addons/account/report/overdue.py +++ b/addons/account/report/overdue.py @@ -35,30 +35,30 @@ from report import report_sxw import pooler class Overdue(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(Overdue, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time' : time, - 'adr_get' : self._adr_get, - 'getLines' : self._lines_get, - }) + def __init__(self, cr, uid, name, context): + super(Overdue, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time' : time, + 'adr_get' : self._adr_get, + 'getLines' : self._lines_get, + }) - def _adr_get(self, partner, type): - res_partner = pooler.get_pool(self.cr.dbname).get('res.partner') - res_partner_address = pooler.get_pool(self.cr.dbname).get('res.partner.address') - addresses = res_partner.address_get(self.cr, self.uid, [partner.id], [type]) - adr_id = addresses and addresses[type] or False - return adr_id and res_partner_address.read(self.cr, self.uid, [adr_id])[0] or False + def _adr_get(self, partner, type): + res_partner = pooler.get_pool(self.cr.dbname).get('res.partner') + res_partner_address = pooler.get_pool(self.cr.dbname).get('res.partner.address') + addresses = res_partner.address_get(self.cr, self.uid, [partner.id], [type]) + adr_id = addresses and addresses[type] or False + return adr_id and res_partner_address.read(self.cr, self.uid, [adr_id])[0] or False - def _lines_get(self, partner): - moveline_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - movelines = moveline_obj.search(self.cr, self.uid, - [('partner_id', '=', partner.id), - ('account_id.type', 'in', ['receivable', 'payable']), - ('state', '<>', 'draft'), ('reconcile_id', '=', False)]) - movelines = moveline_obj.browse(self.cr, self.uid, movelines) - return movelines + def _lines_get(self, partner): + moveline_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + movelines = moveline_obj.search(self.cr, self.uid, + [('partner_id', '=', partner.id), + ('account_id.type', 'in', ['receivable', 'payable']), + ('state', '<>', 'draft'), ('reconcile_id', '=', False)]) + movelines = moveline_obj.browse(self.cr, self.uid, movelines) + return movelines report_sxw.report_sxw('report.account.overdue', 'res.partner', - 'addons/account/report/overdue.rml', parser=Overdue) + 'addons/account/report/overdue.rml', parser=Overdue) diff --git a/addons/account/report/partner_balance.py b/addons/account/report/partner_balance.py index 236debed330..6d50da8ff7a 100644 --- a/addons/account/report/partner_balance.py +++ b/addons/account/report/partner_balance.py @@ -32,201 +32,201 @@ import time from report import report_sxw class partner_balance(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(partner_balance, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self.lines, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_litige': self._sum_litige, - 'sum_sdebit': self._sum_sdebit, - 'sum_scredit': self._sum_scredit, - 'solde_debit': self._solde_balance_debit, - 'solde_credit': self._solde_balance_credit, - 'get_company': self._get_company, - 'get_currency': self._get_currency, - }) + def __init__(self, cr, uid, name, context): + super(partner_balance, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self.lines, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'sum_litige': self._sum_litige, + 'sum_sdebit': self._sum_sdebit, + 'sum_scredit': self._sum_scredit, + 'solde_debit': self._solde_balance_debit, + 'solde_credit': self._solde_balance_credit, + 'get_company': self._get_company, + 'get_currency': self._get_currency, + }) - def preprocess(self, objects, data, ids): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': data['form']['fiscalyear']}) - self.cr.execute('SELECT DISTINCT line.partner_id ' \ - 'FROM account_move_line AS line, account_account AS account ' \ - 'WHERE line.partner_id IS NOT NULL ' \ - 'AND line.date >= %s ' \ - 'AND line.date <= %s ' \ - 'AND ' + line_query + ' ' \ - 'AND line.account_id = account.id ' \ - 'AND account.company_id = %d ' \ - 'AND account.active', - (data['form']['date1'], data['form']['date2'], - data['form']['company_id'])) - new_ids = [id for (id,) in self.cr.fetchall()] - self.cr.execute('SELECT a.id ' \ - 'FROM account_account a ' \ - 'LEFT JOIN account_account_type t ' \ - 'ON (a.type = t.code) ' \ - 'WHERE t.partner_account = TRUE ' \ - 'AND a.company_id = %d ' \ - 'AND a.active', (data['form']['company_id'],)) - self.account_ids = ','.join([str(a) for (a,) in self.cr.fetchall()]) - self.partner_ids = ','.join(map(str, new_ids)) - objects = self.pool.get('res.partner').browse(self.cr, self.uid, new_ids) - super(partner_balance, self).preprocess(objects, data, new_ids) + def preprocess(self, objects, data, ids): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': data['form']['fiscalyear']}) + self.cr.execute('SELECT DISTINCT line.partner_id ' \ + 'FROM account_move_line AS line, account_account AS account ' \ + 'WHERE line.partner_id IS NOT NULL ' \ + 'AND line.date >= %s ' \ + 'AND line.date <= %s ' \ + 'AND ' + line_query + ' ' \ + 'AND line.account_id = account.id ' \ + 'AND account.company_id = %d ' \ + 'AND account.active', + (data['form']['date1'], data['form']['date2'], + data['form']['company_id'])) + new_ids = [id for (id,) in self.cr.fetchall()] + self.cr.execute('SELECT a.id ' \ + 'FROM account_account a ' \ + 'LEFT JOIN account_account_type t ' \ + 'ON (a.type = t.code) ' \ + 'WHERE t.partner_account = TRUE ' \ + 'AND a.company_id = %d ' \ + 'AND a.active', (data['form']['company_id'],)) + self.account_ids = ','.join([str(a) for (a,) in self.cr.fetchall()]) + self.partner_ids = ','.join(map(str, new_ids)) + objects = self.pool.get('res.partner').browse(self.cr, self.uid, new_ids) + super(partner_balance, self).preprocess(objects, data, new_ids) - def lines(self): - if not self.partner_ids: - return [] - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='l', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT p.ref, p.name, sum(debit) as debit, sum(credit) as credit, " \ - "CASE WHEN sum(debit) > sum(credit) " \ - "THEN sum(debit) - sum(credit) " \ - "ELSE 0 " \ - "END AS sdebit, " \ - "CASE WHEN sum(debit) < sum(credit) " \ - "THEN sum(credit) - sum(debit) " \ - "ELSE 0 " \ - "END AS scredit, " \ - "(SELECT sum(debit-credit) " \ - "FROM account_move_line l " \ - "WHERE partner_id = p.id " \ - "AND date >= %s " \ - "AND date <= %s " \ - "AND blocked = TRUE " \ - "AND " + line_query + " " \ - ") AS enlitige " \ - "FROM account_move_line l LEFT JOIN res_partner p ON (l.partner_id=p.id) " \ - "WHERE partner_id IN (" + self.partner_ids + ") " \ - "AND account_id IN (" + self.account_ids + ") " \ - "AND l.date >= %s " \ - "AND l.date <= %s " \ - "AND " + line_query + " " \ - "GROUP BY p.id, p.ref, p.name " \ - "ORDER BY p.ref, p.name", - (self.datas['form']['date1'], self.datas['form']['date2'], - self.datas['form']['date1'], self.datas['form']['date2'])) - res = self.cr.dictfetchall() - return res + def lines(self): + if not self.partner_ids: + return [] + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='l', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT p.ref, p.name, sum(debit) as debit, sum(credit) as credit, " \ + "CASE WHEN sum(debit) > sum(credit) " \ + "THEN sum(debit) - sum(credit) " \ + "ELSE 0 " \ + "END AS sdebit, " \ + "CASE WHEN sum(debit) < sum(credit) " \ + "THEN sum(credit) - sum(debit) " \ + "ELSE 0 " \ + "END AS scredit, " \ + "(SELECT sum(debit-credit) " \ + "FROM account_move_line l " \ + "WHERE partner_id = p.id " \ + "AND date >= %s " \ + "AND date <= %s " \ + "AND blocked = TRUE " \ + "AND " + line_query + " " \ + ") AS enlitige " \ + "FROM account_move_line l LEFT JOIN res_partner p ON (l.partner_id=p.id) " \ + "WHERE partner_id IN (" + self.partner_ids + ") " \ + "AND account_id IN (" + self.account_ids + ") " \ + "AND l.date >= %s " \ + "AND l.date <= %s " \ + "AND " + line_query + " " \ + "GROUP BY p.id, p.ref, p.name " \ + "ORDER BY p.ref, p.name", + (self.datas['form']['date1'], self.datas['form']['date2'], + self.datas['form']['date1'], self.datas['form']['date2'])) + res = self.cr.dictfetchall() + return res - def _sum_debit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - 'SELECT sum(debit) ' \ - 'FROM account_move_line ' \ - 'WHERE partner_id IN (' + self.partner_ids + ') ' \ - 'AND account_id IN (' + self.account_ids + ') ' \ - 'AND date >=%s ' \ - 'AND date <= %s ' \ - 'AND ' + line_query, - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_debit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + 'SELECT sum(debit) ' \ + 'FROM account_move_line ' \ + 'WHERE partner_id IN (' + self.partner_ids + ') ' \ + 'AND account_id IN (' + self.account_ids + ') ' \ + 'AND date >=%s ' \ + 'AND date <= %s ' \ + 'AND ' + line_query, + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _sum_credit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - 'SELECT sum(credit) ' \ - 'FROM account_move_line ' \ - 'WHERE partner_id IN (' + self.partner_ids + ') ' \ - 'AND account_id IN (' + self.account_ids + ') ' \ - 'AND date >= %s ' \ - 'AND date <= %s ' \ - 'AND ' + line_query, - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_credit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + 'SELECT sum(credit) ' \ + 'FROM account_move_line ' \ + 'WHERE partner_id IN (' + self.partner_ids + ') ' \ + 'AND account_id IN (' + self.account_ids + ') ' \ + 'AND date >= %s ' \ + 'AND date <= %s ' \ + 'AND ' + line_query, + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _sum_litige(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - 'SELECT sum(debit-credit) ' \ - 'FROM account_move_line ' \ - 'WHERE partner_id IN (' + self.partner_ids + ') ' \ - 'AND account_id IN (' + self.account_ids + ') ' \ - 'AND date >= %s ' \ - 'AND date <= %s ' \ - 'AND blocked=TRUE ' \ - 'AND ' + line_query, - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_litige(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + 'SELECT sum(debit-credit) ' \ + 'FROM account_move_line ' \ + 'WHERE partner_id IN (' + self.partner_ids + ') ' \ + 'AND account_id IN (' + self.account_ids + ') ' \ + 'AND date >= %s ' \ + 'AND date <= %s ' \ + 'AND blocked=TRUE ' \ + 'AND ' + line_query, + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _sum_sdebit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - 'SELECT CASE WHEN sum(debit) > sum(credit) ' \ - 'THEN sum(debit - credit) ' \ - 'ELSE 0 ' \ - 'END ' \ - 'FROM account_move_line ' \ - 'WHERE partner_id IN (' + self.partner_ids + ') ' \ - 'AND account_id IN (' + self.account_ids + ') ' \ - 'AND date >= %s ' \ - 'AND date <= %s ' \ - 'AND ' + line_query + ' ' \ - 'GROUP BY partner_id', - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_sdebit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + 'SELECT CASE WHEN sum(debit) > sum(credit) ' \ + 'THEN sum(debit - credit) ' \ + 'ELSE 0 ' \ + 'END ' \ + 'FROM account_move_line ' \ + 'WHERE partner_id IN (' + self.partner_ids + ') ' \ + 'AND account_id IN (' + self.account_ids + ') ' \ + 'AND date >= %s ' \ + 'AND date <= %s ' \ + 'AND ' + line_query + ' ' \ + 'GROUP BY partner_id', + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _sum_scredit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - 'SELECT CASE WHEN sum(debit) < sum(credit) ' \ - 'THEN sum(credit - debit) ' \ - 'ELSE 0 ' \ - 'END ' \ - 'FROM account_move_line ' \ - 'WHERE partner_id IN (' + self.partner_ids + ') ' \ - 'AND account_id IN (' + self.account_ids + ') ' \ - 'AND date >= %s ' \ - 'AND date <= %s ' \ - 'AND ' + line_query + ' ' \ - 'GROUP BY partner_id', - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_scredit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + 'SELECT CASE WHEN sum(debit) < sum(credit) ' \ + 'THEN sum(credit - debit) ' \ + 'ELSE 0 ' \ + 'END ' \ + 'FROM account_move_line ' \ + 'WHERE partner_id IN (' + self.partner_ids + ') ' \ + 'AND account_id IN (' + self.account_ids + ') ' \ + 'AND date >= %s ' \ + 'AND date <= %s ' \ + 'AND ' + line_query + ' ' \ + 'GROUP BY partner_id', + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _solde_balance_debit(self): - debit, credit = self._sum_debit(), self._sum_credit() - return debit > credit and debit - credit - - def _solde_balance_credit(self): - debit, credit = self._sum_debit(), self._sum_credit() - return credit > debit and credit - debit + def _solde_balance_debit(self): + debit, credit = self._sum_debit(), self._sum_credit() + return debit > credit and debit - credit + + def _solde_balance_credit(self): + debit, credit = self._sum_debit(), self._sum_credit() + return credit > debit and credit - debit - def _get_company(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name + def _get_company(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name - def _get_currency(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name + def _get_currency(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name report_sxw.report_sxw('report.account.partner.balance', 'res.partner', - 'addons/account/report/partner_balance.rml',parser=partner_balance, - header=False) + 'addons/account/report/partner_balance.rml',parser=partner_balance, + header=False) diff --git a/addons/account/report/tax_report.py b/addons/account/report/tax_report.py index 6fd442e4394..dc08fe36c9a 100644 --- a/addons/account/report/tax_report.py +++ b/addons/account/report/tax_report.py @@ -33,85 +33,85 @@ from report import report_sxw class tax_report(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(tax_report, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'get_period': self._get_period, - 'get_codes': self._get_codes, - 'get_general': self._get_general, - 'get_company': self._get_company, - 'get_currency': self._get_currency, - }) + def __init__(self, cr, uid, name, context): + super(tax_report, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'get_period': self._get_period, + 'get_codes': self._get_codes, + 'get_general': self._get_general, + 'get_company': self._get_company, + 'get_currency': self._get_currency, + }) - def _add_header(self, node): - return True + def _add_header(self, node): + return True - def _get_period(self, period_id): - return self.pool.get('account.period').browse(self.cr, self.uid, period_id).name + def _get_period(self, period_id): + return self.pool.get('account.period').browse(self.cr, self.uid, period_id).name - def _get_general(self, tax_code_id, period_id, company_id, based_on): - res=[] - if based_on == 'payments': - self.cr.execute('SELECT SUM(line.tax_amount) AS tax_amount, \ - SUM(line.debit) AS debit, \ - SUM(line.credit) AS credit, \ - COUNT(*) AS count, \ - account.id AS account_id \ - FROM account_move_line AS line, \ - account_account AS account, \ - account_move AS move \ - LEFT JOIN account_invoice invoice ON \ - (invoice.move_id = move.id) \ - WHERE line.state<>%s \ - AND line.period_id = %d \ - AND line.tax_code_id = %d \ - AND line.account_id = account.id \ - AND account.company_id = %d \ - AND move.id = line.move_id \ - AND ((invoice.state = %s) \ - OR (invoice.id IS NULL)) \ - GROUP BY account.id', ('draft', period_id, tax_code_id, - company_id, 'paid')) - else : - self.cr.execute('SELECT SUM(line.tax_amount) AS tax_amount, \ - SUM(line.debit) AS debit, \ - SUM(line.credit) AS credit, \ - COUNT(*) AS count, \ - account.id AS account_id \ - FROM account_move_line AS line, \ - account_account AS account \ - WHERE line.state <> %s \ - AND line.period_id = %d \ - AND line.tax_code_id = %d \ - AND line.account_id = account.id \ - AND account.company_id = %d \ - AND account.active \ - GROUP BY account.id', ('draft', period_id, tax_code_id, - company_id)) - res = self.cr.dictfetchall() - i = 0 - while i<len(res): - res[i]['account'] = self.pool.get('account.account').browse(self.cr, self.uid, res[i]['account_id']) - i+=1 - return res + def _get_general(self, tax_code_id, period_id, company_id, based_on): + res=[] + if based_on == 'payments': + self.cr.execute('SELECT SUM(line.tax_amount) AS tax_amount, \ + SUM(line.debit) AS debit, \ + SUM(line.credit) AS credit, \ + COUNT(*) AS count, \ + account.id AS account_id \ + FROM account_move_line AS line, \ + account_account AS account, \ + account_move AS move \ + LEFT JOIN account_invoice invoice ON \ + (invoice.move_id = move.id) \ + WHERE line.state<>%s \ + AND line.period_id = %d \ + AND line.tax_code_id = %d \ + AND line.account_id = account.id \ + AND account.company_id = %d \ + AND move.id = line.move_id \ + AND ((invoice.state = %s) \ + OR (invoice.id IS NULL)) \ + GROUP BY account.id', ('draft', period_id, tax_code_id, + company_id, 'paid')) + else : + self.cr.execute('SELECT SUM(line.tax_amount) AS tax_amount, \ + SUM(line.debit) AS debit, \ + SUM(line.credit) AS credit, \ + COUNT(*) AS count, \ + account.id AS account_id \ + FROM account_move_line AS line, \ + account_account AS account \ + WHERE line.state <> %s \ + AND line.period_id = %d \ + AND line.tax_code_id = %d \ + AND line.account_id = account.id \ + AND account.company_id = %d \ + AND account.active \ + GROUP BY account.id', ('draft', period_id, tax_code_id, + company_id)) + res = self.cr.dictfetchall() + i = 0 + while i<len(res): + res[i]['account'] = self.pool.get('account.account').browse(self.cr, self.uid, res[i]['account_id']) + i+=1 + return res - def _get_codes(self, period_id, based_on, parent=False, level=0): - tc = self.pool.get('account.tax.code') - ids = tc.search(self.cr, self.uid, [('parent_id','=',parent)]) - res = [] - for code in tc.browse(self.cr, self.uid, ids, {'period_id': period_id, - 'based_on': based_on}): - res.append((' - '*level*2, code)) - res += self._get_codes(period_id, based_on, code.id, level+1) - return res + def _get_codes(self, period_id, based_on, parent=False, level=0): + tc = self.pool.get('account.tax.code') + ids = tc.search(self.cr, self.uid, [('parent_id','=',parent)]) + res = [] + for code in tc.browse(self.cr, self.uid, ids, {'period_id': period_id, + 'based_on': based_on}): + res.append((' - '*level*2, code)) + res += self._get_codes(period_id, based_on, code.id, level+1) + return res - def _get_company(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name + def _get_company(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name - def _get_currency(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name + def _get_currency(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name report_sxw.report_sxw('report.account.vat.declaration', 'account.tax.code', - 'addons/account/report/tax_report.rml', parser=tax_report, header=False) + 'addons/account/report/tax_report.rml', parser=tax_report, header=False) diff --git a/addons/account/report/third_party_ledger.py b/addons/account/report/third_party_ledger.py index 58f1a2dde71..28ce8692b4d 100644 --- a/addons/account/report/third_party_ledger.py +++ b/addons/account/report/third_party_ledger.py @@ -32,147 +32,147 @@ import time from report import report_sxw class third_party_ledger(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(third_party_ledger, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - 'lines': self.lines, - 'sum_debit_partner': self._sum_debit_partner, - 'sum_credit_partner': self._sum_credit_partner, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'get_company': self._get_company, - 'get_currency': self._get_currency, - }) + def __init__(self, cr, uid, name, context): + super(third_party_ledger, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + 'lines': self.lines, + 'sum_debit_partner': self._sum_debit_partner, + 'sum_credit_partner': self._sum_credit_partner, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'get_company': self._get_company, + 'get_currency': self._get_currency, + }) - def preprocess(self, objects, data, ids): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', - context={'fiscalyear': data['form']['fiscalyear']}) - self.cr.execute( - "SELECT DISTINCT line.partner_id " \ - "FROM account_move_line AS line, account_account AS account " \ - "WHERE line.partner_id IS NOT NULL " \ - "AND line.date >= %s " \ - "AND line.date <= %s " \ - "AND " + line_query + " " \ - "AND line.account_id = account.id " \ - "AND account.company_id = %d " \ - "AND account.active", - (data['form']['date1'], data['form']['date2'], - data['form']['company_id'])) - new_ids = [id for (id,) in self.cr.fetchall()] - self.cr.execute( - "SELECT a.id " \ - "FROM account_account a " \ - "LEFT JOIN account_account_type t " \ - "ON (a.type=t.code) " \ - "WHERE t.partner_account=TRUE " \ - "AND a.company_id = %d " \ - "AND a.active", (data['form']['company_id'],)) - self.account_ids = ','.join([str(a) for (a,) in self.cr.fetchall()]) - self.partner_ids = ','.join(map(str, new_ids)) - objects = self.pool.get('res.partner').browse(self.cr, self.uid, new_ids) - super(third_party_ledger, self).preprocess(objects, data, new_ids) + def preprocess(self, objects, data, ids): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='line', + context={'fiscalyear': data['form']['fiscalyear']}) + self.cr.execute( + "SELECT DISTINCT line.partner_id " \ + "FROM account_move_line AS line, account_account AS account " \ + "WHERE line.partner_id IS NOT NULL " \ + "AND line.date >= %s " \ + "AND line.date <= %s " \ + "AND " + line_query + " " \ + "AND line.account_id = account.id " \ + "AND account.company_id = %d " \ + "AND account.active", + (data['form']['date1'], data['form']['date2'], + data['form']['company_id'])) + new_ids = [id for (id,) in self.cr.fetchall()] + self.cr.execute( + "SELECT a.id " \ + "FROM account_account a " \ + "LEFT JOIN account_account_type t " \ + "ON (a.type=t.code) " \ + "WHERE t.partner_account=TRUE " \ + "AND a.company_id = %d " \ + "AND a.active", (data['form']['company_id'],)) + self.account_ids = ','.join([str(a) for (a,) in self.cr.fetchall()]) + self.partner_ids = ','.join(map(str, new_ids)) + objects = self.pool.get('res.partner').browse(self.cr, self.uid, new_ids) + super(third_party_ledger, self).preprocess(objects, data, new_ids) - def lines(self, partner): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='l', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT l.date, j.code, l.ref, l.name, l.debit, l.credit " \ - "FROM account_move_line l " \ - "LEFT JOIN account_journal j " \ - "ON (l.journal_id = j.id) " \ - "WHERE l.partner_id = %d " \ - "AND l.account_id IN (" + self.account_ids + ") " \ - "AND l.date >= %s " \ - "AND l.date <= %s " - "AND " + line_query + " " \ - "ORDER BY l.id", - (partner.id, self.datas['form']['date1'], self.datas['form']['date2'])) - res = self.cr.dictfetchall() - sum = 0.0 - for r in res: - sum += r['debit'] - r['credit'] - r['progress'] = sum - return res + def lines(self, partner): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, obj='l', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT l.date, j.code, l.ref, l.name, l.debit, l.credit " \ + "FROM account_move_line l " \ + "LEFT JOIN account_journal j " \ + "ON (l.journal_id = j.id) " \ + "WHERE l.partner_id = %d " \ + "AND l.account_id IN (" + self.account_ids + ") " \ + "AND l.date >= %s " \ + "AND l.date <= %s " + "AND " + line_query + " " \ + "ORDER BY l.id", + (partner.id, self.datas['form']['date1'], self.datas['form']['date2'])) + res = self.cr.dictfetchall() + sum = 0.0 + for r in res: + sum += r['debit'] - r['credit'] + r['progress'] = sum + return res - def _sum_debit_partner(self, partner): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT sum(debit) " \ - "FROM account_move_line " \ - "WHERE partner_id = %d " \ - "AND account_id IN (" + self.account_ids + ") " \ - "AND date >= %s " \ - "AND date <= %s " \ - "AND " + line_query, - (partner.id, self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit_partner(self, partner): - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT sum(credit) " \ - "FROM account_move_line " \ - "WHERE partner_id=%d " \ - "AND account_id IN (" + self.account_ids + ") " \ - "AND date >= %s " \ - "AND date <= %s " \ - "AND " + line_query, - (partner.id, self.datas["form"]["date1"], self.datas["form"]["date2"])) - return self.cr.fetchone()[0] or 0.0 - - def _sum_debit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT sum(debit) " \ - "FROM account_move_line " \ - "WHERE partner_id IN (" + self.partner_ids + ") " \ - "AND account_id IN (" + self.account_ids + ") " \ - "AND date >= %s " \ - "AND date <= %s " \ - "AND " + line_query, - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self): - if not self.ids: - return 0.0 - account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - line_query = account_move_line_obj._query_get(self.cr, self.uid, - obj='account_move_line', - context={'fiscalyear': self.datas['form']['fiscalyear']}) - self.cr.execute( - "SELECT sum(credit) " \ - "FROM account_move_line " \ - "WHERE partner_id IN (" + self.partner_ids + ") " \ - "AND account_id IN (" + self.account_ids + ") " \ - "AND date >= %s " \ - "AND date <= %s " \ - "AND " + line_query, - (self.datas['form']['date1'], self.datas['form']['date2'])) - return self.cr.fetchone()[0] or 0.0 + def _sum_debit_partner(self, partner): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT sum(debit) " \ + "FROM account_move_line " \ + "WHERE partner_id = %d " \ + "AND account_id IN (" + self.account_ids + ") " \ + "AND date >= %s " \ + "AND date <= %s " \ + "AND " + line_query, + (partner.id, self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit_partner(self, partner): + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT sum(credit) " \ + "FROM account_move_line " \ + "WHERE partner_id=%d " \ + "AND account_id IN (" + self.account_ids + ") " \ + "AND date >= %s " \ + "AND date <= %s " \ + "AND " + line_query, + (partner.id, self.datas["form"]["date1"], self.datas["form"]["date2"])) + return self.cr.fetchone()[0] or 0.0 + + def _sum_debit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT sum(debit) " \ + "FROM account_move_line " \ + "WHERE partner_id IN (" + self.partner_ids + ") " \ + "AND account_id IN (" + self.account_ids + ") " \ + "AND date >= %s " \ + "AND date <= %s " \ + "AND " + line_query, + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self): + if not self.ids: + return 0.0 + account_move_line_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + line_query = account_move_line_obj._query_get(self.cr, self.uid, + obj='account_move_line', + context={'fiscalyear': self.datas['form']['fiscalyear']}) + self.cr.execute( + "SELECT sum(credit) " \ + "FROM account_move_line " \ + "WHERE partner_id IN (" + self.partner_ids + ") " \ + "AND account_id IN (" + self.account_ids + ") " \ + "AND date >= %s " \ + "AND date <= %s " \ + "AND " + line_query, + (self.datas['form']['date1'], self.datas['form']['date2'])) + return self.cr.fetchone()[0] or 0.0 - def _get_company(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name + def _get_company(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).name - def _get_currency(self, form): - return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name + def _get_currency(self, form): + return pooler.get_pool(self.cr.dbname).get('res.company').browse(self.cr, self.uid, form['company_id']).currency_id.name report_sxw.report_sxw('report.account.third_party_ledger', 'res.partner', - 'addons/account/report/third_party_ledger.rml',parser=third_party_ledger, - header=False) + 'addons/account/report/third_party_ledger.rml',parser=third_party_ledger, + header=False) diff --git a/addons/account/wizard/wizard_account_balance_report.py b/addons/account/wizard/wizard_account_balance_report.py index 47d515ef144..f3ff3f72a2b 100644 --- a/addons/account/wizard/wizard_account_balance_report.py +++ b/addons/account/wizard/wizard_account_balance_report.py @@ -32,35 +32,35 @@ import pooler dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="fiscalyear" colspan="4"/> - <field name="periods" colspan="4"/> + <field name="fiscalyear" colspan="4"/> + <field name="periods" colspan="4"/> </form>''' dates_fields = { - 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year'}, - 'periods': {'string': 'Periods', 'type': 'many2many', 'relation': 'account.period', 'help': 'All periods if empty'} + 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year'}, + 'periods': {'string': 'Periods', 'type': 'many2many', 'relation': 'account.period', 'help': 'All periods if empty'} } class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - data['form']['target_move'] = False - if context.has_key('target_move'): - data['form']['target_move'] = context['target_move'] - return data['form'] + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + data['form']['target_move'] = False + if context.has_key('target_move'): + data['form']['target_move'] = context['target_move'] + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.account.balance', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.account.balance', 'state':'end'} + } + } wizard_report('account.account.balance.report') diff --git a/addons/account/wizard/wizard_account_chart.py b/addons/account/wizard/wizard_account_chart.py index 4809382a019..48854c62255 100644 --- a/addons/account/wizard/wizard_account_chart.py +++ b/addons/account/wizard/wizard_account_chart.py @@ -31,52 +31,52 @@ import wizard import pooler class wizard_account_chart(wizard.interface): - _account_chart_arch = '''<?xml version="1.0"?> - <form string="Account charts"> - <field name="fiscalyear"/> - <field name="target_move"/> - </form>''' - - _account_chart_fields = { - 'fiscalyear': { - 'string': 'Fiscal year', - 'type': 'many2one', - 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year', - }, - 'target_move': { - 'string': 'Target Moves', - 'type': 'selection', - 'selection': [('all','All Entries'),('posted_only','All Posted Entries')], - 'required': True, - 'default': lambda *a:"all", - }, - } + _account_chart_arch = '''<?xml version="1.0"?> + <form string="Account charts"> + <field name="fiscalyear"/> + <field name="target_move"/> + </form>''' + + _account_chart_fields = { + 'fiscalyear': { + 'string': 'Fiscal year', + 'type': 'many2one', + 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year', + }, + 'target_move': { + 'string': 'Target Moves', + 'type': 'selection', + 'selection': [('all','All Entries'),('posted_only','All Posted Entries')], + 'required': True, + 'default': lambda *a:"all", + }, + } - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - return data['form'] + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + return data['form'] - def _account_chart_open_window(self, cr, uid, data, context): - mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') - act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') + def _account_chart_open_window(self, cr, uid, data, context): + mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') + act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') - result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree') - id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] - result = act_obj.read(cr, uid, [id])[0] - result['context'] = str({'fiscalyear': data['form']['fiscalyear'],'target_move':data['form']['target_move']}) - return result + result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree') + id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] + result = act_obj.read(cr, uid, [id])[0] + result['context'] = str({'fiscalyear': data['form']['fiscalyear'],'target_move':data['form']['target_move']}) + return result - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type': 'form', 'arch':_account_chart_arch, 'fields':_account_chart_fields, 'state': [('end', 'Cancel'), ('open', 'Open Charts')]} - }, - 'open': { - 'actions': [], - 'result': {'type': 'action', 'action':_account_chart_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type': 'form', 'arch':_account_chart_arch, 'fields':_account_chart_fields, 'state': [('end', 'Cancel'), ('open', 'Open Charts')]} + }, + 'open': { + 'actions': [], + 'result': {'type': 'action', 'action':_account_chart_open_window, 'state':'end'} + } + } wizard_account_chart('account.chart') diff --git a/addons/account/wizard/wizard_account_duplicate.py b/addons/account/wizard/wizard_account_duplicate.py index 279c091db57..5b14b8dc904 100644 --- a/addons/account/wizard/wizard_account_duplicate.py +++ b/addons/account/wizard/wizard_account_duplicate.py @@ -32,27 +32,27 @@ import pooler duplicate_form = '''<?xml version="1.0"?> <form string="Duplicate account charts"> - <field name="company_id"/> + <field name="company_id"/> </form>''' duplicate_fields = { - 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, + 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, } def _do_duplicate(self, cr, uid, data, context): - account_obj = pooler.get_pool(cr.dbname).get('account.account') - account_obj.copy(cr, uid, data['id'], data['form'], context=context) - return {} + account_obj = pooler.get_pool(cr.dbname).get('account.account') + account_obj.copy(cr, uid, data['id'], data['form'], context=context) + return {} class wizard_account_duplicate(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': duplicate_form, 'fields': duplicate_fields, 'state': (('end', 'Cancel'), ('duplicate', 'Duplicate'))}, - }, - 'duplicate': { - 'actions': [_do_duplicate], - 'result': {'type': 'state', 'state': 'end'}, - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': duplicate_form, 'fields': duplicate_fields, 'state': (('end', 'Cancel'), ('duplicate', 'Duplicate'))}, + }, + 'duplicate': { + 'actions': [_do_duplicate], + 'result': {'type': 'state', 'state': 'end'}, + }, + } wizard_account_duplicate('account.wizard.account.duplicate') diff --git a/addons/account/wizard/wizard_aged_trial_balance.py b/addons/account/wizard/wizard_aged_trial_balance.py index cf9af968671..26bbecd803b 100644 --- a/addons/account/wizard/wizard_aged_trial_balance.py +++ b/addons/account/wizard/wizard_aged_trial_balance.py @@ -37,67 +37,67 @@ from tools.translate import _ _aged_trial_form = """<?xml version="1.0"?> <form string="Aged Trial Balance"> - <field name="company_id"/> - <newline/> - <field name="fiscalyear"/> - <newline/> - <field name="period_length"/> - <newline/> - <field name="sorting_on"/> - <newline/> - <field name="computation"/> + <field name="company_id"/> + <newline/> + <field name="fiscalyear"/> + <newline/> + <field name="period_length"/> + <newline/> + <field name="sorting_on"/> + <newline/> + <field name="computation"/> </form>""" _aged_trial_fields = { - 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, - 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year'}, - 'period_length': {'string': 'Period length (days)', 'type': 'integer', 'required': True, 'default': lambda *a:30}, - 'sorting_on':{'string': 'Sorting On', 'type': 'selection', 'selection': [('partner','By Partner Name (asc)'), ('amount','By Amount (desc)')],'required': True, 'default': lambda *a:'partner'}, - 'computation':{'string': 'Computational Method', 'type': 'selection', 'selection': [("\'receivable\'",'On Receivables Only'), ("\'payable\'",'On Payables Only'), ("\'receivable\',\'payable\'",'On Receivables & Payables')], 'required': True, 'default': lambda *a:"\'receivable\'"} + 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, + 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year'}, + 'period_length': {'string': 'Period length (days)', 'type': 'integer', 'required': True, 'default': lambda *a:30}, + 'sorting_on':{'string': 'Sorting On', 'type': 'selection', 'selection': [('partner','By Partner Name (asc)'), ('amount','By Amount (desc)')],'required': True, 'default': lambda *a:'partner'}, + 'computation':{'string': 'Computational Method', 'type': 'selection', 'selection': [("\'receivable\'",'On Receivables Only'), ("\'payable\'",'On Payables Only'), ("\'receivable\',\'payable\'",'On Receivables & Payables')], 'required': True, 'default': lambda *a:"\'receivable\'"} } def _calc_dates(self, cr, uid, data, context): - res = {} - period_length = data['form']['period_length'] - if period_length<=0: - raise wizard.except_wizard(_('UserError'), _('You must enter a period length that cannot be 0 or below !')) - start = now() - for i in range(5)[::-1]: - stop = start-RelativeDateTime(days=period_length) - res[str(i)] = { - 'name' : str((5-i)*period_length)+' days', - 'stop': start.strftime('%Y-%m-%d'), - 'start' : stop.strftime('%Y-%m-%d'), - } - start = stop - RelativeDateTime(days=1) - return res + res = {} + period_length = data['form']['period_length'] + if period_length<=0: + raise wizard.except_wizard(_('UserError'), _('You must enter a period length that cannot be 0 or below !')) + start = now() + for i in range(5)[::-1]: + stop = start-RelativeDateTime(days=period_length) + res[str(i)] = { + 'name' : str((5-i)*period_length)+' days', + 'stop': start.strftime('%Y-%m-%d'), + 'start' : stop.strftime('%Y-%m-%d'), + } + start = stop - RelativeDateTime(days=1) + return res class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - company_id = user.company_id.id - else: - company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - data['form']['company_id'] = company_id + user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + company_id = user.company_id.id + else: + company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + data['form']['company_id'] = company_id - return data['form'] + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type':'form', 'arch':_aged_trial_form, 'fields':_aged_trial_fields, 'state':[('end','Cancel'),('print','Print Aged Partner Balance')]}, - }, - 'print': { - 'actions': [_calc_dates], - 'result': {'type':'print', 'report':'account.aged.trial.balance', 'state':'end'}, - }, - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type':'form', 'arch':_aged_trial_form, 'fields':_aged_trial_fields, 'state':[('end','Cancel'),('print','Print Aged Partner Balance')]}, + }, + 'print': { + 'actions': [_calc_dates], + 'result': {'type':'print', 'report':'account.aged.trial.balance', 'state':'end'}, + }, + } wizard_report('account.aged.trial.balance') diff --git a/addons/account/wizard/wizard_automatic_reconcile.py b/addons/account/wizard/wizard_automatic_reconcile.py index 8b3c1d7080a..867de1a228f 100644 --- a/addons/account/wizard/wizard_automatic_reconcile.py +++ b/addons/account/wizard/wizard_automatic_reconcile.py @@ -40,11 +40,11 @@ import time # - journal # - compte d'ajustement # - montant max (0,03) - # <field name="max_amount"/> + # <field name="max_amount"/> # - libelle write-off # - devise principale ou secondaire - # devise secondaire = amount_currency - # si devise: pas prendre ceux avec montant_devise = 0 + # devise secondaire = amount_currency + # si devise: pas prendre ceux avec montant_devise = 0 # a demander à fabien: # - checkbox (comme ds sage) "lettrage rapide des comptes soldés"? @@ -54,273 +54,273 @@ import time _reconcile_form = '''<?xml version="1.0"?> <form string="Reconciliation"> - <separator string="Options" colspan="4"/> - <field name="account_ids" colspan="4" domain="[('reconcile','=',1)]" views="account.view_account_list"> - </field> - <field name="date1"/> - <field name="date2"/> - <field name="power"/> - <separator string="Write-Off Move" colspan="4"/> - <field name="max_amount"/> - <field name="writeoff_acc_id"/> - <field name="journal_id"/> - <field name="period_id"/> + <separator string="Options" colspan="4"/> + <field name="account_ids" colspan="4" domain="[('reconcile','=',1)]" views="account.view_account_list"> + </field> + <field name="date1"/> + <field name="date2"/> + <field name="power"/> + <separator string="Write-Off Move" colspan="4"/> + <field name="max_amount"/> + <field name="writeoff_acc_id"/> + <field name="journal_id"/> + <field name="period_id"/> </form>''' _reconcile_fields = { - 'account_ids': { - 'string': 'Account to reconcile', - 'type': 'many2many', - 'relation': 'account.account', - 'domain': [('reconcile','=',1)], - 'required': True - }, - 'writeoff_acc_id': { - 'string': 'Account', - 'type': 'many2one', - 'relation': 'account.account', - 'required': True - }, - 'journal_id': { - 'string': 'Journal', - 'type': 'many2one', - 'relation': 'account.journal', - 'required': True - }, - 'period_id': { - 'string': 'Period', - 'type': 'many2one', - 'relation': 'account.period', - 'required': True - }, - 'max_amount': { - 'string': 'Maximum write-off amount', - 'type': 'float', - }, - #'currency': { - # 'string': 'Reconcile in', - # 'type': 'selection', - # 'selection': [('current','current currency'), ('secondary','secondary currency')], - # 'required': True - #}, - 'power': { - 'string': 'Power', - 'type': 'selection', - 'selection': [(p,str(p)) for p in range(2, 10)], - 'required': True - }, - 'date1': { - 'string': 'Start of period', - 'type': 'date', - 'required': True, - 'default': lambda *a: time.strftime('%Y-01-01') - }, - 'date2': { - 'string': 'End of period', - 'type': 'date', - 'required': True, - 'default': lambda *a: time.strftime('%Y-%m-%d') - }, + 'account_ids': { + 'string': 'Account to reconcile', + 'type': 'many2many', + 'relation': 'account.account', + 'domain': [('reconcile','=',1)], + 'required': True + }, + 'writeoff_acc_id': { + 'string': 'Account', + 'type': 'many2one', + 'relation': 'account.account', + 'required': True + }, + 'journal_id': { + 'string': 'Journal', + 'type': 'many2one', + 'relation': 'account.journal', + 'required': True + }, + 'period_id': { + 'string': 'Period', + 'type': 'many2one', + 'relation': 'account.period', + 'required': True + }, + 'max_amount': { + 'string': 'Maximum write-off amount', + 'type': 'float', + }, + #'currency': { + # 'string': 'Reconcile in', + # 'type': 'selection', + # 'selection': [('current','current currency'), ('secondary','secondary currency')], + # 'required': True + #}, + 'power': { + 'string': 'Power', + 'type': 'selection', + 'selection': [(p,str(p)) for p in range(2, 10)], + 'required': True + }, + 'date1': { + 'string': 'Start of period', + 'type': 'date', + 'required': True, + 'default': lambda *a: time.strftime('%Y-01-01') + }, + 'date2': { + 'string': 'End of period', + 'type': 'date', + 'required': True, + 'default': lambda *a: time.strftime('%Y-%m-%d') + }, } _result_form = '''<?xml version="1.0"?> <form string="Reconciliation result"> - <field name="reconciled"/> - <newline/> - <field name="unreconciled"/> + <field name="reconciled"/> + <newline/> + <field name="unreconciled"/> </form>''' _result_fields = { - 'reconciled': { - 'string': 'Reconciled transactions', - 'type': 'integer', - 'readonly': True - }, - 'unreconciled': { - 'string': 'Not reconciled transactions', - 'type': 'integer', - 'readonly': True - }, + 'reconciled': { + 'string': 'Reconciled transactions', + 'type': 'integer', + 'readonly': True + }, + 'unreconciled': { + 'string': 'Not reconciled transactions', + 'type': 'integer', + 'readonly': True + }, } #TODO: cleanup and comment this code... For now, it is awfulllll # (way too complex, and really slow)... def do_reconcile(cr, uid, credits, debits, max_amount, power, writeoff_acc_id, period_id, journal_id, context={}): - # for one value of a credit, check all debits, and combination of them - # depending on the power. It starts with a power of one and goes up - # to the max power allowed - def check2(value, move_list, power): - def check(value, move_list, power): - for i in range(len(move_list)): - move = move_list[i] - if power == 1: - if abs(value - move[1]) <= max_amount + 0.00001: - return [move[0]] - else: - del move_list[i] - res = check(value - move[1], move_list, power-1) - move_list[i:i] = [move] - if res: - res.append(move[0]) - return res - return False + # for one value of a credit, check all debits, and combination of them + # depending on the power. It starts with a power of one and goes up + # to the max power allowed + def check2(value, move_list, power): + def check(value, move_list, power): + for i in range(len(move_list)): + move = move_list[i] + if power == 1: + if abs(value - move[1]) <= max_amount + 0.00001: + return [move[0]] + else: + del move_list[i] + res = check(value - move[1], move_list, power-1) + move_list[i:i] = [move] + if res: + res.append(move[0]) + return res + return False - for p in range(1, power+1): - res = check(value, move_list, p) - if res: - return res - return False + for p in range(1, power+1): + res = check(value, move_list, p) + if res: + return res + return False - # for a list of credit and debit and a given power, check if there - # are matching tuples of credit and debits, check all debits, and combination of them - # depending on the power. It starts with a power of one and goes up - # to the max power allowed - def check4(list1, list2, power): - def check3(value, list1, list2, list1power, power): - for i in range(len(list1)): - move = list1[i] - if list1power == 1: - res = check2(value + move[1], list2, power - 1) - if res: - return ([move[0]], res) - else: - del list1[i] - res = check3(value + move[1], list1, list2, list1power-1, power-1) - list1[i:i] = [move] - if res: - x, y = res - x.append(move[0]) - return (x, y) - return False + # for a list of credit and debit and a given power, check if there + # are matching tuples of credit and debits, check all debits, and combination of them + # depending on the power. It starts with a power of one and goes up + # to the max power allowed + def check4(list1, list2, power): + def check3(value, list1, list2, list1power, power): + for i in range(len(list1)): + move = list1[i] + if list1power == 1: + res = check2(value + move[1], list2, power - 1) + if res: + return ([move[0]], res) + else: + del list1[i] + res = check3(value + move[1], list1, list2, list1power-1, power-1) + list1[i:i] = [move] + if res: + x, y = res + x.append(move[0]) + return (x, y) + return False - for p in range(1, power): - res = check3(0, list1, list2, p, power) - if res: - return res - return False - - def check5(list1, list2, max_power): - for p in range(2, max_power+1): - res = check4(list1, list2, p) - if res: - return res + for p in range(1, power): + res = check3(0, list1, list2, p, power) + if res: + return res + return False + + def check5(list1, list2, max_power): + for p in range(2, max_power+1): + res = check4(list1, list2, p) + if res: + return res - ok = True - reconciled = 0 - move_line_obj = pooler.get_pool(cr.dbname).get('account.move.line') - while credits and debits and ok: - res = check5(credits, debits, power) - if res: - move_line_obj.reconcile(cr, uid, res[0] + res[1], 'auto', writeoff_acc_id, period_id, journal_id, context) - reconciled += len(res[0]) + len(res[1]) - credits = [(id, credit) for (id, credit) in credits if id not in res[0]] - debits = [(id, debit) for (id, debit) in debits if id not in res[1]] - else: - ok = False - return (reconciled, len(credits)+len(debits)) - + ok = True + reconciled = 0 + move_line_obj = pooler.get_pool(cr.dbname).get('account.move.line') + while credits and debits and ok: + res = check5(credits, debits, power) + if res: + move_line_obj.reconcile(cr, uid, res[0] + res[1], 'auto', writeoff_acc_id, period_id, journal_id, context) + reconciled += len(res[0]) + len(res[1]) + credits = [(id, credit) for (id, credit) in credits if id not in res[0]] + debits = [(id, debit) for (id, debit) in debits if id not in res[1]] + else: + ok = False + return (reconciled, len(credits)+len(debits)) + def _reconcile(self, cr, uid, data, context): - service = netsvc.LocalService("object_proxy") - move_line_obj = pooler.get_pool(cr.dbname).get('account.move.line') - form = data['form'] - max_amount = form.get('max_amount', 0.0) - power = form['power'] - reconciled = unreconciled = 0 - if not form['account_ids']: - return {'reconciled':0, 'unreconciled':[0]} - for account_id in form['account_ids'][0][2]: - - # reconcile automatically all transactions from partners whose balance is 0 - cr.execute( - "SELECT partner_id " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND reconcile_id IS NULL " \ - "AND state <> 'draft' " \ - "GROUP BY partner_id " \ - "HAVING ABS(SUM(debit-credit)) < %f AND count(*)>0", - (account_id, max_amount)) - partner_ids = [id for (id,) in cr.fetchall()] + service = netsvc.LocalService("object_proxy") + move_line_obj = pooler.get_pool(cr.dbname).get('account.move.line') + form = data['form'] + max_amount = form.get('max_amount', 0.0) + power = form['power'] + reconciled = unreconciled = 0 + if not form['account_ids']: + return {'reconciled':0, 'unreconciled':[0]} + for account_id in form['account_ids'][0][2]: + + # reconcile automatically all transactions from partners whose balance is 0 + cr.execute( + "SELECT partner_id " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND reconcile_id IS NULL " \ + "AND state <> 'draft' " \ + "GROUP BY partner_id " \ + "HAVING ABS(SUM(debit-credit)) < %f AND count(*)>0", + (account_id, max_amount)) + partner_ids = [id for (id,) in cr.fetchall()] - for partner_id in partner_ids: - cr.execute( - "SELECT id " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND partner_id=%d " \ - "AND state <> 'draft' " \ - "AND reconcile_id IS NULL", - (account_id, partner_id)) - line_ids = [id for (id,) in cr.fetchall()] - - if len(line_ids): - move_line_obj.reconcile(cr, uid, line_ids, 'auto', form['writeoff_acc_id'], form['period_id'], form['journal_id'], context) - reconciled += len(line_ids) - - # get the list of partners who have more than one unreconciled transaction - cr.execute( - "SELECT partner_id " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND reconcile_id IS NULL " \ - "AND state <> 'draft' " \ - "GROUP BY partner_id " \ - "HAVING count(*)>1", - (account_id,)) - partner_ids = [id for (id,) in cr.fetchall()] - #filter? - for partner_id in partner_ids: - # get the list of unreconciled 'debit transactions' for this partner - cr.execute( - "SELECT id, debit " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND partner_id=%d " \ - "AND reconcile_id IS NULL " \ - "AND state <> 'draft' " \ - "AND debit > 0", - (account_id, partner_id)) - debits = cr.fetchall() - - # get the list of unreconciled 'credit transactions' for this partner - cr.execute( - "SELECT id, credit " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND partner_id=%d " \ - "AND reconcile_id IS NULL " \ - "AND state <> 'draft' " \ - "AND credit > 0", - (account_id, partner_id)) - credits = cr.fetchall() - - (rec, unrec) = do_reconcile(cr, uid, credits, debits, max_amount, power, form['writeoff_acc_id'], form['period_id'], form['journal_id'], context) - reconciled += rec - unreconciled += unrec + for partner_id in partner_ids: + cr.execute( + "SELECT id " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND partner_id=%d " \ + "AND state <> 'draft' " \ + "AND reconcile_id IS NULL", + (account_id, partner_id)) + line_ids = [id for (id,) in cr.fetchall()] + + if len(line_ids): + move_line_obj.reconcile(cr, uid, line_ids, 'auto', form['writeoff_acc_id'], form['period_id'], form['journal_id'], context) + reconciled += len(line_ids) + + # get the list of partners who have more than one unreconciled transaction + cr.execute( + "SELECT partner_id " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND reconcile_id IS NULL " \ + "AND state <> 'draft' " \ + "GROUP BY partner_id " \ + "HAVING count(*)>1", + (account_id,)) + partner_ids = [id for (id,) in cr.fetchall()] + #filter? + for partner_id in partner_ids: + # get the list of unreconciled 'debit transactions' for this partner + cr.execute( + "SELECT id, debit " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND partner_id=%d " \ + "AND reconcile_id IS NULL " \ + "AND state <> 'draft' " \ + "AND debit > 0", + (account_id, partner_id)) + debits = cr.fetchall() + + # get the list of unreconciled 'credit transactions' for this partner + cr.execute( + "SELECT id, credit " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND partner_id=%d " \ + "AND reconcile_id IS NULL " \ + "AND state <> 'draft' " \ + "AND credit > 0", + (account_id, partner_id)) + credits = cr.fetchall() + + (rec, unrec) = do_reconcile(cr, uid, credits, debits, max_amount, power, form['writeoff_acc_id'], form['period_id'], form['journal_id'], context) + reconciled += rec + unreconciled += unrec - # add the number of transactions for partners who have only one - # unreconciled transactions to the unreconciled count - partner_filter = partner_ids and 'AND partner_id not in (%s)' % ','.join(map(str, filter(None, partner_ids))) or '' - cr.execute( - "SELECT count(*) " \ - "FROM account_move_line " \ - "WHERE account_id=%d " \ - "AND reconcile_id IS NULL " \ - "AND state <> 'draft' " + partner_filter, - (account_id,)) - additional_unrec = cr.fetchone()[0] - return {'reconciled':reconciled, 'unreconciled':unreconciled+additional_unrec} + # add the number of transactions for partners who have only one + # unreconciled transactions to the unreconciled count + partner_filter = partner_ids and 'AND partner_id not in (%s)' % ','.join(map(str, filter(None, partner_ids))) or '' + cr.execute( + "SELECT count(*) " \ + "FROM account_move_line " \ + "WHERE account_id=%d " \ + "AND reconcile_id IS NULL " \ + "AND state <> 'draft' " + partner_filter, + (account_id,)) + additional_unrec = cr.fetchone()[0] + return {'reconciled':reconciled, 'unreconciled':unreconciled+additional_unrec} class wiz_reconcile(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':_reconcile_form, 'fields':_reconcile_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} - }, - 'reconcile': { - 'actions': [_reconcile], - 'result': {'type':'form', 'arch':_result_form, 'fields':_result_fields, 'state':[('end','OK')]} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':_reconcile_form, 'fields':_reconcile_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} + }, + 'reconcile': { + 'actions': [_reconcile], + 'result': {'type':'form', 'arch':_result_form, 'fields':_result_fields, 'state':[('end','OK')]} + } + } wiz_reconcile('account.automatic.reconcile') diff --git a/addons/account/wizard/wizard_bank_reconcile.py b/addons/account/wizard/wizard_bank_reconcile.py index 30bcea5510b..8b93fdc791e 100644 --- a/addons/account/wizard/wizard_bank_reconcile.py +++ b/addons/account/wizard/wizard_bank_reconcile.py @@ -32,40 +32,40 @@ from tools.translate import _ _journal_form = '''<?xml version="1.0"?> <form string="%s"> - <field name="journal_id"/> + <field name="journal_id"/> </form>''' % ('Bank reconciliation',) _journal_fields = { - 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, + 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, } def _action_open_window(self, cr, uid, data, context): - form = data['form'] - cr.execute('select default_credit_account_id from account_journal where id=%d', (form['journal_id'],)) - account_id = cr.fetchone()[0] - if not account_id: - raise Exception, _('You have to define the bank account\nin the journal definition for reconciliation.') - return { - 'domain': "[('journal_id','=',%d), ('account_id','=',%d), ('state','<>','draft')]" % (form['journal_id'],account_id), - 'name': 'Saisie Standard', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move.line', - 'view_id': False, - 'context': "{'journal_id':%d}" % (form['journal_id'],), - 'type': 'ir.actions.act_window' - } + form = data['form'] + cr.execute('select default_credit_account_id from account_journal where id=%d', (form['journal_id'],)) + account_id = cr.fetchone()[0] + if not account_id: + raise Exception, _('You have to define the bank account\nin the journal definition for reconciliation.') + return { + 'domain': "[('journal_id','=',%d), ('account_id','=',%d), ('state','<>','draft')]" % (form['journal_id'],account_id), + 'name': 'Saisie Standard', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.move.line', + 'view_id': False, + 'context': "{'journal_id':%d}" % (form['journal_id'],), + 'type': 'ir.actions.act_window' + } class wiz_journal(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for bank reconciliation')]} - }, - 'open': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for bank reconciliation')]} + }, + 'open': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} + } + } wiz_journal('account.move.bank.reconcile') diff --git a/addons/account/wizard/wizard_budget_report.py b/addons/account/wizard/wizard_budget_report.py index 1ddc8188ecf..2c1f1214d2f 100644 --- a/addons/account/wizard/wizard_budget_report.py +++ b/addons/account/wizard/wizard_budget_report.py @@ -32,26 +32,26 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.budget', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.budget', 'state':'end'} + } + } wizard_report('account.budget.report') diff --git a/addons/account/wizard/wizard_budget_spread.py b/addons/account/wizard/wizard_budget_spread.py index b6431f94b3b..1ca2edf4682 100644 --- a/addons/account/wizard/wizard_budget_spread.py +++ b/addons/account/wizard/wizard_budget_spread.py @@ -32,31 +32,31 @@ import netsvc _spread_form = '''<?xml version="1.0"?> <form string="Spread"> - <field name="fiscalyear"/> - <field name="amount"/> + <field name="fiscalyear"/> + <field name="amount"/> </form>''' _spread_fields = { - 'fiscalyear': {'string':'Fiscal Year', 'type':'many2one', 'relation':'account.fiscalyear', 'required':True}, - 'amount': {'string':'Amount', 'type':'float', 'digits':(16,2)}, + 'fiscalyear': {'string':'Fiscal Year', 'type':'many2one', 'relation':'account.fiscalyear', 'required':True}, + 'amount': {'string':'Amount', 'type':'float', 'digits':(16,2)}, } class wizard_budget_spread(wizard.interface): - def _spread(self, cr, uid, data, context): - service = netsvc.LocalService("object_proxy") - form = data['form'] - res = service.execute(cr.dbname, uid, 'account.budget.post', 'spread', data['ids'], form['fiscalyear'], form['amount']) - return {} + def _spread(self, cr, uid, data, context): + service = netsvc.LocalService("object_proxy") + form = data['form'] + res = service.execute(cr.dbname, uid, 'account.budget.post', 'spread', data['ids'], form['fiscalyear'], form['amount']) + return {} - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':_spread_form, 'fields':_spread_fields, 'state':[('end','Cancel'),('spread','Spread')]} - }, - 'spread': { - 'actions': [_spread], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':_spread_form, 'fields':_spread_fields, 'state':[('end','Cancel'),('spread','Spread')]} + }, + 'spread': { + 'actions': [_spread], + 'result': {'type':'state', 'state':'end'} + } + } wizard_budget_spread('account.budget.spread') diff --git a/addons/account/wizard/wizard_fiscalyear_close.py b/addons/account/wizard/wizard_fiscalyear_close.py index 490473af2b5..0b61b28b574 100644 --- a/addons/account/wizard/wizard_fiscalyear_close.py +++ b/addons/account/wizard/wizard_fiscalyear_close.py @@ -34,142 +34,142 @@ from tools.translate import _ _transaction_form = '''<?xml version="1.0"?> <form string="Close Fiscal Year"> - <field name="fy_id"/> - <field name="fy2_id"/> - <field name="report_new"/> - <field name="report_name" colspan="3"/> + <field name="fy_id"/> + <field name="fy2_id"/> + <field name="report_new"/> + <field name="report_name" colspan="3"/> - <separator string="Are you sure ?" colspan="4"/> - <field name="sure"/> + <separator string="Are you sure ?" colspan="4"/> + <field name="sure"/> </form>''' _transaction_fields = { - 'fy_id': {'string':'Fiscal Year to close', 'type':'many2one', 'relation': 'account.fiscalyear','required':True, 'domain':[('state','=','draft')]}, - 'fy2_id': {'string':'New Fiscal Year', 'type':'many2one', 'relation': 'account.fiscalyear', 'domain':[('state','=','draft')], 'required':True}, - 'report_new': {'string':'Create new entries', 'type':'boolean', 'required':True, 'default': lambda *a:True}, - 'report_name': {'string':'Name of new entries', 'type':'char', 'size': 64, 'required':True}, - 'sure': {'string':'Check this box', 'type':'boolean'}, + 'fy_id': {'string':'Fiscal Year to close', 'type':'many2one', 'relation': 'account.fiscalyear','required':True, 'domain':[('state','=','draft')]}, + 'fy2_id': {'string':'New Fiscal Year', 'type':'many2one', 'relation': 'account.fiscalyear', 'domain':[('state','=','draft')], 'required':True}, + 'report_new': {'string':'Create new entries', 'type':'boolean', 'required':True, 'default': lambda *a:True}, + 'report_name': {'string':'Name of new entries', 'type':'char', 'size': 64, 'required':True}, + 'sure': {'string':'Check this box', 'type':'boolean'}, } def _data_load(self, cr, uid, data, context): - data['form']['report_new'] = True - data['form']['report_name'] = 'End of Fiscal Year Entry' - return data['form'] + data['form']['report_new'] = True + data['form']['report_name'] = 'End of Fiscal Year Entry' + return data['form'] def _data_save(self, cr, uid, data, context): - if not data['form']['sure']: - raise wizard.except_wizard(_('UserError'), _('Closing of fiscal year canceled, please check the box !')) - pool = pooler.get_pool(cr.dbname) + if not data['form']['sure']: + raise wizard.except_wizard(_('UserError'), _('Closing of fiscal year canceled, please check the box !')) + pool = pooler.get_pool(cr.dbname) - fy_id = data['form']['fy_id'] - if data['form']['report_new']: - period = pool.get('account.fiscalyear').browse(cr, uid, data['form']['fy2_id']).period_ids[0] - new_fyear = pool.get('account.fiscalyear').browse(cr, uid, data['form']['fy2_id']) - start_jp = new_fyear.start_journal_period_id - if not start_jp: - raise wizard.except_wizard(_('UserError'), - _('The new fiscal year should have a journal for new entries define on it')) + fy_id = data['form']['fy_id'] + if data['form']['report_new']: + period = pool.get('account.fiscalyear').browse(cr, uid, data['form']['fy2_id']).period_ids[0] + new_fyear = pool.get('account.fiscalyear').browse(cr, uid, data['form']['fy2_id']) + start_jp = new_fyear.start_journal_period_id + if not start_jp: + raise wizard.except_wizard(_('UserError'), + _('The new fiscal year should have a journal for new entries define on it')) - new_journal = start_jp.journal_id + new_journal = start_jp.journal_id - if not new_journal.default_credit_account_id or not new_journal.default_debit_account_id: - raise wizard.except_wizard(_('UserError'), - _('The journal must have default credit and debit account')) - if not new_journal.centralisation: - raise wizard.except_wizard(_('UserError'), - _('The journal must have centralised counterpart')) + if not new_journal.default_credit_account_id or not new_journal.default_debit_account_id: + raise wizard.except_wizard(_('UserError'), + _('The journal must have default credit and debit account')) + if not new_journal.centralisation: + raise wizard.except_wizard(_('UserError'), + _('The journal must have centralised counterpart')) - query_line = pool.get('account.move.line')._query_get(cr, uid, - obj='account_move_line', context={'fiscalyear': fy_id}) - cr.execute('select id from account_account WHERE active') - ids = map(lambda x: x[0], cr.fetchall()) - for account in pool.get('account.account').browse(cr, uid, ids, - context={'fiscalyear': fy_id}): - if account.close_method=='none' or account.type == 'view': - continue - if account.close_method=='balance': - if abs(account.balance)>0.0001: - pool.get('account.move.line').create(cr, uid, { - 'debit': account.balance>0 and account.balance, - 'credit': account.balance<0 and -account.balance, - 'name': data['form']['report_name'], - 'date': period.date_start, - 'journal_id': new_journal.id, - 'period_id': period.id, - 'account_id': account.id - }, {'journal_id': new_journal.id, 'period_id':period.id}) - if account.close_method=='unreconciled': - offset = 0 - limit = 100 - while True: - cr.execute('SELECT name, quantity, debit, credit, account_id, ref, ' \ - 'amount_currency, currency_id, blocked, partner_id, ' \ - 'date_maturity, date_created ' \ - 'FROM account_move_line ' \ - 'WHERE account_id = %d ' \ - 'AND ' + query_line + ' ' \ - 'AND reconcile_id is NULL ' \ - 'ORDER BY id ' \ - 'LIMIT %d OFFSET %d', (account.id, limit, offset)) - result = cr.dictfetchall() - if not result: - break - for move in result: - move.update({ - 'date': period.date_start, - 'journal_id': new_journal.id, - 'period_id': period.id, - }) - pool.get('account.move.line').create(cr, uid, move, { - 'journal_id': new_journal.id, - 'period_id': period.id, - }) - offset += limit - if account.close_method=='detail': - offset = 0 - limit = 100 - while True: - cr.execute('SELECT name, quantity, debit, credit, account_id, ref, ' \ - 'amount_currency, currency_id, blocked, partner_id, ' \ - 'date_maturity, date_created ' \ - 'FROM account_move_line ' \ - 'WHERE account_id = %d ' \ - 'AND ' + query_line + ' ' \ - 'ORDER BY id ' \ - 'LIMIT %d OFFSET %d', (account.id,fy_id, limit, offset)) - result = cr.dictfetchall() - if not result: - break - for move in result: - move.update({ - 'date': period.date_start, - 'journal_id': new_journal.id, - 'period_id': period.id, - }) - pool.get('account.move.line').create(cr, uid, move) - offset += limit + query_line = pool.get('account.move.line')._query_get(cr, uid, + obj='account_move_line', context={'fiscalyear': fy_id}) + cr.execute('select id from account_account WHERE active') + ids = map(lambda x: x[0], cr.fetchall()) + for account in pool.get('account.account').browse(cr, uid, ids, + context={'fiscalyear': fy_id}): + if account.close_method=='none' or account.type == 'view': + continue + if account.close_method=='balance': + if abs(account.balance)>0.0001: + pool.get('account.move.line').create(cr, uid, { + 'debit': account.balance>0 and account.balance, + 'credit': account.balance<0 and -account.balance, + 'name': data['form']['report_name'], + 'date': period.date_start, + 'journal_id': new_journal.id, + 'period_id': period.id, + 'account_id': account.id + }, {'journal_id': new_journal.id, 'period_id':period.id}) + if account.close_method=='unreconciled': + offset = 0 + limit = 100 + while True: + cr.execute('SELECT name, quantity, debit, credit, account_id, ref, ' \ + 'amount_currency, currency_id, blocked, partner_id, ' \ + 'date_maturity, date_created ' \ + 'FROM account_move_line ' \ + 'WHERE account_id = %d ' \ + 'AND ' + query_line + ' ' \ + 'AND reconcile_id is NULL ' \ + 'ORDER BY id ' \ + 'LIMIT %d OFFSET %d', (account.id, limit, offset)) + result = cr.dictfetchall() + if not result: + break + for move in result: + move.update({ + 'date': period.date_start, + 'journal_id': new_journal.id, + 'period_id': period.id, + }) + pool.get('account.move.line').create(cr, uid, move, { + 'journal_id': new_journal.id, + 'period_id': period.id, + }) + offset += limit + if account.close_method=='detail': + offset = 0 + limit = 100 + while True: + cr.execute('SELECT name, quantity, debit, credit, account_id, ref, ' \ + 'amount_currency, currency_id, blocked, partner_id, ' \ + 'date_maturity, date_created ' \ + 'FROM account_move_line ' \ + 'WHERE account_id = %d ' \ + 'AND ' + query_line + ' ' \ + 'ORDER BY id ' \ + 'LIMIT %d OFFSET %d', (account.id,fy_id, limit, offset)) + result = cr.dictfetchall() + if not result: + break + for move in result: + move.update({ + 'date': period.date_start, + 'journal_id': new_journal.id, + 'period_id': period.id, + }) + pool.get('account.move.line').create(cr, uid, move) + offset += limit - cr.execute('UPDATE account_journal_period ' \ - 'SET state = %s ' \ - 'WHERE period_id IN (SELECT id FROM account_period WHERE fiscalyear_id = %d)', - ('done',fy_id)) - cr.execute('UPDATE account_period SET state = %s ' \ - 'WHERE fiscalyear_id = %d', ('done',fy_id)) - cr.execute('UPDATE account_fiscalyear ' \ - 'SET state = %s, end_journal_period_id = %d' \ - 'WHERE id = %d', ('done',start_jp,fy_id)) - return {} + cr.execute('UPDATE account_journal_period ' \ + 'SET state = %s ' \ + 'WHERE period_id IN (SELECT id FROM account_period WHERE fiscalyear_id = %d)', + ('done',fy_id)) + cr.execute('UPDATE account_period SET state = %s ' \ + 'WHERE fiscalyear_id = %d', ('done',fy_id)) + cr.execute('UPDATE account_fiscalyear ' \ + 'SET state = %s, end_journal_period_id = %d' \ + 'WHERE id = %d', ('done',start_jp,fy_id)) + return {} class wiz_journal_close(wizard.interface): - states = { - 'init': { - 'actions': [_data_load], - 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('close','Close Fiscal Year')]} - }, - 'close': { - 'actions': [_data_save], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_data_load], + 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('close','Close Fiscal Year')]} + }, + 'close': { + 'actions': [_data_save], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_journal_close('account.fiscalyear.close') diff --git a/addons/account/wizard/wizard_general_ledger_report.py b/addons/account/wizard/wizard_general_ledger_report.py index a0f4f626b13..080bf493c30 100644 --- a/addons/account/wizard/wizard_general_ledger_report.py +++ b/addons/account/wizard/wizard_general_ledger_report.py @@ -32,31 +32,31 @@ import pooler dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="fiscalyear" colspan="4"/> - <field name="periods" colspan="4"/> + <field name="fiscalyear" colspan="4"/> + <field name="periods" colspan="4"/> </form>''' dates_fields = { - 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year'}, - 'periods': {'string': 'Periods', 'type': 'many2many', 'relation': 'account.period', 'help': 'All periods if empty'} + 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year'}, + 'periods': {'string': 'Periods', 'type': 'many2many', 'relation': 'account.period', 'help': 'All periods if empty'} } class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - return data['form'] + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.general.ledger', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.general.ledger', 'state':'end'} + } + } wizard_report('account.general.ledger.report') diff --git a/addons/account/wizard/wizard_invoice_state.py b/addons/account/wizard/wizard_invoice_state.py index aed413a1309..dd73059c038 100644 --- a/addons/account/wizard/wizard_invoice_state.py +++ b/addons/account/wizard/wizard_invoice_state.py @@ -31,34 +31,34 @@ import wizard import netsvc def _invoice_confirm(self, cr, uid, data, context): - wf_service = netsvc.LocalService('workflow') - for id in data['ids']: - wf_service.trg_validate(uid, 'account.invoice', id, 'invoice_open', cr) - return {} + wf_service = netsvc.LocalService('workflow') + for id in data['ids']: + wf_service.trg_validate(uid, 'account.invoice', id, 'invoice_open', cr) + return {} class wizard_invoice_confirm(wizard.interface): - states = { - 'init': { - 'actions': [_invoice_confirm], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_invoice_confirm], + 'result': {'type':'state', 'state':'end'} + } + } wizard_invoice_confirm('account.invoice.state.confirm') def _invoice_cancel(self, cr, uid, data, context): - wf_service = netsvc.LocalService('workflow') - for id in data['ids']: - wf_service.trg_validate(uid, 'account.invoice', id, 'invoice_cancel', cr) - return {} + wf_service = netsvc.LocalService('workflow') + for id in data['ids']: + wf_service.trg_validate(uid, 'account.invoice', id, 'invoice_cancel', cr) + return {} class wizard_invoice_cancel(wizard.interface): - states = { - 'init': { - 'actions': [_invoice_cancel], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_invoice_cancel], + 'result': {'type':'state', 'state':'end'} + } + } wizard_invoice_cancel('account.invoice.state.cancel') diff --git a/addons/account/wizard/wizard_journal.py b/addons/account/wizard/wizard_journal.py index 5f7693c134a..61554405d88 100644 --- a/addons/account/wizard/wizard_journal.py +++ b/addons/account/wizard/wizard_journal.py @@ -34,65 +34,65 @@ from tools.translate import _ _journal_form = '''<?xml version="1.0"?> <form string="Standard entries"> - <field name="journal_id"/> - <newline/> - <field name="period_id"/> + <field name="journal_id"/> + <newline/> + <field name="period_id"/> </form>''' def _period_get(self, cr, uid, datas, ctx={}): - try: - pool = pooler.get_pool(cr.dbname) - ids = pool.get('account.period').find(cr, uid, context=ctx) - return {'period_id': ids[0]} - except: - return {} + try: + pool = pooler.get_pool(cr.dbname) + ids = pool.get('account.period').find(cr, uid, context=ctx) + return {'period_id': ids[0]} + except: + return {} _journal_fields = { - 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, - 'period_id': { - 'string':'Period', - 'type':'many2one', - 'relation':'account.period', - 'required':True, - } + 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, + 'period_id': { + 'string':'Period', + 'type':'many2one', + 'relation':'account.period', + 'required':True, + } } def _action_open_window(self, cr, uid, data, context): - form = data['form'] - cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('account.move.line', 'form')) - view_res = cr.fetchone() - jp = pooler.get_pool(cr.dbname).get('account.journal.period') - ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])]) - if not len(ids): - name = pooler.get_pool(cr.dbname).get('account.journal').read(cr, uid, [form['journal_id']])[0]['name'] - state = pooler.get_pool(cr.dbname).get('account.period').read(cr, uid, [form['period_id']])[0]['state'] - if state == 'done': - raise wizard.except_wizard(_('UserError'), _('This period is already closed !')) - jp.create(cr, uid, {'name':name, 'period_id': form['period_id'], 'journal_id':form['journal_id']}) - ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])]) - jp = jp.browse(cr, uid, ids, context=context)[0] - name = (jp.journal_id.code or '') + ':' + (jp.period_id.code or '') - return { - 'domain': "[('journal_id','=',%d), ('period_id','=',%d)]" % (form['journal_id'],form['period_id']), - 'name': name, - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move.line', - 'view_id': view_res, - 'context': "{'journal_id':%d, 'period_id':%d}" % (form['journal_id'],form['period_id']), - 'type': 'ir.actions.act_window' - } + form = data['form'] + cr.execute('select id,name from ir_ui_view where model=%s and type=%s', ('account.move.line', 'form')) + view_res = cr.fetchone() + jp = pooler.get_pool(cr.dbname).get('account.journal.period') + ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])]) + if not len(ids): + name = pooler.get_pool(cr.dbname).get('account.journal').read(cr, uid, [form['journal_id']])[0]['name'] + state = pooler.get_pool(cr.dbname).get('account.period').read(cr, uid, [form['period_id']])[0]['state'] + if state == 'done': + raise wizard.except_wizard(_('UserError'), _('This period is already closed !')) + jp.create(cr, uid, {'name':name, 'period_id': form['period_id'], 'journal_id':form['journal_id']}) + ids = jp.search(cr, uid, [('journal_id','=',form['journal_id']), ('period_id','=',form['period_id'])]) + jp = jp.browse(cr, uid, ids, context=context)[0] + name = (jp.journal_id.code or '') + ':' + (jp.period_id.code or '') + return { + 'domain': "[('journal_id','=',%d), ('period_id','=',%d)]" % (form['journal_id'],form['period_id']), + 'name': name, + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.move.line', + 'view_id': view_res, + 'context': "{'journal_id':%d, 'period_id':%d}" % (form['journal_id'],form['period_id']), + 'type': 'ir.actions.act_window' + } class wiz_journal(wizard.interface): - states = { - 'init': { - 'actions': [_period_get], - 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open Journal')]} - }, - 'open': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_period_get], + 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open Journal')]} + }, + 'open': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} + } + } wiz_journal('account.move.journal') diff --git a/addons/account/wizard/wizard_journal_select.py b/addons/account/wizard/wizard_journal_select.py index e35c40628a3..20a8ad1cbb7 100644 --- a/addons/account/wizard/wizard_journal_select.py +++ b/addons/account/wizard/wizard_journal_select.py @@ -31,26 +31,26 @@ import wizard import pooler def _action_open_window(self, cr, uid, data, context): - mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') - act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') + mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') + act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') - result = mod_obj._get_id(cr, uid, 'account', 'action_move_line_select') - id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] - result = act_obj.read(cr, uid, [id])[0] + result = mod_obj._get_id(cr, uid, 'account', 'action_move_line_select') + id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] + result = act_obj.read(cr, uid, [id])[0] - cr.execute('select journal_id,period_id from account_journal_period where id=%d', (data['id'],)) - journal_id,period_id = cr.fetchone() + cr.execute('select journal_id,period_id from account_journal_period where id=%d', (data['id'],)) + journal_id,period_id = cr.fetchone() - result['domain'] = str([('journal_id', '=', journal_id), ('period_id', '=', period_id)]) - result['context'] = str({'journal_id': journal_id, 'period_id': period_id}) - return result + result['domain'] = str([('journal_id', '=', journal_id), ('period_id', '=', period_id)]) + result['context'] = str({'journal_id': journal_id, 'period_id': period_id}) + return result class wiz_journal(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} + } + } wiz_journal('account.move.journal.select') diff --git a/addons/account/wizard/wizard_move_line_select.py b/addons/account/wizard/wizard_move_line_select.py index 6642901fbf8..cc1763c9e9b 100644 --- a/addons/account/wizard/wizard_move_line_select.py +++ b/addons/account/wizard/wizard_move_line_select.py @@ -31,35 +31,35 @@ import wizard import pooler class wizard_move_line_select(wizard.interface): - def _open_window(self, cr, uid, data, context): - mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') - act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + def _open_window(self, cr, uid, data, context): + mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') + act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - if not context.get('fiscalyear', False): - fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draft')]) - else: - fiscalyear_ids = [context['fiscalyear']] + if not context.get('fiscalyear', False): + fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draft')]) + else: + fiscalyear_ids = [context['fiscalyear']] - fiscalyears = fiscalyear_obj.browse(cr, uid, fiscalyear_ids) - period_ids = [] - for fiscalyear in fiscalyears: - for period in fiscalyear.period_ids: - period_ids.append(period.id) - domain = str(('period_id', 'in', period_ids)) + fiscalyears = fiscalyear_obj.browse(cr, uid, fiscalyear_ids) + period_ids = [] + for fiscalyear in fiscalyears: + for period in fiscalyear.period_ids: + period_ids.append(period.id) + domain = str(('period_id', 'in', period_ids)) - result = mod_obj._get_id(cr, uid, 'account', 'action_move_line_tree1') - id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] - result = act_obj.read(cr, uid, [id])[0] - result['context'] = str({'fiscalyear': context.get('fiscalyear', False)}) - result['domain']=result['domain'][0:-1]+','+domain+result['domain'][-1] - return result + result = mod_obj._get_id(cr, uid, 'account', 'action_move_line_tree1') + id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] + result = act_obj.read(cr, uid, [id])[0] + result['context'] = str({'fiscalyear': context.get('fiscalyear', False)}) + result['domain']=result['domain'][0:-1]+','+domain+result['domain'][-1] + return result - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': _open_window, 'state': 'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': _open_window, 'state': 'end'} + } + } wizard_move_line_select('account.move.line.select') diff --git a/addons/account/wizard/wizard_open_closed_fiscalyear.py b/addons/account/wizard/wizard_open_closed_fiscalyear.py index 0e2549e3e93..c758498bf76 100644 --- a/addons/account/wizard/wizard_open_closed_fiscalyear.py +++ b/addons/account/wizard/wizard_open_closed_fiscalyear.py @@ -33,53 +33,53 @@ from tools.translate import _ form = """<?xml version="1.0"?> <form string="Choose Fiscal Year"> - <field name="fyear_id" domain="[('state','=','done')]"/> + <field name="fyear_id" domain="[('state','=','done')]"/> </form> """ fields = { - 'fyear_id': {'string': 'Fiscal Year to Open', 'type': 'many2one', 'relation': 'account.fiscalyear', 'required': True}, + 'fyear_id': {'string': 'Fiscal Year to Open', 'type': 'many2one', 'relation': 'account.fiscalyear', 'required': True}, } def _remove_entries(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - data_fyear = pool.get('account.fiscalyear').browse(cr,uid,data['form']['fyear_id']) - if not data_fyear.end_journal_period_id: - raise wizard.except_wizard(_('Error'), _('No journal for ending writings have been defined for the fiscal year')) - period_journal = data_fyear.end_journal_period_id - if not period_journal.journal_id.centralisation: - raise wizard.except_wizard(_('UserError'), _('The journal must have centralised counterpart')) - ids_move = pool.get('account.move').search(cr,uid,[('journal_id','=',period_journal.journal_id.id),('period_id','=',period_journal.period_id.id)]) - pool.get('account.move').unlink(cr,uid,ids_move) - cr.execute('UPDATE account_journal_period ' \ - 'SET state = %s ' \ - 'WHERE period_id IN (SELECT id FROM account_period WHERE fiscalyear_id = %d)', - ('draft',data_fyear)) - cr.execute('UPDATE account_period SET state = %s ' \ - 'WHERE fiscalyear_id = %d', ('draft',data_fyear)) - cr.execute('UPDATE account_fiscalyear ' \ - 'SET state = %s, end_journal_period_id = null '\ - 'WHERE id = %d', ('draft',data_fyear)) - return {} + pool = pooler.get_pool(cr.dbname) + data_fyear = pool.get('account.fiscalyear').browse(cr,uid,data['form']['fyear_id']) + if not data_fyear.end_journal_period_id: + raise wizard.except_wizard(_('Error'), _('No journal for ending writings have been defined for the fiscal year')) + period_journal = data_fyear.end_journal_period_id + if not period_journal.journal_id.centralisation: + raise wizard.except_wizard(_('UserError'), _('The journal must have centralised counterpart')) + ids_move = pool.get('account.move').search(cr,uid,[('journal_id','=',period_journal.journal_id.id),('period_id','=',period_journal.period_id.id)]) + pool.get('account.move').unlink(cr,uid,ids_move) + cr.execute('UPDATE account_journal_period ' \ + 'SET state = %s ' \ + 'WHERE period_id IN (SELECT id FROM account_period WHERE fiscalyear_id = %d)', + ('draft',data_fyear)) + cr.execute('UPDATE account_period SET state = %s ' \ + 'WHERE fiscalyear_id = %d', ('draft',data_fyear)) + cr.execute('UPDATE account_fiscalyear ' \ + 'SET state = %s, end_journal_period_id = null '\ + 'WHERE id = %d', ('draft',data_fyear)) + return {} class open_closed_fiscal(wizard.interface): - states = { - 'init' : { - 'actions' : [], - 'result': { - 'type': 'form', - 'arch': form, - 'fields': fields, - 'state':[('end','Cancel'),('open','Open')] - } - }, - 'open': { - 'actions': [], - 'result': { - 'type':'action', - 'action':_remove_entries, - 'state':'end' - }, - }, - } + states = { + 'init' : { + 'actions' : [], + 'result': { + 'type': 'form', + 'arch': form, + 'fields': fields, + 'state':[('end','Cancel'),('open','Open')] + } + }, + 'open': { + 'actions': [], + 'result': { + 'type':'action', + 'action':_remove_entries, + 'state':'end' + }, + }, + } open_closed_fiscal("account.open_closed_fiscalyear") diff --git a/addons/account/wizard/wizard_partner_balance_report.py b/addons/account/wizard/wizard_partner_balance_report.py index 689f6cbc39a..49979b1098b 100644 --- a/addons/account/wizard/wizard_partner_balance_report.py +++ b/addons/account/wizard/wizard_partner_balance_report.py @@ -33,45 +33,45 @@ import pooler dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="company_id" colspan="4"/> - <newline/> - <field name="fiscalyear" colspan="4"/> - <newline/> - <field name="date1"/> - <field name="date2"/> + <field name="company_id" colspan="4"/> + <newline/> + <field name="fiscalyear" colspan="4"/> + <newline/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, - 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year'}, - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, + 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year'}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - company_id = user.company_id.id - else: - company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - data['form']['company_id'] = company_id + user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + company_id = user.company_id.id + else: + company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + data['form']['company_id'] = company_id - return data['form'] + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print') ]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.partner.balance', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print') ]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.partner.balance', 'state':'end'} + } + } wizard_report('account.partner.balance.report') diff --git a/addons/account/wizard/wizard_pay_invoice.py b/addons/account/wizard/wizard_pay_invoice.py index adfc7211b0a..1971a98bffe 100644 --- a/addons/account/wizard/wizard_pay_invoice.py +++ b/addons/account/wizard/wizard_pay_invoice.py @@ -35,105 +35,105 @@ from tools.translate import _ pay_form = '''<?xml version="1.0"?> <form string="Pay invoice"> - <field name="amount"/> - <newline/> - <field name="name"/> - <field name="date"/> - <field name="journal_id"/> - <field name="period_id"/> + <field name="amount"/> + <newline/> + <field name="name"/> + <field name="date"/> + <field name="journal_id"/> + <field name="period_id"/> </form>''' pay_fields = { - 'amount': {'string': 'Amount paid', 'type':'float', 'required':True}, - 'name': {'string': 'Entry Name', 'type':'char', 'size': 64, 'required':True}, - 'date': {'string': 'Payment date', 'type':'date', 'required':True, 'default':lambda *args: time.strftime('%Y-%m-%d')}, - 'journal_id': {'string': 'Journal', 'type': 'many2one', 'relation':'account.journal', 'required':True, 'domain':[('type','=','cash')]}, - 'period_id': {'string': 'Period', 'type': 'many2one', 'relation':'account.period', 'required':True}, + 'amount': {'string': 'Amount paid', 'type':'float', 'required':True}, + 'name': {'string': 'Entry Name', 'type':'char', 'size': 64, 'required':True}, + 'date': {'string': 'Payment date', 'type':'date', 'required':True, 'default':lambda *args: time.strftime('%Y-%m-%d')}, + 'journal_id': {'string': 'Journal', 'type': 'many2one', 'relation':'account.journal', 'required':True, 'domain':[('type','=','cash')]}, + 'period_id': {'string': 'Period', 'type': 'many2one', 'relation':'account.period', 'required':True}, } def _pay_and_reconcile(self, cr, uid, data, context): - form = data['form'] - period_id = form.get('period_id', False) - journal_id = form.get('journal_id', False) - writeoff_account_id = form.get('writeoff_acc_id', False) - writeoff_journal_id = form.get('writeoff_journal_id', False) - pool = pooler.get_pool(cr.dbname) - cur_obj = pool.get('res.currency') - amount = form['amount'] + form = data['form'] + period_id = form.get('period_id', False) + journal_id = form.get('journal_id', False) + writeoff_account_id = form.get('writeoff_acc_id', False) + writeoff_journal_id = form.get('writeoff_journal_id', False) + pool = pooler.get_pool(cr.dbname) + cur_obj = pool.get('res.currency') + amount = form['amount'] - invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) - journal = pool.get('account.journal').browse(cr, uid, data['form']['journal_id'], context) - if journal.currency and invoice.company_id.currency_id.id<>journal.currency.id: - ctx = {'date':data['form']['date']} - amount = cur_obj.compute(cr, uid, journal.currency.id, invoice.company_id.currency_id.id, amount, context=ctx) + invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) + journal = pool.get('account.journal').browse(cr, uid, data['form']['journal_id'], context) + if journal.currency and invoice.company_id.currency_id.id<>journal.currency.id: + ctx = {'date':data['form']['date']} + amount = cur_obj.compute(cr, uid, journal.currency.id, invoice.company_id.currency_id.id, amount, context=ctx) - acc_id = journal.default_credit_account_id and journal.default_credit_account_id.id - if not acc_id: - raise wizard.except_wizard(_('Error !'), _('Your journal must have a default credit and debit account.')) - pool.get('account.invoice').pay_and_reconcile(cr, uid, [data['id']], - amount, acc_id, period_id, journal_id, writeoff_account_id, - period_id, writeoff_journal_id, context, data['form']['name']) - return {} + acc_id = journal.default_credit_account_id and journal.default_credit_account_id.id + if not acc_id: + raise wizard.except_wizard(_('Error !'), _('Your journal must have a default credit and debit account.')) + pool.get('account.invoice').pay_and_reconcile(cr, uid, [data['id']], + amount, acc_id, period_id, journal_id, writeoff_account_id, + period_id, writeoff_journal_id, context, data['form']['name']) + return {} def _wo_check(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) - journal = pool.get('account.journal').browse(cr, uid, data['form']['journal_id'], context) - if invoice.company_id.currency_id.id<>journal.currency.id: - return 'addendum' - if pool.get('res.currency').is_zero(cr, uid, invoice.currency_id, - (data['form']['amount'] - invoice.amount_total)): - return 'reconcile' - return 'addendum' + pool = pooler.get_pool(cr.dbname) + invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) + journal = pool.get('account.journal').browse(cr, uid, data['form']['journal_id'], context) + if invoice.company_id.currency_id.id<>journal.currency.id: + return 'addendum' + if pool.get('res.currency').is_zero(cr, uid, invoice.currency_id, + (data['form']['amount'] - invoice.amount_total)): + return 'reconcile' + return 'addendum' _transaction_add_form = '''<?xml version="1.0"?> <form string="Information addendum"> - <separator string="Write-Off Move" colspan="4"/> - <field name="writeoff_acc_id"/> - <field name="writeoff_journal_id"/> + <separator string="Write-Off Move" colspan="4"/> + <field name="writeoff_acc_id"/> + <field name="writeoff_journal_id"/> </form>''' _transaction_add_fields = { - 'writeoff_acc_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account', 'required':True}, - 'writeoff_journal_id': {'string': 'Write-Off journal', 'type': 'many2one', 'relation':'account.journal', 'required':True}, + 'writeoff_acc_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account', 'required':True}, + 'writeoff_journal_id': {'string': 'Write-Off journal', 'type': 'many2one', 'relation':'account.journal', 'required':True}, } def _get_value_addendum(self, cr, uid, data, context={}): - return {} + return {} def _get_period(self, cr, uid, data, context={}): - pool = pooler.get_pool(cr.dbname) - ids = pool.get('account.period').find(cr, uid, context=context) - period_id = False - if len(ids): - period_id = ids[0] - invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) - if invoice.state == 'draft': - raise wizard.except_wizard(_('Error !'), _('Can not pay draft invoice.')) - return { - 'period_id': period_id, - 'amount': invoice.amount_total, - 'date': time.strftime('%Y-%m-%d') - } + pool = pooler.get_pool(cr.dbname) + ids = pool.get('account.period').find(cr, uid, context=context) + period_id = False + if len(ids): + period_id = ids[0] + invoice = pool.get('account.invoice').browse(cr, uid, data['id'], context) + if invoice.state == 'draft': + raise wizard.except_wizard(_('Error !'), _('Can not pay draft invoice.')) + return { + 'period_id': period_id, + 'amount': invoice.amount_total, + 'date': time.strftime('%Y-%m-%d') + } class wizard_pay_invoice(wizard.interface): - states = { - 'init': { - 'actions': [_get_period], - 'result': {'type':'form', 'arch':pay_form, 'fields':pay_fields, 'state':[('end','Cancel'),('reconcile','Partial Payment'),('writeoff_check','Full Payment')]} - }, - 'writeoff_check': { - 'actions': [], - 'result' : {'type': 'choice', 'next_state': _wo_check } - }, - 'addendum': { - 'actions': [_get_value_addendum], - 'result': {'type': 'form', 'arch':_transaction_add_form, 'fields':_transaction_add_fields, 'state':[('end','Cancel'),('reconcile','Pay and reconcile')]} - }, - 'reconcile': { - 'actions': [_pay_and_reconcile], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_period], + 'result': {'type':'form', 'arch':pay_form, 'fields':pay_fields, 'state':[('end','Cancel'),('reconcile','Partial Payment'),('writeoff_check','Full Payment')]} + }, + 'writeoff_check': { + 'actions': [], + 'result' : {'type': 'choice', 'next_state': _wo_check } + }, + 'addendum': { + 'actions': [_get_value_addendum], + 'result': {'type': 'form', 'arch':_transaction_add_form, 'fields':_transaction_add_fields, 'state':[('end','Cancel'),('reconcile','Pay and reconcile')]} + }, + 'reconcile': { + 'actions': [_pay_and_reconcile], + 'result': {'type':'state', 'state':'end'} + } + } wizard_pay_invoice('account.invoice.pay') diff --git a/addons/account/wizard/wizard_period_close.py b/addons/account/wizard/wizard_period_close.py index ff32fce3284..a7d15f154a0 100644 --- a/addons/account/wizard/wizard_period_close.py +++ b/addons/account/wizard/wizard_period_close.py @@ -31,32 +31,32 @@ import wizard _transaction_form = '''<?xml version="1.0"?> <form string="Close Period"> - <separator string="Are you sure ?" colspan="4"/> - <field name="sure"/> + <separator string="Are you sure ?" colspan="4"/> + <field name="sure"/> </form>''' _transaction_fields = { - 'sure': {'string':'Check this box', 'type':'boolean'}, + 'sure': {'string':'Check this box', 'type':'boolean'}, } def _data_save(self, cr, uid, data, context): - mode = 'done' - if data['form']['sure']: - for id in data['ids']: - cr.execute('update account_journal_period set state=%s where period_id=%d', (mode, id)) - cr.execute('update account_period set state=%s where id=%d', (mode, id)) - return {} + mode = 'done' + if data['form']['sure']: + for id in data['ids']: + cr.execute('update account_journal_period set state=%s where period_id=%d', (mode, id)) + cr.execute('update account_period set state=%s where id=%d', (mode, id)) + return {} class wiz_journal_close(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('close','Close Period')]} - }, - 'close': { - 'actions': [_data_save], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('close','Close Period')]} + }, + 'close': { + 'actions': [_data_save], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_journal_close('account.period.close') diff --git a/addons/account/wizard/wizard_reconcile.py b/addons/account/wizard/wizard_reconcile.py index b4fe58063d2..d67ddf168fe 100644 --- a/addons/account/wizard/wizard_reconcile.py +++ b/addons/account/wizard/wizard_reconcile.py @@ -35,108 +35,108 @@ import pooler _transaction_form = '''<?xml version="1.0"?> <form string="Reconciliation"> - <separator string="Reconciliation transactions" colspan="4"/> - <field name="trans_nbr"/> - <newline/> - <field name="credit"/> - <field name="debit"/> - <separator string="Write-Off" colspan="4"/> - <field name="writeoff"/> + <separator string="Reconciliation transactions" colspan="4"/> + <field name="trans_nbr"/> + <newline/> + <field name="credit"/> + <field name="debit"/> + <separator string="Write-Off" colspan="4"/> + <field name="writeoff"/> </form>''' _transaction_fields = { - 'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True}, - 'credit': {'string':'Credit amount', 'type':'float', 'readonly':True}, - 'debit': {'string':'Debit amount', 'type':'float', 'readonly':True}, - 'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True}, + 'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True}, + 'credit': {'string':'Credit amount', 'type':'float', 'readonly':True}, + 'debit': {'string':'Debit amount', 'type':'float', 'readonly':True}, + 'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True}, } def _trans_rec_get(self, cr, uid, data, context=None): - pool = pooler.get_pool(cr.dbname) - account_move_line_obj = pool.get('account.move.line') - credit = debit = 0 - account_id = False - count = 0 - for line in account_move_line_obj.browse(cr, uid, data['ids'], context=context): - if not line.reconcile_id and not line.reconcile_id.id: - count += 1 - credit += line.credit - debit += line.debit - account_id = line.account_id.id - return {'trans_nbr': count, 'account_id': account_id, 'credit': credit, 'debit': debit, 'writeoff': debit - credit} + pool = pooler.get_pool(cr.dbname) + account_move_line_obj = pool.get('account.move.line') + credit = debit = 0 + account_id = False + count = 0 + for line in account_move_line_obj.browse(cr, uid, data['ids'], context=context): + if not line.reconcile_id and not line.reconcile_id.id: + count += 1 + credit += line.credit + debit += line.debit + account_id = line.account_id.id + return {'trans_nbr': count, 'account_id': account_id, 'credit': credit, 'debit': debit, 'writeoff': debit - credit} def _trans_rec_reconcile_partial(self, cr, uid, data, context=None): - pool = pooler.get_pool(cr.dbname) - account_move_line_obj = pool.get('account.move.line') - account_move_line_obj.reconcile_partial(cr, uid, data['ids'], 'manual', context=context) - return {} + pool = pooler.get_pool(cr.dbname) + account_move_line_obj = pool.get('account.move.line') + account_move_line_obj.reconcile_partial(cr, uid, data['ids'], 'manual', context=context) + return {} def _trans_rec_reconcile(self, cr, uid, data, context=None): - pool = pooler.get_pool(cr.dbname) - account_move_line_obj = pool.get('account.move.line') + pool = pooler.get_pool(cr.dbname) + account_move_line_obj = pool.get('account.move.line') - form = data['form'] - account_id = form.get('writeoff_acc_id', False) - period_id = form.get('period_id', False) - journal_id = form.get('journal_id', False) - account_move_line_obj.reconcile(cr, uid, data['ids'], 'manual', account_id, - period_id, journal_id, context=context) - return {} + form = data['form'] + account_id = form.get('writeoff_acc_id', False) + period_id = form.get('period_id', False) + journal_id = form.get('journal_id', False) + account_move_line_obj.reconcile(cr, uid, data['ids'], 'manual', account_id, + period_id, journal_id, context=context) + return {} def _partial_check(self, cr, uid, data, context): - if _trans_rec_get(self,cr,uid, data, context)['writeoff'] == 0: - return 'init_full' - return 'init_partial' + if _trans_rec_get(self,cr,uid, data, context)['writeoff'] == 0: + return 'init_full' + return 'init_partial' _transaction_add_form = '''<?xml version="1.0"?> <form string="Information addendum"> - <separator string="Write-Off Move" colspan="4"/> - <field name="journal_id"/> - <field name="period_id"/> - <field name="writeoff_acc_id" domain="[('type', '<>', 'view')]"/> + <separator string="Write-Off Move" colspan="4"/> + <field name="journal_id"/> + <field name="period_id"/> + <field name="writeoff_acc_id" domain="[('type', '<>', 'view')]"/> </form>''' _transaction_add_fields = { - 'journal_id': {'string': 'Write-Off Journal', 'type': 'many2one', 'relation':'account.journal', 'required':True}, - 'period_id': {'string': 'Write-Off Period', 'type': 'many2one', 'relation':'account.period', 'required':True}, - 'writeoff_acc_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account', 'required':True}, + 'journal_id': {'string': 'Write-Off Journal', 'type': 'many2one', 'relation':'account.journal', 'required':True}, + 'period_id': {'string': 'Write-Off Period', 'type': 'many2one', 'relation':'account.period', 'required':True}, + 'writeoff_acc_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account', 'required':True}, } def _trans_rec_addendum(self, cr, uid, data, context={}): - pool = pooler.get_pool(cr.dbname) - ids = pool.get('account.period').find(cr, uid, context=context) - period_id = False - if len(ids): - period_id = ids[0] - return {'period_id':period_id} + pool = pooler.get_pool(cr.dbname) + ids = pool.get('account.period').find(cr, uid, context=context) + period_id = False + if len(ids): + period_id = ids[0] + return {'period_id':period_id} class wiz_reconcile(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'choice', 'next_state': _partial_check} - }, - 'init_full': { - 'actions': [_trans_rec_get], - 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} - }, - 'init_partial': { - 'actions': [_trans_rec_get], - 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('addendum','Reconcile With Write-Off'),('partial','Partial Reconcile')]} - }, - 'addendum': { - 'actions': [_trans_rec_addendum], - 'result': {'type': 'form', 'arch':_transaction_add_form, 'fields':_transaction_add_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} - }, - 'reconcile': { - 'actions': [_trans_rec_reconcile], - 'result': {'type': 'state', 'state':'end'} - }, - 'partial': { - 'actions': [_trans_rec_reconcile_partial], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'choice', 'next_state': _partial_check} + }, + 'init_full': { + 'actions': [_trans_rec_get], + 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} + }, + 'init_partial': { + 'actions': [_trans_rec_get], + 'result': {'type': 'form', 'arch':_transaction_form, 'fields':_transaction_fields, 'state':[('end','Cancel'),('addendum','Reconcile With Write-Off'),('partial','Partial Reconcile')]} + }, + 'addendum': { + 'actions': [_trans_rec_addendum], + 'result': {'type': 'form', 'arch':_transaction_add_form, 'fields':_transaction_add_fields, 'state':[('end','Cancel'),('reconcile','Reconcile')]} + }, + 'reconcile': { + 'actions': [_trans_rec_reconcile], + 'result': {'type': 'state', 'state':'end'} + }, + 'partial': { + 'actions': [_trans_rec_reconcile_partial], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_reconcile('account.move.line.reconcile') diff --git a/addons/account/wizard/wizard_reconcile_select.py b/addons/account/wizard/wizard_reconcile_select.py index be3849666e3..1ec1a675226 100644 --- a/addons/account/wizard/wizard_reconcile_select.py +++ b/addons/account/wizard/wizard_reconcile_select.py @@ -31,34 +31,34 @@ import wizard _journal_form = '''<?xml version="1.0"?> <form string="%s"> - <field name="account_id"/> + <field name="account_id"/> </form>''' % ('Reconciliation',) _journal_fields = { - 'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account','domain': [('reconcile','=',1)], 'required':True}, + 'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account','domain': [('reconcile','=',1)], 'required':True}, } def _action_open_window(self, cr, uid, data, context): - return { - 'domain': "[('account_id','=',%d),('reconcile_id','=',False),('state','<>','draft')]" % data['form']['account_id'], - 'name': 'Reconciliation', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'view_id': False, - 'res_model': 'account.move.line', - 'type': 'ir.actions.act_window' - } + return { + 'domain': "[('account_id','=',%d),('reconcile_id','=',False),('state','<>','draft')]" % data['form']['account_id'], + 'name': 'Reconciliation', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'view_id': False, + 'res_model': 'account.move.line', + 'type': 'ir.actions.act_window' + } class wiz_rec_select(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for reconciliation')]} - }, - 'open': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for reconciliation')]} + }, + 'open': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} + } + } wiz_rec_select('account.move.line.reconcile.select') diff --git a/addons/account/wizard/wizard_refund.py b/addons/account/wizard/wizard_refund.py index 81b653c9939..f65d82d4402 100644 --- a/addons/account/wizard/wizard_refund.py +++ b/addons/account/wizard/wizard_refund.py @@ -32,35 +32,35 @@ import pooler sur_form = '''<?xml version="1.0"?> <form string="Refund Invoice"> - <label string="Are you sure you want to refund this invoice ?"/> + <label string="Are you sure you want to refund this invoice ?"/> </form>''' sur_fields = { } class wiz_refund(wizard.interface): - def _invoice_refund(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - ids = pool.get('account.invoice').refund(cr, uid, data['ids']) - return { - 'domain': "[('id','in', ["+','.join(map(str,ids))+"])]", - 'name': 'Invoices', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.invoice', - 'view_id': False, - 'context': "{'type':'out_refund'}", - 'type': 'ir.actions.act_window' - } - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':sur_form, 'fields':sur_fields, 'state':[('end','Cancel'),('refund','Refund Invoice')]} - }, - 'refund': { - 'actions': [], - 'result': {'type':'action', 'action':_invoice_refund, 'state':'end'} - } - } + def _invoice_refund(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + ids = pool.get('account.invoice').refund(cr, uid, data['ids']) + return { + 'domain': "[('id','in', ["+','.join(map(str,ids))+"])]", + 'name': 'Invoices', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.invoice', + 'view_id': False, + 'context': "{'type':'out_refund'}", + 'type': 'ir.actions.act_window' + } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':sur_form, 'fields':sur_fields, 'state':[('end','Cancel'),('refund','Refund Invoice')]} + }, + 'refund': { + 'actions': [], + 'result': {'type':'action', 'action':_invoice_refund, 'state':'end'} + } + } wiz_refund('account.invoice.refund') diff --git a/addons/account/wizard/wizard_subscription_generate.py b/addons/account/wizard/wizard_subscription_generate.py index c6e6c02dddd..d680c37b0f6 100644 --- a/addons/account/wizard/wizard_subscription_generate.py +++ b/addons/account/wizard/wizard_subscription_generate.py @@ -37,30 +37,30 @@ import pooler _subscription_form = '''<?xml version="1.0"?> <form string="%s"> - <seperator string="Generate entries before:" colspan="4"/> - <field name="date"/> + <seperator string="Generate entries before:" colspan="4"/> + <field name="date"/> </form>''' % ('Subscription Compute',) _subscription_fields = { - 'date': {'string':'Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, + 'date': {'string':'Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, } class wiz_subscription(wizard.interface): - def _action_generate(self, cr, uid, data, context): - cr.execute('select id from account_subscription_line where date<%s and move_id is null', (data['form']['date'],)) - ids = map(lambda x: x[0], cr.fetchall()) - pooler.get_pool(cr.dbname).get('account.subscription.line').move_create(cr, uid, ids) - return {} + def _action_generate(self, cr, uid, data, context): + cr.execute('select id from account_subscription_line where date<%s and move_id is null', (data['form']['date'],)) + ids = map(lambda x: x[0], cr.fetchall()) + pooler.get_pool(cr.dbname).get('account.subscription.line').move_create(cr, uid, ids) + return {} - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_subscription_form, 'fields':_subscription_fields, 'state':[('end','Cancel'),('generate','Compute Entry Dates')]} - }, - 'generate': { - 'actions': [_action_generate], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_subscription_form, 'fields':_subscription_fields, 'state':[('end','Cancel'),('generate','Compute Entry Dates')]} + }, + 'generate': { + 'actions': [_action_generate], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_subscription('account.subscription.generate') diff --git a/addons/account/wizard/wizard_third_party_ledger.py b/addons/account/wizard/wizard_third_party_ledger.py index d09efe9b3bb..cfd087d8230 100644 --- a/addons/account/wizard/wizard_third_party_ledger.py +++ b/addons/account/wizard/wizard_third_party_ledger.py @@ -33,45 +33,45 @@ import pooler dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="company_id" colspan="4"/> - <field name="fiscalyear" colspan="4"/> - <newline/> - <field name="date1"/> - <field name="date2"/> + <field name="company_id" colspan="4"/> + <field name="fiscalyear" colspan="4"/> + <newline/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, - 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', - 'help': 'Keep empty for all open fiscal year'}, - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'company_id': {'string': 'Company', 'type': 'many2one', 'relation': 'res.company', 'required': True}, + 'fiscalyear': {'string': 'Fiscal year', 'type': 'many2one', 'relation': 'account.fiscalyear', + 'help': 'Keep empty for all open fiscal year'}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') - data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) + def _get_defaults(self, cr, uid, data, context): + fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') + data['form']['fiscalyear'] = fiscalyear_obj.find(cr, uid) - user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - company_id = user.company_id.id - else: - company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - data['form']['company_id'] = company_id + user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + company_id = user.company_id.id + else: + company_id = pooler.get_pool(cr.dbname).get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + data['form']['company_id'] = company_id - return data['form'] + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print') ]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'account.third_party_ledger', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print') ]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'account.third_party_ledger', 'state':'end'} + } + } wizard_report('account.third_party_ledger.report') diff --git a/addons/account/wizard/wizard_unreconcile.py b/addons/account/wizard/wizard_unreconcile.py index 899d81ee29d..05245cad2f5 100644 --- a/addons/account/wizard/wizard_unreconcile.py +++ b/addons/account/wizard/wizard_unreconcile.py @@ -32,49 +32,49 @@ import pooler _info_form = '''<?xml version="1.0"?> <form string="Unreconciliation"> - <separator string="Unreconciliation transactions" colspan="4"/> - <image name="gtk-dialog-info" colspan="2"/> - <label string="If you unreconciliate transactions, you must also verify all the actions that are linked to those transactions because they will not be disable" colspan="2"/> + <separator string="Unreconciliation transactions" colspan="4"/> + <image name="gtk-dialog-info" colspan="2"/> + <label string="If you unreconciliate transactions, you must also verify all the actions that are linked to those transactions because they will not be disable" colspan="2"/> </form>''' def _trans_unrec(self, cr, uid, data, context): - recs = pooler.get_pool(cr.dbname).get('account.move.line').read(cr, uid, data['ids'], ['reconcile_id',]) - recs = filter(lambda x: x['reconcile_id'], recs) - rec_ids = [rec['reconcile_id'][0] for rec in recs] - if len(rec_ids): - pooler.get_pool(cr.dbname).get('account.move.reconcile').unlink(cr, uid, rec_ids) - return {} + recs = pooler.get_pool(cr.dbname).get('account.move.line').read(cr, uid, data['ids'], ['reconcile_id',]) + recs = filter(lambda x: x['reconcile_id'], recs) + rec_ids = [rec['reconcile_id'][0] for rec in recs] + if len(rec_ids): + pooler.get_pool(cr.dbname).get('account.move.reconcile').unlink(cr, uid, rec_ids) + return {} class wiz_unreconcile(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': _info_form, 'fields': {}, 'state':[('end', 'Cancel'), ('unrec', 'Unreconcile')]} - }, - 'unrec': { - 'actions': [_trans_unrec], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': _info_form, 'fields': {}, 'state':[('end', 'Cancel'), ('unrec', 'Unreconcile')]} + }, + 'unrec': { + 'actions': [_trans_unrec], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_unreconcile('account.move.line.unreconcile') def _trans_unrec_reconcile(self, cr, uid, data, context): - rec_ids = data['ids'] - if len(rec_ids): - pooler.get_pool(cr.dbname).get('account.move.reconcile').unlink(cr, uid, rec_ids) - return {} + rec_ids = data['ids'] + if len(rec_ids): + pooler.get_pool(cr.dbname).get('account.move.reconcile').unlink(cr, uid, rec_ids) + return {} class wiz_unreconcile_reconcile(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': _info_form, 'fields': {}, 'state':[('end', 'Cancel'), ('unrec', 'Unreconcile')]} - }, - 'unrec': { - 'actions': [_trans_unrec_reconcile], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': _info_form, 'fields': {}, 'state':[('end', 'Cancel'), ('unrec', 'Unreconcile')]} + }, + 'unrec': { + 'actions': [_trans_unrec_reconcile], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_unreconcile_reconcile('account.reconcile.unreconcile') diff --git a/addons/account/wizard/wizard_unreconcile_select.py b/addons/account/wizard/wizard_unreconcile_select.py index ec1e68358f9..104a3d04ce3 100644 --- a/addons/account/wizard/wizard_unreconcile_select.py +++ b/addons/account/wizard/wizard_unreconcile_select.py @@ -31,34 +31,34 @@ import wizard _journal_form = '''<?xml version="1.0"?> <form string="%s"> - <field name="account_id"/> + <field name="account_id"/> </form>''' % ('Unreconciliation',) _journal_fields = { - 'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account', 'required':True}, + 'account_id': {'string':'Account', 'type':'many2one', 'relation':'account.account', 'required':True}, } def _action_open_window(self, cr, uid, data, context): - return { - 'domain': "[('account_id','=',%d),('reconcile_id','<>',False),('state','<>','draft')]" % data['form']['account_id'], - 'name': 'Unreconciliation', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'view_id': False, - 'res_model': 'account.move.line', - 'type': 'ir.actions.act_window' - } + return { + 'domain': "[('account_id','=',%d),('reconcile_id','<>',False),('state','<>','draft')]" % data['form']['account_id'], + 'name': 'Unreconciliation', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'view_id': False, + 'res_model': 'account.move.line', + 'type': 'ir.actions.act_window' + } class wiz_unrec_select(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for unreconciliation')]} - }, - 'open': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('open','Open for unreconciliation')]} + }, + 'open': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'} + } + } wiz_unrec_select('account.move.line.unreconcile.select') diff --git a/addons/account/wizard/wizard_validate_account_move.py b/addons/account/wizard/wizard_validate_account_move.py index d5cec6a5ca9..68d39a3f571 100644 --- a/addons/account/wizard/wizard_validate_account_move.py +++ b/addons/account/wizard/wizard_validate_account_move.py @@ -34,58 +34,58 @@ from tools.translate import _ _journal_form = '''<?xml version="1.0"?> <form string="Standard entries"> - <field name="journal_id"/> - <newline/> - <field name="period_id"/> + <field name="journal_id"/> + <newline/> + <field name="period_id"/> </form>''' _journal_fields = { - 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, - 'period_id': {'string':'Period', 'type':'many2one', 'relation':'account.period', 'required':True, 'domain':"[('state','<>','done')]"}, + 'journal_id': {'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required':True}, + 'period_id': {'string':'Period', 'type':'many2one', 'relation':'account.period', 'required':True, 'domain':"[('state','<>','done')]"}, } def _validate_move(self, cr, uid, data, context={}): - pool = pooler.get_pool(cr.dbname) - move_obj = pool.get('account.move') - ids_move = move_obj.search(cr,uid,[('state','=','draft'),('journal_id','=',data['form']['journal_id']),('period_id','=',data['form']['period_id'])]) - if not ids_move: - raise wizard.except_wizard(_('Warning'), _('Specified Journal does not have any account move entries in draft state for this period')) - res = move_obj.button_validate(cr, uid, ids_move, context) - return {} + pool = pooler.get_pool(cr.dbname) + move_obj = pool.get('account.move') + ids_move = move_obj.search(cr,uid,[('state','=','draft'),('journal_id','=',data['form']['journal_id']),('period_id','=',data['form']['period_id'])]) + if not ids_move: + raise wizard.except_wizard(_('Warning'), _('Specified Journal does not have any account move entries in draft state for this period')) + res = move_obj.button_validate(cr, uid, ids_move, context) + return {} class validate_account_move(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('validate','Validate')]} - }, - 'validate': { - 'actions': [_validate_move], - 'result': {'type': 'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_journal_form, 'fields':_journal_fields, 'state':[('end','Cancel'),('validate','Validate')]} + }, + 'validate': { + 'actions': [_validate_move], + 'result': {'type': 'state', 'state':'end'} + }, + } validate_account_move('account.move.validate') def _validate_move_lines(self, cr, uid, data, context={}): - move_ids = [] - pool = pooler.get_pool(cr.dbname) - move_line_obj = pool.get('account.move.line') - move_obj = pool.get('account.move') - data_line = move_line_obj.browse(cr,uid,data['ids'],context) - for line in data_line: - if line.move_id.state=='draft': - move_ids.append(line.move_id.id) - move_ids = list(set(move_ids)) - if not move_ids: - raise wizard.except_wizard(_('Warning'), _('Selected Move lines does not have any account move enties in draft state')) - res = move_obj.button_validate(cr, uid, move_ids, context) - return {} + move_ids = [] + pool = pooler.get_pool(cr.dbname) + move_line_obj = pool.get('account.move.line') + move_obj = pool.get('account.move') + data_line = move_line_obj.browse(cr,uid,data['ids'],context) + for line in data_line: + if line.move_id.state=='draft': + move_ids.append(line.move_id.id) + move_ids = list(set(move_ids)) + if not move_ids: + raise wizard.except_wizard(_('Warning'), _('Selected Move lines does not have any account move enties in draft state')) + res = move_obj.button_validate(cr, uid, move_ids, context) + return {} class validate_account_move_lines(wizard.interface): - states = { - 'init': { - 'actions': [_validate_move_lines], - 'result': {'type': 'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [_validate_move_lines], + 'result': {'type': 'state', 'state':'end'} + }, + } validate_account_move_lines('account.move_line.validate') diff --git a/addons/account/wizard/wizard_vat.py b/addons/account/wizard/wizard_vat.py index de65abf7e2b..42baa751027 100644 --- a/addons/account/wizard/wizard_vat.py +++ b/addons/account/wizard/wizard_vat.py @@ -33,64 +33,64 @@ import pooler dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="company_id"/> - <newline/> - <field name="period_id"/> - <newline/> - <field name="based_on"/> + <field name="company_id"/> + <newline/> + <field name="period_id"/> + <newline/> + <field name="based_on"/> </form>''' dates_fields = { - 'company_id': {'string': 'Company', 'type': 'many2one', - 'relation': 'res.company', 'required': True}, - 'period_id': {'string':'Period', 'type':'many2one', - 'relation': 'account.period', 'required':True}, - 'based_on':{'string':'Base on', 'type':'selection', 'selection':[ - ('invoices','Invoices'), - ('payments','Payments'), - ], 'required':True, 'default': lambda *a: 'invoices'}, + 'company_id': {'string': 'Company', 'type': 'many2one', + 'relation': 'res.company', 'required': True}, + 'period_id': {'string':'Period', 'type':'many2one', + 'relation': 'account.period', 'required':True}, + 'based_on':{'string':'Base on', 'type':'selection', 'selection':[ + ('invoices','Invoices'), + ('payments','Payments'), + ], 'required':True, 'default': lambda *a: 'invoices'}, } class wizard_report(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - period_obj = pool.get('account.period') - data['form']['period_id'] = period_obj.find(cr, uid)[0] + def _get_defaults(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + period_obj = pool.get('account.period') + data['form']['period_id'] = period_obj.find(cr, uid)[0] - user = pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - company_id = user.company_id.id - else: - company_id = pool.get('res.company').search(cr, uid, - [('parent_id', '=', False)])[0] - data['form']['company_id'] = company_id + user = pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + company_id = user.company_id.id + else: + company_id = pool.get('res.company').search(cr, uid, + [('parent_id', '=', False)])[0] + data['form']['company_id'] = company_id - return data['form'] + return data['form'] - states = { - 'init': { - 'actions': [_get_defaults], - 'result': { - 'type': 'form', - 'arch': dates_form, - 'fields': dates_fields, - 'state': [ - ('end', 'Cancel'), - ('report', 'Print VAT Decl.') - ] - } - }, - 'report': { - 'actions': [], - 'result': { - 'type': 'print', - 'report': 'account.vat.declaration', - 'state':'end' - } - } - } + states = { + 'init': { + 'actions': [_get_defaults], + 'result': { + 'type': 'form', + 'arch': dates_form, + 'fields': dates_fields, + 'state': [ + ('end', 'Cancel'), + ('report', 'Print VAT Decl.') + ] + } + }, + 'report': { + 'actions': [], + 'result': { + 'type': 'print', + 'report': 'account.vat.declaration', + 'state':'end' + } + } + } wizard_report('account.vat.declaration') diff --git a/addons/account_analytic_analysis/__terp__.py b/addons/account_analytic_analysis/__terp__.py index 532ac1ee758..51f8a4b2b3d 100644 --- a/addons/account_analytic_analysis/__terp__.py +++ b/addons/account_analytic_analysis/__terp__.py @@ -26,22 +26,22 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "report_account_analytic", - "description": """Modifiy the account analytic view to show + "name" : "report_account_analytic", + "description": """Modifiy the account analytic view to show important data for project manager of services companies. Add menu to show relevant information for each manager.""", - "version" : "1.0", - "author" : "Camptocamp", - "category" : "Generic Modules/Accounting", - "module": "", - "website": "http://www.camptocamp.com/", - "depends" : ["account","hr_timesheet","hr_timesheet_invoice"], - "init_xml" : [], - "update_xml" : [ - "account_analytic_analysis_view.xml", - "account_analytic_analysis_menu.xml", - ], - "demo_xml" : [], - "active": False, - "installable": True + "version" : "1.0", + "author" : "Camptocamp", + "category" : "Generic Modules/Accounting", + "module": "", + "website": "http://www.camptocamp.com/", + "depends" : ["account","hr_timesheet","hr_timesheet_invoice"], + "init_xml" : [], + "update_xml" : [ + "account_analytic_analysis_view.xml", + "account_analytic_analysis_menu.xml", + ], + "demo_xml" : [], + "active": False, + "installable": True } diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index f204ea3ceaf..708fe1700b6 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -34,743 +34,743 @@ from tools.translate import _ class account_analytic_account(osv.osv): - _name = "account.analytic.account" - _inherit = "account.analytic.account" - - def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id, sum(amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_journal.type = 'sale' \ - group by account_analytic_line.account_id" % acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - res2 = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - # Amount uninvoiced hours to invoice at sale price - acc_set = ",".join(map(str, ids2)) - cr.execute("""SELECT account_analytic_account.id, \ - sum (product_template.list_price * \ - account_analytic_line.unit_amount * \ - ((100-hr_timesheet_invoice_factor.factor)/100)) \ - AS ca_to_invoice \ - FROM product_template \ - join product_product \ - on product_template.id = product_product.product_tmpl_id \ - JOIN account_analytic_line \ - on account_analytic_line.product_id = product_product.id \ - JOIN account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - JOIN account_analytic_account \ - on account_analytic_account.id = account_analytic_line.account_id \ - JOIN hr_timesheet_invoice_factor \ - on hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \ - WHERE account_analytic_account.id IN (%s) \ - AND account_analytic_line.invoice_id is null \ - AND account_analytic_line.to_invoice IS NOT NULL \ - GROUP BY account_analytic_account.id;"""%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - - # Expense amount and purchase invoice - acc_set = ",".join(map(str, ids2)) - cr.execute ("select account_analytic_line.account_id, sum(amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_journal.type = 'purchase' \ - GROUP BY account_analytic_line.account_id;"%acc_set) - for account_id, sum in cr.fetchall(): - res2[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - res2.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - res2[obj_id] += res2.get(child_id, 0.0) - # sum both result on account_id - for id in ids: - res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) - return res - - def _hours_qtt_non_invoiced_calc (self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id, sum(unit_amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_journal.type='general' \ - and invoice_id is null \ - AND to_invoice IS NOT NULL \ - GROUP BY account_analytic_line.account_id;"%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _hours_quantity_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id,sum(unit_amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_journal.type='general' \ - GROUP BY account_analytic_line.account_id"%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _total_cost_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("""select account_analytic_line.account_id,sum(amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and amount<0 \ - GROUP BY account_analytic_line.account_id"""%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - res2 = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - # First part with expense and purchase - cr.execute("""select account_analytic_line.account_id,sum(amount) \ - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_journal.type = 'purchase' \ - GROUP BY account_analytic_line.account_id"""%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = round(sum,2) - - # Second part with timesheet (with invoice factor) - acc_set = ",".join(map(str, ids2)) - cr.execute("""select account_analytic_line.account_id as account_id, \ - sum((account_analytic_line.unit_amount * pt.list_price) \ - - (account_analytic_line.unit_amount * pt.list_price \ - * hr.factor)) as somme - from account_analytic_line \ - join account_analytic_journal \ - on account_analytic_line.journal_id = account_analytic_journal.id \ - join product_product pp \ - on (account_analytic_line.product_id = pp.id) \ - join product_template pt \ - on (pp.product_tmpl_id = pt.id) \ - join account_analytic_account a \ - on (a.id=account_analytic_line.account_id) \ - join hr_timesheet_invoice_factor hr \ - on (hr.id=a.to_invoice) \ - where account_analytic_line.account_id IN (%s) \ - and a.to_invoice IS NOT NULL \ - GROUP BY account_analytic_line.account_id"""%acc_set) - for account_id, sum in cr.fetchall(): - res2[account_id] = round(sum,2) - - for obj_id in ids: - res.setdefault(obj_id, 0.0) - res2.setdefault(obj_id, 0.0) - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if child_id != obj_id: - res[obj_id] += res.get(child_id, 0.0) - res[obj_id] += res2.get(child_id, 0.0) - - # sum both result on account_id - for id in ids: - res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) - return res - - def _last_worked_date_calc (self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id, max(date) \ - from account_analytic_line \ - where account_id IN (%s) \ - and invoice_id is null \ - GROUP BY account_analytic_line.account_id" % acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = sum - for obj_id in ids: - res.setdefault(obj_id, '') - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if res[obj_id] < res.get(child_id, ''): - res[obj_id] = res.get(child_id, '') - for id in ids: - res[id] = res.get(id, '') - return res - - def _last_invoice_date_calc (self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute ("select account_analytic_line.account_id, \ - date(max(account_invoice.date_invoice)) \ - from account_analytic_line \ - join account_invoice \ - on account_analytic_line.invoice_id = account_invoice.id \ - where account_analytic_line.account_id IN (%s) \ - and account_analytic_line.invoice_id is not null \ - GROUP BY account_analytic_line.account_id"%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = sum - for obj_id in ids: - res.setdefault(obj_id, '') - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if res[obj_id] < res.get(child_id, ''): - res[obj_id] = res.get(child_id, '') - for id in ids: - res[id] = res.get(id, '') - return res - - def _last_worked_invoiced_date_calc (self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - if ids2: - acc_set = ",".join(map(str, ids2)) - cr.execute("select account_analytic_line.account_id, max(date) \ - from account_analytic_line \ - where account_id IN (%s) \ - and invoice_id is not null \ - GROUP BY account_analytic_line.account_id;"%acc_set) - for account_id, sum in cr.fetchall(): - res[account_id] = sum - for obj_id in ids: - res.setdefault(obj_id, '') - for child_id in self.search(cr, uid, - [('parent_id', 'child_of', [obj_id])]): - if res[obj_id] < res.get(child_id, ''): - res[obj_id] = res.get(child_id, '') - for id in ids: - res[id] = res.get(id, '') - return res - - def _remaining_hours_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - if account.quantity_max <> 0: - res[account.id] = account.quantity_max - account.hours_quantity - else: - res[account.id]=0.0 - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - res[account.id] = account.hours_quantity - account.hours_qtt_non_invoiced - if res[account.id] < 0: - res[account.id]=0.0 - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - if account.hours_qtt_invoiced == 0: - res[account.id]=0.0 - else: - res[account.id] = account.ca_invoiced / account.hours_qtt_invoiced - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - if account.ca_invoiced == 0: - res[account.id]=0.0 - elif account.real_margin <> 0.0: - res[account.id] = (account.ca_invoiced / account.real_margin) * 100 - else: - res[account.id] = 0.0 - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _remaining_ca_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - if account.amount_max <> 0: - res[account.id] = account.amount_max - account.ca_invoiced - else: - res[account.id]=0.0 - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _real_margin_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - res[account.id] = account.ca_invoiced + account.total_cost - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _theorical_margin_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - res[account.id] = account.ca_theorical + account.total_cost - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - def _month(self, cr, uid, ids, name, arg, context=None): - res = {} - for id in ids: - ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) - cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \ - 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \ - 'AND unit_amount <> 0.0') - res[id] = [int(id * 1000000 + int(x[0])) for x in cr.fetchall()] - return res - - def _user(self, cr, uid, ids, name, arg, context=None): - res = {} - cr.execute('SELECT MAX(id) FROM res_users') - max_user = cr.fetchone()[0] - for id in ids: - ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) - cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \ - 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \ - 'AND unit_amount <> 0.0') - res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()] - return res - - _columns ={ - 'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced amount'), - 'total_cost': fields.function(_total_cost_calc, method=True, type='float', string='Total cost'), - 'ca_to_invoice': fields.function(_ca_to_invoice_calc, method=True, type='float', string='Uninvoiced amount'), - 'ca_theorical': fields.function(_ca_theorical_calc, method=True, type='float', string='Theorical revenue'), - 'hours_quantity': fields.function(_hours_quantity_calc, method=True, type='float', string='Hours tot'), - 'last_invoice_date': fields.function(_last_invoice_date_calc, method=True, type='date', string='Last invoice date'), - 'last_worked_invoiced_date': fields.function(_last_worked_invoiced_date_calc, method=True, type='date', string='Last invoiced worked date'), - 'last_worked_date': fields.function(_last_worked_date_calc, method=True, type='date', string='Last worked date'), - 'hours_qtt_non_invoiced': fields.function(_hours_qtt_non_invoiced_calc, method=True, type='float', string='Uninvoiced hours'), - 'hours_qtt_invoiced': fields.function(_hours_qtt_invoiced_calc, method=True, type='float', string='Invoiced hours'), - 'remaining_hours': fields.function(_remaining_hours_calc, method=True, type='float', string='Remaining hours'), - 'remaining_ca': fields.function(_remaining_ca_calc, method=True, type='float', string='Remaining revenue'), - 'revenue_per_hour': fields.function(_revenue_per_hour_calc, method=True, type='float', string='Revenue per hours (real)'), - 'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real margin'), - 'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical margin'), - 'real_margin_rate': fields.function(_real_margin_rate_calc, method=True, type='float', string='Real margin rate (%)'), - 'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'), - 'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'), - } + _name = "account.analytic.account" + _inherit = "account.analytic.account" + + def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("select account_analytic_line.account_id, sum(amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_journal.type = 'sale' \ + group by account_analytic_line.account_id" % acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + res2 = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + # Amount uninvoiced hours to invoice at sale price + acc_set = ",".join(map(str, ids2)) + cr.execute("""SELECT account_analytic_account.id, \ + sum (product_template.list_price * \ + account_analytic_line.unit_amount * \ + ((100-hr_timesheet_invoice_factor.factor)/100)) \ + AS ca_to_invoice \ + FROM product_template \ + join product_product \ + on product_template.id = product_product.product_tmpl_id \ + JOIN account_analytic_line \ + on account_analytic_line.product_id = product_product.id \ + JOIN account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + JOIN account_analytic_account \ + on account_analytic_account.id = account_analytic_line.account_id \ + JOIN hr_timesheet_invoice_factor \ + on hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \ + WHERE account_analytic_account.id IN (%s) \ + AND account_analytic_line.invoice_id is null \ + AND account_analytic_line.to_invoice IS NOT NULL \ + GROUP BY account_analytic_account.id;"""%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + + # Expense amount and purchase invoice + acc_set = ",".join(map(str, ids2)) + cr.execute ("select account_analytic_line.account_id, sum(amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_journal.type = 'purchase' \ + GROUP BY account_analytic_line.account_id;"%acc_set) + for account_id, sum in cr.fetchall(): + res2[account_id] = round(sum,2) + for obj_id in ids: + res.setdefault(obj_id, 0.0) + res2.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + res2[obj_id] += res2.get(child_id, 0.0) + # sum both result on account_id + for id in ids: + res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) + return res + + def _hours_qtt_non_invoiced_calc (self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("select account_analytic_line.account_id, sum(unit_amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_journal.type='general' \ + and invoice_id is null \ + AND to_invoice IS NOT NULL \ + GROUP BY account_analytic_line.account_id;"%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _hours_quantity_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("select account_analytic_line.account_id,sum(unit_amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_journal.type='general' \ + GROUP BY account_analytic_line.account_id"%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _total_cost_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("""select account_analytic_line.account_id,sum(amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and amount<0 \ + GROUP BY account_analytic_line.account_id"""%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + res2 = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + # First part with expense and purchase + cr.execute("""select account_analytic_line.account_id,sum(amount) \ + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_journal.type = 'purchase' \ + GROUP BY account_analytic_line.account_id"""%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = round(sum,2) + + # Second part with timesheet (with invoice factor) + acc_set = ",".join(map(str, ids2)) + cr.execute("""select account_analytic_line.account_id as account_id, \ + sum((account_analytic_line.unit_amount * pt.list_price) \ + - (account_analytic_line.unit_amount * pt.list_price \ + * hr.factor)) as somme + from account_analytic_line \ + join account_analytic_journal \ + on account_analytic_line.journal_id = account_analytic_journal.id \ + join product_product pp \ + on (account_analytic_line.product_id = pp.id) \ + join product_template pt \ + on (pp.product_tmpl_id = pt.id) \ + join account_analytic_account a \ + on (a.id=account_analytic_line.account_id) \ + join hr_timesheet_invoice_factor hr \ + on (hr.id=a.to_invoice) \ + where account_analytic_line.account_id IN (%s) \ + and a.to_invoice IS NOT NULL \ + GROUP BY account_analytic_line.account_id"""%acc_set) + for account_id, sum in cr.fetchall(): + res2[account_id] = round(sum,2) + + for obj_id in ids: + res.setdefault(obj_id, 0.0) + res2.setdefault(obj_id, 0.0) + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if child_id != obj_id: + res[obj_id] += res.get(child_id, 0.0) + res[obj_id] += res2.get(child_id, 0.0) + + # sum both result on account_id + for id in ids: + res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2) + return res + + def _last_worked_date_calc (self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("select account_analytic_line.account_id, max(date) \ + from account_analytic_line \ + where account_id IN (%s) \ + and invoice_id is null \ + GROUP BY account_analytic_line.account_id" % acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = sum + for obj_id in ids: + res.setdefault(obj_id, '') + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if res[obj_id] < res.get(child_id, ''): + res[obj_id] = res.get(child_id, '') + for id in ids: + res[id] = res.get(id, '') + return res + + def _last_invoice_date_calc (self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute ("select account_analytic_line.account_id, \ + date(max(account_invoice.date_invoice)) \ + from account_analytic_line \ + join account_invoice \ + on account_analytic_line.invoice_id = account_invoice.id \ + where account_analytic_line.account_id IN (%s) \ + and account_analytic_line.invoice_id is not null \ + GROUP BY account_analytic_line.account_id"%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = sum + for obj_id in ids: + res.setdefault(obj_id, '') + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if res[obj_id] < res.get(child_id, ''): + res[obj_id] = res.get(child_id, '') + for id in ids: + res[id] = res.get(id, '') + return res + + def _last_worked_invoiced_date_calc (self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + if ids2: + acc_set = ",".join(map(str, ids2)) + cr.execute("select account_analytic_line.account_id, max(date) \ + from account_analytic_line \ + where account_id IN (%s) \ + and invoice_id is not null \ + GROUP BY account_analytic_line.account_id;"%acc_set) + for account_id, sum in cr.fetchall(): + res[account_id] = sum + for obj_id in ids: + res.setdefault(obj_id, '') + for child_id in self.search(cr, uid, + [('parent_id', 'child_of', [obj_id])]): + if res[obj_id] < res.get(child_id, ''): + res[obj_id] = res.get(child_id, '') + for id in ids: + res[id] = res.get(id, '') + return res + + def _remaining_hours_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + if account.quantity_max <> 0: + res[account.id] = account.quantity_max - account.hours_quantity + else: + res[account.id]=0.0 + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + res[account.id] = account.hours_quantity - account.hours_qtt_non_invoiced + if res[account.id] < 0: + res[account.id]=0.0 + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + if account.hours_qtt_invoiced == 0: + res[account.id]=0.0 + else: + res[account.id] = account.ca_invoiced / account.hours_qtt_invoiced + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + if account.ca_invoiced == 0: + res[account.id]=0.0 + elif account.real_margin <> 0.0: + res[account.id] = (account.ca_invoiced / account.real_margin) * 100 + else: + res[account.id] = 0.0 + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _remaining_ca_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + if account.amount_max <> 0: + res[account.id] = account.amount_max - account.ca_invoiced + else: + res[account.id]=0.0 + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _real_margin_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + res[account.id] = account.ca_invoiced + account.total_cost + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _theorical_margin_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + res[account.id] = account.ca_theorical + account.total_cost + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + def _month(self, cr, uid, ids, name, arg, context=None): + res = {} + for id in ids: + ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) + cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \ + 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \ + 'AND unit_amount <> 0.0') + res[id] = [int(id * 1000000 + int(x[0])) for x in cr.fetchall()] + return res + + def _user(self, cr, uid, ids, name, arg, context=None): + res = {} + cr.execute('SELECT MAX(id) FROM res_users') + max_user = cr.fetchone()[0] + for id in ids: + ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) + cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \ + 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \ + 'AND unit_amount <> 0.0') + res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()] + return res + + _columns ={ + 'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced amount'), + 'total_cost': fields.function(_total_cost_calc, method=True, type='float', string='Total cost'), + 'ca_to_invoice': fields.function(_ca_to_invoice_calc, method=True, type='float', string='Uninvoiced amount'), + 'ca_theorical': fields.function(_ca_theorical_calc, method=True, type='float', string='Theorical revenue'), + 'hours_quantity': fields.function(_hours_quantity_calc, method=True, type='float', string='Hours tot'), + 'last_invoice_date': fields.function(_last_invoice_date_calc, method=True, type='date', string='Last invoice date'), + 'last_worked_invoiced_date': fields.function(_last_worked_invoiced_date_calc, method=True, type='date', string='Last invoiced worked date'), + 'last_worked_date': fields.function(_last_worked_date_calc, method=True, type='date', string='Last worked date'), + 'hours_qtt_non_invoiced': fields.function(_hours_qtt_non_invoiced_calc, method=True, type='float', string='Uninvoiced hours'), + 'hours_qtt_invoiced': fields.function(_hours_qtt_invoiced_calc, method=True, type='float', string='Invoiced hours'), + 'remaining_hours': fields.function(_remaining_hours_calc, method=True, type='float', string='Remaining hours'), + 'remaining_ca': fields.function(_remaining_ca_calc, method=True, type='float', string='Remaining revenue'), + 'revenue_per_hour': fields.function(_revenue_per_hour_calc, method=True, type='float', string='Revenue per hours (real)'), + 'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real margin'), + 'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical margin'), + 'real_margin_rate': fields.function(_real_margin_rate_calc, method=True, type='float', string='Real margin rate (%)'), + 'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'), + 'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'), + } account_analytic_account() class account_analytic_account_summary_user(osv.osv): - _name = "account_analytic_analysis.summary.user" - _description = "Hours summary by user" - _order='user' - _auto = False - _rec_name = 'user' - - def _unit_amount(self, cr, uid, ids, name, arg, context=None): - res = {} - account_obj = self.pool.get('account.analytic.account') - cr.execute('SELECT MAX(id) FROM res_users') - max_user = cr.fetchone()[0] - account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids] - user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids] - account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)]) - user_set = ','.join([str(x) for x in user_ids]) - if account_ids2: - acc_set = ','.join([str(x) for x in account_ids2]) - cr.execute('SELECT id, unit_amount ' \ - 'FROM account_analytic_analysis_summary_user ' \ - 'WHERE account_id in (%s) ' \ - 'AND "user" in (%s) ' % \ - (acc_set, user_set)) - for sum_id, unit_amount in cr.fetchall(): - res[sum_id] = unit_amount - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in account_obj.search(cr, uid, - [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]): - if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))): - res[obj_id] += res.get((child_id * max_user) + obj_id -((obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)) * max_user), 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0), 2) - return res - - _columns = { - 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), - 'unit_amount': fields.function(_unit_amount, method=True, type='float', - string='Total Time'), - 'user' : fields.many2one('res.users', 'User'), - } - def init(self, cr): - cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \ - 'SELECT ' \ - '(u.account_id * u.max_user) + u."user" AS id, ' \ - 'u.account_id AS account_id, ' \ - 'u."user" AS "user", ' \ - 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \ - 'FROM ' \ - '(SELECT ' \ - 'a.id AS account_id, ' \ - 'u1.id AS "user", ' \ - 'MAX(u2.id) AS max_user ' \ - 'FROM ' \ - 'res_users AS u1, ' \ - 'res_users AS u2, ' \ - 'account_analytic_account AS a ' \ - 'GROUP BY u1.id, a.id ' \ - ') AS u ' \ - 'LEFT JOIN ' \ - '(SELECT ' \ - 'l.account_id AS account_id, ' \ - 'l.user_id AS "user", ' \ - 'SUM(l.unit_amount) AS unit_amount ' \ - 'FROM account_analytic_line AS l, ' \ - 'account_analytic_journal AS j ' \ - 'WHERE j.type = \'general\' ' \ - 'GROUP BY l.account_id, l.user_id ' \ - ') AS l ' - 'ON (' \ - 'u.account_id = l.account_id ' \ - 'AND u."user" = l."user"' \ - ') ' \ - 'GROUP BY u."user", u.account_id, u.max_user' \ - ')') - - def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): - if not context: - context={} - if not ids: - return [] - - if fields==None: - fields = self._columns.keys() - - # construct a clause for the rules : - d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) - - # all inherited fields + all non inherited fields for which the attribute whose name is in load is True - fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() - - res = [] - cr.execute('SELECT MAX(id) FROM res_users') - max_user = cr.fetchone()[0] - if len(fields_pre) : - fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - if d1: - cr.execute('select %s from \"%s\" where id in (%s) ' \ - 'and account_id in (%s) ' \ - 'and "user" in (%s) and %s order by %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), - ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]), - ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1, - self._order),d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % self._description) - else: - cr.execute('select %s from \"%s\" where id in (%s) ' \ - 'and account_id in (%s) ' \ - 'and "user" in (%s) order by %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), - ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]), - ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), - self._order)) - res.extend(cr.dictfetchall()) - else: - res = map(lambda x: {'id': x}, ids) - - for f in fields_pre: - if self._columns[f].translate: - ids = map(lambda x: x['id'], res) - res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) - for r in res: - r[f] = res_trans.get(r['id'], False) or r[f] - - for table in self._inherits: - col = self._inherits[table] - cols = intersect(self._inherit_fields.keys(), fields) - if not cols: - continue - res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) - - res3 = {} - for r in res2: - res3[r['id']] = r - del r['id'] - - for record in res: - record.update(res3[record[col]]) - if col not in fields: - del record[col] - - # all fields which need to be post-processed by a simple function (symbol_get) - fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) - if fields_post: - # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need - # to get the _symbol_get in each occurence - for r in res: - for f in fields_post: - r[f] = self.columns[f]._symbol_get(r[f]) - ids = map(lambda x: x['id'], res) - - # all non inherited fields for which the attribute whose name is in load is False - fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) - for f in fields_post: - # get the value of that field for all records/ids - res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) - for record in res: - record[f] = res2[record['id']] - - return res + _name = "account_analytic_analysis.summary.user" + _description = "Hours summary by user" + _order='user' + _auto = False + _rec_name = 'user' + + def _unit_amount(self, cr, uid, ids, name, arg, context=None): + res = {} + account_obj = self.pool.get('account.analytic.account') + cr.execute('SELECT MAX(id) FROM res_users') + max_user = cr.fetchone()[0] + account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids] + user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids] + account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)]) + user_set = ','.join([str(x) for x in user_ids]) + if account_ids2: + acc_set = ','.join([str(x) for x in account_ids2]) + cr.execute('SELECT id, unit_amount ' \ + 'FROM account_analytic_analysis_summary_user ' \ + 'WHERE account_id in (%s) ' \ + 'AND "user" in (%s) ' % \ + (acc_set, user_set)) + for sum_id, unit_amount in cr.fetchall(): + res[sum_id] = unit_amount + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in account_obj.search(cr, uid, + [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]): + if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))): + res[obj_id] += res.get((child_id * max_user) + obj_id -((obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)) * max_user), 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0), 2) + return res + + _columns = { + 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'unit_amount': fields.function(_unit_amount, method=True, type='float', + string='Total Time'), + 'user' : fields.many2one('res.users', 'User'), + } + def init(self, cr): + cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \ + 'SELECT ' \ + '(u.account_id * u.max_user) + u."user" AS id, ' \ + 'u.account_id AS account_id, ' \ + 'u."user" AS "user", ' \ + 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \ + 'FROM ' \ + '(SELECT ' \ + 'a.id AS account_id, ' \ + 'u1.id AS "user", ' \ + 'MAX(u2.id) AS max_user ' \ + 'FROM ' \ + 'res_users AS u1, ' \ + 'res_users AS u2, ' \ + 'account_analytic_account AS a ' \ + 'GROUP BY u1.id, a.id ' \ + ') AS u ' \ + 'LEFT JOIN ' \ + '(SELECT ' \ + 'l.account_id AS account_id, ' \ + 'l.user_id AS "user", ' \ + 'SUM(l.unit_amount) AS unit_amount ' \ + 'FROM account_analytic_line AS l, ' \ + 'account_analytic_journal AS j ' \ + 'WHERE j.type = \'general\' ' \ + 'GROUP BY l.account_id, l.user_id ' \ + ') AS l ' + 'ON (' \ + 'u.account_id = l.account_id ' \ + 'AND u."user" = l."user"' \ + ') ' \ + 'GROUP BY u."user", u.account_id, u.max_user' \ + ')') + + def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): + if not context: + context={} + if not ids: + return [] + + if fields==None: + fields = self._columns.keys() + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + + # all inherited fields + all non inherited fields for which the attribute whose name is in load is True + fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() + + res = [] + cr.execute('SELECT MAX(id) FROM res_users') + max_user = cr.fetchone()[0] + if len(fields_pre) : + fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + if d1: + cr.execute('select %s from \"%s\" where id in (%s) ' \ + 'and account_id in (%s) ' \ + 'and "user" in (%s) and %s order by %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]), + ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1, + self._order),d2) + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % self._description) + else: + cr.execute('select %s from \"%s\" where id in (%s) ' \ + 'and account_id in (%s) ' \ + 'and "user" in (%s) order by %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]), + ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), + self._order)) + res.extend(cr.dictfetchall()) + else: + res = map(lambda x: {'id': x}, ids) + + for f in fields_pre: + if self._columns[f].translate: + ids = map(lambda x: x['id'], res) + res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) + for r in res: + r[f] = res_trans.get(r['id'], False) or r[f] + + for table in self._inherits: + col = self._inherits[table] + cols = intersect(self._inherit_fields.keys(), fields) + if not cols: + continue + res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) + + res3 = {} + for r in res2: + res3[r['id']] = r + del r['id'] + + for record in res: + record.update(res3[record[col]]) + if col not in fields: + del record[col] + + # all fields which need to be post-processed by a simple function (symbol_get) + fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) + if fields_post: + # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need + # to get the _symbol_get in each occurence + for r in res: + for f in fields_post: + r[f] = self.columns[f]._symbol_get(r[f]) + ids = map(lambda x: x['id'], res) + + # all non inherited fields for which the attribute whose name is in load is False + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + # get the value of that field for all records/ids + res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) + for record in res: + record[f] = res2[record['id']] + + return res account_analytic_account_summary_user() class account_analytic_account_summary_month(osv.osv): - _name = "account_analytic_analysis.summary.month" - _description = "Hours summary by month" - _auto = False - _rec_name = 'month' - _order = 'month' - - def _unit_amount(self, cr, uid, ids, name, arg, context=None): - res = {} - account_obj = self.pool.get('account.analytic.account') - account_ids = [int(str(int(x))[:-6]) for x in ids] - month_ids = [int(str(int(x))[-6:]) for x in ids] - account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)]) - month_set = ','.join([str(x) for x in month_ids]) - if account_ids2: - acc_set = ','.join([str(x) for x in account_ids2]) - cr.execute('SELECT id, unit_amount ' \ - 'FROM account_analytic_analysis_summary_month ' \ - 'WHERE account_id in (%s) ' \ - 'AND month_id in (%s) ' % \ - (acc_set, month_set)) - for sum_id, unit_amount in cr.fetchall(): - res[sum_id] = unit_amount - for obj_id in ids: - res.setdefault(obj_id, 0.0) - for child_id in account_obj.search(cr, uid, - [('parent_id', 'child_of', [int(str(int(obj_id))[:-6])])]): - if child_id != int(str(int(obj_id))[:-6]): - res[obj_id] += res.get(int(child_id * 1000000 + int(obj_id)), 0.0) - for id in ids: - res[id] = round(res.get(id, 0.0), 2) - return res - - _columns = { - 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', - readonly=True), - 'unit_amount': fields.function(_unit_amount, method=True, type='float', - string='Total Time'), - 'month': fields.char('Month', size=25, readonly=True), - } - def init(self, cr): - cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_month AS (' \ - 'SELECT ' \ - '(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000))::integer AS id, ' \ - 'd.account_id AS account_id, ' \ - 'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \ - 'TO_CHAR(d.month, \'YYYYMM\') AS month_id, ' \ - 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \ - 'FROM ' \ - '(SELECT ' \ - 'd2.account_id, ' \ - 'd2.month ' \ - 'FROM ' \ - '(SELECT ' \ - 'a.id AS account_id, ' \ - 'l.month AS month ' \ - 'FROM ' \ - '(SELECT ' \ - 'DATE_TRUNC(\'month\', l.date) AS month ' \ - 'FROM account_analytic_line AS l, ' \ - 'account_analytic_journal AS j ' \ - 'WHERE j.type = \'general\' ' \ - 'GROUP BY DATE_TRUNC(\'month\', l.date) ' \ - ') AS l, ' \ - 'account_analytic_account AS a ' \ - 'GROUP BY l.month, a.id ' \ - ') AS d2 ' \ - 'GROUP BY d2.account_id, d2.month ' \ - ') AS d ' \ - 'LEFT JOIN ' \ - '(SELECT ' \ - 'l.account_id AS account_id, ' \ - 'DATE_TRUNC(\'month\', l.date) AS month, ' \ - 'SUM(l.unit_amount) AS unit_amount ' \ - 'FROM account_analytic_line AS l, ' \ - 'account_analytic_journal AS j ' \ - 'WHERE j.type = \'general\' ' \ - 'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \ - ') AS l ' - 'ON (' \ - 'd.account_id = l.account_id ' \ - 'AND d.month = l.month' \ - ') ' \ - 'GROUP BY d.month, d.account_id ' \ - ')') - - def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): - if not context: - context={} - if not ids: - return [] - - if fields==None: - fields = self._columns.keys() - - # construct a clause for the rules : - d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) - - # all inherited fields + all non inherited fields for which the attribute whose name is in load is True - fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() - - res = [] - if len(fields_pre) : - fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - if d1: - cr.execute('select %s from \"%s\" where id in (%s) ' \ - 'and account_id in (%s) ' \ - 'and month_id in (%s) and %s order by %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), - ','.join([str(x)[:-6] for x in sub_ids]), - ','.join([str(x)[-6:] for x in sub_ids]), d1, - self._order),d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % self._description) - else: - cr.execute('select %s from \"%s\" where id in (%s) ' \ - 'and account_id in (%s) ' \ - 'and month_id in (%s) order by %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), - ','.join([str(x)[:-6] for x in sub_ids]), - ','.join([str(x)[-6:] for x in sub_ids]), - self._order)) - res.extend(cr.dictfetchall()) - else: - res = map(lambda x: {'id': x}, ids) - - for f in fields_pre: - if self._columns[f].translate: - ids = map(lambda x: x['id'], res) - res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) - for r in res: - r[f] = res_trans.get(r['id'], False) or r[f] - - for table in self._inherits: - col = self._inherits[table] - cols = intersect(self._inherit_fields.keys(), fields) - if not cols: - continue - res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) - - res3 = {} - for r in res2: - res3[r['id']] = r - del r['id'] - - for record in res: - record.update(res3[record[col]]) - if col not in fields: - del record[col] - - # all fields which need to be post-processed by a simple function (symbol_get) - fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) - if fields_post: - # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need - # to get the _symbol_get in each occurence - for r in res: - for f in fields_post: - r[f] = self.columns[f]._symbol_get(r[f]) - ids = map(lambda x: x['id'], res) - - # all non inherited fields for which the attribute whose name is in load is False - fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) - for f in fields_post: - # get the value of that field for all records/ids - res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) - for record in res: - record[f] = res2[record['id']] - - return res + _name = "account_analytic_analysis.summary.month" + _description = "Hours summary by month" + _auto = False + _rec_name = 'month' + _order = 'month' + + def _unit_amount(self, cr, uid, ids, name, arg, context=None): + res = {} + account_obj = self.pool.get('account.analytic.account') + account_ids = [int(str(int(x))[:-6]) for x in ids] + month_ids = [int(str(int(x))[-6:]) for x in ids] + account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)]) + month_set = ','.join([str(x) for x in month_ids]) + if account_ids2: + acc_set = ','.join([str(x) for x in account_ids2]) + cr.execute('SELECT id, unit_amount ' \ + 'FROM account_analytic_analysis_summary_month ' \ + 'WHERE account_id in (%s) ' \ + 'AND month_id in (%s) ' % \ + (acc_set, month_set)) + for sum_id, unit_amount in cr.fetchall(): + res[sum_id] = unit_amount + for obj_id in ids: + res.setdefault(obj_id, 0.0) + for child_id in account_obj.search(cr, uid, + [('parent_id', 'child_of', [int(str(int(obj_id))[:-6])])]): + if child_id != int(str(int(obj_id))[:-6]): + res[obj_id] += res.get(int(child_id * 1000000 + int(obj_id)), 0.0) + for id in ids: + res[id] = round(res.get(id, 0.0), 2) + return res + + _columns = { + 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', + readonly=True), + 'unit_amount': fields.function(_unit_amount, method=True, type='float', + string='Total Time'), + 'month': fields.char('Month', size=25, readonly=True), + } + def init(self, cr): + cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_month AS (' \ + 'SELECT ' \ + '(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000))::integer AS id, ' \ + 'd.account_id AS account_id, ' \ + 'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \ + 'TO_CHAR(d.month, \'YYYYMM\') AS month_id, ' \ + 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \ + 'FROM ' \ + '(SELECT ' \ + 'd2.account_id, ' \ + 'd2.month ' \ + 'FROM ' \ + '(SELECT ' \ + 'a.id AS account_id, ' \ + 'l.month AS month ' \ + 'FROM ' \ + '(SELECT ' \ + 'DATE_TRUNC(\'month\', l.date) AS month ' \ + 'FROM account_analytic_line AS l, ' \ + 'account_analytic_journal AS j ' \ + 'WHERE j.type = \'general\' ' \ + 'GROUP BY DATE_TRUNC(\'month\', l.date) ' \ + ') AS l, ' \ + 'account_analytic_account AS a ' \ + 'GROUP BY l.month, a.id ' \ + ') AS d2 ' \ + 'GROUP BY d2.account_id, d2.month ' \ + ') AS d ' \ + 'LEFT JOIN ' \ + '(SELECT ' \ + 'l.account_id AS account_id, ' \ + 'DATE_TRUNC(\'month\', l.date) AS month, ' \ + 'SUM(l.unit_amount) AS unit_amount ' \ + 'FROM account_analytic_line AS l, ' \ + 'account_analytic_journal AS j ' \ + 'WHERE j.type = \'general\' ' \ + 'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \ + ') AS l ' + 'ON (' \ + 'd.account_id = l.account_id ' \ + 'AND d.month = l.month' \ + ') ' \ + 'GROUP BY d.month, d.account_id ' \ + ')') + + def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): + if not context: + context={} + if not ids: + return [] + + if fields==None: + fields = self._columns.keys() + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + + # all inherited fields + all non inherited fields for which the attribute whose name is in load is True + fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() + + res = [] + if len(fields_pre) : + fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + if d1: + cr.execute('select %s from \"%s\" where id in (%s) ' \ + 'and account_id in (%s) ' \ + 'and month_id in (%s) and %s order by %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + ','.join([str(x)[:-6] for x in sub_ids]), + ','.join([str(x)[-6:] for x in sub_ids]), d1, + self._order),d2) + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % self._description) + else: + cr.execute('select %s from \"%s\" where id in (%s) ' \ + 'and account_id in (%s) ' \ + 'and month_id in (%s) order by %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + ','.join([str(x)[:-6] for x in sub_ids]), + ','.join([str(x)[-6:] for x in sub_ids]), + self._order)) + res.extend(cr.dictfetchall()) + else: + res = map(lambda x: {'id': x}, ids) + + for f in fields_pre: + if self._columns[f].translate: + ids = map(lambda x: x['id'], res) + res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) + for r in res: + r[f] = res_trans.get(r['id'], False) or r[f] + + for table in self._inherits: + col = self._inherits[table] + cols = intersect(self._inherit_fields.keys(), fields) + if not cols: + continue + res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) + + res3 = {} + for r in res2: + res3[r['id']] = r + del r['id'] + + for record in res: + record.update(res3[record[col]]) + if col not in fields: + del record[col] + + # all fields which need to be post-processed by a simple function (symbol_get) + fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) + if fields_post: + # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need + # to get the _symbol_get in each occurence + for r in res: + for f in fields_post: + r[f] = self.columns[f]._symbol_get(r[f]) + ids = map(lambda x: x['id'], res) + + # all non inherited fields for which the attribute whose name is in load is False + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + # get the value of that field for all records/ids + res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) + for record in res: + record[f] = res2[record['id']] + + return res account_analytic_account_summary_month() diff --git a/addons/account_chart/__terp__.py b/addons/account_chart/__terp__.py index b24487fb394..9ad17e51c63 100644 --- a/addons/account_chart/__terp__.py +++ b/addons/account_chart/__terp__.py @@ -26,20 +26,20 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Charts of Accounts", - "version" : "1.1", - "depends" : ["account"], - "author" : "Tiny", - "description": """Remove minimal account chart""", - "website" : "http://tinyerp.com/module_account.html", - "category" : "Generic Modules/Accounting", - "init_xml" : [ - ], - "demo_xml" : [ - ], - "update_xml" : [ + "name" : "Charts of Accounts", + "version" : "1.1", + "depends" : ["account"], + "author" : "Tiny", + "description": """Remove minimal account chart""", + "website" : "http://tinyerp.com/module_account.html", + "category" : "Generic Modules/Accounting", + "init_xml" : [ + ], + "demo_xml" : [ + ], + "update_xml" : [ # Must be loaded by each modules -# "account_chart.xml", - ], - "installable": True +# "account_chart.xml", + ], + "installable": True } diff --git a/addons/account_followup/__terp__.py b/addons/account_followup/__terp__.py index fbef0c95c1c..d5952f073c0 100644 --- a/addons/account_followup/__terp__.py +++ b/addons/account_followup/__terp__.py @@ -26,32 +26,32 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Accounting follow-ups management", - "version" : "1.0", - "depends" : ["account"], - "author" : "Tiny", - "description": """ - Modules to automate letters for unpaid invoices, with multi-level recalls. + "name" : "Accounting follow-ups management", + "version" : "1.0", + "depends" : ["account"], + "author" : "Tiny", + "description": """ + Modules to automate letters for unpaid invoices, with multi-level recalls. - You can define your multiple levels of recall through the menu: - Financial Management/Configuration/Payment Terms/Follow-Ups + You can define your multiple levels of recall through the menu: + Financial Management/Configuration/Payment Terms/Follow-Ups - Once it's defined, you can automatically prints recall every days - through simply clicking on the menu: - Financial_Management/Periodical_Processing/Print_Follow-Ups + Once it's defined, you can automatically prints recall every days + through simply clicking on the menu: + Financial_Management/Periodical_Processing/Print_Follow-Ups - It will generate a PDF with all the letters according the the - different levels of recall defined. You can define different policies - for different companies.""", - "website" : "http://tinyerp.com/module_account.html", - "category" : "Generic Modules/Accounting", - "init_xml" : [], - "demo_xml" : ["followup_demo.xml"], - "update_xml" : [ - "followup_view.xml", - "followup_report_view.xml" - ], - "active": False, - "installable": True + It will generate a PDF with all the letters according the the + different levels of recall defined. You can define different policies + for different companies.""", + "website" : "http://tinyerp.com/module_account.html", + "category" : "Generic Modules/Accounting", + "init_xml" : [], + "demo_xml" : ["followup_demo.xml"], + "update_xml" : [ + "followup_view.xml", + "followup_report_view.xml" + ], + "active": False, + "installable": True } diff --git a/addons/account_followup/followup.py b/addons/account_followup/followup.py index 0376ccc9d16..52e944adfa7 100644 --- a/addons/account_followup/followup.py +++ b/addons/account_followup/followup.py @@ -31,36 +31,36 @@ from osv import fields, osv class followup(osv.osv): - _name = 'account_followup.followup' - _description = 'Follow-Ups' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'description': fields.text('Description'), - 'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-Up'), - 'company_id': fields.many2one('res.company', 'Company'), - } + _name = 'account_followup.followup' + _description = 'Follow-Ups' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'description': fields.text('Description'), + 'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-Up'), + 'company_id': fields.many2one('res.company', 'Company'), + } followup() class followup_line(osv.osv): - _name = 'account_followup.followup.line' - _description = 'Follow-Ups Criteria' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'sequence': fields.integer('Sequence'), - 'delay': fields.integer('Days of delay'), - 'start': fields.selection([('days','Net Days'),('end_of_month','End of Month')], 'Type of Term', size=64, required=True), - 'followup_id': fields.many2one('account_followup.followup', 'Follow Ups', required=True, ondelete="cascade"), - 'description': fields.text('Printed Message'), - #'prints': fields.one2many('account_followup.followup.print', 'line_id'), - } + _name = 'account_followup.followup.line' + _description = 'Follow-Ups Criteria' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'sequence': fields.integer('Sequence'), + 'delay': fields.integer('Days of delay'), + 'start': fields.selection([('days','Net Days'),('end_of_month','End of Month')], 'Type of Term', size=64, required=True), + 'followup_id': fields.many2one('account_followup.followup', 'Follow Ups', required=True, ondelete="cascade"), + 'description': fields.text('Printed Message'), + #'prints': fields.one2many('account_followup.followup.print', 'line_id'), + } followup_line() class account_move_line(osv.osv): - _name = 'account.move.line' - _inherit = 'account.move.line' - _columns = { - 'followup_line_id': fields.many2one('account_followup.followup.line', 'Follow-up Level'), - 'followup_date': fields.date('Latest Follow-up'), - } + _name = 'account.move.line' + _inherit = 'account.move.line' + _columns = { + 'followup_line_id': fields.many2one('account_followup.followup.line', 'Follow-up Level'), + 'followup_date': fields.date('Latest Follow-up'), + } account_move_line() diff --git a/addons/account_followup/followup_report.py b/addons/account_followup/followup_report.py index cf8a79041e1..c99487d40a2 100644 --- a/addons/account_followup/followup_report.py +++ b/addons/account_followup/followup_report.py @@ -31,55 +31,55 @@ from osv import fields, osv def _code_get(self, cr, uid, context={}): - acc_type_obj = self.pool.get('account.account.type') - ids = acc_type_obj.search(cr, uid, []) - res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) - return [(r['code'], r['name']) for r in res] + acc_type_obj = self.pool.get('account.account.type') + ids = acc_type_obj.search(cr, uid, []) + res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) + return [(r['code'], r['name']) for r in res] class account_followup_stat(osv.osv): - _name = "account_followup.stat" - _description = "Followup statistics" - _auto = False - _columns = { - 'name': fields.many2one('res.partner', 'Partner', readonly=True), - 'account_type': fields.selection(_code_get, 'Account Type', readonly=True), - 'date_move':fields.date('First move', readonly=True), - 'date_move_last':fields.date('Last move', readonly=True), - 'date_followup':fields.date('Latest followup', readonly=True), - 'followup_id': fields.many2one('account_followup.followup.line', - 'Follow Ups', readonly=True, ondelete="cascade"), - 'balance':fields.float('Balance', readonly=True), - 'debit':fields.float('Debit', readonly=True), - 'credit':fields.float('Credit', readonly=True), - } - _order = 'date_move' - def init(self, cr): - cr.execute(""" - create or replace view account_followup_stat as ( - select - min(l.id) as id, - l.partner_id as name, - min(l.date) as date_move, - max(l.date) as date_move_last, - max(l.followup_date) as date_followup, - max(l.followup_line_id) as followup_id, - sum(l.debit) as debit, - sum(l.credit) as credit, - sum(l.debit - l.credit) as balance, - a.type as account_type - from - account_move_line l - left join - account_account a on (l.account_id=a.id) - where - l.reconcile_id is NULL and - a.type in ('receivable', 'payable') - and a.active and - l.partner_id is not null - group by - l.partner_id, a.type - )""") + _name = "account_followup.stat" + _description = "Followup statistics" + _auto = False + _columns = { + 'name': fields.many2one('res.partner', 'Partner', readonly=True), + 'account_type': fields.selection(_code_get, 'Account Type', readonly=True), + 'date_move':fields.date('First move', readonly=True), + 'date_move_last':fields.date('Last move', readonly=True), + 'date_followup':fields.date('Latest followup', readonly=True), + 'followup_id': fields.many2one('account_followup.followup.line', + 'Follow Ups', readonly=True, ondelete="cascade"), + 'balance':fields.float('Balance', readonly=True), + 'debit':fields.float('Debit', readonly=True), + 'credit':fields.float('Credit', readonly=True), + } + _order = 'date_move' + def init(self, cr): + cr.execute(""" + create or replace view account_followup_stat as ( + select + min(l.id) as id, + l.partner_id as name, + min(l.date) as date_move, + max(l.date) as date_move_last, + max(l.followup_date) as date_followup, + max(l.followup_line_id) as followup_id, + sum(l.debit) as debit, + sum(l.credit) as credit, + sum(l.debit - l.credit) as balance, + a.type as account_type + from + account_move_line l + left join + account_account a on (l.account_id=a.id) + where + l.reconcile_id is NULL and + a.type in ('receivable', 'payable') + and a.active and + l.partner_id is not null + group by + l.partner_id, a.type + )""") account_followup_stat() diff --git a/addons/account_followup/report/report_followup_print.py b/addons/account_followup/report/report_followup_print.py index 8648ac4ec07..286f8424534 100644 --- a/addons/account_followup/report/report_followup_print.py +++ b/addons/account_followup/report/report_followup_print.py @@ -34,39 +34,39 @@ from osv import osv from report import report_sxw class report_rappel(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(report_rappel, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time' : time, - 'ids_to_objects': self._ids_to_objects, - 'adr_get' : self._adr_get, - 'getLines' : self._lines_get, - }) + def __init__(self, cr, uid, name, context): + super(report_rappel, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time' : time, + 'ids_to_objects': self._ids_to_objects, + 'adr_get' : self._adr_get, + 'getLines' : self._lines_get, + }) - def _ids_to_objects(self, partners_ids): - pool = pooler.get_pool(self.cr.dbname) - all_partners = [] - for partner in partners_ids: - partners = pool.get('account_followup.stat').browse(self.cr, self.uid, partner[2]) - for par in partners: - all_partners.append(par.name) - return all_partners + def _ids_to_objects(self, partners_ids): + pool = pooler.get_pool(self.cr.dbname) + all_partners = [] + for partner in partners_ids: + partners = pool.get('account_followup.stat').browse(self.cr, self.uid, partner[2]) + for par in partners: + all_partners.append(par.name) + return all_partners - def _adr_get(self, partner, type): - res_partner = pooler.get_pool(self.cr.dbname).get('res.partner') - res_partner_address = pooler.get_pool(self.cr.dbname).get('res.partner.address') - adr = res_partner.address_get(self.cr, self.uid, [partner.id], [type])[type] - return res_partner_address.read(self.cr, self.uid, [adr]) + def _adr_get(self, partner, type): + res_partner = pooler.get_pool(self.cr.dbname).get('res.partner') + res_partner_address = pooler.get_pool(self.cr.dbname).get('res.partner.address') + adr = res_partner.address_get(self.cr, self.uid, [partner.id], [type])[type] + return res_partner_address.read(self.cr, self.uid, [adr]) - def _lines_get(self, partner): - moveline_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') - movelines = moveline_obj.search(self.cr, self.uid, - [('partner_id', '=', partner.id), - ('account_id.type', '=', 'receivable'), - ('reconcile_id', '=', False), ('state', '<>', 'draft')]) - movelines = moveline_obj.read(self.cr, self.uid, movelines) - return movelines + def _lines_get(self, partner): + moveline_obj = pooler.get_pool(self.cr.dbname).get('account.move.line') + movelines = moveline_obj.search(self.cr, self.uid, + [('partner_id', '=', partner.id), + ('account_id.type', '=', 'receivable'), + ('reconcile_id', '=', False), ('state', '<>', 'draft')]) + movelines = moveline_obj.read(self.cr, self.uid, movelines) + return movelines report_sxw.report_sxw('report.account_followup.followup.print', - 'res.partner', 'addons/account_followup/report/rappel.rml', - parser=report_rappel) + 'res.partner', 'addons/account_followup/report/rappel.rml', + parser=report_rappel) diff --git a/addons/account_followup/wizard/wizard_followup_print.py b/addons/account_followup/wizard/wizard_followup_print.py index b758f4eb9ac..35ffdddca4e 100644 --- a/addons/account_followup/wizard/wizard_followup_print.py +++ b/addons/account_followup/wizard/wizard_followup_print.py @@ -38,73 +38,73 @@ from tools.translate import _ _followup_wizard_date_form = """<?xml version="1.0"?> <form string="Select a date"> - <field name="date"/> + <field name="date"/> </form>""" _followup_wizard_date_fields = { - 'date': {'string': 'Date', 'type': 'date', 'required': True}, + 'date': {'string': 'Date', 'type': 'date', 'required': True}, } form = """<?xml version="1.0"?> <form string="Mail"> - <field name="mail_sent" colspan="4"/> - <newline/> - <newline/> - <separator string="Mail not sent to following Partners , Email not available !" colspan="4"/> - <field name="mail_notsent" colspan="4" nolabel="1"/> + <field name="mail_sent" colspan="4"/> + <newline/> + <newline/> + <separator string="Mail not sent to following Partners , Email not available !" colspan="4"/> + <field name="mail_notsent" colspan="4" nolabel="1"/> </form>""" fields = { - 'mail_sent': {'string':'Number of Mail sent', 'type':'char', 'readonly':True }, - 'mail_notsent': {'string':'Error Messages', 'type':'text', 'readonly':True , 'nolabel':'1'}, - } + 'mail_sent': {'string':'Number of Mail sent', 'type':'char', 'readonly':True }, + 'mail_notsent': {'string':'Error Messages', 'type':'text', 'readonly':True , 'nolabel':'1'}, + } _followup_wizard_all_form = """<?xml version="1.0"?> <form string="Select partners" colspan="4"> - <notebook> - <page string="FollowUp selection"> - <separator string="Select partners to remind" colspan="4"/> - <field name="partner_ids" colspan="4" nolabel="1"/> - </page> - <page string="Email confirmation"> - <field name="email_conf" colspan="4"/> - <field name="email_subject" colspan="4"/> - <separator string="Email body" colspan="4"/> - <field name="email_body" colspan="4" nolabel="1"/> - <separator string="Legend" colspan="4"/> + <notebook> + <page string="FollowUp selection"> + <separator string="Select partners to remind" colspan="4"/> + <field name="partner_ids" colspan="4" nolabel="1"/> + </page> + <page string="Email confirmation"> + <field name="email_conf" colspan="4"/> + <field name="email_subject" colspan="4"/> + <separator string="Email body" colspan="4"/> + <field name="email_body" colspan="4" nolabel="1"/> + <separator string="Legend" colspan="4"/> - <label string="%(partner_name)s: Partner name" colspan="2"/> - <label string="%(followup_amount)s: Total Amount Due" colspan="2"/> - <label string="%(user_signature)s: User name" colspan="2"/> - <label string="%(company_name)s: User's Company name" colspan="2"/> - <label string="%(line)s: Account Move lines" colspan="2"/> - <label string="%(heading)s: Move line header" colspan="2"/> - <label string="%(date)s: Current Date" colspan="2"/> - </page> - </notebook> + <label string="%(partner_name)s: Partner name" colspan="2"/> + <label string="%(followup_amount)s: Total Amount Due" colspan="2"/> + <label string="%(user_signature)s: User name" colspan="2"/> + <label string="%(company_name)s: User's Company name" colspan="2"/> + <label string="%(line)s: Account Move lines" colspan="2"/> + <label string="%(heading)s: Move line header" colspan="2"/> + <label string="%(date)s: Current Date" colspan="2"/> + </page> + </notebook> </form>""" _followup_wizard_all_fields = { - 'partner_ids': { - 'string': "Partners", - 'type': 'many2many', - 'relation': 'account_followup.stat', - 'domain':"[('account_type','=','receivable')]" - }, - 'email_conf': { - 'string': "Send email confirmation", - 'type': 'boolean', - }, - 'email_subject' : { - 'string' : "Email Subject", - 'type' : "char", - 'size': 64, - 'default': 'Account Follow-up' - }, - 'email_body': { - 'string': "Email body", - 'type': 'text', - 'default': ''' + 'partner_ids': { + 'string': "Partners", + 'type': 'many2many', + 'relation': 'account_followup.stat', + 'domain':"[('account_type','=','receivable')]" + }, + 'email_conf': { + 'string': "Send email confirmation", + 'type': 'boolean', + }, + 'email_subject' : { + 'string' : "Email Subject", + 'type' : "char", + 'size': 64, + 'default': 'Account Follow-up' + }, + 'email_body': { + 'string': "Email body", + 'type': 'text', + 'default': ''' Date : %(date)s Dear %(partner_name)s, @@ -126,183 +126,183 @@ Thanks, -- %(user_signature)s %(company_name)s - ''' - } + ''' + } } class followup_all_print(wizard.interface): - def _update_partners(self, cr, uid, data, context): - to_update = data['form']['to_update'] - for id in to_update.keys(): - cr.execute( - "UPDATE account_move_line "\ - "SET followup_line_id=%d, followup_date=%s "\ - "WHERE id=%d", - (to_update[id], - data['form']['date'], int(id),)) - return {} + def _update_partners(self, cr, uid, data, context): + to_update = data['form']['to_update'] + for id in to_update.keys(): + cr.execute( + "UPDATE account_move_line "\ + "SET followup_line_id=%d, followup_date=%s "\ + "WHERE id=%d", + (to_update[id], + data['form']['date'], int(id),)) + return {} - def _sendmail(self ,cr, uid, data, context): - self._update_partners(cr, uid, data, context) - mail_notsent = '' - count = 0 - if data['form']['email_conf']: - pool = pooler.get_pool(cr.dbname) - data_user = pool.get('res.users').browse(cr,uid,uid) - line_obj = pool.get('account_followup.stat') - move_lines = line_obj.browse(cr,uid,data['form']['partner_ids'][0][2]) - partners = [] - dict_lines = {} - for line in move_lines: - partners.append(line.name) - dict_lines[line.name.id] =line - for partner in partners: - ids_lines = pool.get('account.move.line').search(cr,uid,[('partner_id','=',partner.id),('reconcile_id','=',False),('account_id.type','in',['receivable','payable'])]) - data_lines = pool.get('account.move.line').browse(cr,uid,ids_lines) - followup_data = dict_lines[partner.id] - dest = False - if partner.address: - for adr in partner.address: - if adr.type=='contact': - if adr.email: - dest = [adr.email] - if (not dest) and adr.type=='default': - if adr.email: - dest = [adr.email] - src = tools.config.options['smtp_user'] - body=data['form']['email_body'] - total_amt = followup_data.debit - followup_data.credit - move_line = '' - subtotal_due = 0.0 - subtotal_paid = 0.0 - subtotal_maturity = 0.0 - balance = 0.0 - l = '--------------------------------------------------------------------------------------------------------------------------' - head = l+ '\n' + 'Date'.rjust(10) + '\t' + 'Description'.rjust(10) + '\t' + 'Ref'.rjust(10) + '\t' + 'Maturity date'.rjust(10) + '\t' + 'Due'.rjust(10) + '\t' + 'Paid'.rjust(10) + '\t' + 'Maturity'.rjust(10) + '\t' + 'Litigation'.rjust(10) + '\n' + l - for i in data_lines: - maturity = '' - if i.date_maturity < time.strftime('%Y-%m-%d') and (i.debit - i.credit): - maturity = i.debit - i.credit - subtotal_due = subtotal_due + i.debit - subtotal_paid = subtotal_paid + i.credit - subtotal_maturity = subtotal_maturity + int(maturity) - balance = balance + (i.debit - i.credit) - move_line = move_line + (i.date).rjust(10) + '\t'+ (i.name).rjust(10) + '\t'+ (i.ref or '').rjust(10) + '\t' + (i.date_maturity or '').rjust(10) + '\t' + str(i.debit).rjust(10) + '\t' + str(i.credit).rjust(10) + '\t' + str(maturity).rjust(10) + '\t' + str(i.blocked).rjust(10) + '\n' - move_line = move_line + l + '\n'+ '\t\t\t' + 'Sub total'.rjust(35) + '\t' + (str(subtotal_due) or '').rjust(10) + '\t' + (str(subtotal_paid) or '').rjust(10) + '\t' + (str(subtotal_maturity) or '').rjust(10)+ '\n' - move_line = move_line + '\t\t\t' + 'Balance'.rjust(33) + '\t' + str(balance).rjust(10) + '\n' + l - val = { - 'partner_name':partner.name, - 'followup_amount':total_amt, - 'user_signature':data_user.name, - 'company_name':data_user.company_id.name, - 'line':move_line, - 'heading': head, - 'date':time.strftime('%Y-%m-%d') - } - body = body%val - sub = str(data['form']['email_subject']) - if dest: - tools.email_send(src,dest,sub,body) - count = count + 1 - else: - mail_notsent = mail_notsent + partner.name + ',' - return {'mail_notsent' : str(mail_notsent) , 'mail_sent' : str(count)} + def _sendmail(self ,cr, uid, data, context): + self._update_partners(cr, uid, data, context) + mail_notsent = '' + count = 0 + if data['form']['email_conf']: + pool = pooler.get_pool(cr.dbname) + data_user = pool.get('res.users').browse(cr,uid,uid) + line_obj = pool.get('account_followup.stat') + move_lines = line_obj.browse(cr,uid,data['form']['partner_ids'][0][2]) + partners = [] + dict_lines = {} + for line in move_lines: + partners.append(line.name) + dict_lines[line.name.id] =line + for partner in partners: + ids_lines = pool.get('account.move.line').search(cr,uid,[('partner_id','=',partner.id),('reconcile_id','=',False),('account_id.type','in',['receivable','payable'])]) + data_lines = pool.get('account.move.line').browse(cr,uid,ids_lines) + followup_data = dict_lines[partner.id] + dest = False + if partner.address: + for adr in partner.address: + if adr.type=='contact': + if adr.email: + dest = [adr.email] + if (not dest) and adr.type=='default': + if adr.email: + dest = [adr.email] + src = tools.config.options['smtp_user'] + body=data['form']['email_body'] + total_amt = followup_data.debit - followup_data.credit + move_line = '' + subtotal_due = 0.0 + subtotal_paid = 0.0 + subtotal_maturity = 0.0 + balance = 0.0 + l = '--------------------------------------------------------------------------------------------------------------------------' + head = l+ '\n' + 'Date'.rjust(10) + '\t' + 'Description'.rjust(10) + '\t' + 'Ref'.rjust(10) + '\t' + 'Maturity date'.rjust(10) + '\t' + 'Due'.rjust(10) + '\t' + 'Paid'.rjust(10) + '\t' + 'Maturity'.rjust(10) + '\t' + 'Litigation'.rjust(10) + '\n' + l + for i in data_lines: + maturity = '' + if i.date_maturity < time.strftime('%Y-%m-%d') and (i.debit - i.credit): + maturity = i.debit - i.credit + subtotal_due = subtotal_due + i.debit + subtotal_paid = subtotal_paid + i.credit + subtotal_maturity = subtotal_maturity + int(maturity) + balance = balance + (i.debit - i.credit) + move_line = move_line + (i.date).rjust(10) + '\t'+ (i.name).rjust(10) + '\t'+ (i.ref or '').rjust(10) + '\t' + (i.date_maturity or '').rjust(10) + '\t' + str(i.debit).rjust(10) + '\t' + str(i.credit).rjust(10) + '\t' + str(maturity).rjust(10) + '\t' + str(i.blocked).rjust(10) + '\n' + move_line = move_line + l + '\n'+ '\t\t\t' + 'Sub total'.rjust(35) + '\t' + (str(subtotal_due) or '').rjust(10) + '\t' + (str(subtotal_paid) or '').rjust(10) + '\t' + (str(subtotal_maturity) or '').rjust(10)+ '\n' + move_line = move_line + '\t\t\t' + 'Balance'.rjust(33) + '\t' + str(balance).rjust(10) + '\n' + l + val = { + 'partner_name':partner.name, + 'followup_amount':total_amt, + 'user_signature':data_user.name, + 'company_name':data_user.company_id.name, + 'line':move_line, + 'heading': head, + 'date':time.strftime('%Y-%m-%d') + } + body = body%val + sub = str(data['form']['email_subject']) + if dest: + tools.email_send(src,dest,sub,body) + count = count + 1 + else: + mail_notsent = mail_notsent + partner.name + ',' + return {'mail_notsent' : str(mail_notsent) , 'mail_sent' : str(count)} - def _get_partners(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - cr.execute( - "SELECT l.partner_id, l.followup_line_id, l.date, l.id "\ - "FROM account_move_line AS l "\ - "LEFT JOIN account_account AS a "\ - "ON (l.account_id=a.id) "\ - "WHERE (l.reconcile_id IS NULL) "\ - "AND (a.type='receivable') "\ - "AND (l.state<>'draft') "\ - "AND (l.reconcile_id is NULL) "\ - "AND partner_id is NOT NULL "\ - "AND a.active "\ - "ORDER BY l.date") - move_lines = cr.fetchall() + def _get_partners(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + cr.execute( + "SELECT l.partner_id, l.followup_line_id, l.date, l.id "\ + "FROM account_move_line AS l "\ + "LEFT JOIN account_account AS a "\ + "ON (l.account_id=a.id) "\ + "WHERE (l.reconcile_id IS NULL) "\ + "AND (a.type='receivable') "\ + "AND (l.state<>'draft') "\ + "AND (l.reconcile_id is NULL) "\ + "AND partner_id is NOT NULL "\ + "AND a.active "\ + "ORDER BY l.date") + move_lines = cr.fetchall() - old = None - fups = {} - fup_ids = pool.get('account_followup.followup').search(cr, uid, []) - if not fup_ids: - raise wizard.except_wizard(_('No Follow up Defined'), - _('You must define at least one follow up for your company !')) - fup_id = fup_ids[0] + old = None + fups = {} + fup_ids = pool.get('account_followup.followup').search(cr, uid, []) + if not fup_ids: + raise wizard.except_wizard(_('No Follow up Defined'), + _('You must define at least one follow up for your company !')) + fup_id = fup_ids[0] - current_date = datetime.date(*time.strptime(data['form']['date'], - '%Y-%m-%d')[:3]) - cr.execute( - "SELECT * "\ - "FROM account_followup_followup_line "\ - "WHERE followup_id=%d "\ - "ORDER BY sequence", (fup_id,)) - for result in cr.dictfetchall(): - delay = datetime.timedelta(days=result['delay']) - fups[old] = (current_date - delay, result['id']) - if result['start'] == 'end_of_month': - fups[old][0].replace(day=1) - old = result['id'] + current_date = datetime.date(*time.strptime(data['form']['date'], + '%Y-%m-%d')[:3]) + cr.execute( + "SELECT * "\ + "FROM account_followup_followup_line "\ + "WHERE followup_id=%d "\ + "ORDER BY sequence", (fup_id,)) + for result in cr.dictfetchall(): + delay = datetime.timedelta(days=result['delay']) + fups[old] = (current_date - delay, result['id']) + if result['start'] == 'end_of_month': + fups[old][0].replace(day=1) + old = result['id'] - fups[old] = (datetime.date(datetime.MAXYEAR, 12, 31), old) + fups[old] = (datetime.date(datetime.MAXYEAR, 12, 31), old) - partner_list = [] - to_update = {} - for partner_id, followup_line_id, date, id in move_lines: - if not partner_id: - continue - if partner_id in partner_list: - to_update[str(id)] = fups[followup_line_id][1] - if followup_line_id not in fups: - continue - if date <= fups[followup_line_id][0].strftime('%Y-%m-%d'): - partner_list.append(partner_id) - to_update[str(id)] = fups[followup_line_id][1] - return {'partner_ids': partner_list, 'to_update': to_update} + partner_list = [] + to_update = {} + for partner_id, followup_line_id, date, id in move_lines: + if not partner_id: + continue + if partner_id in partner_list: + to_update[str(id)] = fups[followup_line_id][1] + if followup_line_id not in fups: + continue + if date <= fups[followup_line_id][0].strftime('%Y-%m-%d'): + partner_list.append(partner_id) + to_update[str(id)] = fups[followup_line_id][1] + return {'partner_ids': partner_list, 'to_update': to_update} - def _get_date(self, cursor, user, data, context): - return {'date': time.strftime('%Y-%m-%d')} + def _get_date(self, cursor, user, data, context): + return {'date': time.strftime('%Y-%m-%d')} - states = { - 'init': { - 'actions': [_get_date], - 'result': {'type': 'form', - 'arch': _followup_wizard_date_form, - 'fields': _followup_wizard_date_fields, - 'state': [ - ('end', 'Cancel'), - ('next', 'Continue'), - ] - }, - }, - 'next': { - 'actions': [_get_partners], - 'result': {'type': 'form', - 'arch': _followup_wizard_all_form, - 'fields': _followup_wizard_all_fields, - 'state': [ - ('end','Cancel'), - ('print','Print Follow Ups'), - ('sendmail','Send Mail') - ] - }, - }, - 'print': { - 'actions': [_update_partners], - 'result': {'type': 'print', - 'report':'account_followup.followup.print', - 'state':'end'}, - }, - 'sendmail': { - 'actions': [_sendmail], - 'result': {'type': 'form', - 'arch': form, - 'fields': fields, - 'state':[('end','Ok')]}, - }, - } + states = { + 'init': { + 'actions': [_get_date], + 'result': {'type': 'form', + 'arch': _followup_wizard_date_form, + 'fields': _followup_wizard_date_fields, + 'state': [ + ('end', 'Cancel'), + ('next', 'Continue'), + ] + }, + }, + 'next': { + 'actions': [_get_partners], + 'result': {'type': 'form', + 'arch': _followup_wizard_all_form, + 'fields': _followup_wizard_all_fields, + 'state': [ + ('end','Cancel'), + ('print','Print Follow Ups'), + ('sendmail','Send Mail') + ] + }, + }, + 'print': { + 'actions': [_update_partners], + 'result': {'type': 'print', + 'report':'account_followup.followup.print', + 'state':'end'}, + }, + 'sendmail': { + 'actions': [_sendmail], + 'result': {'type': 'form', + 'arch': form, + 'fields': fields, + 'state':[('end','Ok')]}, + }, + } followup_all_print('account_followup.followup.print.all') diff --git a/addons/account_payment/__terp__.py b/addons/account_payment/__terp__.py index 5bb2dd3bc2d..c37edb62e6d 100644 --- a/addons/account_payment/__terp__.py +++ b/addons/account_payment/__terp__.py @@ -26,22 +26,22 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name": "Payment Management", - "version": "1.0", - "author": "Tiny", - "category": "Generic Modules/Payment", - "depends": ["account"], - "init_xml": [], - "description": """ - This module provide : - * a more efficient way to manage invoice payment. - * a basic mechanism to easily plug various automated payment. - """, - "demo_xml": [], - "update_xml": ["payment_wizard.xml", "payment_view.xml", - "payment_workflow.xml", "payment_sequence.xml", - "account_invoice_view.xml", "payment_report.xml"], - "active": False, - "installable": True + "name": "Payment Management", + "version": "1.0", + "author": "Tiny", + "category": "Generic Modules/Payment", + "depends": ["account"], + "init_xml": [], + "description": """ + This module provide : + * a more efficient way to manage invoice payment. + * a basic mechanism to easily plug various automated payment. + """, + "demo_xml": [], + "update_xml": ["payment_wizard.xml", "payment_view.xml", + "payment_workflow.xml", "payment_sequence.xml", + "account_invoice_view.xml", "payment_report.xml"], + "active": False, + "installable": True } diff --git a/addons/account_payment/account_invoice.py b/addons/account_payment/account_invoice.py index 6a18d97d7af..224cb0d1d26 100644 --- a/addons/account_payment/account_invoice.py +++ b/addons/account_payment/account_invoice.py @@ -31,28 +31,28 @@ from osv import fields, osv from mx import DateTime class Invoice(osv.osv): - _inherit = 'account.invoice' + _inherit = 'account.invoice' - def _amount_to_pay(self, cursor, user, ids, name, args, context=None): - '''Return the amount still to pay regarding all the payment orders''' - if not ids: - return {} - res = {} - for invoice in self.browse(cursor, user, ids, context=context): - res[invoice.id] = 0.0 - if invoice.move_id: - for line in invoice.move_id.line_id: - if not line.date_maturity or \ - DateTime.strptime(line.date_maturity, '%Y-%m-%d') \ - < DateTime.now(): - res[invoice.id] += line.amount_to_pay - return res + def _amount_to_pay(self, cursor, user, ids, name, args, context=None): + '''Return the amount still to pay regarding all the payment orders''' + if not ids: + return {} + res = {} + for invoice in self.browse(cursor, user, ids, context=context): + res[invoice.id] = 0.0 + if invoice.move_id: + for line in invoice.move_id.line_id: + if not line.date_maturity or \ + DateTime.strptime(line.date_maturity, '%Y-%m-%d') \ + < DateTime.now(): + res[invoice.id] += line.amount_to_pay + return res - _columns = { - 'amount_to_pay': fields.function(_amount_to_pay, method=True, - type='float', string='Amount to be paid', - help='The amount which should be paid at the current date\n' \ - 'minus the amount which is already in payment order'), - } + _columns = { + 'amount_to_pay': fields.function(_amount_to_pay, method=True, + type='float', string='Amount to be paid', + help='The amount which should be paid at the current date\n' \ + 'minus the amount which is already in payment order'), + } Invoice() diff --git a/addons/account_payment/account_move_line.py b/addons/account_payment/account_move_line.py index 22435fa3ae8..2d3c15ec878 100644 --- a/addons/account_payment/account_move_line.py +++ b/addons/account_payment/account_move_line.py @@ -18,12 +18,12 @@ # # 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## @@ -31,88 +31,88 @@ from osv import fields, osv class account_move_line(osv.osv): - _inherit = "account.move.line" + _inherit = "account.move.line" - def amount_to_pay(self, cr, uid, ids, name, arg={}, context={}): - """ Return the amount still to pay regarding all the payemnt orders - (excepting cancelled orders)""" - if not ids: - return {} - cr.execute("""SELECT ml.id, - CASE WHEN ml.amount_currency < 0 - THEN - ml.amount_currency - ELSE ml.credit - END - - (SELECT coalesce(sum(amount_currency),0) - FROM payment_line pl - INNER JOIN payment_order po - ON (pl.order_id = po.id) - WHERE move_line_id = ml.id - AND po.state != 'cancel') as amount - FROM account_move_line ml - WHERE id in (%s)""" % (",".join(map(str, ids)))) - r=dict(cr.fetchall()) - return r + def amount_to_pay(self, cr, uid, ids, name, arg={}, context={}): + """ Return the amount still to pay regarding all the payemnt orders + (excepting cancelled orders)""" + if not ids: + return {} + cr.execute("""SELECT ml.id, + CASE WHEN ml.amount_currency < 0 + THEN - ml.amount_currency + ELSE ml.credit + END - + (SELECT coalesce(sum(amount_currency),0) + FROM payment_line pl + INNER JOIN payment_order po + ON (pl.order_id = po.id) + WHERE move_line_id = ml.id + AND po.state != 'cancel') as amount + FROM account_move_line ml + WHERE id in (%s)""" % (",".join(map(str, ids)))) + r=dict(cr.fetchall()) + return r - def _to_pay_search(self, cr, uid, obj, name, args): - if not len(args): - return [] - line_obj = self.pool.get('account.move.line') - query = line_obj._query_get(cr, uid, context={}) - where = ' and '.join(map(lambda x: '''(SELECT - CASE WHEN l.amount_currency < 0 - THEN - l.amount_currency - ELSE l.credit - END - coalesce(sum(pl.amount_currency), 0) - FROM payment_line pl - INNER JOIN payment_order po ON (pl.order_id = po.id) - WHERE move_line_id = l.id AND po.state != 'cancel')''' \ - + x[1] + str(x[2])+' ',args)) + def _to_pay_search(self, cr, uid, obj, name, args): + if not len(args): + return [] + line_obj = self.pool.get('account.move.line') + query = line_obj._query_get(cr, uid, context={}) + where = ' and '.join(map(lambda x: '''(SELECT + CASE WHEN l.amount_currency < 0 + THEN - l.amount_currency + ELSE l.credit + END - coalesce(sum(pl.amount_currency), 0) + FROM payment_line pl + INNER JOIN payment_order po ON (pl.order_id = po.id) + WHERE move_line_id = l.id AND po.state != 'cancel')''' \ + + x[1] + str(x[2])+' ',args)) - cr.execute(('''select id - from account_move_line l - where account_id in (select id - from account_account - where type=%s and active) - and reconcile_id is null - and credit > 0 - and ''' + where + ' and ' + query), ('payable',) ) + cr.execute(('''select id + from account_move_line l + where account_id in (select id + from account_account + where type=%s and active) + and reconcile_id is null + and credit > 0 + and ''' + where + ' and ' + query), ('payable',) ) - res = cr.fetchall() - if not len(res): - return [('id','=','0')] - return [('id','in',map(lambda x:x[0], res))] + res = cr.fetchall() + if not len(res): + return [('id','=','0')] + return [('id','in',map(lambda x:x[0], res))] - def line2bank(self, cr, uid, ids, payment_type='manual', context=None): - """ - Try to return for each account move line a corresponding bank - account according to the payment type. This work using one of - the bank of the partner defined on the invoice eventually - associated to the line. - Return the first suitable bank for the corresponding partner. - """ - payment_mode_obj = self.pool.get('payment.mode') - line2bank = {} - if not ids: - return {} - bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, - context=context) - for line in self.browse(cr, uid, ids, context=context): - line2bank[line.id] = False - if line.invoice and line.invoice.partner_bank: - line2bank[line.id] = line.invoice.partner_bank.id - elif line.partner_id: - for bank in line.partner_id.bank_ids: - if bank.state in bank_type: - line2bank[line.id] = bank.id - break - if line.id not in line2bank and line.partner_id.bank_ids: - line2bank[line.id] = line.partner_id.bank_ids[0].id - return line2bank + def line2bank(self, cr, uid, ids, payment_type='manual', context=None): + """ + Try to return for each account move line a corresponding bank + account according to the payment type. This work using one of + the bank of the partner defined on the invoice eventually + associated to the line. + Return the first suitable bank for the corresponding partner. + """ + payment_mode_obj = self.pool.get('payment.mode') + line2bank = {} + if not ids: + return {} + bank_type = payment_mode_obj.suitable_bank_types(cr, uid, payment_type, + context=context) + for line in self.browse(cr, uid, ids, context=context): + line2bank[line.id] = False + if line.invoice and line.invoice.partner_bank: + line2bank[line.id] = line.invoice.partner_bank.id + elif line.partner_id: + for bank in line.partner_id.bank_ids: + if bank.state in bank_type: + line2bank[line.id] = bank.id + break + if line.id not in line2bank and line.partner_id.bank_ids: + line2bank[line.id] = line.partner_id.bank_ids[0].id + return line2bank - _columns = { - 'amount_to_pay' : fields.function(amount_to_pay, method=True, - type='float', string='Amount to pay', fnct_search=_to_pay_search), - } + _columns = { + 'amount_to_pay' : fields.function(amount_to_pay, method=True, + type='float', string='Amount to pay', fnct_search=_to_pay_search), + } account_move_line() diff --git a/addons/account_payment/payment.py b/addons/account_payment/payment.py index 5eee19dc779..238abcc1d02 100644 --- a/addons/account_payment/payment.py +++ b/addons/account_payment/payment.py @@ -18,12 +18,12 @@ # # 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 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## @@ -35,471 +35,471 @@ import pooler class payment_type(osv.osv): - _name= 'payment.type' - _description= 'Payment type' - _columns= { - 'name': fields.char('Name', size=64, required=True), - 'code': fields.char('Code', size=64, required=True), - 'suitable_bank_types': fields.many2many('res.partner.bank.type', - 'bank_type_payment_type_rel', - 'pay_type_id','bank_type_id', - 'Suitable bank types') - } + _name= 'payment.type' + _description= 'Payment type' + _columns= { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=64, required=True), + 'suitable_bank_types': fields.many2many('res.partner.bank.type', + 'bank_type_payment_type_rel', + 'pay_type_id','bank_type_id', + 'Suitable bank types') + } payment_type() class payment_mode(osv.osv): - _name= 'payment.mode' - _description= 'Payment mode' - _columns= { - 'name': fields.char('Name', size=64, required=True), - 'bank_id': fields.many2one('res.partner.bank', "Bank account", - required=True), - 'journal': fields.many2one('account.journal', 'Journal', required=True, - domain=[('type', '=', 'cash')]), - 'type': fields.many2one('payment.type','Payment type',required=True), - } - - def suitable_bank_types(self,cr,uid,payment_code= 'manual',context={}): - """Return the codes of the bank type that are suitable - for the given payment type code""" - cr.execute(""" select t.code - from res_partner_bank_type t - join bank_type_payment_type_rel r on (r.bank_type_id = t.id) - join payment_type pt on (r.pay_type_id = pt.id) - where pt.code = %s """, [payment_code]) - return [x[0] for x in cr.fetchall()] + _name= 'payment.mode' + _description= 'Payment mode' + _columns= { + 'name': fields.char('Name', size=64, required=True), + 'bank_id': fields.many2one('res.partner.bank', "Bank account", + required=True), + 'journal': fields.many2one('account.journal', 'Journal', required=True, + domain=[('type', '=', 'cash')]), + 'type': fields.many2one('payment.type','Payment type',required=True), + } + + def suitable_bank_types(self,cr,uid,payment_code= 'manual',context={}): + """Return the codes of the bank type that are suitable + for the given payment type code""" + cr.execute(""" select t.code + from res_partner_bank_type t + join bank_type_payment_type_rel r on (r.bank_type_id = t.id) + join payment_type pt on (r.pay_type_id = pt.id) + where pt.code = %s """, [payment_code]) + return [x[0] for x in cr.fetchall()] payment_mode() class payment_order(osv.osv): - _name = 'payment.order' - _description = 'Payment Order' - _rec_name = 'date' - - def get_wizard(self,type): - logger = netsvc.Logger() - logger.notifyChannel("warning", netsvc.LOG_WARNING, - "No wizard found for the payment type '%s'." % type) - return None - - def _total(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - res = {} - for order in self.browse(cursor, user, ids, context=context): - if order.line_ids: - res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0) - else: - res[order.id] = 0.0 - return res - - _columns = { - 'date_planned': fields.date('Scheduled date if fixed'), - 'reference': fields.char('Reference',size=128,required=1), - 'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1), - 'state': fields.selection([ - ('draft', 'Draft'), - ('open','Confirmed'), - ('cancel','Cancelled'), - ('done','Done')], 'State', select=True), - 'line_ids': fields.one2many('payment.line','order_id','Payment lines',states={'done':[('readonly',True)]}), - 'total': fields.function(_total, string="Total", method=True, - type='float'), - 'user_id': fields.many2one('res.users','User',required=True), - 'date_prefered': fields.selection([ - ('now', 'Directly'), - ('due', 'Due date'), - ('fixed', 'Fixed date') - ], "Prefered date", change_default=True, required=True), - 'date_created': fields.date('Creation date', readonly=True), - 'date_done': fields.date('Execution date', readonly=True), - } - - _defaults = { - 'user_id': lambda self,cr,uid,context: uid, - 'state': lambda *a: 'draft', - 'date_prefered': lambda *a: 'due', - 'date_created': lambda *a: time.strftime('%Y-%m-%d'), - 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'), - } - - def set_to_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'draft'}) - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_create(uid, 'payment.order', id, cr) - return True - - def action_open(self, cr, uid, ids, *args): - for order in self.read(cr,uid,ids,['reference']): - if not order['reference']: - reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order') - self.write(cr,uid,order['id'],{'reference':reference}) - return True - - def set_done(self, cr, uid, id, *args): - self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'), - 'state': 'done',}) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'payment.order', id, 'done', cr) - return True + _name = 'payment.order' + _description = 'Payment Order' + _rec_name = 'date' + + def get_wizard(self,type): + logger = netsvc.Logger() + logger.notifyChannel("warning", netsvc.LOG_WARNING, + "No wizard found for the payment type '%s'." % type) + return None + + def _total(self, cursor, user, ids, name, args, context=None): + if not ids: + return {} + res = {} + for order in self.browse(cursor, user, ids, context=context): + if order.line_ids: + res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0) + else: + res[order.id] = 0.0 + return res + + _columns = { + 'date_planned': fields.date('Scheduled date if fixed'), + 'reference': fields.char('Reference',size=128,required=1), + 'mode': fields.many2one('payment.mode','Payment mode', select=True, required=1), + 'state': fields.selection([ + ('draft', 'Draft'), + ('open','Confirmed'), + ('cancel','Cancelled'), + ('done','Done')], 'State', select=True), + 'line_ids': fields.one2many('payment.line','order_id','Payment lines',states={'done':[('readonly',True)]}), + 'total': fields.function(_total, string="Total", method=True, + type='float'), + 'user_id': fields.many2one('res.users','User',required=True), + 'date_prefered': fields.selection([ + ('now', 'Directly'), + ('due', 'Due date'), + ('fixed', 'Fixed date') + ], "Prefered date", change_default=True, required=True), + 'date_created': fields.date('Creation date', readonly=True), + 'date_done': fields.date('Execution date', readonly=True), + } + + _defaults = { + 'user_id': lambda self,cr,uid,context: uid, + 'state': lambda *a: 'draft', + 'date_prefered': lambda *a: 'due', + 'date_created': lambda *a: time.strftime('%Y-%m-%d'), + 'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'payment.order'), + } + + def set_to_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'draft'}) + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_create(uid, 'payment.order', id, cr) + return True + + def action_open(self, cr, uid, ids, *args): + for order in self.read(cr,uid,ids,['reference']): + if not order['reference']: + reference = self.pool.get('ir.sequence').get(cr, uid, 'payment.order') + self.write(cr,uid,order['id'],{'reference':reference}) + return True + + def set_done(self, cr, uid, id, *args): + self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'), + 'state': 'done',}) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'payment.order', id, 'done', cr) + return True payment_order() class payment_line(osv.osv): - _name = 'payment.line' - _description = 'Payment Line' - - def partner_payable(self, cr, uid, ids, name, args, context={}): - if not ids: return {} - partners= self.read(cr, uid, ids, ['partner_id'], context) - partners= dict(map(lambda x: (x['id'], x['partner_id'][0]), partners)) - debit = self.pool.get('res.partner')._debit_get(cr, uid, - partners.values(), name, args, context) - for i in partners: - partners[i] = debit[partners[i]] - return partners - - def translate(self, orig): - return { -# "to_pay": "credit", - "due_date": "date_maturity", - "reference": "ref"}.get(orig, orig) - - def info_owner(self, cr, uid, ids, name=None, args=None, context=None): - if not ids: return {} - result = {} - info='' - for line in self.browse(cr, uid, ids, context=context): - owner=line.order_id.mode.bank_id.partner_id - result[line.id]=False - if owner.address: - for ads in owner.address: - if ads.type=='default': - st=ads.street and ads.street or '' - st1=ads.street2 and ads.street2 or '' - if 'zip_id' in ads: - zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' - else: - zip=ads.zip and ads.zip or '' - city= ads.city and ads.city or '' - zip_city= zip + ' ' + city - cntry= ads.country_id and ads.country_id.name or '' - info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry - result[line.id]=info - break - return result - - def info_partner(self, cr, uid, ids, name=None, args=None, context=None): - if not ids: return {} - result = {} - info='' - for line in self.browse(cr, uid, ids, context=context): - result[line.id]=False - if not line.partner_id: - break - partner = line.partner_id.name or '' - if line.partner_id.address: - for ads in line.partner_id.address: - if ads.type=='default': - st=ads.street and ads.street or '' - st1=ads.street2 and ads.street2 or '' - if 'zip_id' in ads: - zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' - else: - zip=ads.zip and ads.zip or '' - city= ads.city and ads.city or '' - zip_city= zip + ' ' + city - cntry= ads.country_id and ads.country_id.name or '' - info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry - result[line.id]=info - break - return result - - def select_by_name(self, cr, uid, ids, name, args, context=None): - if not ids: return {} - - partner_obj = self.pool.get('res.partner') - cr.execute("""SELECT pl.id, ml.%s - from account_move_line ml - inner join payment_line pl - on (ml.id = pl.move_line_id) - where pl.id in (%s)"""% - (self.translate(name), ','.join(map(str,ids))) ) - res = dict(cr.fetchall()) - print "res: ", res - print "name ", name - - if name == 'partner_id': - partner_name = {} - for p_id, p_name in partner_obj.name_get(cr,uid, - filter(lambda x:x and x != 0,res.values()),context=context): - partner_name[p_id] = p_name - - for id in ids: - if id in res and partner_name: - res[id] = (res[id],partner_name[res[id]]) - else: - res[id] = (False,False) - else: - for id in ids: - res.setdefault(id, (False, "")) - return res - -# def _currency(self, cursor, user, ids, name, args, context=None): -# if not ids: -# return {} -# res = {} + _name = 'payment.line' + _description = 'Payment Line' + + def partner_payable(self, cr, uid, ids, name, args, context={}): + if not ids: return {} + partners= self.read(cr, uid, ids, ['partner_id'], context) + partners= dict(map(lambda x: (x['id'], x['partner_id'][0]), partners)) + debit = self.pool.get('res.partner')._debit_get(cr, uid, + partners.values(), name, args, context) + for i in partners: + partners[i] = debit[partners[i]] + return partners + + def translate(self, orig): + return { +# "to_pay": "credit", + "due_date": "date_maturity", + "reference": "ref"}.get(orig, orig) + + def info_owner(self, cr, uid, ids, name=None, args=None, context=None): + if not ids: return {} + result = {} + info='' + for line in self.browse(cr, uid, ids, context=context): + owner=line.order_id.mode.bank_id.partner_id + result[line.id]=False + if owner.address: + for ads in owner.address: + if ads.type=='default': + st=ads.street and ads.street or '' + st1=ads.street2 and ads.street2 or '' + if 'zip_id' in ads: + zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' + else: + zip=ads.zip and ads.zip or '' + city= ads.city and ads.city or '' + zip_city= zip + ' ' + city + cntry= ads.country_id and ads.country_id.name or '' + info=owner.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry + result[line.id]=info + break + return result + + def info_partner(self, cr, uid, ids, name=None, args=None, context=None): + if not ids: return {} + result = {} + info='' + for line in self.browse(cr, uid, ids, context=context): + result[line.id]=False + if not line.partner_id: + break + partner = line.partner_id.name or '' + if line.partner_id.address: + for ads in line.partner_id.address: + if ads.type=='default': + st=ads.street and ads.street or '' + st1=ads.street2 and ads.street2 or '' + if 'zip_id' in ads: + zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' + else: + zip=ads.zip and ads.zip or '' + city= ads.city and ads.city or '' + zip_city= zip + ' ' + city + cntry= ads.country_id and ads.country_id.name or '' + info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry + result[line.id]=info + break + return result + + def select_by_name(self, cr, uid, ids, name, args, context=None): + if not ids: return {} + + partner_obj = self.pool.get('res.partner') + cr.execute("""SELECT pl.id, ml.%s + from account_move_line ml + inner join payment_line pl + on (ml.id = pl.move_line_id) + where pl.id in (%s)"""% + (self.translate(name), ','.join(map(str,ids))) ) + res = dict(cr.fetchall()) + print "res: ", res + print "name ", name + + if name == 'partner_id': + partner_name = {} + for p_id, p_name in partner_obj.name_get(cr,uid, + filter(lambda x:x and x != 0,res.values()),context=context): + partner_name[p_id] = p_name + + for id in ids: + if id in res and partner_name: + res[id] = (res[id],partner_name[res[id]]) + else: + res[id] = (False,False) + else: + for id in ids: + res.setdefault(id, (False, "")) + return res + +# def _currency(self, cursor, user, ids, name, args, context=None): +# if not ids: +# return {} +# res = {} # -# currency_obj = self.pool.get('res.currency') -# account_obj = self.pool.get('account.account') -# cursor.execute('''SELECT pl.id, ml.currency_id, ml.account_id -# FROM account_move_line ml -# INNER JOIN payment_line pl -# ON (ml.id = pl.move_line_id) -# WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')') +# currency_obj = self.pool.get('res.currency') +# account_obj = self.pool.get('account.account') +# cursor.execute('''SELECT pl.id, ml.currency_id, ml.account_id +# FROM account_move_line ml +# INNER JOIN payment_line pl +# ON (ml.id = pl.move_line_id) +# WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')') # -# res2 = {} -# account_ids = [] -# for payment_line_id, currency_id, account_id in cursor.fetchall(): -# res2[payment_line_id] = [currency_id, account_id] -# account_ids.append(account_id) +# res2 = {} +# account_ids = [] +# for payment_line_id, currency_id, account_id in cursor.fetchall(): +# res2[payment_line_id] = [currency_id, account_id] +# account_ids.append(account_id) # -# account2currency_id = {} -# for account in account_obj.browse(cursor, user, account_ids, -# context=context): -# account2currency_id[account.id] = account.company_currency_id.id +# account2currency_id = {} +# for account in account_obj.browse(cursor, user, account_ids, +# context=context): +# account2currency_id[account.id] = account.company_currency_id.id # -# for payment_line_id in ids: -# if res2[payment_line_id][0]: -# res[payment_line_id] = res2[payment_line_id][0] -# else: -# res[payment_line_id] = \ -# account2currency_id[res2[payment_line_id][1]] +# for payment_line_id in ids: +# if res2[payment_line_id][0]: +# res[payment_line_id] = res2[payment_line_id][0] +# else: +# res[payment_line_id] = \ +# account2currency_id[res2[payment_line_id][1]] # -# currency_names = {} -# for currency_id, name in currency_obj.name_get(cursor, user, res.values(), -# context=context): -# currency_names[currency_id] = name -# for payment_line_id in ids: -# res[payment_line_id] = (res[payment_line_id], -# currency_names[res[payment_line_id]]) -# return res +# currency_names = {} +# for currency_id, name in currency_obj.name_get(cursor, user, res.values(), +# context=context): +# currency_names[currency_id] = name +# for payment_line_id in ids: +# res[payment_line_id] = (res[payment_line_id], +# currency_names[res[payment_line_id]]) +# return res # -# def _to_pay_currency(self, cursor, user, ids, name , args, context=None): -# if not ids: -# return {} +# def _to_pay_currency(self, cursor, user, ids, name , args, context=None): +# if not ids: +# return {} # -# cursor.execute('''SELECT pl.id, -# CASE WHEN ml.amount_currency < 0 -# THEN - ml.amount_currency -# ELSE ml.credit -# END -# FROM account_move_line ml -# INNER JOIN payment_line pl -# ON (ml.id = pl.move_line_id) -# WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')') -# return dict(cursor.fetchall()) - - def _amount(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - currency_obj = self.pool.get('res.currency') - if context is None: - context = {} - res = {} - for line in self.browse(cursor, user, ids, context=context): - ctx = context.copy() - ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d') - res[line.id] = currency_obj.compute(cursor, user, line.currency.id, - line.company_currency.id, - line.amount_currency, context=ctx) - return res - - def _value_date(self, cursor, user, ids, name, args, context=None): - if not ids: - return {} - res = {} - for line in self.browse(cursor, user, ids, context=context): - if line.order_id.date_prefered == 'fixed': - res[line.id] = line.order_id.date_planned - elif line.order_id.date_prefered == 'due': - res[line.id] = line.due_date or time.strftime('%Y-%m-%d') - else: - res[line.id] = time.strftime('%Y-%m-%d') - return res - - def _get_currency(self, cr, uid, context): - user = self.pool.get('res.users').browse(cr, uid, uid) - if user.company_id: - return user.company_id.currency_id.id - else: - return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] - -# def select_move_lines(*a): -# print a -# return [] - -# def create(self, cr, uid, vals, context): -# print "created!!!" -# vals['company_currency'] = self._get_currency(cr, uid, context) -# return super(payment_line, self).create(cr, uid, vals, context) - - def _get_ml_inv_ref(self, cr, uid, ids, *a): - res={} - for id in self.browse(cr, uid, ids): - res[id.id] = "" - print "test" - if id.move_line_id: - print "blablabl" - res[id.id] = "pas de invoice" - if id.move_line_id.invoice: - res[id.id] = str(id.move_line_id.invoice.number) - if id.move_line_id.invoice.name: - res[id.id] += " " + id.move_line_id.invoice.name - return res - - def _get_ml_maturity_date(self, cr, uid, ids, *a): - res={} - for id in self.browse(cr, uid, ids): - if id.move_line_id: - res[id.id] = id.move_line_id.date_maturity - else: - res[id.id] = "" - return res - - def _get_ml_created_date(self, cr, uid, ids, *a): - res={} - for id in self.browse(cr, uid, ids): - if id.move_line_id: - res[id.id] = id.move_line_id.date_created - else: - res[id.id] = "" - return res - - _columns = { - 'name': fields.char('Your Reference', size=64, required=True), - 'communication': fields.char('Communication', size=64, required=True), - 'communication2': fields.char('Communication 2', size=64), - 'move_line_id': fields.many2one('account.move.line','Entry line', domain=[('reconcile_id','=', False), ('account_id.type', '=','payable')]), - 'amount_currency': fields.float('Amount', digits=(16,2), - required=True, help='Payment amount in the partner currency'), -# 'to_pay_currency': fields.function(_to_pay_currency, string='To Pay', -# method=True, type='float', -# help='Amount to pay in the partner currency'), -# 'currency': fields.function(_currency, string='Currency', -# method=True, type='many2one', obj='res.currency'), - 'currency': fields.many2one('res.currency','Currency',required=True), - 'company_currency': fields.many2one('res.currency','Currency',readonly=True), - 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'), - 'order_id': fields.many2one('payment.order', 'Order', required=True, - ondelete='cascade', select=True), - 'partner_id': fields.many2one('res.partner', string="Partner",required=True), - 'amount': fields.function(_amount, string='Amount', - method=True, type='float', - help='Payment amount in the company currency'), -# 'to_pay': fields.function(select_by_name, string="To Pay", method=True, -# type='float', help='Amount to pay in the company currency'), -# 'due_date': fields.function(select_by_name, string="Due date", -# method=True, type='date'), - 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date", - method=True, type='date',help="Invoice Effective Date"), -# 'reference': fields.function(select_by_name, string="Ref", method=True, -# type='char'), - 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='char', string='Maturity Date'), - 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='char', string='Invoice Ref'), - 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text"), - 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text"), - 'partner_payable': fields.function(partner_payable, - string="Partner payable", method=True, type='float'), -# 'value_date': fields.function(_value_date, string='Value Date', -# method=True, type='date'), - 'date': fields.date('Payment Date'), - 'create_date': fields.datetime('Created' ,readonly=True), - 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True) - } - _defaults = { - 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence' - ).get(cursor, user, 'payment.line'), - 'state': lambda *args: 'normal', - 'currency': _get_currency, - 'company_currency': _get_currency, - } - _sql_constraints = [ - ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'), - ] - - def onchange_move_line(self,cr,uid,ids,move_line_id,payment_type,context=None): - data={} - data['amount_currency']=data['currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=False - if move_line_id: - line=self.pool.get('account.move.line').browse(cr,uid,move_line_id) - data['amount_currency']=line.amount_to_pay - data['partner_id']=line.partner_id.id - data['currency']=line.currency_id and line.currency_id.id or False - if not data['currency']: - data['currency']=line.invoice and line.invoice.currency_id.id or False - # calling onchange of partner and updating data dictionary - temp_dict=self.onchange_partner(cr,uid,ids,line.partner_id.id) - data.update(temp_dict['value']) - - data['reference']=line.ref - data['date_created']=line.date_created - data['communication']=line.ref - - if payment_type: - payment_mode = self.pool.get('payment.mode').browse(cr,uid,payment_type).type.code - else: - payment_mode=False - -# data['bank_id']=self.pool.get('account.move.line').line2bank(cr, uid, -# [move_line_id], -# payment_mode or 'manual', context)[move_line_id] - - return {'value': data} - - def onchange_partner(self,cr,uid,ids,partner_id,context=None): - data={} - data['info_partner']=data['bank_id']=False - - if partner_id: - part_obj=self.pool.get('res.partner').browse(cr,uid,partner_id) - partner=part_obj.name or '' - - if part_obj.address: - for ads in part_obj.address: - if ads.type=='default': - st=ads.street and ads.street or '' - st1=ads.street2 and ads.street2 or '' - - if 'zip_id' in ads: - zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' - else: - zip=ads.zip and ads.zip or '' - city= ads.city and ads.city or '' - zip_city= zip + ' ' + city - - cntry= ads.country_id and ads.country_id.name or '' - info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry - - data['info_partner']=info - - if part_obj.bank_ids and len(part_obj.bank_ids)==1: - data['bank_id']=self.pool.get('res.partner.bank').name_get(cr,uid,[part_obj.bank_ids[0].id])[0][0] - - return {'value': data} - - def fields_get(self, cr, uid, fields=None, context=None): - res = super(payment_line, self).fields_get(cr, uid, fields, context) - if 'communication2' in res: - res['communication2'].setdefault('states', {}) - res['communication2']['states']['structured'] = [('readonly', True)] - res['communication2']['states']['normal'] = [('readonly', False)] - - return res +# cursor.execute('''SELECT pl.id, +# CASE WHEN ml.amount_currency < 0 +# THEN - ml.amount_currency +# ELSE ml.credit +# END +# FROM account_move_line ml +# INNER JOIN payment_line pl +# ON (ml.id = pl.move_line_id) +# WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')') +# return dict(cursor.fetchall()) + + def _amount(self, cursor, user, ids, name, args, context=None): + if not ids: + return {} + currency_obj = self.pool.get('res.currency') + if context is None: + context = {} + res = {} + for line in self.browse(cursor, user, ids, context=context): + ctx = context.copy() + ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d') + res[line.id] = currency_obj.compute(cursor, user, line.currency.id, + line.company_currency.id, + line.amount_currency, context=ctx) + return res + + def _value_date(self, cursor, user, ids, name, args, context=None): + if not ids: + return {} + res = {} + for line in self.browse(cursor, user, ids, context=context): + if line.order_id.date_prefered == 'fixed': + res[line.id] = line.order_id.date_planned + elif line.order_id.date_prefered == 'due': + res[line.id] = line.due_date or time.strftime('%Y-%m-%d') + else: + res[line.id] = time.strftime('%Y-%m-%d') + return res + + def _get_currency(self, cr, uid, context): + user = self.pool.get('res.users').browse(cr, uid, uid) + if user.company_id: + return user.company_id.currency_id.id + else: + return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] + +# def select_move_lines(*a): +# print a +# return [] + +# def create(self, cr, uid, vals, context): +# print "created!!!" +# vals['company_currency'] = self._get_currency(cr, uid, context) +# return super(payment_line, self).create(cr, uid, vals, context) + + def _get_ml_inv_ref(self, cr, uid, ids, *a): + res={} + for id in self.browse(cr, uid, ids): + res[id.id] = "" + print "test" + if id.move_line_id: + print "blablabl" + res[id.id] = "pas de invoice" + if id.move_line_id.invoice: + res[id.id] = str(id.move_line_id.invoice.number) + if id.move_line_id.invoice.name: + res[id.id] += " " + id.move_line_id.invoice.name + return res + + def _get_ml_maturity_date(self, cr, uid, ids, *a): + res={} + for id in self.browse(cr, uid, ids): + if id.move_line_id: + res[id.id] = id.move_line_id.date_maturity + else: + res[id.id] = "" + return res + + def _get_ml_created_date(self, cr, uid, ids, *a): + res={} + for id in self.browse(cr, uid, ids): + if id.move_line_id: + res[id.id] = id.move_line_id.date_created + else: + res[id.id] = "" + return res + + _columns = { + 'name': fields.char('Your Reference', size=64, required=True), + 'communication': fields.char('Communication', size=64, required=True), + 'communication2': fields.char('Communication 2', size=64), + 'move_line_id': fields.many2one('account.move.line','Entry line', domain=[('reconcile_id','=', False), ('account_id.type', '=','payable')]), + 'amount_currency': fields.float('Amount', digits=(16,2), + required=True, help='Payment amount in the partner currency'), +# 'to_pay_currency': fields.function(_to_pay_currency, string='To Pay', +# method=True, type='float', +# help='Amount to pay in the partner currency'), +# 'currency': fields.function(_currency, string='Currency', +# method=True, type='many2one', obj='res.currency'), + 'currency': fields.many2one('res.currency','Currency',required=True), + 'company_currency': fields.many2one('res.currency','Currency',readonly=True), + 'bank_id': fields.many2one('res.partner.bank', 'Destination Bank account'), + 'order_id': fields.many2one('payment.order', 'Order', required=True, + ondelete='cascade', select=True), + 'partner_id': fields.many2one('res.partner', string="Partner",required=True), + 'amount': fields.function(_amount, string='Amount', + method=True, type='float', + help='Payment amount in the company currency'), +# 'to_pay': fields.function(select_by_name, string="To Pay", method=True, +# type='float', help='Amount to pay in the company currency'), +# 'due_date': fields.function(select_by_name, string="Due date", +# method=True, type='date'), + 'ml_date_created': fields.function(_get_ml_created_date, string="Effective Date", + method=True, type='date',help="Invoice Effective Date"), +# 'reference': fields.function(select_by_name, string="Ref", method=True, +# type='char'), + 'ml_maturity_date': fields.function(_get_ml_maturity_date, method=True, type='char', string='Maturity Date'), + 'ml_inv_ref': fields.function(_get_ml_inv_ref, method=True, type='char', string='Invoice Ref'), + 'info_owner': fields.function(info_owner, string="Owner Account", method=True, type="text"), + 'info_partner': fields.function(info_partner, string="Destination Account", method=True, type="text"), + 'partner_payable': fields.function(partner_payable, + string="Partner payable", method=True, type='float'), +# 'value_date': fields.function(_value_date, string='Value Date', +# method=True, type='date'), + 'date': fields.date('Payment Date'), + 'create_date': fields.datetime('Created' ,readonly=True), + 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True) + } + _defaults = { + 'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence' + ).get(cursor, user, 'payment.line'), + 'state': lambda *args: 'normal', + 'currency': _get_currency, + 'company_currency': _get_currency, + } + _sql_constraints = [ + ('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'), + ] + + def onchange_move_line(self,cr,uid,ids,move_line_id,payment_type,context=None): + data={} + data['amount_currency']=data['currency']=data['communication']=data['partner_id']=data['reference']=data['date_created']=data['bank_id']=False + if move_line_id: + line=self.pool.get('account.move.line').browse(cr,uid,move_line_id) + data['amount_currency']=line.amount_to_pay + data['partner_id']=line.partner_id.id + data['currency']=line.currency_id and line.currency_id.id or False + if not data['currency']: + data['currency']=line.invoice and line.invoice.currency_id.id or False + # calling onchange of partner and updating data dictionary + temp_dict=self.onchange_partner(cr,uid,ids,line.partner_id.id) + data.update(temp_dict['value']) + + data['reference']=line.ref + data['date_created']=line.date_created + data['communication']=line.ref + + if payment_type: + payment_mode = self.pool.get('payment.mode').browse(cr,uid,payment_type).type.code + else: + payment_mode=False + +# data['bank_id']=self.pool.get('account.move.line').line2bank(cr, uid, +# [move_line_id], +# payment_mode or 'manual', context)[move_line_id] + + return {'value': data} + + def onchange_partner(self,cr,uid,ids,partner_id,context=None): + data={} + data['info_partner']=data['bank_id']=False + + if partner_id: + part_obj=self.pool.get('res.partner').browse(cr,uid,partner_id) + partner=part_obj.name or '' + + if part_obj.address: + for ads in part_obj.address: + if ads.type=='default': + st=ads.street and ads.street or '' + st1=ads.street2 and ads.street2 or '' + + if 'zip_id' in ads: + zip_city= ads.zip_id and self.pool.get('res.partner.zip').name_get(cr,uid,[ads.zip_id.id])[0][1] or '' + else: + zip=ads.zip and ads.zip or '' + city= ads.city and ads.city or '' + zip_city= zip + ' ' + city + + cntry= ads.country_id and ads.country_id.name or '' + info=partner + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry + + data['info_partner']=info + + if part_obj.bank_ids and len(part_obj.bank_ids)==1: + data['bank_id']=self.pool.get('res.partner.bank').name_get(cr,uid,[part_obj.bank_ids[0].id])[0][0] + + return {'value': data} + + def fields_get(self, cr, uid, fields=None, context=None): + res = super(payment_line, self).fields_get(cr, uid, fields, context) + if 'communication2' in res: + res['communication2'].setdefault('states', {}) + res['communication2']['states']['structured'] = [('readonly', True)] + res['communication2']['states']['normal'] = [('readonly', False)] + + return res payment_line() diff --git a/addons/account_payment/wizard/wizard_pay.py b/addons/account_payment/wizard/wizard_pay.py index 7aac31b1535..c0281ae0375 100644 --- a/addons/account_payment/wizard/wizard_pay.py +++ b/addons/account_payment/wizard/wizard_pay.py @@ -35,39 +35,39 @@ import time def _launch_wizard(self, cr, uid, data, context): - """ - Search for a wizard to launch according to the type. - If type is manual. just confirm the order. - """ + """ + Search for a wizard to launch according to the type. + If type is manual. just confirm the order. + """ - order_ref= pooler.get_pool(cr.dbname).get('payment.order') - order= order_ref.browse(cr,uid,data['id'],context) - t= order.mode and order.mode.type.code or 'manual' - if t == 'manual' : - order_ref.set_done(cr,uid,data['id'],context) - return {} + order_ref= pooler.get_pool(cr.dbname).get('payment.order') + order= order_ref.browse(cr,uid,data['id'],context) + t= order.mode and order.mode.type.code or 'manual' + if t == 'manual' : + order_ref.set_done(cr,uid,data['id'],context) + return {} - gw= order_ref.get_wizard(t) - if not gw: - order_ref.set_done(cr,uid,data['id'],context) - return {} + gw= order_ref.get_wizard(t) + if not gw: + order_ref.set_done(cr,uid,data['id'],context) + return {} - mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') - act_obj = pooler.get_pool(cr.dbname).get('ir.actions.wizard') - module, wizard= gw - result = mod_obj._get_id(cr, uid, module, wizard) - id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] - result = act_obj.read(cr, uid, [id])[0] - #result['context'] = str({'fiscalyear': data['form']['fiscalyear']}) - return result + mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') + act_obj = pooler.get_pool(cr.dbname).get('ir.actions.wizard') + module, wizard= gw + result = mod_obj._get_id(cr, uid, module, wizard) + id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id'] + result = act_obj.read(cr, uid, [id])[0] + #result['context'] = str({'fiscalyear': data['form']['fiscalyear']}) + return result class wizard_pay(wizard.interface): - states= {'init' : {'actions': [], - 'result':{'type':'action', - 'action':_launch_wizard, - 'state':'end'} - } - } + states= {'init' : {'actions': [], + 'result':{'type':'action', + 'action':_launch_wizard, + 'state':'end'} + } + } wizard_pay('pay_payment') diff --git a/addons/account_payment/wizard/wizard_payment_order.py b/addons/account_payment/wizard/wizard_payment_order.py index 276bf41f2c4..89046303a73 100644 --- a/addons/account_payment/wizard/wizard_payment_order.py +++ b/addons/account_payment/wizard/wizard_payment_order.py @@ -35,12 +35,12 @@ import time FORM = UpdateableStr() FIELDS = { - 'entries': {'string':'Entries', 'type':'many2many', - 'relation': 'account.move.line',}, + 'entries': {'string':'Entries', 'type':'many2many', + 'relation': 'account.move.line',}, } field_duedate={ - 'duedate': {'string':'Due Date', 'type':'date','required':True, 'default': lambda *a: time.strftime('%Y-%m-%d'),}, - } + 'duedate': {'string':'Due Date', 'type':'date','required':True, 'default': lambda *a: time.strftime('%Y-%m-%d'),}, + } arch_duedate='''<?xml version="1.0"?> <form string="Search Payment lines"> <field name="duedate" /> @@ -48,103 +48,103 @@ arch_duedate='''<?xml version="1.0"?> def search_entries(self, cr, uid, data, context): - search_due_date=data['form']['duedate'] - - pool = pooler.get_pool(cr.dbname) - order_obj = pool.get('payment.order') - line_obj = pool.get('account.move.line') - - payment = order_obj.browse(cr, uid, data['id'], - context=context) - ctx = '' - if payment.mode: - ctx = '''context="{'journal_id': %d}"''' % payment.mode.journal.id - - # Search for move line to pay: - line_ids = line_obj.search(cr, uid, [ - ('reconcile_id', '=', False), - ('amount_to_pay', '>', 0),('date_maturity','<=',search_due_date)], context=context) - - - FORM.string = '''<?xml version="1.0"?> + search_due_date=data['form']['duedate'] + + pool = pooler.get_pool(cr.dbname) + order_obj = pool.get('payment.order') + line_obj = pool.get('account.move.line') + + payment = order_obj.browse(cr, uid, data['id'], + context=context) + ctx = '' + if payment.mode: + ctx = '''context="{'journal_id': %d}"''' % payment.mode.journal.id + + # Search for move line to pay: + line_ids = line_obj.search(cr, uid, [ + ('reconcile_id', '=', False), + ('amount_to_pay', '>', 0),('date_maturity','<=',search_due_date)], context=context) + + + FORM.string = '''<?xml version="1.0"?> <form string="Populate Payment:"> - <field name="entries" colspan="4" height="300" width="800" nolabel="1" - domain="[('id', 'in', [%s])]" %s/> + <field name="entries" colspan="4" height="300" width="800" nolabel="1" + domain="[('id', 'in', [%s])]" %s/> </form>''' % (','.join([str(x) for x in line_ids]), ctx) - return {'entries': line_ids} + return {'entries': line_ids} def create_payment(self, cr, uid, data, context): - line_ids= data['form']['entries'][0][2] - if not line_ids: return {} - - pool= pooler.get_pool(cr.dbname) - order_obj = pool.get('payment.order') - line_obj = pool.get('account.move.line') - - payment = order_obj.browse(cr, uid, data['id'], - context=context) - t = payment.mode and payment.mode.type.code or 'manual' - line2bank= pool.get('account.move.line').line2bank(cr, uid, - line_ids, t, context) - - ## Finally populate the current payment with new lines: - for line in line_obj.browse(cr, uid, line_ids, context=context): - pool.get('payment.line').create(cr,uid,{ - 'move_line_id': line.id, - 'amount_currency': line.amount_to_pay, - 'bank_id': line2bank.get(line.id), - 'order_id': payment.id, - 'partner_id': line.partner_id and line.partner_id.id or False, - 'communication': line.ref or '/' - }, context=context) - return {} + line_ids= data['form']['entries'][0][2] + if not line_ids: return {} + + pool= pooler.get_pool(cr.dbname) + order_obj = pool.get('payment.order') + line_obj = pool.get('account.move.line') + + payment = order_obj.browse(cr, uid, data['id'], + context=context) + t = payment.mode and payment.mode.type.code or 'manual' + line2bank= pool.get('account.move.line').line2bank(cr, uid, + line_ids, t, context) + + ## Finally populate the current payment with new lines: + for line in line_obj.browse(cr, uid, line_ids, context=context): + pool.get('payment.line').create(cr,uid,{ + 'move_line_id': line.id, + 'amount_currency': line.amount_to_pay, + 'bank_id': line2bank.get(line.id), + 'order_id': payment.id, + 'partner_id': line.partner_id and line.partner_id.id or False, + 'communication': line.ref or '/' + }, context=context) + return {} class wizard_payment_order(wizard.interface): - """ - Create a payment object with lines corresponding to the account move line - to pay according to the date and the mode provided by the user. - Hypothesis: - - Small number of non-reconcilied move line , payment mode and bank account type, - - Big number of partner and bank account. - - If a type is given, unsuitable account move lines are ignored. - """ - states = { - - 'init': { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': arch_duedate, - 'fields':field_duedate, - 'state': [ - ('end','_Cancel'), - ('search','_Search', '', True) - ] - }, - }, - - 'search': { - 'actions': [search_entries], - 'result': { - 'type': 'form', - 'arch': FORM, - 'fields': FIELDS, - 'state': [ - ('end','_Cancel'), - ('create','_Add to payment order', '', True) - ] - }, - }, - 'create': { - 'actions': [], - 'result': { - 'type': 'action', - 'action': create_payment, - 'state': 'end'} - }, - } + """ + Create a payment object with lines corresponding to the account move line + to pay according to the date and the mode provided by the user. + Hypothesis: + - Small number of non-reconcilied move line , payment mode and bank account type, + - Big number of partner and bank account. + + If a type is given, unsuitable account move lines are ignored. + """ + states = { + + 'init': { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': arch_duedate, + 'fields':field_duedate, + 'state': [ + ('end','_Cancel'), + ('search','_Search', '', True) + ] + }, + }, + + 'search': { + 'actions': [search_entries], + 'result': { + 'type': 'form', + 'arch': FORM, + 'fields': FIELDS, + 'state': [ + ('end','_Cancel'), + ('create','_Add to payment order', '', True) + ] + }, + }, + 'create': { + 'actions': [], + 'result': { + 'type': 'action', + 'action': create_payment, + 'state': 'end'} + }, + } wizard_payment_order('populate_payment') diff --git a/addons/account_payment/wizard/wizard_populate_statement.py b/addons/account_payment/wizard/wizard_populate_statement.py index 9c771b86dd4..407564734ca 100644 --- a/addons/account_payment/wizard/wizard_populate_statement.py +++ b/addons/account_payment/wizard/wizard_populate_statement.py @@ -33,91 +33,91 @@ from tools.misc import UpdateableStr FORM = UpdateableStr() FIELDS = { - 'lines': {'string': 'Payment Lines', 'type': 'many2many', - 'relation': 'payment.line'}, + 'lines': {'string': 'Payment Lines', 'type': 'many2many', + 'relation': 'payment.line'}, } def _search_entries(obj, cursor, user, data, context): - pool = pooler.get_pool(cursor.dbname) - line_obj = pool.get('payment.line') - statement_obj = pool.get('account.bank.statement') + pool = pooler.get_pool(cursor.dbname) + line_obj = pool.get('payment.line') + statement_obj = pool.get('account.bank.statement') - statement = statement_obj.browse(cursor, user, data['id'], context=context) + statement = statement_obj.browse(cursor, user, data['id'], context=context) - line_ids = line_obj.search(cursor, user, [ - ('move_line_id.reconcile_id', '=', False), - ('order_id.mode.journal.id', '=', statement.journal_id.id)]) - line_ids.extend(line_obj.search(cursor, user, [ - ('move_line_id.reconcile_id', '=', False), - ('order_id.mode', '=', False)])) + line_ids = line_obj.search(cursor, user, [ + ('move_line_id.reconcile_id', '=', False), + ('order_id.mode.journal.id', '=', statement.journal_id.id)]) + line_ids.extend(line_obj.search(cursor, user, [ + ('move_line_id.reconcile_id', '=', False), + ('order_id.mode', '=', False)])) - FORM.string = '''<?xml version="1.0"?> + FORM.string = '''<?xml version="1.0"?> <form string="Populate Statement:"> - <field name="lines" colspan="4" height="300" width="800" nolabel="1" - domain="[('id', 'in', [%s])]"/> + <field name="lines" colspan="4" height="300" width="800" nolabel="1" + domain="[('id', 'in', [%s])]"/> </form>''' % (','.join([str(x) for x in line_ids])) - return {} + return {} def _populate_statement(obj, cursor, user, data, context): - line_ids = data['form']['lines'][0][2] - if not line_ids: - return {} + line_ids = data['form']['lines'][0][2] + if not line_ids: + return {} - pool = pooler.get_pool(cursor.dbname) - line_obj = pool.get('payment.line') - statement_obj = pool.get('account.bank.statement') - statement_line_obj = pool.get('account.bank.statement.line') - currency_obj = pool.get('res.currency') - statement_reconcile_obj = pool.get('account.bank.statement.reconcile') + pool = pooler.get_pool(cursor.dbname) + line_obj = pool.get('payment.line') + statement_obj = pool.get('account.bank.statement') + statement_line_obj = pool.get('account.bank.statement.line') + currency_obj = pool.get('res.currency') + statement_reconcile_obj = pool.get('account.bank.statement.reconcile') - statement = statement_obj.browse(cursor, user, data['id'], context=context) + statement = statement_obj.browse(cursor, user, data['id'], context=context) - for line in line_obj.browse(cursor, user, line_ids, context=context): - ctx = context.copy() - ctx['date'] = line.value_date - amount = currency_obj.compute(cursor, user, line.currency.id, - statement.currency.id, line.amount_currency, context=ctx) - reconcile_id = statement_reconcile_obj.create(cursor, user, { - 'line_ids': [(6, 0, [line.move_line_id.id])] - }, context=context) - statement_line_obj.create(cursor, user, { - 'name': line.order_id.reference or '?', - 'amount': - amount, - 'type': 'supplier', - 'partner_id': line.partner_id.id, - 'account_id': line.move_line_id.account_id.id, - 'statement_id': statement.id, - 'ref': line.reference, - 'reconcile_id': reconcile_id, - }, context=context) - return {} + for line in line_obj.browse(cursor, user, line_ids, context=context): + ctx = context.copy() + ctx['date'] = line.value_date + amount = currency_obj.compute(cursor, user, line.currency.id, + statement.currency.id, line.amount_currency, context=ctx) + reconcile_id = statement_reconcile_obj.create(cursor, user, { + 'line_ids': [(6, 0, [line.move_line_id.id])] + }, context=context) + statement_line_obj.create(cursor, user, { + 'name': line.order_id.reference or '?', + 'amount': - amount, + 'type': 'supplier', + 'partner_id': line.partner_id.id, + 'account_id': line.move_line_id.account_id.id, + 'statement_id': statement.id, + 'ref': line.reference, + 'reconcile_id': reconcile_id, + }, context=context) + return {} class PopulateStatement(wizard.interface): - """ - Populate the current statement with selected payement lines - """ - states = { - 'init': { - 'actions': [_search_entries], - 'result': { - 'type': 'form', - 'arch': FORM, - 'fields': FIELDS, - 'state': [ - ('end', '_Cancel'), - ('add', '_Add', '', True) - ] - }, - }, - 'add': { - 'actions': [], - 'result': { - 'type': 'action', - 'action': _populate_statement, - 'state': 'end' - }, - }, - } + """ + Populate the current statement with selected payement lines + """ + states = { + 'init': { + 'actions': [_search_entries], + 'result': { + 'type': 'form', + 'arch': FORM, + 'fields': FIELDS, + 'state': [ + ('end', '_Cancel'), + ('add', '_Add', '', True) + ] + }, + }, + 'add': { + 'actions': [], + 'result': { + 'type': 'action', + 'action': _populate_statement, + 'state': 'end' + }, + }, + } PopulateStatement('populate_statement') diff --git a/addons/account_report/__terp__.py b/addons/account_report/__terp__.py index 667dd27446b..532751e1679 100644 --- a/addons/account_report/__terp__.py +++ b/addons/account_report/__terp__.py @@ -26,19 +26,19 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Reporting for accounting", - "version" : "1.0", - "depends" : ["account"], - "author" : "Tiny", - "description": """Financial and accounting reporting""", - "website" : "http://tinyerp.com/module_account.html", - "category" : "Generic Modules/Accounting", - "init_xml" : [ ], - "demo_xml" : [ ], - "update_xml" : [ "account_view.xml","account_report.xml" ], -# "translations" : { -# "fr": "i18n/french_fr.csv" -# }, - "active": False, - "installable": True + "name" : "Reporting for accounting", + "version" : "1.0", + "depends" : ["account"], + "author" : "Tiny", + "description": """Financial and accounting reporting""", + "website" : "http://tinyerp.com/module_account.html", + "category" : "Generic Modules/Accounting", + "init_xml" : [ ], + "demo_xml" : [ ], + "update_xml" : [ "account_view.xml","account_report.xml" ], +# "translations" : { +# "fr": "i18n/french_fr.csv" +# }, + "active": False, + "installable": True } diff --git a/addons/account_report/account.py b/addons/account_report/account.py index 8fc790f2525..d38f69b4d17 100644 --- a/addons/account_report/account.py +++ b/addons/account_report/account.py @@ -38,141 +38,141 @@ from mx.DateTime import RelativeDateTime, now, DateTime, localtime class account_report(osv.osv): - _name = "account.report.report" - _description = "Account reporting" - _color = [ - ('', ''), - ('green','Green'), - ('red','Red'), - ('pink','Pink'), - ('blue','Blue'), - ('yellow','Yellow'), - ('cyan','Cyan'), - ('lightblue','Light Blue'), - ('orange','Orange'), - ] - _style = [ - ('1','Header 1'), - ('2','Header 2'), - ('3','Header 3'), - ('4','Header 4'), - ('5','Normal'), - ('6', 'Small'), - ] + _name = "account.report.report" + _description = "Account reporting" + _color = [ + ('', ''), + ('green','Green'), + ('red','Red'), + ('pink','Pink'), + ('blue','Blue'), + ('yellow','Yellow'), + ('cyan','Cyan'), + ('lightblue','Light Blue'), + ('orange','Orange'), + ] + _style = [ + ('1','Header 1'), + ('2','Header 2'), + ('3','Header 3'), + ('4','Header 4'), + ('5','Normal'), + ('6', 'Small'), + ] - def _amount_get(self, cr, uid, ids, field_name, arg, context={}): - def _calc_credit(*code): - acc = self.pool.get('account.account') - acc_id = acc.search(cr, uid, [('code','in',code)]) - return reduce(lambda y,x=0: x.credit+y, acc.browse(cr, uid, acc_id, context),0) - def _calc_debit(*code): - acc = self.pool.get('account.account') - acc_id = acc.search(cr, uid, [('code','in',code)]) - return reduce(lambda y,x=0: x.debit+y, acc.browse(cr, uid, acc_id, context),0) - def _calc_balance(*code): - acc = self.pool.get('account.account') - acc_id = acc.search(cr, uid, [('code','in',code)]) - return reduce(lambda y,x=0: x.balance+y, acc.browse(cr, uid, acc_id, context),0) - def _calc_report(*code): - acc = self.pool.get('account.report.report') - acc_id = acc.search(cr, uid, [('code','in',code)]) - return reduce(lambda y,x=0: x.amount+y, acc.browse(cr, uid, acc_id, context),0) - result = {} - for rep in self.browse(cr, uid, ids, context): - objdict = { - 'debit': _calc_debit, - 'credit': _calc_credit, - 'balance': _calc_balance, - 'report': _calc_report, - } - if field_name=='status': - fld_name = 'expression_status' - else: - fld_name = 'expression' - try: - val = eval(getattr(rep, fld_name), objdict) - except: - val = 0.0 - if field_name=='status': - if val<-1: - result[rep.id] = 'very bad' - elif val<0: - result[rep.id] = 'bad' - elif val==0: - result[rep.id] = 'normal' - elif val<1: - result[rep.id] = 'good' - else: - result[rep.id] = 'excellent' - else: - result[rep.id] = val - return result + def _amount_get(self, cr, uid, ids, field_name, arg, context={}): + def _calc_credit(*code): + acc = self.pool.get('account.account') + acc_id = acc.search(cr, uid, [('code','in',code)]) + return reduce(lambda y,x=0: x.credit+y, acc.browse(cr, uid, acc_id, context),0) + def _calc_debit(*code): + acc = self.pool.get('account.account') + acc_id = acc.search(cr, uid, [('code','in',code)]) + return reduce(lambda y,x=0: x.debit+y, acc.browse(cr, uid, acc_id, context),0) + def _calc_balance(*code): + acc = self.pool.get('account.account') + acc_id = acc.search(cr, uid, [('code','in',code)]) + return reduce(lambda y,x=0: x.balance+y, acc.browse(cr, uid, acc_id, context),0) + def _calc_report(*code): + acc = self.pool.get('account.report.report') + acc_id = acc.search(cr, uid, [('code','in',code)]) + return reduce(lambda y,x=0: x.amount+y, acc.browse(cr, uid, acc_id, context),0) + result = {} + for rep in self.browse(cr, uid, ids, context): + objdict = { + 'debit': _calc_debit, + 'credit': _calc_credit, + 'balance': _calc_balance, + 'report': _calc_report, + } + if field_name=='status': + fld_name = 'expression_status' + else: + fld_name = 'expression' + try: + val = eval(getattr(rep, fld_name), objdict) + except: + val = 0.0 + if field_name=='status': + if val<-1: + result[rep.id] = 'very bad' + elif val<0: + result[rep.id] = 'bad' + elif val==0: + result[rep.id] = 'normal' + elif val<1: + result[rep.id] = 'good' + else: + result[rep.id] = 'excellent' + else: + result[rep.id] = val + return result - def onchange_parent_id(self, cr, uid, ids, parent_id): - v={} - if parent_id: - acc=self.pool.get('account.report.report').browse(cr,uid,parent_id) - v['type']=acc.type - if int(acc.style) < 6: - v['style'] = str(int(acc.style)+1) - return {'value': v} + def onchange_parent_id(self, cr, uid, ids, parent_id): + v={} + if parent_id: + acc=self.pool.get('account.report.report').browse(cr,uid,parent_id) + v['type']=acc.type + if int(acc.style) < 6: + v['style'] = str(int(acc.style)+1) + return {'value': v} - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'active': fields.boolean('Active'), - 'sequence': fields.integer('Sequence'), - 'code': fields.char('Code', size=64, required=True), - 'type': fields.selection([ - ('fiscal', 'Fiscal statement'), - ('indicator','Indicator'), - ('other','Others')], - 'Type', required=True), - 'expression': fields.char('Expression', size=240, required=True), - 'expression_status': fields.char('Status expression', size=240, required=True), - 'parent_id': fields.many2one('account.report.report', 'Parent'), - 'child_ids': fields.one2many('account.report.report', 'parent_id', 'Childs'), - 'note': fields.text('Note'), - 'amount': fields.function(_amount_get, method=True, string='Value'), - 'status': fields.function(_amount_get, - method=True, - type="selection", - selection=[ - ('very bad', 'Very Bad'), - ('bad', 'Bad'), - ('normal', ''), - ('good', 'Good'), - ('excellent', 'Excellent') - ], - string='Status'), - 'style': fields.selection(_style, 'Style', required=True), - 'color_font' : fields.selection(_color, 'Font Color', help="Font Color for the report"), - 'color_back' : fields.selection(_color, 'Back Color') - } - _defaults = { - 'style': lambda *args: '5', - 'active': lambda *args: True, - 'type': lambda *args: 'indicator', - } + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'active': fields.boolean('Active'), + 'sequence': fields.integer('Sequence'), + 'code': fields.char('Code', size=64, required=True), + 'type': fields.selection([ + ('fiscal', 'Fiscal statement'), + ('indicator','Indicator'), + ('other','Others')], + 'Type', required=True), + 'expression': fields.char('Expression', size=240, required=True), + 'expression_status': fields.char('Status expression', size=240, required=True), + 'parent_id': fields.many2one('account.report.report', 'Parent'), + 'child_ids': fields.one2many('account.report.report', 'parent_id', 'Childs'), + 'note': fields.text('Note'), + 'amount': fields.function(_amount_get, method=True, string='Value'), + 'status': fields.function(_amount_get, + method=True, + type="selection", + selection=[ + ('very bad', 'Very Bad'), + ('bad', 'Bad'), + ('normal', ''), + ('good', 'Good'), + ('excellent', 'Excellent') + ], + string='Status'), + 'style': fields.selection(_style, 'Style', required=True), + 'color_font' : fields.selection(_color, 'Font Color', help="Font Color for the report"), + 'color_back' : fields.selection(_color, 'Back Color') + } + _defaults = { + 'style': lambda *args: '5', + 'active': lambda *args: True, + 'type': lambda *args: 'indicator', + } - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = [] - if name: - ids = self.search(cr, user, [('code','=',name)]+ args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) - else: - ids = self.search(cr, user, args, limit=limit, context=context) - return self.name_get(cr, user, ids, context=context) + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = [] + if name: + ids = self.search(cr, user, [('code','=',name)]+ args, limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) + else: + ids = self.search(cr, user, args, limit=limit, context=context) + return self.name_get(cr, user, ids, context=context) - _constraints = [ - #TODO Put an expression to valid expression and expression_status - ] - _sql_constraints = [ - ('code_uniq', 'unique (code)', 'The code of the report entry must be unique !') - ] + _constraints = [ + #TODO Put an expression to valid expression and expression_status + ] + _sql_constraints = [ + ('code_uniq', 'unique (code)', 'The code of the report entry must be unique !') + ] account_report() diff --git a/addons/account_report/report/__init__.py b/addons/account_report/report/__init__.py index b0dce84aa4a..b0dce84aa4a 100755..100644 --- a/addons/account_report/report/__init__.py +++ b/addons/account_report/report/__init__.py diff --git a/addons/account_report/report/accounting_report.py b/addons/account_report/report/accounting_report.py index b3b35c7897c..de49000af71 100755..100644 --- a/addons/account_report/report/accounting_report.py +++ b/addons/account_report/report/accounting_report.py @@ -34,33 +34,33 @@ from report import report_sxw class accounting_report(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(accounting_report, self).__init__(cr, uid, name, context) - self.ret_list = [] - self.localcontext.update({ - 'time': time, - 'childs':self.process - }) + def __init__(self, cr, uid, name, context): + super(accounting_report, self).__init__(cr, uid, name, context) + self.ret_list = [] + self.localcontext.update({ + 'time': time, + 'childs':self.process + }) - def process(self,id,level=0): - res = pooler.get_pool(self.cr.dbname).get('account.report.report').read(self.cr,self.uid,[id]) - ret_dict={ - 'name':res[0]['name'], - 'code':res[0]['code'], - 'amount':res[0]['amount'], - 'note':res[0]['note'], - 'level': level, - 'color_font':res[0]['color_font'], - 'color_back':res[0]['color_back'], - } + def process(self,id,level=0): + res = pooler.get_pool(self.cr.dbname).get('account.report.report').read(self.cr,self.uid,[id]) + ret_dict={ + 'name':res[0]['name'], + 'code':res[0]['code'], + 'amount':res[0]['amount'], + 'note':res[0]['note'], + 'level': level, + 'color_font':res[0]['color_font'], + 'color_back':res[0]['color_back'], + } - self.ret_list.append(ret_dict) - for child_id in res[0]['child_ids']: - self.process(child_id,level+1) - return self.ret_list + self.ret_list.append(ret_dict) + for child_id in res[0]['child_ids']: + self.process(child_id,level+1) + return self.ret_list report_sxw.report_sxw('report.accounting.report', 'account.report.report', - 'addons/account_report/report/accounting_report.rml', - parser=accounting_report, header=False) + 'addons/account_report/report/accounting_report.rml', + parser=accounting_report, header=False) diff --git a/addons/account_tax_include/__terp__.py b/addons/account_tax_include/__terp__.py index bf031f25462..cce548629f4 100644 --- a/addons/account_tax_include/__terp__.py +++ b/addons/account_tax_include/__terp__.py @@ -26,20 +26,20 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Invoices and prices with taxes included", - "version" : "1.0", - "depends" : ["account"], - "author" : "Tiny", - "description": """Allow the user to work tax included prices. + "name" : "Invoices and prices with taxes included", + "version" : "1.0", + "depends" : ["account"], + "author" : "Tiny", + "description": """Allow the user to work tax included prices. Especially useful for b2c businesses. - + This module implement the modification on the invoice form. """, - "website" : "http://tinyerp.com/module_account.html", - "category" : "Generic Modules/Accounting", - "init_xml" : [ ], - "demo_xml" : [ ], - "update_xml" : [ 'invoice_tax_incl.xml' ], - "active": False, - "installable": True + "website" : "http://tinyerp.com/module_account.html", + "category" : "Generic Modules/Accounting", + "init_xml" : [ ], + "demo_xml" : [ ], + "update_xml" : [ 'invoice_tax_incl.xml' ], + "active": False, + "installable": True } diff --git a/addons/account_tax_include/invoice_tax_incl.py b/addons/account_tax_include/invoice_tax_incl.py index 2eb39f099ea..8dcb6194252 100644 --- a/addons/account_tax_include/invoice_tax_incl.py +++ b/addons/account_tax_include/invoice_tax_incl.py @@ -34,183 +34,183 @@ from osv import fields, osv import ir class account_invoice(osv.osv): - def _amount_untaxed(self, cr, uid, ids, name, args, context={}): - res = {} - for invoice in self.browse(cr,uid,ids): - if invoice.price_type == 'tax_included': - res[invoice.id] = reduce( lambda x, y: x+y.price_subtotal, invoice.invoice_line,0) - else: - res[invoice.id] = super(account_invoice, self)._amount_untaxed(cr, uid, [invoice.id], name, args, context)[invoice.id] - return res - - def _amount_tax(self, cr, uid, ids, name, args, context={}): - res = {} - for invoice in self.browse(cr,uid,ids): - if invoice.price_type == 'tax_included': - res[invoice.id] = reduce( lambda x, y: x+y.amount, invoice.tax_line,0) - else: - res[invoice.id] = super(account_invoice, self)._amount_tax(cr, uid, [invoice.id], name, args, context)[invoice.id] - return res - - def _amount_total(self, cr, uid, ids, name, args, context={}): - res = {} - for invoice in self.browse(cr,uid,ids): - if invoice.price_type == 'tax_included': - res[invoice.id]= invoice.amount_untaxed + invoice.amount_tax - else: - res[invoice.id] = super(account_invoice, self)._amount_total(cr, uid, [invoice.id], name, args, context)[invoice.id] - return res - - _inherit = "account.invoice" - _columns = { - 'price_type': fields.selection([('tax_included','Tax included'), - ('tax_excluded','Tax excluded')], - 'Price method', required=True, readonly=True, - states={'draft':[('readonly',False)]}), - 'amount_untaxed': fields.function(_amount_untaxed, digits=(16,2), method=True,string='Untaxed Amount'), - 'amount_tax': fields.function(_amount_tax, method=True, string='Tax', store=True), - 'amount_total': fields.function(_amount_total, method=True, string='Total', store=True), - } - _defaults = { - 'price_type': lambda *a: 'tax_excluded', - } + def _amount_untaxed(self, cr, uid, ids, name, args, context={}): + res = {} + for invoice in self.browse(cr,uid,ids): + if invoice.price_type == 'tax_included': + res[invoice.id] = reduce( lambda x, y: x+y.price_subtotal, invoice.invoice_line,0) + else: + res[invoice.id] = super(account_invoice, self)._amount_untaxed(cr, uid, [invoice.id], name, args, context)[invoice.id] + return res + + def _amount_tax(self, cr, uid, ids, name, args, context={}): + res = {} + for invoice in self.browse(cr,uid,ids): + if invoice.price_type == 'tax_included': + res[invoice.id] = reduce( lambda x, y: x+y.amount, invoice.tax_line,0) + else: + res[invoice.id] = super(account_invoice, self)._amount_tax(cr, uid, [invoice.id], name, args, context)[invoice.id] + return res + + def _amount_total(self, cr, uid, ids, name, args, context={}): + res = {} + for invoice in self.browse(cr,uid,ids): + if invoice.price_type == 'tax_included': + res[invoice.id]= invoice.amount_untaxed + invoice.amount_tax + else: + res[invoice.id] = super(account_invoice, self)._amount_total(cr, uid, [invoice.id], name, args, context)[invoice.id] + return res + + _inherit = "account.invoice" + _columns = { + 'price_type': fields.selection([('tax_included','Tax included'), + ('tax_excluded','Tax excluded')], + 'Price method', required=True, readonly=True, + states={'draft':[('readonly',False)]}), + 'amount_untaxed': fields.function(_amount_untaxed, digits=(16,2), method=True,string='Untaxed Amount'), + 'amount_tax': fields.function(_amount_tax, method=True, string='Tax', store=True), + 'amount_total': fields.function(_amount_total, method=True, string='Total', store=True), + } + _defaults = { + 'price_type': lambda *a: 'tax_excluded', + } account_invoice() class account_invoice_line(osv.osv): - _inherit = "account.invoice.line" - def _amount_line(self, cr, uid, ids, name, args, context={}): - """ - Return the subtotal excluding taxes with respect to price_type. - """ - res = {} - tax_obj = self.pool.get('account.tax') - res = super(account_invoice_line, self)._amount_line(cr, uid, ids, name, args, context) - res2 = res.copy() - for line in self.browse(cr, uid, ids): - if not line.quantity: - res[line.id] = 0.0 - continue - if line.invoice_id and line.invoice_id.price_type == 'tax_included': - product_taxes = None - if line.product_id: - if line.invoice_id.type in ('out_invoice', 'out_refund'): - product_taxes = line.product_id.taxes_id - else: - product_taxes = line.product_id.supplier_taxes_id - if product_taxes: - for tax in tax_obj.compute_inv(cr, uid, product_taxes, res[line.id]/line.quantity, line.quantity): - res[line.id] = res[line.id] - round(tax['amount'], 2) - else: - for tax in tax_obj.compute_inv(cr, uid,line.invoice_line_tax_id, res[line.id]/line.quantity, line.quantity): - res[line.id] = res[line.id] - round(tax['amount'], 2) - if name == 'price_subtotal_incl' and line.invoice_id and line.invoice_id.price_type == 'tax_included': - prod_taxe_ids = None - line_taxe_ids = None - if product_taxes: - prod_taxe_ids = [ t.id for t in product_taxes ] - prod_taxe_ids.sort() - line_taxe_ids = [ t.id for t in line.invoice_line_tax_id ] - line_taxe_ids.sort() - if product_taxes and prod_taxe_ids == line_taxe_ids: - res[line.id] = res2[line.id] - elif not line.product_id: - res[line.id] = res2[line.id] - else: - for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, res[line.id]/line.quantity, line.quantity): - res[line.id] = res[line.id] + tax['amount'] - res[line.id]= round(res[line.id], 2) - return res - - def _price_unit_default(self, cr, uid, context={}): - if 'check_total' in context: - t = context['check_total'] - if context.get('price_type', False) == 'tax_included': - for l in context.get('invoice_line', {}): - if len(l) >= 3 and l[2]: - p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0) - t = t - (p * l[2].get('quantity')) - return t - return super(account_invoice_line, self)._price_unit_default(cr, uid, context) - return 0 - - _columns = { - 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o tax', store=True), - 'price_subtotal_incl': fields.function(_amount_line, method=True, string='Subtotal'), - } - - _defaults = { - 'price_unit': _price_unit_default, - } - - def move_line_get_item(self, cr, uid, line, context={}): - return { - 'type':'src', - 'name':line.name, - 'price_unit':line.price_subtotal / line.quantity, - 'quantity':line.quantity, - 'price':line.price_subtotal, - 'account_id':line.account_id.id, - 'product_id': line.product_id.id, - 'uos_id':line.uos_id.id, - 'account_analytic_id':line.account_analytic_id.id, - } - - def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}): - if context.get('price_type', False) == 'tax_included': - return {'price_unit': price_unit,'invoice_line_tax_id': tax_id} - else: - return super(account_invoice_line, self).product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context) - - def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, price_type='tax_excluded', context={}): - context.update({'price_type': price_type}) - return super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, price_unit, address_invoice_id, context=context) + _inherit = "account.invoice.line" + def _amount_line(self, cr, uid, ids, name, args, context={}): + """ + Return the subtotal excluding taxes with respect to price_type. + """ + res = {} + tax_obj = self.pool.get('account.tax') + res = super(account_invoice_line, self)._amount_line(cr, uid, ids, name, args, context) + res2 = res.copy() + for line in self.browse(cr, uid, ids): + if not line.quantity: + res[line.id] = 0.0 + continue + if line.invoice_id and line.invoice_id.price_type == 'tax_included': + product_taxes = None + if line.product_id: + if line.invoice_id.type in ('out_invoice', 'out_refund'): + product_taxes = line.product_id.taxes_id + else: + product_taxes = line.product_id.supplier_taxes_id + if product_taxes: + for tax in tax_obj.compute_inv(cr, uid, product_taxes, res[line.id]/line.quantity, line.quantity): + res[line.id] = res[line.id] - round(tax['amount'], 2) + else: + for tax in tax_obj.compute_inv(cr, uid,line.invoice_line_tax_id, res[line.id]/line.quantity, line.quantity): + res[line.id] = res[line.id] - round(tax['amount'], 2) + if name == 'price_subtotal_incl' and line.invoice_id and line.invoice_id.price_type == 'tax_included': + prod_taxe_ids = None + line_taxe_ids = None + if product_taxes: + prod_taxe_ids = [ t.id for t in product_taxes ] + prod_taxe_ids.sort() + line_taxe_ids = [ t.id for t in line.invoice_line_tax_id ] + line_taxe_ids.sort() + if product_taxes and prod_taxe_ids == line_taxe_ids: + res[line.id] = res2[line.id] + elif not line.product_id: + res[line.id] = res2[line.id] + else: + for tax in tax_obj.compute(cr, uid, line.invoice_line_tax_id, res[line.id]/line.quantity, line.quantity): + res[line.id] = res[line.id] + tax['amount'] + res[line.id]= round(res[line.id], 2) + return res + + def _price_unit_default(self, cr, uid, context={}): + if 'check_total' in context: + t = context['check_total'] + if context.get('price_type', False) == 'tax_included': + for l in context.get('invoice_line', {}): + if len(l) >= 3 and l[2]: + p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0) + t = t - (p * l[2].get('quantity')) + return t + return super(account_invoice_line, self)._price_unit_default(cr, uid, context) + return 0 + + _columns = { + 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal w/o tax', store=True), + 'price_subtotal_incl': fields.function(_amount_line, method=True, string='Subtotal'), + } + + _defaults = { + 'price_unit': _price_unit_default, + } + + def move_line_get_item(self, cr, uid, line, context={}): + return { + 'type':'src', + 'name':line.name, + 'price_unit':line.price_subtotal / line.quantity, + 'quantity':line.quantity, + 'price':line.price_subtotal, + 'account_id':line.account_id.id, + 'product_id': line.product_id.id, + 'uos_id':line.uos_id.id, + 'account_analytic_id':line.account_analytic_id.id, + } + + def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context={}): + if context.get('price_type', False) == 'tax_included': + return {'price_unit': price_unit,'invoice_line_tax_id': tax_id} + else: + return super(account_invoice_line, self).product_id_change_unit_price_inv(cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=context) + + def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, price_unit=False, address_invoice_id=False, price_type='tax_excluded', context={}): + context.update({'price_type': price_type}) + return super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, price_unit, address_invoice_id, context=context) account_invoice_line() class account_invoice_tax(osv.osv): - _inherit = "account.invoice.tax" - - def compute(self, cr, uid, invoice_id): - tax_grouped = {} - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) - cur = inv.currency_id - - if inv.price_type=='tax_excluded': - return super(account_invoice_tax,self).compute(cr, uid, invoice_id) - - for line in inv.invoice_line: - for tax in tax_obj.compute_inv(cr, uid, line.invoice_line_tax_id, (line.price_unit * (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id): - val={} - val['invoice_id'] = inv.id - val['name'] = tax['name'] - val['amount'] = cur_obj.round(cr, uid, cur, tax['amount']) - val['manual'] = False - val['sequence'] = tax['sequence'] - val['base'] = tax['price_unit'] * line['quantity'] - - if inv.type in ('out_invoice','in_invoice'): - val['base_code_id'] = tax['base_code_id'] - val['tax_code_id'] = tax['tax_code_id'] - val['base_amount'] = val['base'] * tax['base_sign'] - val['tax_amount'] = val['amount'] * tax['tax_sign'] - val['account_id'] = tax['account_collected_id'] or line.account_id.id - else: - val['base_code_id'] = tax['ref_base_code_id'] - val['tax_code_id'] = tax['ref_tax_code_id'] - val['base_amount'] = val['base'] * tax['ref_base_sign'] - val['tax_amount'] = val['amount'] * tax['ref_tax_sign'] - val['account_id'] = tax['account_paid_id'] or line.account_id.id - - key = (val['tax_code_id'], val['base_code_id'], val['account_id']) - if not key in tax_grouped: - tax_grouped[key] = val - else: - tax_grouped[key]['amount'] += val['amount'] - tax_grouped[key]['base'] += val['base'] - tax_grouped[key]['base_amount'] += val['base_amount'] - tax_grouped[key]['tax_amount'] += val['tax_amount'] - - return tax_grouped + _inherit = "account.invoice.tax" + + def compute(self, cr, uid, invoice_id): + tax_grouped = {} + tax_obj = self.pool.get('account.tax') + cur_obj = self.pool.get('res.currency') + inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id) + cur = inv.currency_id + + if inv.price_type=='tax_excluded': + return super(account_invoice_tax,self).compute(cr, uid, invoice_id) + + for line in inv.invoice_line: + for tax in tax_obj.compute_inv(cr, uid, line.invoice_line_tax_id, (line.price_unit * (1-(line.discount or 0.0)/100.0)), line.quantity, inv.address_invoice_id.id, line.product_id, inv.partner_id): + val={} + val['invoice_id'] = inv.id + val['name'] = tax['name'] + val['amount'] = cur_obj.round(cr, uid, cur, tax['amount']) + val['manual'] = False + val['sequence'] = tax['sequence'] + val['base'] = tax['price_unit'] * line['quantity'] + + if inv.type in ('out_invoice','in_invoice'): + val['base_code_id'] = tax['base_code_id'] + val['tax_code_id'] = tax['tax_code_id'] + val['base_amount'] = val['base'] * tax['base_sign'] + val['tax_amount'] = val['amount'] * tax['tax_sign'] + val['account_id'] = tax['account_collected_id'] or line.account_id.id + else: + val['base_code_id'] = tax['ref_base_code_id'] + val['tax_code_id'] = tax['ref_tax_code_id'] + val['base_amount'] = val['base'] * tax['ref_base_sign'] + val['tax_amount'] = val['amount'] * tax['ref_tax_sign'] + val['account_id'] = tax['account_paid_id'] or line.account_id.id + + key = (val['tax_code_id'], val['base_code_id'], val['account_id']) + if not key in tax_grouped: + tax_grouped[key] = val + else: + tax_grouped[key]['amount'] += val['amount'] + tax_grouped[key]['base'] += val['base'] + tax_grouped[key]['base_amount'] += val['base_amount'] + tax_grouped[key]['tax_amount'] += val['tax_amount'] + + return tax_grouped account_invoice_tax() diff --git a/addons/base_iban/__terp__.py b/addons/base_iban/__terp__.py index 2399f02cd11..99b6e8f3237 100644 --- a/addons/base_iban/__terp__.py +++ b/addons/base_iban/__terp__.py @@ -26,17 +26,17 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "IBAN", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Base", - "depends" : ["base"], - "description": """ + "name" : "IBAN", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Base", + "depends" : ["base"], + "description": """ This module install the base for IBAN bank accounts. - """, - "update_xml" : ["base_iban_view.xml"], - "init_xml" : ["base_iban_data.xml"], - "active": False, - "installable": True + """, + "update_xml" : ["base_iban_view.xml"], + "init_xml" : ["base_iban_data.xml"], + "active": False, + "installable": True } diff --git a/addons/base_iban/base_iban.py b/addons/base_iban/base_iban.py index 36587dfac24..8dc6db53238 100644 --- a/addons/base_iban/base_iban.py +++ b/addons/base_iban/base_iban.py @@ -31,21 +31,21 @@ import netsvc from osv import fields, osv class res_partner_bank(osv.osv): - _inherit = "res.partner.bank" - _columns = { - 'iban': fields.char('IBAN', size=34, readonly=True, help="International Bank Account Number"), - } + _inherit = "res.partner.bank" + _columns = { + 'iban': fields.char('IBAN', size=34, readonly=True, help="International Bank Account Number"), + } - def name_get(self, cr, uid, ids, context=None): - res = [] - to_check_ids = [] - for id in self.browse(cr, uid, ids): - if id.state=='iban': - res.append((id.id,id.iban)) - else: - to_check_ids.append(id.id) - res += super(res_partner_bank, self).name_get(cr, uid, to_check_ids, context) - return res + def name_get(self, cr, uid, ids, context=None): + res = [] + to_check_ids = [] + for id in self.browse(cr, uid, ids): + if id.state=='iban': + res.append((id.id,id.iban)) + else: + to_check_ids.append(id.id) + res += super(res_partner_bank, self).name_get(cr, uid, to_check_ids, context) + return res res_partner_bank() diff --git a/addons/base_module_publish/__terp__.py b/addons/base_module_publish/__terp__.py index 7e6e59da7d9..e3eb87ccac7 100644 --- a/addons/base_module_publish/__terp__.py +++ b/addons/base_module_publish/__terp__.py @@ -26,12 +26,12 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Module publisher", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "category" : "Generic Modules/Base", - "description": """ + "name" : "Module publisher", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "category" : "Generic Modules/Base", + "description": """ This module can be used by developpers to automatically publish their modules in a few click to the following websites: * http://TinyERP.com, section module @@ -41,10 +41,10 @@ in a few click to the following websites: It adds a button "Publish module" on each module, so that you simply have to call this button when you want to release a new version of your module. - """, - "depends" : ["base"], - "init_xml" : [ ], - "demo_xml" : [ ], - "update_xml" : [ "base_module_publish_wizard.xml" ], - "installable": True + """, + "depends" : ["base"], + "init_xml" : [ ], + "demo_xml" : [ ], + "update_xml" : [ "base_module_publish_wizard.xml" ], + "installable": True } diff --git a/addons/base_module_publish/wizard/base_module_publish.py b/addons/base_module_publish/wizard/base_module_publish.py index fd774f79d47..ccb4e30cad3 100644 --- a/addons/base_module_publish/wizard/base_module_publish.py +++ b/addons/base_module_publish/wizard/base_module_publish.py @@ -39,13 +39,13 @@ import tools intro_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="Publication information" colspan="4"/> - <field name="text" colspan="4" nolabel="1" width="300" height="200"/> + <separator string="Publication information" colspan="4"/> + <field name="text" colspan="4" nolabel="1" width="300" height="200"/> </form>''' intro_fields = { - 'text': {'string':'Introduction', 'type':'text', 'readonly':True, - 'default': lambda *args: """ + 'text': {'string':'Introduction', 'type':'text', 'readonly':True, + 'default': lambda *args: """ This system will automatically publish the selected module to the Tiny ERP official website. You can use it to quickly publish a new module or update an existing one (new version). @@ -59,298 +59,298 @@ Thank you for contributing! } def _get_selection(self, cr, uid, datas, *args): - a = urllib.urlopen('http://www.openerp.com/mtree_interface.php') - contents = a.read() - content = filter(None, contents.split('\n')) - result = map(lambda x:x.split('='), content) + a = urllib.urlopen('http://www.openerp.com/mtree_interface.php') + contents = a.read() + content = filter(None, contents.split('\n')) + result = map(lambda x:x.split('='), content) - return result + return result check_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="Verify your module information" colspan="4"/> - <notebook> - <page string="General"> - <field name="name"/> - <field name="version"/> - <field name="author"/> - <field name="website" widget="url"/> - <field name="shortdesc"/> - <field name="operation"/> - <field name="category" colspan="4"/> - <newline/> - <field name="license"/> - <field name="include_src"/> - <newline/> - <field name="url_download" colspan="4" widget="url"/> - <field name="docurl" colspan="4" widget="url"/> - <field name="demourl" colspan="4" widget="url"/> - <field name="image"/> - </page> - <page string="Description"> - <field name="description" colspan="4" nolabel="1"/> - </page> - </notebook> + <separator string="Verify your module information" colspan="4"/> + <notebook> + <page string="General"> + <field name="name"/> + <field name="version"/> + <field name="author"/> + <field name="website" widget="url"/> + <field name="shortdesc"/> + <field name="operation"/> + <field name="category" colspan="4"/> + <newline/> + <field name="license"/> + <field name="include_src"/> + <newline/> + <field name="url_download" colspan="4" widget="url"/> + <field name="docurl" colspan="4" widget="url"/> + <field name="demourl" colspan="4" widget="url"/> + <field name="image"/> + </page> + <page string="Description"> + <field name="description" colspan="4" nolabel="1"/> + </page> + </notebook> </form>''' check_fields = { - 'name': {'string':'Name', 'type':'char', 'size':64, 'readonly':True}, - 'shortdesc': {'string':'Small description', 'type':'char', 'size':200, - 'readonly':True}, - 'author': {'string':'Author', 'type':'char', 'size':128, 'readonly':True}, - 'website': {'string':'Website', 'type':'char', 'size':200, 'readonly':True}, - 'url': {'string':'Download URL', 'type':'char', 'size':200, 'readonly':True}, - 'image': {'string':'Image file', 'type':'image', 'help': 'support only .png files'}, - 'description': {'string':'Description', 'type':'text', 'readonly':True}, - 'version': {'string':'Version', 'type':'char', 'readonly':True}, - 'demourl': {'string':'Demo URL', 'type':'char', 'size':128, - 'help': 'empty to keep existing value'}, - 'docurl': {'string':'Documentation URL', 'type':'char', 'size':128, - 'help': 'Empty to keep existing value'}, - 'category': {'string':'Category', 'type':'selection', 'size':64, 'required':True, - 'selection': _get_selection}, - 'license': { - 'string':'Licence', 'type':'selection', 'size':64, 'required':True, - 'selection': [('GPL-2', 'GPL-2'), ('Other proprietary','Other proprietary')], - 'default': lambda *args: 'GPL-2' - }, - 'include_src': {'string': 'Include source', 'type': 'boolean', - 'default': lambda *a: True}, - 'operation': { - 'string':'Operation', - 'type':'selection', - 'readonly':True, - 'selection':[('0','Creation'),('1','Modification')], - }, - 'url_download': {'string':'Download URL', 'type':'char', 'size':128, - 'help': 'Keep empty for an auto upload of the module'}, + 'name': {'string':'Name', 'type':'char', 'size':64, 'readonly':True}, + 'shortdesc': {'string':'Small description', 'type':'char', 'size':200, + 'readonly':True}, + 'author': {'string':'Author', 'type':'char', 'size':128, 'readonly':True}, + 'website': {'string':'Website', 'type':'char', 'size':200, 'readonly':True}, + 'url': {'string':'Download URL', 'type':'char', 'size':200, 'readonly':True}, + 'image': {'string':'Image file', 'type':'image', 'help': 'support only .png files'}, + 'description': {'string':'Description', 'type':'text', 'readonly':True}, + 'version': {'string':'Version', 'type':'char', 'readonly':True}, + 'demourl': {'string':'Demo URL', 'type':'char', 'size':128, + 'help': 'empty to keep existing value'}, + 'docurl': {'string':'Documentation URL', 'type':'char', 'size':128, + 'help': 'Empty to keep existing value'}, + 'category': {'string':'Category', 'type':'selection', 'size':64, 'required':True, + 'selection': _get_selection}, + 'license': { + 'string':'Licence', 'type':'selection', 'size':64, 'required':True, + 'selection': [('GPL-2', 'GPL-2'), ('Other proprietary','Other proprietary')], + 'default': lambda *args: 'GPL-2' + }, + 'include_src': {'string': 'Include source', 'type': 'boolean', + 'default': lambda *a: True}, + 'operation': { + 'string':'Operation', + 'type':'selection', + 'readonly':True, + 'selection':[('0','Creation'),('1','Modification')], + }, + 'url_download': {'string':'Download URL', 'type':'char', 'size':128, + 'help': 'Keep empty for an auto upload of the module'}, } upload_info_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="User information" colspan="4"/> - <label string="Please provide here your login on the Tiny ERP website." - align="0.0" colspan="4"/> - <label string="If you don't have an access, you can create one http://www.openerp.com/" - align="0.0" colspan="4"/> - <field name="login"/> - <newline/> - <field name="password"/> - <newline/> - <field name="email"/> + <separator string="User information" colspan="4"/> + <label string="Please provide here your login on the Tiny ERP website." + align="0.0" colspan="4"/> + <label string="If you don't have an access, you can create one http://www.openerp.com/" + align="0.0" colspan="4"/> + <field name="login"/> + <newline/> + <field name="password"/> + <newline/> + <field name="email"/> </form>''' def _get_edit(self, cr, uid, datas, *args): - pool = pooler.get_pool(cr.dbname) - name = pool.get('ir.module.module').read(cr, uid, [datas['id']], ['name'])[0]['name'] - a = urllib.urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (name,)) - content = a.read() - try: - email = self.pool.get('res.users').browse(cr, uid, uid).address.email or '' - except: - email ='' - result = {'operation': ((content[0]=='0') and '0') or '1', 'email':email} - if (content[0]<>'0'): - result['category'] = content.split('\n')[1] - return result + pool = pooler.get_pool(cr.dbname) + name = pool.get('ir.module.module').read(cr, uid, [datas['id']], ['name'])[0]['name'] + a = urllib.urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (name,)) + content = a.read() + try: + email = self.pool.get('res.users').browse(cr, uid, uid).address.email or '' + except: + email ='' + result = {'operation': ((content[0]=='0') and '0') or '1', 'email':email} + if (content[0]<>'0'): + result['category'] = content.split('\n')[1] + return result upload_info_fields = { - 'login': {'string':'Login', 'type':'char', 'size':32, 'required':True}, - 'email': {'string':'Email', 'type':'char', 'size':100, 'required':True}, - 'password': {'string':'Password', 'type':'char', 'size':32, 'required':True, - 'invisible':True}, + 'login': {'string':'Login', 'type':'char', 'size':32, 'required':True}, + 'email': {'string':'Email', 'type':'char', 'size':100, 'required':True}, + 'password': {'string':'Password', 'type':'char', 'size':32, 'required':True, + 'invisible':True}, } end_form = '''<?xml version="1.0"?> <form string="Module publication"> - <notebook> - <page string="Information"> - <field name="text_end" colspan="4" nolabel="1" width="300" height="200"/> - </page> - <page string="Result"> - <field name="result" colspan="4" nolabel="1"/> - </page> - </notebook> + <notebook> + <page string="Information"> + <field name="text_end" colspan="4" nolabel="1" width="300" height="200"/> + </page> + <page string="Result"> + <field name="result" colspan="4" nolabel="1"/> + </page> + </notebook> </form>''' end_fields = { - 'text_end': {'string':'Summary', 'type':'text', 'readonly':True, - 'default': lambda *args: """ + 'text_end': {'string':'Summary', 'type':'text', 'readonly':True, + 'default': lambda *args: """ Thank you for contributing ! Your module has been successfully uploaded to the official website. You must wait a few hours/days so that the Tiny ERP core team review your module for approval on the website. """}, - 'result': {'string':'Result page', 'type':'text', 'readonly':True} + 'result': {'string':'Result page', 'type':'text', 'readonly':True} } import httplib, mimetypes def post_multipart(host, selector, fields, files): - def encode_multipart_formdata(fields, files): - BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' - CRLF = '\r\n' - L = [] - for (key, value) in fields: - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"' % key) - L.append('') - L.append(value) - for (key,fname,value) in files: - L.append('--' + BOUNDARY) - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' - % (key, fname)) - L.append('Content-Type: application/octet-stream') - L.append('') - L.append(value) - L.append('--' + BOUNDARY + '--') - L.append('') - body = CRLF.join(L) - content_type = 'multipart/form-data; boundary=%s' % BOUNDARY - return content_type, body - content_type, body = encode_multipart_formdata(fields, files) - import httplib + def encode_multipart_formdata(fields, files): + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key,fname,value) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' + % (key, fname)) + L.append('Content-Type: application/octet-stream') + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + content_type, body = encode_multipart_formdata(fields, files) + import httplib - headers = {"Content-type": content_type, "Accept": "*/*"} - conn = httplib.HTTPConnection(host) - conn.request("POST", selector, body, headers = headers) - response = conn.getresponse() - val = response.status - res = response.read() - conn.close() - return res + headers = {"Content-type": content_type, "Accept": "*/*"} + conn = httplib.HTTPConnection(host) + conn.request("POST", selector, body, headers = headers) + response = conn.getresponse() + val = response.status + res = response.read() + conn.close() + return res def _upload(self, cr, uid, datas, context): - pool = pooler.get_pool(cr.dbname) - mod = pool.get('ir.module.module').browse(cr, uid, datas['id']) - download = datas['form']['url_download'] or '' - if not download: - res = module_zip.createzip(cr, uid, datas['id'], context, - b64enc=False, src=datas['form']['include_src']) - download = 'http://www.openerp.com/download/modules/'+res['module_filename'] - result = post_multipart('www.openerp.com', '/mtree_upload.php', - [ - ('login',datas['form']['login']), - ('password',datas['form']['password']), - ('module_name',mod.name) - ], [ - ('module', res['module_filename'], res['module_file']) - ]) - if result[0] == "1": - raise wizard.except_wizard(_('Error'), _('Login failed!')) - elif result[0] == "2": - raise wizard.except_wizard(_('Error'), - _('This version of the module is already exist on the server')) - elif result[0] != "0": - raise wizard.except_wizard(_('Error'), _('Failed to upload the file')) + pool = pooler.get_pool(cr.dbname) + mod = pool.get('ir.module.module').browse(cr, uid, datas['id']) + download = datas['form']['url_download'] or '' + if not download: + res = module_zip.createzip(cr, uid, datas['id'], context, + b64enc=False, src=datas['form']['include_src']) + download = 'http://www.openerp.com/download/modules/'+res['module_filename'] + result = post_multipart('www.openerp.com', '/mtree_upload.php', + [ + ('login',datas['form']['login']), + ('password',datas['form']['password']), + ('module_name',mod.name) + ], [ + ('module', res['module_filename'], res['module_file']) + ]) + if result[0] == "1": + raise wizard.except_wizard(_('Error'), _('Login failed!')) + elif result[0] == "2": + raise wizard.except_wizard(_('Error'), + _('This version of the module is already exist on the server')) + elif result[0] != "0": + raise wizard.except_wizard(_('Error'), _('Failed to upload the file')) - updata = { - 'link_name': mod.shortdesc or '', - 'new_cat_id': datas['form']['category'], - 'link_desc': (mod.description or '').replace('\n','<br/>\n'), - 'website': mod.website or '', - 'price': '0.0', - 'email': datas['form']['email'] or '', - 'cust_1': download, - 'cust_2': datas['form']['demourl'] or '', # Put here the download url - 'cust_3': mod.url or '/', - 'cust_4': datas['form']['docurl'] or '', - 'cust_5': datas['form']['license'] or '', - 'cust_6': mod.installed_version or '0', - 'cust_7': mod.name, - 'option': 'com_mtree', - 'task': 'savelisting', - 'Itemid': '99999999', - 'cat_id': '0', - 'adminForm': '', - 'auto_login': datas['form']['login'], - 'auto_password': datas['form']['password'] - } - name = mod.name - a = urllib.urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (name,)) - aa = a.read() - if aa[0]<>'0': - updata['link_id']=aa.split('\n')[0] - updata['option'] = 'mtree' + updata = { + 'link_name': mod.shortdesc or '', + 'new_cat_id': datas['form']['category'], + 'link_desc': (mod.description or '').replace('\n','<br/>\n'), + 'website': mod.website or '', + 'price': '0.0', + 'email': datas['form']['email'] or '', + 'cust_1': download, + 'cust_2': datas['form']['demourl'] or '', # Put here the download url + 'cust_3': mod.url or '/', + 'cust_4': datas['form']['docurl'] or '', + 'cust_5': datas['form']['license'] or '', + 'cust_6': mod.installed_version or '0', + 'cust_7': mod.name, + 'option': 'com_mtree', + 'task': 'savelisting', + 'Itemid': '99999999', + 'cat_id': '0', + 'adminForm': '', + 'auto_login': datas['form']['login'], + 'auto_password': datas['form']['password'] + } + name = mod.name + a = urllib.urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (name,)) + aa = a.read() + if aa[0]<>'0': + updata['link_id']=aa.split('\n')[0] + updata['option'] = 'mtree' - files = [] - if datas['form']['image']: - files.append(('link_image', 'link_image.png', - base64.decodestring(datas['form']['image']))) - result = post_multipart('www.openerp.com', '/index.php', updata.items(), files) - return {'result': result} + files = [] + if datas['form']['image']: + files.append(('link_image', 'link_image.png', + base64.decodestring(datas['form']['image']))) + result = post_multipart('www.openerp.com', '/index.php', updata.items(), files) + return {'result': result} def module_check(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - module = pool.get('ir.module.module').browse(cr, uid, data['id'], context) - if module.state != 'installed': - raise wizard.except_wizard(_('Error'), _('You could not publish a module that is not installed!')) - return { - 'name': module.name, - 'shortdesc': module.shortdesc, - 'author': module.author, - 'website': module.website, - 'url': module.url, - 'description': module.description, - 'version': module.installed_version, - 'license': module.license, - } + pool = pooler.get_pool(cr.dbname) + module = pool.get('ir.module.module').browse(cr, uid, data['id'], context) + if module.state != 'installed': + raise wizard.except_wizard(_('Error'), _('You could not publish a module that is not installed!')) + return { + 'name': module.name, + 'shortdesc': module.shortdesc, + 'author': module.author, + 'website': module.website, + 'url': module.url, + 'description': module.description, + 'version': module.installed_version, + 'license': module.license, + } class base_module_publish(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': { - 'type':'form', - 'arch':intro_form, - 'fields':intro_fields, - 'state':[ - ('end', 'Cancel', 'gtk-cancel'), - ('step1', 'Continue', 'gtk-go-forward', True) - ] - } - }, - 'step1': { - 'actions': [module_check, _get_edit], - 'result': { - 'type':'form', - 'arch':check_form, - 'fields':check_fields, - 'state':[ - ('end','Cancel', 'gtk-cancel'), - ('init', 'Previous', 'gtk-go-back'), - ('step2','Continue', 'gtk-go-forward', True) - ] - } - }, - 'step2': { - 'actions': [], - 'result': { - 'type':'form', - 'arch':upload_info_form, - 'fields':upload_info_fields, - 'state':[ - ('end','Cancel', 'gtk-cancel'), - ('step1', 'Previous', 'gtk-go-back'), - ('publish','Publish', 'gtk-ok', True) - ] - } - }, - 'publish': { - 'actions': [_upload], # Action to develop: upload method - 'result': { - 'type':'form', - 'arch':end_form, - 'fields':end_fields, - 'state':[ - ('end','Close', 'gtk-ok', True) - ] - } - } - } + states = { + 'init': { + 'actions': [], + 'result': { + 'type':'form', + 'arch':intro_form, + 'fields':intro_fields, + 'state':[ + ('end', 'Cancel', 'gtk-cancel'), + ('step1', 'Continue', 'gtk-go-forward', True) + ] + } + }, + 'step1': { + 'actions': [module_check, _get_edit], + 'result': { + 'type':'form', + 'arch':check_form, + 'fields':check_fields, + 'state':[ + ('end','Cancel', 'gtk-cancel'), + ('init', 'Previous', 'gtk-go-back'), + ('step2','Continue', 'gtk-go-forward', True) + ] + } + }, + 'step2': { + 'actions': [], + 'result': { + 'type':'form', + 'arch':upload_info_form, + 'fields':upload_info_fields, + 'state':[ + ('end','Cancel', 'gtk-cancel'), + ('step1', 'Previous', 'gtk-go-back'), + ('publish','Publish', 'gtk-ok', True) + ] + } + }, + 'publish': { + 'actions': [_upload], # Action to develop: upload method + 'result': { + 'type':'form', + 'arch':end_form, + 'fields':end_fields, + 'state':[ + ('end','Close', 'gtk-ok', True) + ] + } + } + } base_module_publish('base_module_publish.module_publish') diff --git a/addons/base_module_publish/wizard/base_module_publish_all.py b/addons/base_module_publish/wizard/base_module_publish_all.py index fbfbb11c222..15a9afc412d 100644 --- a/addons/base_module_publish/wizard/base_module_publish_all.py +++ b/addons/base_module_publish/wizard/base_module_publish_all.py @@ -35,13 +35,13 @@ from urllib import urlopen intro_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="Publication information" colspan="4"/> - <field name="text" colspan="4" nolabel="1"/> + <separator string="Publication information" colspan="4"/> + <field name="text" colspan="4" nolabel="1"/> </form>''' intro_fields = { - 'text': {'string': 'Introduction', 'type': 'text', 'readonly': True, - 'default': lambda *a: """ + 'text': {'string': 'Introduction', 'type': 'text', 'readonly': True, + 'default': lambda *a: """ This system will automatically publish and upload the selected modules to the Tiny ERP official website. You can use it to quickly update a set of module (new version). @@ -56,129 +56,129 @@ Thanks you for contributing! login_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="User information" colspan="4"/> - <label string="Please provide here your login on the Tiny ERP website." - align="0.0" colspan="4"/> - <label string="If you don't have an access, you can create one http://www.openerp.com/" - align="0.0" colspan="4"/> - <field name="login"/> - <newline/> - <field name="password"/> - <newline/> - <field name="email"/> + <separator string="User information" colspan="4"/> + <label string="Please provide here your login on the Tiny ERP website." + align="0.0" colspan="4"/> + <label string="If you don't have an access, you can create one http://www.openerp.com/" + align="0.0" colspan="4"/> + <field name="login"/> + <newline/> + <field name="password"/> + <newline/> + <field name="email"/> </form>''' login_fields = { - 'login': {'string':'Login', 'type':'char', 'size':32, 'required':True}, - 'email': {'string':'Email', 'type':'char', 'size':100, 'required':True}, - 'password': {'string':'Password', 'type':'char', 'size':32, 'required':True, - 'invisible':True}, + 'login': {'string':'Login', 'type':'char', 'size':32, 'required':True}, + 'email': {'string':'Email', 'type':'char', 'size':100, 'required':True}, + 'password': {'string':'Password', 'type':'char', 'size':32, 'required':True, + 'invisible':True}, } end_form = '''<?xml version="1.0"?> <form string="Module publication"> - <separator string="Upload information" colspan="4"/> - <field name="update" colspan="4"/> - <field name="already" colspan="4"/> - <field name="error" colspan="4"/> + <separator string="Upload information" colspan="4"/> + <field name="update" colspan="4"/> + <field name="already" colspan="4"/> + <field name="error" colspan="4"/> </form>''' end_fields= { - 'update': {'type': 'text', 'string': 'Modules updated', 'readonly': True}, - 'already': {'type': 'text', 'string': 'Modules already updated', - 'readonly': True}, - 'error': {'type': 'text', 'string': 'Modules in error', 'readonly': True}, + 'update': {'type': 'text', 'string': 'Modules updated', 'readonly': True}, + 'already': {'type': 'text', 'string': 'Modules already updated', + 'readonly': True}, + 'error': {'type': 'text', 'string': 'Modules in error', 'readonly': True}, } def _upload(self, cr, uid, datas, context): - pool = pooler.get_pool(cr.dbname) - modules = pool.get('ir.module.module').browse(cr, uid, datas['ids']) - log = [[], [], []] # [update, already, error] - for mod in modules: # whoooouuuuffff update - if mod.state != 'installed': - result[2].append(mod.name) - continue - res = module_zip.createzip(cr, uid, mod.id, context, b64enc=False, - src=(mod.license in ('GPL-2'))) - download = 'http://www.openerp.com/download/modules/'+res['module_filename'] - result = post_multipart('www.openerp.com', '/mtree_upload.php', - [('login', datas['form']['login']), - ('password', datas['form']['password']), - ('module_name', mod.name) - ], [('module', res['module_filename'], - res['module_file']) - ]) - if result[0] == "1": - raise wizard.except_wizard('Error', 'Login failed!') - elif result[0] == "0": - log[0].append(mod.name) - elif result[0] == "2": - log[1].append(mod.name) - else: - log[2].append(mod.name) - updata = { - 'link_name': mod.shortdesc or '', - 'link_desc': (mod.description or '').replace('\n','<br/>\n'), - 'website': mod.website or '', - 'email': datas['form']['email'] or '', - 'cust_1': download, - 'cust_3': mod.url or '/', - 'cust_6': mod.installed_version or '0', - 'cust_7': mod.name, - 'option': 'com_mtree', - 'task': 'savelisting', - 'Itemid': '99999999', - 'cat_id': '0', - 'adminForm': '', - 'auto_login': datas['form']['login'], - 'auto_password': datas['form']['password'] - } - a = urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (mod.name,)) - aa = a.read() - if aa[0]<>'0': - updata['link_id']=aa.split('\n')[0] - updata['cat_id']=aa.split('\n')[1] - updata['option'] = 'mtree' - result = post_multipart('www.openerp.com', '/index.php', updata.items(), []) - return {'update': '\n'.join(log[0]), 'already': '\n'.join(log[1]), - 'error': '\n'.join(log[2])} + pool = pooler.get_pool(cr.dbname) + modules = pool.get('ir.module.module').browse(cr, uid, datas['ids']) + log = [[], [], []] # [update, already, error] + for mod in modules: # whoooouuuuffff update + if mod.state != 'installed': + result[2].append(mod.name) + continue + res = module_zip.createzip(cr, uid, mod.id, context, b64enc=False, + src=(mod.license in ('GPL-2'))) + download = 'http://www.openerp.com/download/modules/'+res['module_filename'] + result = post_multipart('www.openerp.com', '/mtree_upload.php', + [('login', datas['form']['login']), + ('password', datas['form']['password']), + ('module_name', mod.name) + ], [('module', res['module_filename'], + res['module_file']) + ]) + if result[0] == "1": + raise wizard.except_wizard('Error', 'Login failed!') + elif result[0] == "0": + log[0].append(mod.name) + elif result[0] == "2": + log[1].append(mod.name) + else: + log[2].append(mod.name) + updata = { + 'link_name': mod.shortdesc or '', + 'link_desc': (mod.description or '').replace('\n','<br/>\n'), + 'website': mod.website or '', + 'email': datas['form']['email'] or '', + 'cust_1': download, + 'cust_3': mod.url or '/', + 'cust_6': mod.installed_version or '0', + 'cust_7': mod.name, + 'option': 'com_mtree', + 'task': 'savelisting', + 'Itemid': '99999999', + 'cat_id': '0', + 'adminForm': '', + 'auto_login': datas['form']['login'], + 'auto_password': datas['form']['password'] + } + a = urlopen('http://www.openerp.com/mtree_interface.php?module=%s' % (mod.name,)) + aa = a.read() + if aa[0]<>'0': + updata['link_id']=aa.split('\n')[0] + updata['cat_id']=aa.split('\n')[1] + updata['option'] = 'mtree' + result = post_multipart('www.openerp.com', '/index.php', updata.items(), []) + return {'update': '\n'.join(log[0]), 'already': '\n'.join(log[1]), + 'error': '\n'.join(log[2])} class base_module_publish_all(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': intro_form, - 'fields': intro_fields, - 'state': [ - ('end', 'Cancel'), - ('login', 'Ok'), - ] - } - }, - 'login': { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': login_form, - 'fields': login_fields, - 'state': [ - ('end', 'Cancel'), - ('publish', 'Publish') - ] - } - }, - 'publish': { - 'actions': [_upload], - 'result': { - 'type': 'form', - 'arch': end_form, - 'fields': end_fields, - 'state': [ - ('end', 'Close') - ] - } - }, - } + states = { + 'init': { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': intro_form, + 'fields': intro_fields, + 'state': [ + ('end', 'Cancel'), + ('login', 'Ok'), + ] + } + }, + 'login': { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': login_form, + 'fields': login_fields, + 'state': [ + ('end', 'Cancel'), + ('publish', 'Publish') + ] + } + }, + 'publish': { + 'actions': [_upload], + 'result': { + 'type': 'form', + 'arch': end_form, + 'fields': end_fields, + 'state': [ + ('end', 'Close') + ] + } + }, + } base_module_publish_all('base_module_publish.module_publish_all') diff --git a/addons/base_module_publish/wizard/module_zip.py b/addons/base_module_publish/wizard/module_zip.py index 6aca0381ccc..981a7c4ad71 100644 --- a/addons/base_module_publish/wizard/module_zip.py +++ b/addons/base_module_publish/wizard/module_zip.py @@ -36,46 +36,46 @@ import StringIO import base64 def _zippy(archive, fromurl, path, src=True): - url = os.path.join(fromurl, path) - if os.path.isdir(url): - if path.split('/')[-1].startswith('.'): - return False - for fname in os.listdir(url): - _zippy(archive, fromurl, path and os.path.join(path, fname) or fname, src=src) - else: - if src: - exclude = ['pyo', 'pyc'] - else: - exclude = ['py','pyo','pyc'] - if (path.split('.')[-1] not in exclude) or (os.path.basename(path)=='__terp__.py'): - archive.write(os.path.join(fromurl, path), path) - return True + url = os.path.join(fromurl, path) + if os.path.isdir(url): + if path.split('/')[-1].startswith('.'): + return False + for fname in os.listdir(url): + _zippy(archive, fromurl, path and os.path.join(path, fname) or fname, src=src) + else: + if src: + exclude = ['pyo', 'pyc'] + else: + exclude = ['py','pyo','pyc'] + if (path.split('.')[-1] not in exclude) or (os.path.basename(path)=='__terp__.py'): + archive.write(os.path.join(fromurl, path), path) + return True def createzip(cr, uid, moduleid, context, b64enc=True, src=True): - pool = pooler.get_pool(cr.dbname) - module_obj = pool.get('ir.module.module') + pool = pooler.get_pool(cr.dbname) + module_obj = pool.get('ir.module.module') - module = module_obj.browse(cr, uid, moduleid) + module = module_obj.browse(cr, uid, moduleid) - if module.state != 'installed': - raise wizard.except_wizard(_('Error'), - _('Can not export module that is not installed!')) + if module.state != 'installed': + raise wizard.except_wizard(_('Error'), + _('Can not export module that is not installed!')) - ad = tools.config['addons_path'] - if os.path.isdir(os.path.join(ad, module.name)): - archname = StringIO.StringIO('wb') - archive = PyZipFile(archname, "w", ZIP_DEFLATED) - archive.writepy(os.path.join(ad, module.name)) - _zippy(archive, ad, module.name, src=src) - archive.close() - val =archname.getvalue() - archname.close() - elif os.path.isfile(os.path.join(ad, module.name + '.zip')): - val = file(os.path.join(ad, module.name + '.zip'), 'rb').read() - else: - raise wizard.except_wizard(_('Error'), _('Could not find the module to export!')) - if b64enc: - val =base64.encodestring(val) - return {'module_file':val, 'module_filename': module.name + '-' + \ - (module.installed_version or '0') + '.zip'} + ad = tools.config['addons_path'] + if os.path.isdir(os.path.join(ad, module.name)): + archname = StringIO.StringIO('wb') + archive = PyZipFile(archname, "w", ZIP_DEFLATED) + archive.writepy(os.path.join(ad, module.name)) + _zippy(archive, ad, module.name, src=src) + archive.close() + val =archname.getvalue() + archname.close() + elif os.path.isfile(os.path.join(ad, module.name + '.zip')): + val = file(os.path.join(ad, module.name + '.zip'), 'rb').read() + else: + raise wizard.except_wizard(_('Error'), _('Could not find the module to export!')) + if b64enc: + val =base64.encodestring(val) + return {'module_file':val, 'module_filename': module.name + '-' + \ + (module.installed_version or '0') + '.zip'} diff --git a/addons/base_module_publish/wizard/wizard_module_export.py b/addons/base_module_publish/wizard/wizard_module_export.py index 245c5c0292b..a401227cc9f 100644 --- a/addons/base_module_publish/wizard/wizard_module_export.py +++ b/addons/base_module_publish/wizard/wizard_module_export.py @@ -35,42 +35,42 @@ import module_zip init_form = '''<?xml version="1.0"?> <form string="Export module"> - <separator string="Export module" colspan="4"/> - <field name="include_src"/> + <separator string="Export module" colspan="4"/> + <field name="include_src"/> </form>''' init_fields = { - 'include_src': {'string': 'Include sources', 'type': 'boolean', - 'default': lambda *a: True,}, + 'include_src': {'string': 'Include sources', 'type': 'boolean', + 'default': lambda *a: True,}, } finish_form = '''<?xml version="1.0"?> <form string="Finish"> - <separator string="Module successfully exported !" colspan="4"/> - <field name="module_file"/> - <newline/> - <field name="module_filename"/> + <separator string="Module successfully exported !" colspan="4"/> + <field name="module_file"/> + <newline/> + <field name="module_filename"/> </form>''' finish_fields = { - 'module_file': {'string': 'Module .zip file', 'type':'binary', 'readonly':True}, - 'module_filename': {'string': 'Filename', 'type':'char', 'size': 64, 'readonly':True}, + 'module_file': {'string': 'Module .zip file', 'type':'binary', 'readonly':True}, + 'module_filename': {'string': 'Filename', 'type':'char', 'size': 64, 'readonly':True}, } class move_module_wizard(wizard.interface): - def createzip(self, cr, uid, data, context): - return module_zip.createzip(cr, uid, data['id'], context, src=data['form']['include_src']) + def createzip(self, cr, uid, data, context): + return module_zip.createzip(cr, uid, data['id'], context, src=data['form']['include_src']) - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': init_form, - 'fields': init_fields, 'state': [('end', 'Cancel'), ('zip', 'Ok')]}, - }, - 'zip': { - 'actions': [createzip], - 'result': {'type': 'form', 'arch': finish_form, - 'fields': finish_fields, 'state': [('end', 'Close')]}, - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': init_form, + 'fields': init_fields, 'state': [('end', 'Cancel'), ('zip', 'Ok')]}, + }, + 'zip': { + 'actions': [createzip], + 'result': {'type': 'form', 'arch': finish_form, + 'fields': finish_fields, 'state': [('end', 'Close')]}, + } + } move_module_wizard('base_module_publish.module_export') diff --git a/addons/base_setup/__terp__.py b/addons/base_setup/__terp__.py index 7773df092c5..aa4549137cf 100644 --- a/addons/base_setup/__terp__.py +++ b/addons/base_setup/__terp__.py @@ -26,34 +26,34 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Base Setup", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "category" : "Generic Modules/Base", - "description": """ - This module implements a configuration system that helps user - to configure the system at the installation of a new database. + "name" : "Base Setup", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "category" : "Generic Modules/Base", + "description": """ + This module implements a configuration system that helps user + to configure the system at the installation of a new database. - It allows you to select between a list of profiles to install: - * Minimal profile - * Accounting only - * Services companies - * Manufacturing companies + It allows you to select between a list of profiles to install: + * Minimal profile + * Accounting only + * Services companies + * Manufacturing companies - It also asks screens to help easily configure your company, the header and - footer, the account chart to install and the language. - """, - "depends" : ["base"], - "init_xml" : [ - "base_setup_data.xml", - ], - "demo_xml" : [ - "base_setup_demo.xml", - ], - "update_xml" : [ - "base_setup_wizard.xml", - ], - "active": True, - "installable": True + It also asks screens to help easily configure your company, the header and + footer, the account chart to install and the language. + """, + "depends" : ["base"], + "init_xml" : [ + "base_setup_data.xml", + ], + "demo_xml" : [ + "base_setup_demo.xml", + ], + "update_xml" : [ + "base_setup_wizard.xml", + ], + "active": True, + "installable": True } diff --git a/addons/base_setup/base_setup.py b/addons/base_setup/base_setup.py index 34f8824735c..e6ae53523be 100644 --- a/addons/base_setup/base_setup.py +++ b/addons/base_setup/base_setup.py @@ -30,8 +30,8 @@ from osv import fields, osv class base_setup(osv.osv): - _name="base_setup.base_setup" - _columns={ - 'name': fields.char("Name", size=128), - } + _name="base_setup.base_setup" + _columns={ + 'name': fields.char("Name", size=128), + } base_setup() diff --git a/addons/base_setup/wizard/wizard_base_setup.py b/addons/base_setup/wizard/wizard_base_setup.py index c24c674b44e..486d2a8301f 100644 --- a/addons/base_setup/wizard/wizard_base_setup.py +++ b/addons/base_setup/wizard/wizard_base_setup.py @@ -35,418 +35,418 @@ import os view_form_profit = """<?xml version="1.0"?> <form string="Setup"> - <image name="gtk-dialog-info"/> - <group> - <separator string="Select a profile" colspan="2"/> - <newline/> - <field align="0.0" name="profile"/> - <newline/> - <label string="A profile sets a pre-selection of modules for enterprise needs." colspan="2" align="0.0"/> - <newline/> - <label string="You'll be able to install others modules later through the Administration menu." colspan="2" align="0.0"/> - </group> + <image name="gtk-dialog-info"/> + <group> + <separator string="Select a profile" colspan="2"/> + <newline/> + <field align="0.0" name="profile"/> + <newline/> + <label string="A profile sets a pre-selection of modules for enterprise needs." colspan="2" align="0.0"/> + <newline/> + <label string="You'll be able to install others modules later through the Administration menu." colspan="2" align="0.0"/> + </group> </form>""" view_form_charts = """<?xml version="1.0"?> <form string="Setup"> - <image name="gtk-dialog-info" colspan="2"/> - <group> - <separator string="Select a chart of accounts" colspan="2"/> - <newline/> - <field name="charts" align="0.0"/> - <newline/> - <label string="There are many other chart of accounts available on the OpenERP website." colspan="2" align="0.0"/> - <newline/> - <label string="If you don't select one now, you'll be able to install an other one through the Administration menu." colspan="2" align="0.0"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group> + <separator string="Select a chart of accounts" colspan="2"/> + <newline/> + <field name="charts" align="0.0"/> + <newline/> + <label string="There are many other chart of accounts available on the OpenERP website." colspan="2" align="0.0"/> + <newline/> + <label string="If you don't select one now, you'll be able to install an other one through the Administration menu." colspan="2" align="0.0"/> + </group> </form>""" view_form_company = """<?xml version="1.0"?> <form string="Setup"> - <image name="gtk-dialog-info" colspan="2"/> - <group> - <separator string="Define main company" colspan="4"/> - <newline/> - <field name="name" align="0.0" colspan="4" required="True"/> - <newline/> - <field name="street" align="0.0"/> - <field name="street2" align="0.0"/> - <field name="zip" align="0.0"/> - <field name="city" align="0.0"/> - <field name="country_id" align="0.0"/> - <field name="state_id" align="0.0"/> - <field name="email" align="0.0"/> - <field name="phone" align="0.0"/> - <field name="currency" align="0.0"/> - <separator string="Report header" colspan="4"/> - <newline/> - <field name="rml_header1" align="0.0" colspan="4"/> - <field name="rml_footer1" align="0.0" colspan="4"/> - <field name="rml_footer2" align="0.0" colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group> + <separator string="Define main company" colspan="4"/> + <newline/> + <field name="name" align="0.0" colspan="4" required="True"/> + <newline/> + <field name="street" align="0.0"/> + <field name="street2" align="0.0"/> + <field name="zip" align="0.0"/> + <field name="city" align="0.0"/> + <field name="country_id" align="0.0"/> + <field name="state_id" align="0.0"/> + <field name="email" align="0.0"/> + <field name="phone" align="0.0"/> + <field name="currency" align="0.0"/> + <separator string="Report header" colspan="4"/> + <newline/> + <field name="rml_header1" align="0.0" colspan="4"/> + <field name="rml_footer1" align="0.0" colspan="4"/> + <field name="rml_footer2" align="0.0" colspan="4"/> + </group> </form>""" view_form_update = """<?xml version="1.0"?> <form string="Setup"> - <image name="gtk-dialog-info" colspan="2"/> - <group> - <separator string="Summary" colspan="2"/> - <newline/> - <field name="profile" align="0.0" readonly="1"/> - <newline/> - <field name="charts" align="0.0" readonly="1"/> - <newline/> - <field name="name" align="0.0" readonly="1"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group> + <separator string="Summary" colspan="2"/> + <newline/> + <field name="profile" align="0.0" readonly="1"/> + <newline/> + <field name="charts" align="0.0" readonly="1"/> + <newline/> + <field name="name" align="0.0" readonly="1"/> + </group> </form> """ view_form_finish = """<?xml version="1.0"?> <form string="Setup"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator colspan="4" string="Installation Done"/> - <label align="0.0" colspan="4" string="Your new database is now fully installed."/> - <label align="0.0" colspan="4" string="You can start configuring the system or connect directly to the database using the default setup."/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator colspan="4" string="Installation Done"/> + <label align="0.0" colspan="4" string="Your new database is now fully installed."/> + <label align="0.0" colspan="4" string="You can start configuring the system or connect directly to the database using the default setup."/> + </group> </form> """ class wizard_base_setup(wizard.interface): - def _get_profiles(self, cr, uid, context): - module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') - ids=module_obj.search(cr, uid, [('category_id', '=', 'Profile'), - ('state', '<>', 'uninstallable')]) - res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)] - res.append((-1, 'Minimal Profile')) - res.sort() - return res + def _get_profiles(self, cr, uid, context): + module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') + ids=module_obj.search(cr, uid, [('category_id', '=', 'Profile'), + ('state', '<>', 'uninstallable')]) + res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)] + res.append((-1, 'Minimal Profile')) + res.sort() + return res - def _get_charts(self, cr, uid, context): - module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') - ids=module_obj.search(cr, uid, [('category_id', '=', 'Account charts'), - ('state', '<>', 'uninstallable')]) - res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)] - res.append((-1, 'None')) - res.sort(lambda x,y: cmp(x[1],y[1])) - return res + def _get_charts(self, cr, uid, context): + module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') + ids=module_obj.search(cr, uid, [('category_id', '=', 'Account charts'), + ('state', '<>', 'uninstallable')]) + res=[(m.id, m.shortdesc) for m in module_obj.browse(cr, uid, ids)] + res.append((-1, 'None')) + res.sort(lambda x,y: cmp(x[1],y[1])) + return res - def _get_company(self, cr, uid, data, context): - pool=pooler.get_pool(cr.dbname) - company_obj=pool.get('res.company') - ids=company_obj.search(cr, uid, []) - if not ids: - return {} - company=company_obj.browse(cr, uid, ids)[0] - self.fields['name']['default']=company.name - self.fields['currency']['default']=company.currency_id.id - return {} - #self.fields['rml_header1']['default']=company.rml_header1 - #self.fields['rml_footer1']['default']=company.rml_footer1 - #self.fields['rml_footer2']['default']=company.rml_footer2 - #if not company.partner_id.address: - # return {} - #address=company.partner_id.address[0] - #self.fields['street']['default']=address.street - #self.fields['street2']['default']=address.street2 - #self.fields['zip']['default']=address.zip - #self.fields['city']['default']=address.city - #self.fields['email']['default']=address.email - #self.fields['phone']['default']=address.phone - #if address.state_id: - # self.fields['state_id']['default']=address.state_id.id - #else: - # self.fields['state_id']['default']=-1 - #if address.country_id: - # self.fields['country_id']['default']=address.country_id.id - #else: - # self.fields['country_id']['default']=-1 - #return {} + def _get_company(self, cr, uid, data, context): + pool=pooler.get_pool(cr.dbname) + company_obj=pool.get('res.company') + ids=company_obj.search(cr, uid, []) + if not ids: + return {} + company=company_obj.browse(cr, uid, ids)[0] + self.fields['name']['default']=company.name + self.fields['currency']['default']=company.currency_id.id + return {} + #self.fields['rml_header1']['default']=company.rml_header1 + #self.fields['rml_footer1']['default']=company.rml_footer1 + #self.fields['rml_footer2']['default']=company.rml_footer2 + #if not company.partner_id.address: + # return {} + #address=company.partner_id.address[0] + #self.fields['street']['default']=address.street + #self.fields['street2']['default']=address.street2 + #self.fields['zip']['default']=address.zip + #self.fields['city']['default']=address.city + #self.fields['email']['default']=address.email + #self.fields['phone']['default']=address.phone + #if address.state_id: + # self.fields['state_id']['default']=address.state_id.id + #else: + # self.fields['state_id']['default']=-1 + #if address.country_id: + # self.fields['country_id']['default']=address.country_id.id + #else: + # self.fields['country_id']['default']=-1 + #return {} - def _get_states(self, cr, uid, context): - pool=pooler.get_pool(cr.dbname) - state_obj=pool.get('res.country.state') - ids=state_obj.search(cr, uid, []) - res=[(state.id, state.name) for state in state_obj.browse(cr, uid, ids)] - res.append((-1, '')) - res.sort(lambda x,y: cmp(x[1],y[1])) - return res + def _get_states(self, cr, uid, context): + pool=pooler.get_pool(cr.dbname) + state_obj=pool.get('res.country.state') + ids=state_obj.search(cr, uid, []) + res=[(state.id, state.name) for state in state_obj.browse(cr, uid, ids)] + res.append((-1, '')) + res.sort(lambda x,y: cmp(x[1],y[1])) + return res - def _get_countries(self, cr, uid, context): - pool=pooler.get_pool(cr.dbname) - country_obj=pool.get('res.country') - ids=country_obj.search(cr, uid, []) - res=[(country.id, country.name) for country in country_obj.browse(cr, uid, ids)] - res.sort(lambda x,y: cmp(x[1],y[1])) - return res + def _get_countries(self, cr, uid, context): + pool=pooler.get_pool(cr.dbname) + country_obj=pool.get('res.country') + ids=country_obj.search(cr, uid, []) + res=[(country.id, country.name) for country in country_obj.browse(cr, uid, ids)] + res.sort(lambda x,y: cmp(x[1],y[1])) + return res - def _update(self, cr, uid, data, context): - pool=pooler.get_pool(cr.dbname) - form=data['form'] - if 'profile' in data['form'] and data['form']['profile'] > 0: - module_obj=pool.get('ir.module.module') - module_obj.state_change(cr, uid, [data['form']['profile']], 'to install', context) - if 'charts' in data['form'] and data['form']['charts'] > 0: - module_obj=pool.get('ir.module.module') - module_obj.state_change(cr, uid, [data['form']['charts']], 'to install', context) + def _update(self, cr, uid, data, context): + pool=pooler.get_pool(cr.dbname) + form=data['form'] + if 'profile' in data['form'] and data['form']['profile'] > 0: + module_obj=pool.get('ir.module.module') + module_obj.state_change(cr, uid, [data['form']['profile']], 'to install', context) + if 'charts' in data['form'] and data['form']['charts'] > 0: + module_obj=pool.get('ir.module.module') + module_obj.state_change(cr, uid, [data['form']['charts']], 'to install', context) - company_obj=pool.get('res.company') - partner_obj=pool.get('res.partner') - address_obj=pool.get('res.partner.address') - ids=company_obj.search(cr, uid, []) - company=company_obj.browse(cr, uid, ids)[0] - company_obj.write(cr, uid, [company.id], { - 'name': form['name'], - 'rml_header1': form['rml_header1'], - 'rml_footer1': form['rml_footer1'], - 'rml_footer2': form['rml_footer2'], - 'currency_id': form['currency'], - }) - partner_obj.write(cr, uid, [company.partner_id.id], { - 'name': form['name'], - }) - values={ - 'name': form['name'], - 'street': form['street'], - 'street2': form['street2'], - 'zip': form['zip'], - 'city': form['city'], - 'email': form['email'], - 'phone': form['phone'], - 'country_id': form['country_id'], - } - if form['state_id'] > 0: - values['state_id']=form['state_id'] - if company.partner_id.address: - address=company.partner_id.address[0] - address_obj.write(cr, uid, [address.id], values) - else: - values['partner_id']=company.partner_id.id - add_id=address_obj.create(cr, uid, values) + company_obj=pool.get('res.company') + partner_obj=pool.get('res.partner') + address_obj=pool.get('res.partner.address') + ids=company_obj.search(cr, uid, []) + company=company_obj.browse(cr, uid, ids)[0] + company_obj.write(cr, uid, [company.id], { + 'name': form['name'], + 'rml_header1': form['rml_header1'], + 'rml_footer1': form['rml_footer1'], + 'rml_footer2': form['rml_footer2'], + 'currency_id': form['currency'], + }) + partner_obj.write(cr, uid, [company.partner_id.id], { + 'name': form['name'], + }) + values={ + 'name': form['name'], + 'street': form['street'], + 'street2': form['street2'], + 'zip': form['zip'], + 'city': form['city'], + 'email': form['email'], + 'phone': form['phone'], + 'country_id': form['country_id'], + } + if form['state_id'] > 0: + values['state_id']=form['state_id'] + if company.partner_id.address: + address=company.partner_id.address[0] + address_obj.write(cr, uid, [address.id], values) + else: + values['partner_id']=company.partner_id.id + add_id=address_obj.create(cr, uid, values) - cr.commit() - (db, pool)=pooler.restart_pool(cr.dbname, update_module=True) + cr.commit() + (db, pool)=pooler.restart_pool(cr.dbname, update_module=True) - lang_obj=pool.get('res.lang') - lang_ids=lang_obj.search(cr, uid, []) - langs=lang_obj.browse(cr, uid, lang_ids) - for lang in langs: - if lang.code and lang.code != 'en_US': - filename=os.path.join(tools.config["root_path"], "i18n", lang.code + ".csv") - tools.trans_load(cr.dbname, filename, lang.code) - return {} + lang_obj=pool.get('res.lang') + lang_ids=lang_obj.search(cr, uid, []) + langs=lang_obj.browse(cr, uid, lang_ids) + for lang in langs: + if lang.code and lang.code != 'en_US': + filename=os.path.join(tools.config["root_path"], "i18n", lang.code + ".csv") + tools.trans_load(cr.dbname, filename, lang.code) + return {} - def _menu(self, cr, uid, data, context): - users_obj=pooler.get_pool(cr.dbname).get('res.users') - action_obj=pooler.get_pool(cr.dbname).get('ir.actions.act_window') + def _menu(self, cr, uid, data, context): + users_obj=pooler.get_pool(cr.dbname).get('res.users') + action_obj=pooler.get_pool(cr.dbname).get('ir.actions.act_window') - ids=action_obj.search(cr, uid, [('name', '=', 'Menu')]) - menu=action_obj.browse(cr, uid, ids)[0] + ids=action_obj.search(cr, uid, [('name', '=', 'Menu')]) + menu=action_obj.browse(cr, uid, ids)[0] - ids=users_obj.search(cr, uid, [('action_id', '=', 'Setup')]) - users_obj.write(cr, uid, ids, {'action_id': menu.id}) - ids=users_obj.search(cr, uid, [('menu_id', '=', 'Setup')]) - users_obj.write(cr, uid, ids, {'menu_id': menu.id}) + ids=users_obj.search(cr, uid, [('action_id', '=', 'Setup')]) + users_obj.write(cr, uid, ids, {'action_id': menu.id}) + ids=users_obj.search(cr, uid, [('menu_id', '=', 'Setup')]) + users_obj.write(cr, uid, ids, {'menu_id': menu.id}) - return { - 'name': menu.name, - 'type': menu.type, - 'view_id': (menu.view_id and\ - (menu.view_id.id, menu.view_id.name)) or False, - 'domain': menu.domain, - 'res_model': menu.res_model, - 'src_model': menu.src_model, - 'view_type': menu.view_type, - 'view_mode': menu.view_mode, - 'views': menu.views, - } + return { + 'name': menu.name, + 'type': menu.type, + 'view_id': (menu.view_id and\ + (menu.view_id.id, menu.view_id.name)) or False, + 'domain': menu.domain, + 'res_model': menu.res_model, + 'src_model': menu.src_model, + 'view_type': menu.view_type, + 'view_mode': menu.view_mode, + 'views': menu.views, + } - def _next(self, cr, uid, data, context): - if not data['form']['profile'] or data['form']['profile'] <= 0: - return 'company' - return 'charts' + def _next(self, cr, uid, data, context): + if not data['form']['profile'] or data['form']['profile'] <= 0: + return 'company' + return 'charts' - def _previous(self, cr, uid, data, context): - if 'profile' not in data['form'] or data['form']['profile'] <= 0: - return 'init' - return 'charts' + def _previous(self, cr, uid, data, context): + if 'profile' not in data['form'] or data['form']['profile'] <= 0: + return 'init' + return 'charts' - def _config(self, cr, uid, data, context=None): - users_obj=pooler.get_pool(cr.dbname).get('res.users') - action_obj=pooler.get_pool(cr.dbname).get('ir.actions.act_window') + def _config(self, cr, uid, data, context=None): + users_obj=pooler.get_pool(cr.dbname).get('res.users') + action_obj=pooler.get_pool(cr.dbname).get('ir.actions.act_window') - ids=action_obj.search(cr, uid, [('name', '=', 'Menu')]) - menu=action_obj.browse(cr, uid, ids)[0] + ids=action_obj.search(cr, uid, [('name', '=', 'Menu')]) + menu=action_obj.browse(cr, uid, ids)[0] - ids=users_obj.search(cr, uid, [('action_id', '=', 'Setup')]) - users_obj.write(cr, uid, ids, {'action_id': menu.id}) - ids=users_obj.search(cr, uid, [('menu_id', '=', 'Setup')]) - users_obj.write(cr, uid, ids, {'menu_id': menu.id}) - return { + ids=users_obj.search(cr, uid, [('action_id', '=', 'Setup')]) + users_obj.write(cr, uid, ids, {'action_id': menu.id}) + ids=users_obj.search(cr, uid, [('menu_id', '=', 'Setup')]) + users_obj.write(cr, uid, ids, {'menu_id': menu.id}) + return { 'view_type': 'form', "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', } - fields={ - 'profile':{ - 'string':'Profile', - 'type':'selection', - 'selection':_get_profiles, - 'default': -1, - 'required': True, - }, - 'charts':{ - 'string':'Chart of accounts', - 'type':'selection', - 'selection':_get_charts, - 'default': -1, - 'required': True, - }, - 'name':{ - 'string': 'Company Name', - 'type': 'char', - 'size': 64, - }, - 'street':{ - 'string': 'Street', - 'type': 'char', - 'size': 128, - }, - 'street2':{ - 'string': 'Street2', - 'type': 'char', - 'size': 128, - }, - 'zip':{ - 'string': 'Zip code', - 'type': 'char', - 'size': 24, - }, - 'city':{ - 'string': 'City', - 'type': 'char', - 'size': 128, - }, - 'state_id':{ - 'string': 'State', - 'type': 'selection', - 'selection':_get_states, - }, - 'country_id':{ - 'string': 'Country', - 'type': 'selection', - 'selection':_get_countries, - }, - 'email':{ - 'string': 'E-mail', - 'type': 'char', - 'size': 64, - }, - 'phone':{ - 'string': 'Phone', - 'type': 'char', - 'size': 64, - }, - 'currency': { - 'string': 'Currency', - 'type': 'many2one', - 'relation': 'res.currency', - 'required': True, - }, - 'rml_header1':{ - 'string': 'Report Header', - 'type': 'char', - 'help': """This sentence will appear at the top right corner of your reports. + fields={ + 'profile':{ + 'string':'Profile', + 'type':'selection', + 'selection':_get_profiles, + 'default': -1, + 'required': True, + }, + 'charts':{ + 'string':'Chart of accounts', + 'type':'selection', + 'selection':_get_charts, + 'default': -1, + 'required': True, + }, + 'name':{ + 'string': 'Company Name', + 'type': 'char', + 'size': 64, + }, + 'street':{ + 'string': 'Street', + 'type': 'char', + 'size': 128, + }, + 'street2':{ + 'string': 'Street2', + 'type': 'char', + 'size': 128, + }, + 'zip':{ + 'string': 'Zip code', + 'type': 'char', + 'size': 24, + }, + 'city':{ + 'string': 'City', + 'type': 'char', + 'size': 128, + }, + 'state_id':{ + 'string': 'State', + 'type': 'selection', + 'selection':_get_states, + }, + 'country_id':{ + 'string': 'Country', + 'type': 'selection', + 'selection':_get_countries, + }, + 'email':{ + 'string': 'E-mail', + 'type': 'char', + 'size': 64, + }, + 'phone':{ + 'string': 'Phone', + 'type': 'char', + 'size': 64, + }, + 'currency': { + 'string': 'Currency', + 'type': 'many2one', + 'relation': 'res.currency', + 'required': True, + }, + 'rml_header1':{ + 'string': 'Report Header', + 'type': 'char', + 'help': """This sentence will appear at the top right corner of your reports. We suggest you to put a slogan here: "Open Source Business Solutions".""", - 'size': 200, - }, - 'rml_footer1':{ - 'string': 'Report Footer 1', - 'type': 'char', - 'help': """This sentence will appear at the bottom of your reports. + 'size': 200, + }, + 'rml_footer1':{ + 'string': 'Report Footer 1', + 'type': 'char', + 'help': """This sentence will appear at the bottom of your reports. We suggest you to write legal sentences here: Web: http://openerp.com - Fax: +32.81.73.35.01 - Fortis Bank: 126-2013269-07""", - 'size': 200, - }, - 'rml_footer2':{ - 'string': 'Report Footer 2', - 'help': """This sentence will appear at the bottom of your reports. + 'size': 200, + }, + 'rml_footer2':{ + 'string': 'Report Footer 2', + 'help': """This sentence will appear at the bottom of your reports. We suggest you to put bank information here: IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701""", - 'type': 'char', - 'size': 200, - }, - } - states={ - 'init':{ - 'actions': [_get_company], - 'result': {'type': 'form', 'arch': view_form_profit, 'fields': fields, - 'state': [ - ('menu', 'Cancel', 'gtk-cancel'), - ('next', 'Next', 'gtk-go-forward', True) - ] - } - }, - 'next': { - 'actions': [], - 'result': {'type': 'choice', 'next_state': _next} - }, - 'charts':{ - 'actions': [], - 'result': {'type': 'form', 'arch': view_form_charts, 'fields': fields, - 'state':[ - ('init', 'Previous', 'gtk-go-back'), - ('company', 'Next', 'gtk-go-forward', True) - ] - } - }, - 'company':{ - 'actions': [], - 'result': {'type': 'form', 'arch': view_form_company, 'fields': fields, - 'state': [ - ('previous', 'Previous', 'gtk-go-back'), - ('update', 'Next', 'gtk-go-forward', True) - ] - } - }, - 'previous':{ - 'actions': [], - 'result': {'type': 'choice', 'next_state': _previous} - }, - 'update':{ - 'actions': [], - 'result': {'type': 'form', 'arch': view_form_update, 'fields': fields, - 'state': [ - ('company', 'Previous', 'gtk-go-back'), - ('finish', 'Install', 'gtk-ok', True) - ] - } - }, - 'finish':{ - 'actions': [_update], - 'result': {'type': 'form', 'arch': view_form_finish, 'fields': {}, - 'state': [ - ('menu', 'Use Directly'), - ('config', 'Start Configuration', 'gtk-ok', True) - ] - } - }, - 'config': { + 'type': 'char', + 'size': 200, + }, + } + states={ + 'init':{ + 'actions': [_get_company], + 'result': {'type': 'form', 'arch': view_form_profit, 'fields': fields, + 'state': [ + ('menu', 'Cancel', 'gtk-cancel'), + ('next', 'Next', 'gtk-go-forward', True) + ] + } + }, + 'next': { + 'actions': [], + 'result': {'type': 'choice', 'next_state': _next} + }, + 'charts':{ + 'actions': [], + 'result': {'type': 'form', 'arch': view_form_charts, 'fields': fields, + 'state':[ + ('init', 'Previous', 'gtk-go-back'), + ('company', 'Next', 'gtk-go-forward', True) + ] + } + }, + 'company':{ + 'actions': [], + 'result': {'type': 'form', 'arch': view_form_company, 'fields': fields, + 'state': [ + ('previous', 'Previous', 'gtk-go-back'), + ('update', 'Next', 'gtk-go-forward', True) + ] + } + }, + 'previous':{ + 'actions': [], + 'result': {'type': 'choice', 'next_state': _previous} + }, + 'update':{ + 'actions': [], + 'result': {'type': 'form', 'arch': view_form_update, 'fields': fields, + 'state': [ + ('company', 'Previous', 'gtk-go-back'), + ('finish', 'Install', 'gtk-ok', True) + ] + } + }, + 'finish':{ + 'actions': [_update], + 'result': {'type': 'form', 'arch': view_form_finish, 'fields': {}, + 'state': [ + ('menu', 'Use Directly'), + ('config', 'Start Configuration', 'gtk-ok', True) + ] + } + }, + 'config': { 'result': { 'type': 'action', 'action': _config, 'state': 'end', }, }, - 'menu': { - 'actions': [], - 'result': {'type': 'action', 'action': _menu, 'state': 'end'} - }, - } + 'menu': { + 'actions': [], + 'result': {'type': 'action', 'action': _menu, 'state': 'end'} + }, + } wizard_base_setup('base_setup.base_setup') diff --git a/addons/base_vat/__terp__.py b/addons/base_vat/__terp__.py index bf510a5943e..47034887f3f 100644 --- a/addons/base_vat/__terp__.py +++ b/addons/base_vat/__terp__.py @@ -26,12 +26,12 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "VAT", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Base", - "depends" : ["base"], - "update_xml" : ["base_vat_data.xml"], - "active": False, - "installable": True + "name" : "VAT", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Base", + "depends" : ["base"], + "update_xml" : ["base_vat_data.xml"], + "active": False, + "installable": True } diff --git a/addons/board/__terp__.py b/addons/board/__terp__.py index 8a51131b651..6ee2ecc9b0b 100644 --- a/addons/board/__terp__.py +++ b/addons/board/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Dashboard main module", - "version":"1.0", - "author":"Tiny", - "category":"Board/Base", - "depends":["base"], - "demo_xml":[], - "update_xml":["board_view.xml"], - "description": "Base module for all dashboards.", - "active":False, - "installable":True, + "name":"Dashboard main module", + "version":"1.0", + "author":"Tiny", + "category":"Board/Base", + "depends":["base"], + "demo_xml":[], + "update_xml":["board_view.xml"], + "description": "Base module for all dashboards.", + "active":False, + "installable":True, } diff --git a/addons/board/board.py b/addons/board/board.py index 9d0f7aa3c4b..b9979a60864 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -31,94 +31,94 @@ import time from osv import fields,osv class board_board(osv.osv): - _name = 'board.board' - def create(self, cr, user, vals, context=None): - if not 'name' in vals: - return False - return super(board_board, self).create(cr, user, vals, context) - def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): - if context and ('view' in context): - board = self.pool.get('board.board').browse(cr, user, int(context['view']), context) - left = [] - right = [] - for line in board.line_ids: - linestr = '<action string="%s" name="%d" colspan="4"' % (line.name, line.action_id.id) - if line.height: - linestr+=(' height="%d"' % (line.height,)) - if line.width: - linestr+=(' width="%d"' % (line.width,)) - linestr += '/>' - if line.position=='left': - left.append(linestr) - else: - right.append(linestr) - arch = """<form string="My Board"> + _name = 'board.board' + def create(self, cr, user, vals, context=None): + if not 'name' in vals: + return False + return super(board_board, self).create(cr, user, vals, context) + def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): + if context and ('view' in context): + board = self.pool.get('board.board').browse(cr, user, int(context['view']), context) + left = [] + right = [] + for line in board.line_ids: + linestr = '<action string="%s" name="%d" colspan="4"' % (line.name, line.action_id.id) + if line.height: + linestr+=(' height="%d"' % (line.height,)) + if line.width: + linestr+=(' width="%d"' % (line.width,)) + linestr += '/>' + if line.position=='left': + left.append(linestr) + else: + right.append(linestr) + arch = """<form string="My Board"> <hpaned> - <child1> - %s - </child1> - <child2> - %s - </child2> + <child1> + %s + </child1> + <child2> + %s + </child2> </hpaned> </form>""" % ('\n'.join(left), '\n'.join(right)) - result = { - 'toolbar': {'print':[],'action':[],'relate':[]}, - 'fields': {}, - 'arch': arch - } - return result - res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, context, toolbar) - res['toolbar'] = {'print':[],'action':[],'relate':[]} - return res - _columns = { - 'name': fields.char('Dashboard', size=64, required=True), - 'line_ids': fields.one2many('board.board.line', 'board_id', 'Action Views') - } + result = { + 'toolbar': {'print':[],'action':[],'relate':[]}, + 'fields': {}, + 'arch': arch + } + return result + res = super(board_board, self).fields_view_get(cr, user, view_id, view_type, context, toolbar) + res['toolbar'] = {'print':[],'action':[],'relate':[]} + return res + _columns = { + 'name': fields.char('Dashboard', size=64, required=True), + 'line_ids': fields.one2many('board.board.line', 'board_id', 'Action Views') + } board_board() class board_line(osv.osv): - _name = 'board.board.line' - _order = 'position,sequence' - _columns = { - 'name': fields.char('Board', size=64, required=True), - 'sequence': fields.integer('Sequence'), - 'height': fields.integer('Height'), - 'width': fields.integer('Width'), - 'board_id': fields.many2one('board.board', 'Dashboard', required=True, ondelete='cascade'), - 'action_id': fields.many2one('ir.actions.act_window', 'Action', required=True), - 'position': fields.selection([('left','Left'),('right','Right')], 'Position', required=True) - } - _defaults = { - 'position': lambda *args: 'left' - } + _name = 'board.board.line' + _order = 'position,sequence' + _columns = { + 'name': fields.char('Board', size=64, required=True), + 'sequence': fields.integer('Sequence'), + 'height': fields.integer('Height'), + 'width': fields.integer('Width'), + 'board_id': fields.many2one('board.board', 'Dashboard', required=True, ondelete='cascade'), + 'action_id': fields.many2one('ir.actions.act_window', 'Action', required=True), + 'position': fields.selection([('left','Left'),('right','Right')], 'Position', required=True) + } + _defaults = { + 'position': lambda *args: 'left' + } board_line() class board_note_type(osv.osv): - _name = 'board.note.type' - _columns = { - 'name': fields.char('Note Type', size=64, required=True), - } + _name = 'board.note.type' + _columns = { + 'name': fields.char('Note Type', size=64, required=True), + } board_note_type() def _type_get(self, cr, uid, context={}): - obj = self.pool.get('board.note.type') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['name'], context) - res = [(r['name'], r['name']) for r in res] - return res + obj = self.pool.get('board.note.type') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['name'], context) + res = [(r['name'], r['name']) for r in res] + return res class board_note(osv.osv): - _name = 'board.note' - _columns = { - 'name': fields.char('Subject', size=128, required=True), - 'note': fields.text('Note'), - 'user_id': fields.many2one('res.users', 'Author', size=64), - 'date': fields.date('Date', size=64, required=True), - 'type': fields.selection(_type_get, 'Note type', size=64), - } - _defaults = { - 'user_id': lambda object,cr,uid,context: uid, - 'date': lambda object,cr,uid,context: time.strftime('%Y-%m-%d'), - } + _name = 'board.note' + _columns = { + 'name': fields.char('Subject', size=128, required=True), + 'note': fields.text('Note'), + 'user_id': fields.many2one('res.users', 'Author', size=64), + 'date': fields.date('Date', size=64, required=True), + 'type': fields.selection(_type_get, 'Note type', size=64), + } + _defaults = { + 'user_id': lambda object,cr,uid,context: uid, + 'date': lambda object,cr,uid,context: time.strftime('%Y-%m-%d'), + } board_note() diff --git a/addons/board/wizard/board_wizard.py b/addons/board/wizard/board_wizard.py index 7865b900804..17cc6ed74ac 100644 --- a/addons/board/wizard/board_wizard.py +++ b/addons/board/wizard/board_wizard.py @@ -35,47 +35,47 @@ import pooler section_form = '''<?xml version="1.0"?> <form string="Create Menu For Dashboard"> - <separator string="Menu Information" colspan="4"/> - <field name="menu_name"/> - <field name="menu_parent_id"/> + <separator string="Menu Information" colspan="4"/> + <field name="menu_name"/> + <field name="menu_parent_id"/> </form>''' section_fields = { - 'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64}, - 'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True}, + 'menu_name': {'string':'Menu Name', 'type':'char', 'required':True, 'size':64}, + 'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True}, } def board_menu_create(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - board = pool.get('board.board').browse(cr, uid, data['id']) - action_id = pool.get('ir.actions.act_window').create(cr, uid, { - 'name': board.name, - 'view_type':'form', - 'view_mode':'form', - 'context': "{'view':%d}" % (board.id,), - 'res_model': 'board.board' - }) - pool.get('ir.ui.menu').create(cr, uid, { - 'name': data['form']['menu_name'], - 'parent_id': data['form']['menu_parent_id'], - 'icon': 'STOCK_SELECT_COLOR', - 'action': 'ir.actions.act_window,'+str(action_id) - }, context) - return {} + pool = pooler.get_pool(cr.dbname) + board = pool.get('board.board').browse(cr, uid, data['id']) + action_id = pool.get('ir.actions.act_window').create(cr, uid, { + 'name': board.name, + 'view_type':'form', + 'view_mode':'form', + 'context': "{'view':%d}" % (board.id,), + 'res_model': 'board.board' + }) + pool.get('ir.ui.menu').create(cr, uid, { + 'name': data['form']['menu_name'], + 'parent_id': data['form']['menu_parent_id'], + 'icon': 'STOCK_SELECT_COLOR', + 'action': 'ir.actions.act_window,'+str(action_id) + }, context) + return {} class wizard_section_menu_create(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]} - }, - 'create_menu': { - 'actions': [board_menu_create], - 'result': { - 'type':'state', - 'state':'end' - } - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('create_menu','Create Menu')]} + }, + 'create_menu': { + 'actions': [board_menu_create], + 'result': { + 'type':'state', + 'state':'end' + } + } + } wizard_section_menu_create('board.board.menu.create') diff --git a/addons/board_account/__terp__.py b/addons/board_account/__terp__.py index b365ad5a0b4..2116224a5d1 100644 --- a/addons/board_account/__terp__.py +++ b/addons/board_account/__terp__.py @@ -26,30 +26,30 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Board for accountant", - "version":"1.0", - "author":"Tiny", - "category":"Board/Accounting", - "depends":[ - "account", - "hr_timesheet_invoice", - "board", - "report_account", - "report_analytic", - "report_analytic_line", - "account_report", - ], - "demo_xml":["board_account_demo.xml"], - "update_xml":["board_account_view.xml"], - "description": """ - This module creates a dashboards for accountants that includes: - * List of analytic accounts to close - * List of uninvoiced quotations - * List of invoices to confirm - * Graph of costs to invoice - * Graph of aged receivables - * Graph of aged incomes - """, - "active":False, - "installable":True, + "name":"Board for accountant", + "version":"1.0", + "author":"Tiny", + "category":"Board/Accounting", + "depends":[ + "account", + "hr_timesheet_invoice", + "board", + "report_account", + "report_analytic", + "report_analytic_line", + "account_report", + ], + "demo_xml":["board_account_demo.xml"], + "update_xml":["board_account_view.xml"], + "description": """ + This module creates a dashboards for accountants that includes: + * List of analytic accounts to close + * List of uninvoiced quotations + * List of invoices to confirm + * Graph of costs to invoice + * Graph of aged receivables + * Graph of aged incomes + """, + "active":False, + "installable":True, } diff --git a/addons/board_project/__terp__.py b/addons/board_project/__terp__.py index 99403a0cf98..afe5ba44eaf 100644 --- a/addons/board_project/__terp__.py +++ b/addons/board_project/__terp__.py @@ -26,29 +26,29 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Board for project users", - "version":"1.0", - "author":"Tiny", - "category":"Board/Projects & Services", - "depends":[ - "project", - "report_timesheet", - "board", - "report_analytic_planning", - "report_analytic_line", - "report_task", - "hr_timesheet_sheet" - ], - "demo_xml":["board_project_demo.xml"], - "update_xml":["board_project_view.xml", "board_project_manager_view.xml"], - "description": """ + "name":"Board for project users", + "version":"1.0", + "author":"Tiny", + "category":"Board/Projects & Services", + "depends":[ + "project", + "report_timesheet", + "board", + "report_analytic_planning", + "report_analytic_line", + "report_task", + "hr_timesheet_sheet" + ], + "demo_xml":["board_project_demo.xml"], + "update_xml":["board_project_view.xml", "board_project_manager_view.xml"], + "description": """ This module implements a dashboard for project member that includes: - * List of my open tasks - * List of my next deadlines - * List of public notes - * Graph of my timesheet - * Graph of my work analysis - """, - "active":False, - "installable":True, + * List of my open tasks + * List of my next deadlines + * List of public notes + * Graph of my timesheet + * Graph of my work analysis + """, + "active":False, + "installable":True, } diff --git a/addons/board_sale/__terp__.py b/addons/board_sale/__terp__.py index 70666fbcf4a..e44e30679cd 100644 --- a/addons/board_sale/__terp__.py +++ b/addons/board_sale/__terp__.py @@ -26,21 +26,21 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Dashboard for sales", - "version":"1.0", - "author":"Tiny", - "category":"Board/Sales & Purchase", - "depends":["board","sale", "report_crm", "report_sale"], - "demo_xml":["board_sale_demo.xml"], - "update_xml":["board_sale_view.xml"], - "description": """ + "name":"Dashboard for sales", + "version":"1.0", + "author":"Tiny", + "category":"Board/Sales & Purchase", + "depends":["board","sale", "report_crm", "report_sale"], + "demo_xml":["board_sale_demo.xml"], + "update_xml":["board_sale_view.xml"], + "description": """ This module implements a dashboard for salesman that includes: - * You open quotations - * Top 10 sales of the month - * Cases statistics - * Graph of sales by product - * Graph of cases of the month - """, - "active":False, - "installable":True, + * You open quotations + * Top 10 sales of the month + * Cases statistics + * Graph of sales by product + * Graph of cases of the month + """, + "active":False, + "installable":True, } diff --git a/addons/crm/__terp__.py b/addons/crm/__terp__.py index 6c740714b17..026ec77f827 100644 --- a/addons/crm/__terp__.py +++ b/addons/crm/__terp__.py @@ -26,12 +26,12 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Customer & Supplier Relationship Management", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_crm.html", - "category" : "Generic Modules/CRM & SRM", - "description": """The Tiny ERP case and request tracker enables a group of + "name" : "Customer & Supplier Relationship Management", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_crm.html", + "category" : "Generic Modules/CRM & SRM", + "description": """The Tiny ERP case and request tracker enables a group of people to intelligently and efficiently manage tasks, issues, and requests. It manages key tasks such as communication, identification, prioritization, assignment, resolution and notification. @@ -48,10 +48,10 @@ place. The CRM module has a email gateway for the synchronisation interface between mails and Tiny ERP.""", - "depends" : ["base"], - "init_xml" : ["crm_data.xml"], - "demo_xml" : ["crm_demo.xml"], - "update_xml" : ["crm_view.xml", "crm_report.xml", "crm_wizard.xml", "crm_security.xml"], - "active": False, - "installable": True + "depends" : ["base"], + "init_xml" : ["crm_data.xml"], + "demo_xml" : ["crm_demo.xml"], + "update_xml" : ["crm_view.xml", "crm_report.xml", "crm_wizard.xml", "crm_security.xml"], + "active": False, + "installable": True } diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 5fc211da6c0..307fd5a81d3 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -37,736 +37,736 @@ from tools.translate import _ MAX_LEVEL = 15 AVAILABLE_STATES = [ - ('draft','Draft'), - ('open','Open'), - ('cancel', 'Cancel'), - ('done', 'Close'), - ('pending','Pending') + ('draft','Draft'), + ('open','Open'), + ('cancel', 'Cancel'), + ('done', 'Close'), + ('pending','Pending') ] AVAILABLE_PRIORITIES = [ - ('5','Lowest'), - ('4','Low'), - ('3','Normal'), - ('2','High'), - ('1','Highest') + ('5','Lowest'), + ('4','Low'), + ('3','Normal'), + ('2','High'), + ('1','Highest') ] icon_lst = { - 'form':'STOCK_NEW', - 'tree':'STOCK_JUSTIFY_FILL', - 'calendar':'STOCK_SELECT_COLOR' + 'form':'STOCK_NEW', + 'tree':'STOCK_JUSTIFY_FILL', + 'calendar':'STOCK_SELECT_COLOR' } class crm_case_section(osv.osv): - _name = "crm.case.section" - _description = "Case Section" - _columns = { - 'name': fields.char('Case Section',size=64, required=True), - 'code': fields.char('Section Code',size=8), - 'active': fields.boolean('Active'), - 'sequence': fields.integer('Sequence'), - 'user_id': fields.many2one('res.users', 'Responsible'), - 'reply_to': fields.char('Reply-To', size=64, help="The email address wich is the 'Reply-To' of all email sent by Tiny ERP for cases in this section"), - 'parent_id': fields.many2one('crm.case.section', 'Parent Section'), - 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Childs Sections'), - } - _defaults = { - 'active': lambda *a: 1, - } - _sql_constraints = [ - ('code_uniq', 'unique (code)', 'The code of the section must be unique !') - ] - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from crm_case_section where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive sections.', ['parent_id']) - ] - - # Mainly used by the wizard - def menu_create_data(self, cr, uid, data, menu_lst, context): - menus = {} - menus[0] = data['menu_parent_id'] - section = self.browse(cr, uid, data['section_id'], context) - for (index, mname, mdomain, latest, view_mode) in menu_lst: - view_mode = data['menu'+str(index)+'_option'] - if view_mode=='no': - menus[index] = data['menu_parent_id'] - continue - icon = icon_lst.get(view_mode.split(',')[0], 'STOCK_JUSTIFY_FILL') - menu_id=self.pool.get('ir.ui.menu').create(cr, uid, { - 'name': data['menu'+str(index)], - 'parent_id': menus[latest], - 'icon': icon - }) - menus[index] = menu_id - action_id = self.pool.get('ir.actions.act_window').create(cr,uid, { - 'name': data['menu'+str(index)], - 'res_model': 'crm.case', - 'domain': mdomain.replace('SECTION_ID', str(data['section_id'])), - 'view_type': 'form', - 'view_mode': view_mode, - }) - seq = 0 - for mode in view_mode.split(','): - self.pool.get('ir.actions.act_window.view').create(cr, uid, { - 'sequence': seq, - 'view_id': data['view_'+mode], - 'view_mode': mode, - 'act_window_id': action_id, - 'multi': True - }) - seq+=1 - self.pool.get('ir.values').create(cr, uid, { - 'name': data['menu'+str(index)], - 'key2': 'tree_but_open', - 'model': 'ir.ui.menu', - 'res_id': menu_id, - 'value': 'ir.actions.act_window,%d'%action_id, - 'object': True - }) - return True - - # - # Used when called from .XML file - # - def menu_create(self, cr, uid, ids, name, menu_parent_id=False, context={}): - menus = {} - menus[-1] = menu_parent_id - for section in self.browse(cr, uid, ids, context): - for (index, mname, mdomain, latest) in [ - (0,'',"[('section_id','=',"+str(section.id)+")]", -1), - (1,'My ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid)]", 0), - (2,'My Unclosed ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','<>','cancel'), ('state','<>','done')]", 1), - (5,'My Open ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','open')]", 2), - (6,'My Pending ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','pending')]", 2), - (7,'My Draft ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','draft')]", 2), - - (3,'My Late ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 1), - (4,'My Canceled ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','cancel')]", 1), - (8,'All ',"[('section_id','=',"+str(section.id)+"),]", 0), - (9,'Unassigned ',"[('section_id','=',"+str(section.id)+"),('user_id','=',False)]", 8), - (10,'Late ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 8), - (11,'Canceled ',"[('section_id','=',"+str(section.id)+"),('state','=','cancel')]", 8), - (12,'Unclosed ',"[('section_id','=',"+str(section.id)+"),('state','<>','cancel'), ('state','<>','done')]", 8), - (13,'Open ',"[('section_id','=',"+str(section.id)+"),('state','=','open')]", 12), - (14,'Pending ',"[('section_id','=',"+str(section.id)+"),('state','=','pending')]", 12), - (15,'Draft ',"[('section_id','=',"+str(section.id)+"),('state','=','draft')]", 12), - (16,'Unassigned ',"[('section_id','=',"+str(section.id)+"),('user_id','=',False),('state','<>','cancel'),('state','<>','done')]", 12), - ]: - view_mode = 'tree,form' - icon = 'STOCK_JUSTIFY_FILL' - if index==0: - view_mode = 'form,tree' - icon = 'STOCK_NEW' - menu_id=self.pool.get('ir.ui.menu').create(cr, uid, { - 'name': mname+name, - 'parent_id': menus[latest], - 'icon': icon - }) - menus[index] = menu_id - action_id = self.pool.get('ir.actions.act_window').create(cr,uid, { - 'name': mname+name+' Cases', - 'res_model': 'crm.case', - 'domain': mdomain, - 'view_type': 'form', - 'view_mode': view_mode, - }) - self.pool.get('ir.values').create(cr, uid, { - 'name': 'Open Cases', - 'key2': 'tree_but_open', - 'model': 'ir.ui.menu', - 'res_id': menu_id, - 'value': 'ir.actions.act_window,%d'%action_id, - 'object': True - }) - return True - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','parent_id'], context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res + _name = "crm.case.section" + _description = "Case Section" + _columns = { + 'name': fields.char('Case Section',size=64, required=True), + 'code': fields.char('Section Code',size=8), + 'active': fields.boolean('Active'), + 'sequence': fields.integer('Sequence'), + 'user_id': fields.many2one('res.users', 'Responsible'), + 'reply_to': fields.char('Reply-To', size=64, help="The email address wich is the 'Reply-To' of all email sent by Tiny ERP for cases in this section"), + 'parent_id': fields.many2one('crm.case.section', 'Parent Section'), + 'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Childs Sections'), + } + _defaults = { + 'active': lambda *a: 1, + } + _sql_constraints = [ + ('code_uniq', 'unique (code)', 'The code of the section must be unique !') + ] + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from crm_case_section where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive sections.', ['parent_id']) + ] + + # Mainly used by the wizard + def menu_create_data(self, cr, uid, data, menu_lst, context): + menus = {} + menus[0] = data['menu_parent_id'] + section = self.browse(cr, uid, data['section_id'], context) + for (index, mname, mdomain, latest, view_mode) in menu_lst: + view_mode = data['menu'+str(index)+'_option'] + if view_mode=='no': + menus[index] = data['menu_parent_id'] + continue + icon = icon_lst.get(view_mode.split(',')[0], 'STOCK_JUSTIFY_FILL') + menu_id=self.pool.get('ir.ui.menu').create(cr, uid, { + 'name': data['menu'+str(index)], + 'parent_id': menus[latest], + 'icon': icon + }) + menus[index] = menu_id + action_id = self.pool.get('ir.actions.act_window').create(cr,uid, { + 'name': data['menu'+str(index)], + 'res_model': 'crm.case', + 'domain': mdomain.replace('SECTION_ID', str(data['section_id'])), + 'view_type': 'form', + 'view_mode': view_mode, + }) + seq = 0 + for mode in view_mode.split(','): + self.pool.get('ir.actions.act_window.view').create(cr, uid, { + 'sequence': seq, + 'view_id': data['view_'+mode], + 'view_mode': mode, + 'act_window_id': action_id, + 'multi': True + }) + seq+=1 + self.pool.get('ir.values').create(cr, uid, { + 'name': data['menu'+str(index)], + 'key2': 'tree_but_open', + 'model': 'ir.ui.menu', + 'res_id': menu_id, + 'value': 'ir.actions.act_window,%d'%action_id, + 'object': True + }) + return True + + # + # Used when called from .XML file + # + def menu_create(self, cr, uid, ids, name, menu_parent_id=False, context={}): + menus = {} + menus[-1] = menu_parent_id + for section in self.browse(cr, uid, ids, context): + for (index, mname, mdomain, latest) in [ + (0,'',"[('section_id','=',"+str(section.id)+")]", -1), + (1,'My ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid)]", 0), + (2,'My Unclosed ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','<>','cancel'), ('state','<>','done')]", 1), + (5,'My Open ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','open')]", 2), + (6,'My Pending ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','pending')]", 2), + (7,'My Draft ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','draft')]", 2), + + (3,'My Late ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 1), + (4,'My Canceled ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('state','=','cancel')]", 1), + (8,'All ',"[('section_id','=',"+str(section.id)+"),]", 0), + (9,'Unassigned ',"[('section_id','=',"+str(section.id)+"),('user_id','=',False)]", 8), + (10,'Late ',"[('section_id','=',"+str(section.id)+"),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 8), + (11,'Canceled ',"[('section_id','=',"+str(section.id)+"),('state','=','cancel')]", 8), + (12,'Unclosed ',"[('section_id','=',"+str(section.id)+"),('state','<>','cancel'), ('state','<>','done')]", 8), + (13,'Open ',"[('section_id','=',"+str(section.id)+"),('state','=','open')]", 12), + (14,'Pending ',"[('section_id','=',"+str(section.id)+"),('state','=','pending')]", 12), + (15,'Draft ',"[('section_id','=',"+str(section.id)+"),('state','=','draft')]", 12), + (16,'Unassigned ',"[('section_id','=',"+str(section.id)+"),('user_id','=',False),('state','<>','cancel'),('state','<>','done')]", 12), + ]: + view_mode = 'tree,form' + icon = 'STOCK_JUSTIFY_FILL' + if index==0: + view_mode = 'form,tree' + icon = 'STOCK_NEW' + menu_id=self.pool.get('ir.ui.menu').create(cr, uid, { + 'name': mname+name, + 'parent_id': menus[latest], + 'icon': icon + }) + menus[index] = menu_id + action_id = self.pool.get('ir.actions.act_window').create(cr,uid, { + 'name': mname+name+' Cases', + 'res_model': 'crm.case', + 'domain': mdomain, + 'view_type': 'form', + 'view_mode': view_mode, + }) + self.pool.get('ir.values').create(cr, uid, { + 'name': 'Open Cases', + 'key2': 'tree_but_open', + 'model': 'ir.ui.menu', + 'res_id': menu_id, + 'value': 'ir.actions.act_window,%d'%action_id, + 'object': True + }) + return True + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','parent_id'], context) + res = [] + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1]+' / '+name + res.append((record['id'], name)) + return res crm_case_section() class crm_case_categ(osv.osv): - _name = "crm.case.categ" - _description = "Category of case" - _columns = { - 'name': fields.char('Case Category Name', size=64, required=True), - 'probability': fields.float('Probability', required=True), - 'section_id': fields.many2one('crm.case.section', 'Case Section'), - } - _defaults = { - 'probability': lambda *args: 0.0 - } + _name = "crm.case.categ" + _description = "Category of case" + _columns = { + 'name': fields.char('Case Category Name', size=64, required=True), + 'probability': fields.float('Probability', required=True), + 'section_id': fields.many2one('crm.case.section', 'Case Section'), + } + _defaults = { + 'probability': lambda *args: 0.0 + } crm_case_categ() class crm_case_rule(osv.osv): - _name = "crm.case.rule" - _description = "Case Rule" - _columns = { - 'name': fields.char('Rule Name',size=64, required=True), - 'active': fields.boolean('Active'), - 'sequence': fields.integer('Sequence'), - - 'trg_state_from': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case state from', size=16), - 'trg_state_to': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case state to', size=16), - - 'trg_date_type': fields.selection([ - ('none','None'), - ('create','Creation Date'), - ('action_last','Last Action Date'), - ('deadline','Deadline'), - ], 'Trigger Date', size=16), - 'trg_date_range': fields.integer('Delay after trigger date'), - 'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'), - - 'trg_section_id': fields.many2one('crm.case.section', 'Section'), - 'trg_categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',trg_section_id)]"), - 'trg_user_id': fields.many2one('res.users', 'Responsible'), - - 'trg_partner_id': fields.many2one('res.partner', 'Partner'), - 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), - - 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Priority min'), - 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Priority max'), - - 'act_method': fields.char('Call Object Method', size=64), - 'act_state': fields.selection([('','')]+AVAILABLE_STATES, 'Set state to', size=16), - 'act_section_id': fields.many2one('crm.case.section', 'Set section to'), - 'act_user_id': fields.many2one('res.users', 'Set responsible to'), - 'act_priority': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to'), - 'act_email_cc': fields.char('Add watchers (Cc)', size=250), - - 'act_remind_partner': fields.boolean('Remind partner'), - 'act_remind_user': fields.boolean('Remind responsible'), - 'act_remind_attach': fields.boolean('Remind with attachment'), - - 'act_mail_to_user': fields.boolean('Mail to responsible'), - 'act_mail_to_partner': fields.boolean('Mail to partner'), - 'act_mail_to_watchers': fields.boolean('Mail to watchers (Cc)'), - 'act_mail_to_email': fields.char('Mail to these emails', size=128), - 'act_mail_body': fields.text('Mail body') - } - _defaults = { - 'active': lambda *a: 1, - 'trg_date_type': lambda *a: 'none', - 'trg_date_range_type': lambda *a: 'day', - 'act_mail_to_user': lambda *a: 0, - 'act_remind_partner': lambda *a: 0, - 'act_remind_user': lambda *a: 0, - 'act_mail_to_partner': lambda *a: 0, - 'act_mail_to_watchers': lambda *a: 0, - } - _order = 'sequence' - - def _check(self, cr, uid, ids=False, context={}): - ''' - Function called by the sceduler to proccess cases for date actions - Only works on not done and canceled cases - ''' - cr.execute('select * from crm_case \ - where (date_action_last<%s or date_action_last is null) \ - and (date_action_next<=%s or date_action_next is null) \ - and state not in (\'cancel\',\'done\')', - (time.strftime("%Y-%m-%d %H:%M:%S"), - time.strftime('%Y-%m-%d %H:%M:%S'))) - ids2 = map(lambda x: x[0], cr.fetchall() or []) - case_obj = self.pool.get('crm.case') - cases = case_obj.browse(cr, uid, ids2, context) - return case_obj._action(cr, uid, cases, False, context=context) + _name = "crm.case.rule" + _description = "Case Rule" + _columns = { + 'name': fields.char('Rule Name',size=64, required=True), + 'active': fields.boolean('Active'), + 'sequence': fields.integer('Sequence'), + + 'trg_state_from': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case state from', size=16), + 'trg_state_to': fields.selection([('',''),('escalate','Escalate')]+AVAILABLE_STATES, 'Case state to', size=16), + + 'trg_date_type': fields.selection([ + ('none','None'), + ('create','Creation Date'), + ('action_last','Last Action Date'), + ('deadline','Deadline'), + ], 'Trigger Date', size=16), + 'trg_date_range': fields.integer('Delay after trigger date'), + 'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),('day','Days'),('month','Months')], 'Delay type'), + + 'trg_section_id': fields.many2one('crm.case.section', 'Section'), + 'trg_categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',trg_section_id)]"), + 'trg_user_id': fields.many2one('res.users', 'Responsible'), + + 'trg_partner_id': fields.many2one('res.partner', 'Partner'), + 'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'), + + 'trg_priority_from': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Priority min'), + 'trg_priority_to': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Priority max'), + + 'act_method': fields.char('Call Object Method', size=64), + 'act_state': fields.selection([('','')]+AVAILABLE_STATES, 'Set state to', size=16), + 'act_section_id': fields.many2one('crm.case.section', 'Set section to'), + 'act_user_id': fields.many2one('res.users', 'Set responsible to'), + 'act_priority': fields.selection([('','')] + AVAILABLE_PRIORITIES, 'Set priority to'), + 'act_email_cc': fields.char('Add watchers (Cc)', size=250), + + 'act_remind_partner': fields.boolean('Remind partner'), + 'act_remind_user': fields.boolean('Remind responsible'), + 'act_remind_attach': fields.boolean('Remind with attachment'), + + 'act_mail_to_user': fields.boolean('Mail to responsible'), + 'act_mail_to_partner': fields.boolean('Mail to partner'), + 'act_mail_to_watchers': fields.boolean('Mail to watchers (Cc)'), + 'act_mail_to_email': fields.char('Mail to these emails', size=128), + 'act_mail_body': fields.text('Mail body') + } + _defaults = { + 'active': lambda *a: 1, + 'trg_date_type': lambda *a: 'none', + 'trg_date_range_type': lambda *a: 'day', + 'act_mail_to_user': lambda *a: 0, + 'act_remind_partner': lambda *a: 0, + 'act_remind_user': lambda *a: 0, + 'act_mail_to_partner': lambda *a: 0, + 'act_mail_to_watchers': lambda *a: 0, + } + _order = 'sequence' + + def _check(self, cr, uid, ids=False, context={}): + ''' + Function called by the sceduler to proccess cases for date actions + Only works on not done and canceled cases + ''' + cr.execute('select * from crm_case \ + where (date_action_last<%s or date_action_last is null) \ + and (date_action_next<=%s or date_action_next is null) \ + and state not in (\'cancel\',\'done\')', + (time.strftime("%Y-%m-%d %H:%M:%S"), + time.strftime('%Y-%m-%d %H:%M:%S'))) + ids2 = map(lambda x: x[0], cr.fetchall() or []) + case_obj = self.pool.get('crm.case') + cases = case_obj.browse(cr, uid, ids2, context) + return case_obj._action(cr, uid, cases, False, context=context) crm_case_rule() def _links_get(self, cr, uid, context={}): - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context) - return [(r['object'], r['name']) for r in res] + obj = self.pool.get('res.request.link') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['object', 'name'], context) + return [(r['object'], r['name']) for r in res] class crm_case(osv.osv): - _name = "crm.case" - _description = "Case" - - def _email_last(self, cursor, user, ids, name, arg, context=None): - res = {} - for case in self.browse(cursor, user, ids): - if case.history_line: - res[case.id] = case.history_line[0].description - else: - res[case.id] = False - return res - def copy(self, cr, uid, id, default=None, context={}): - if not default: default = {} - default.update( {'state':'draft', 'id':False, 'history_line':[],'log_ids':[]}) - return super(crm_case, self).copy(cr, uid, id, default, context) - - _columns = { - 'id': fields.integer('ID', readonly=True), - 'name': fields.char('Description',size=64, required=True), - 'priority': fields.selection(AVAILABLE_PRIORITIES, 'Priority'), - 'active': fields.boolean('Active'), - 'description': fields.text('Your action'), - 'section_id': fields.many2one('crm.case.section', 'Section', required=True, select=True), - 'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',section_id)]"), - 'planned_revenue': fields.float('Planned Revenue'), - 'planned_cost': fields.float('Planned Costs'), - 'probability': fields.float('Probability (0.50)'), - 'email_from': fields.char('Partner Email', size=128), - 'email_cc': fields.char('Watchers Emails', size=252), - 'email_last': fields.function(_email_last, method=True, - string='Latest E-Mail', type='text'), - 'partner_id': fields.many2one('res.partner', 'Partner'), - 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', domain="[('partner_id','=',partner_id)]"), - 'som': fields.many2one('res.partner.som', 'State of Mind'), - 'date': fields.datetime('Date'), - 'create_date': fields.datetime('Created' ,readonly=True), - 'date_deadline': fields.datetime('Deadline'), - 'date_closed': fields.datetime('Closed', readonly=True), - 'canal_id': fields.many2one('res.partner.canal', 'Channel'), - 'user_id': fields.many2one('res.users', 'User Responsible'), - 'history_line': fields.one2many('crm.case.history', 'case_id', 'Communication'), - 'log_ids': fields.one2many('crm.case.log', 'case_id', 'Logs History'), - 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), - 'ref' : fields.reference('Reference', selection=_links_get, size=128), - 'ref2' : fields.reference('Reference 2', selection=_links_get, size=128), - - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - } - def _get_default_partner_address(self, cr, uid, context): - if not context.get('portal',False): - return False - return self.pool.get('res.users').browse(cr, uid, uid, context).address_id.id - def _get_default_partner(self, cr, uid, context): - if not context.get('portal',False): - return False - user = self.pool.get('res.users').browse(cr, uid, uid, context) - if not user.address_id: - return False - return user.address_id.partner_id.id - def _get_default_email(self, cr, uid, context): - if not context.get('portal',False): - return False - user = self.pool.get('res.users').browse(cr, uid, uid, context) - if not user.address_id: - return False - return user.address_id.email - def _get_default_user(self, cr, uid, context): - if context.get('portal', False): - return False - return uid - _defaults = { - 'active': lambda *a: 1, - 'user_id': _get_default_user, - 'partner_id': _get_default_partner, - 'partner_address_id': _get_default_partner_address, - 'email_from': _get_default_email, - 'state': lambda *a: 'draft', - 'priority': lambda *a: AVAILABLE_PRIORITIES[2][0], - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } - _order = 'priority, date_deadline desc, id desc' - - def _action(self, cr, uid, cases, state_to, scrit=None, context={}): - if not scrit: - scrit = [] - action_ids = self.pool.get('crm.case.rule').search(cr, uid, scrit) - level = MAX_LEVEL - while len(action_ids) and level: - newactions = [] - actions = self.pool.get('crm.case.rule').browse(cr, uid, action_ids, context) - for case in cases: - for action in actions: - ok = True - ok = ok and (not action.trg_state_from or action.trg_state_from==case.state) - ok = ok and (not action.trg_state_to or action.trg_state_to==state_to) - ok = ok and (not action.trg_section_id or action.trg_section_id.id==case.section_id.id) - ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==case.categ_id.id) - ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==case.user_id.id) - ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==case.partner_id.id) - ok = ok and ( - not action.trg_partner_categ_id.id or - ( - case.partner_id.id and - (action.trg_partner_categ_id.id in map(lambda x: x.id, case.partner_id.category_id or [])) - ) - ) - ok = ok and (not action.trg_priority_from or action.trg_priority_from>=case.priority) - ok = ok and (not action.trg_priority_to or action.trg_priority_to<=case.priority) - if not ok: - continue - - base = False - if action.trg_date_type=='create': - base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='action_last': - if case.date_action_last: - base = mx.DateTime.strptime(case.date_action_last, '%Y-%m-%d %H:%M:%S') - else: - base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') - elif action.trg_date_type=='deadline' and case.date_deadline: - base = mx.DateTime.strptime(case.date_deadline, '%Y-%m-%d') - if base: - fnct = { - 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), - 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), - 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), - 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), - } - d = base + fnct[action.trg_date_range_type](action.trg_date_range) - dt = d.strftime('%Y-%m-%d %H:%M:%S') - ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ - ((not case.date_action_next) or \ - (dt >= case.date_action_next and \ - case.date_action_last < case.date_action_next)) - if not ok: - if not case.date_action_next or dt < case.date_action_next: - case.date_action_next = dt - self.write(cr, uid, [case.id], {'date_action_next': dt}, context) - - else: - ok = action.trg_date_type=='none' - - if ok: - write = {} - if action.act_state: - case.state = action.act_state - write['state'] = action.act_state - if action.act_section_id: - case.section_id = action.act_section_id - write['section_id'] = action.act_section_id.id - if action.act_user_id: - case.user_id = action.act_user_id - write['user_id'] = action.act_user_id.id - if action.act_priority: - case.priority = action.act_priority - write['priority'] = action.act_priority - if action.act_email_cc: - if '@' in (case.email_cc or ''): - write['email_cc'] = case.email_cc+','+action.act_email_cc - else: - write['email_cc'] = action.act_email_cc - write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S') - self.write(cr, uid, [case.id], write, context) - caseobj = self.pool.get('crm.case') - if action.act_remind_user: - caseobj.remind_user(cr, uid, [case.id], context, attach=action.act_remind_attach) - if action.act_remind_partner: - caseobj.remind_partner(cr, uid, [case.id], context, attach=action.act_remind_attach) - if action.act_method: - getattr(caseobj, 'act_method')(cr, uid, [case.id], action, context) - emails = [] - if action.act_mail_to_user: - if case.user_id and case.user_id.address_id: - emails.append(case.user_id.address_id.email) - if action.act_mail_to_partner: - emails.append(case.email_from) - if action.act_mail_to_watchers: - emails += (case.email_cc or '').split(',') - if action.act_mail_to_email: - emails += (action.act_mail_to_email or '').split(',') - emails = filter(None, emails) - if len(emails) and action.act_mail_body: - self.email_send(cr, uid, case, emails, action.act_mail_body) - break - action_ids = newactions - level -= 1 - return True - - def email_send(self, cr, uid, case, emails, body, context={}): - data = { - 'case_id': case.id, - 'case_subject': case.name, - 'case_date': case.date, - - 'case_user': case.user_id.name or '/', - 'case_user_email': (case.user_id and case.user_id.address_id and case.user_id.address_id.email) or '/', - 'case_user_phone': (case.user_id and case.user_id.address_id and case.user_id.address_id.phone) or '/', - - 'email_from': case.email_from, - 'partner': (case.partner_id and case.partner_id.name) or '/', - 'partner_email': (case.partner_address_id and case.partner_address_id.email) or '/', - } - body = body % data - if case.user_id and case.user_id.address_id and case.user_id.address_id.email: - emailfrom = case.user_id.address_id.email - else: - emailfrom = case.section_id.reply_to - tools.email_send(emailfrom, emails, '['+str(case.id)+'] '+case.name, body, reply_to=case.section_id.reply_to, tinycrm=str(case.id)) - return True - def __log(self, cr, uid, cases, keyword, context={}): - if not self.pool.get('res.partner.event.type').check(cr, uid, 'crm_case_'+keyword): - return False - for case in cases: - if case.partner_id: - name = 'Case ' + keyword + ': ' + case.name - if isinstance(name, str): - name = unicode(name, 'utf-8') - if len(name) > 64: - name = name[:61] + '...' - self.pool.get('res.partner.event').create(cr, uid, { - 'name': name, - 'som':(case.som or False) and case.som.id, - 'description':case.description, - 'partner_id':case.partner_id.id, - 'date':time.strftime('%Y-%m-%d %H:%M:%S'), - 'canal_id':(case.canal_id or False) and case.canal_id.id, - 'user_id':uid, - 'document': 'crm.case,%i' % case.id, - }) - return True - - def __history(self, cr, uid, cases, keyword, history=False, email=False, context={}): - for case in cases: - data = { - 'name': keyword, - 'som': case.som.id, - 'canal_id': case.canal_id.id, - 'user_id': uid, - 'case_id': case.id - } - obj = self.pool.get('crm.case.log') - if history and case.description: - obj = self.pool.get('crm.case.history') - data['description'] = case.description - data['email'] = email or \ - (case.user_id and case.user_id.address_id and \ - case.user_id.address_id.email) or False - obj.create(cr, uid, data, context) - return True - - def create(self, cr, uid, *args, **argv): - res = super(crm_case, self).create(cr, uid, *args, **argv) - cases = self.browse(cr, uid, [res]) - cases[0].state # to fill the browse record cache - self.__log(cr,uid, cases, 'draft') - self._action(cr,uid, cases, 'draft') - return res - - def remind_partner(self, cr, uid, ids, context={}, attach=False): - return self.remind_user(cr, uid, ids, context, attach, - destination=False) - - def remind_user(self, cr, uid, ids, context={}, attach=False, - destination=True): - for case in self.browse(cr, uid, ids): - if case.user_id and case.user_id.address_id \ - and case.user_id.address_id.email \ - and case.email_from: - src = case.email_from - dest = case.user_id.address_id.email - body = case.email_last or case.description - if not destination: - src,dest = dest,src - if case.user_id.signature: - body += '\n\n%s' % (case.user_id.signature) - dest = [dest] - if not attach: - tools.email_send( - src, - dest, - 'Reminder: '+'['+str(case.id)+']'+' '+case.name, - body, - reply_to=case.section_id.reply_to, tinycrm=str(case.id) - ) - else: - attach_ids = self.pool.get('ir.attachment').search(cr, uid, - [('res_model', '=', 'crm.case'), - ('res_id', '=', case.id)]) - res = self.pool.get('ir.attachment').read(cr, uid, - attach_ids, ['datas_fname','datas']) - res = map(lambda x: (x['datas_fname'], - base64.decodestring(x['datas'])), res) - tools.email_send_attach( - src, - dest, - 'Reminder: '+'['+str(case.id)+']'+' '+case.name, - body, - reply_to=case.section_id.reply_to, - attach=res, tinycrm=str(case.id) - ) - return True - - def add_reply(self, cursor, user, ids, context=None): - for case in self.browse(cursor, user, ids, context=context): - if case.history_line: - description = case.history_line[0].description - self.write(cursor, user, case.id, { - 'description': '> ' + description.replace('\n','\n> '), - }, context=context) - return True - - def case_log(self, cr, uid, ids,context={}, email=False, *args): - cases = self.browse(cr, uid, ids) - self.__history(cr, uid, cases, 'Historize', history=True, email=email) - return self.write(cr, uid, ids, {'description': False, 'som': False, - 'canal_id': False}) - - def case_log_reply(self, cr, uid, ids, context={}, email=False, *args): - cases = self.browse(cr, uid, ids) - for case in cases: - if not case.email_from: - raise osv.except_osv(_('Error!'), - _('You must put a Partner eMail to use this action!')) - self.__history(cr, uid, cases, 'Send', history=True, email=False) - for case in cases: - self.write(cr, uid, [case.id], { - 'description': False, - 'som': False, - 'canal_id': False, - }) - emails = [case.email_from] + (case.email_cc or '').split(',') - emails = filter(None, emails) - body = case.description - if case.user_id.signature: - body += '\n\n%s' % (case.user_id.signature) - tools.email_send(case.user_id.address_id.email, emails, - '['+str(case.id)+'] '+case.name, body, - reply_to=case.section_id.reply_to, tinycrm=str(case.id)) - return True - - def onchange_partner_id(self, cr, uid, ids, part, email=False): - if not part: - return {'value':{'partner_address_id': False}} - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) - data = {'partner_address_id':addr['contact']} - if addr['contact'] and not email: - data['email_from'] = self.pool.get('res.partner.address').browse(cr, uid, addr['contact']).email - return {'value':data} - - def onchange_categ_id(self, cr, uid, ids, categ, context={}): - if not categ: - return {'value':{}} - cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability - return {'value':{'probability':cat}} - - - def onchange_partner_address_id(self, cr, uid, ids, part, email=False): - if not part: - return {'value':{}} - data = {} - if not email: - data['email_from'] = self.pool.get('res.partner.address').browse(cr, uid, part).email - return {'value':data} - - def case_close(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - cases[0].state # to fill the browse record cache - self.__log(cr,uid, cases, 'done') - self.__history(cr, uid, cases, 'Close') - self.write(cr, uid, ids, {'state':'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')}) - # - # We use the cache of cases to keep the old case state - # - self._action(cr,uid, cases, 'done') - return True - - def case_escalate(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - for case in cases: - data = {'active':True, 'user_id': False} - if case.section_id.parent_id: - data['section_id'] = case.section_id.parent_id.id - if case.section_id.parent_id.user_id: - data['user_id'] = case.section_id.parent_id.user_id.id - else: - raise osv.except_osv(_('Error !'), _('You can not escalate this case.\nYou are already at the top level.')) - self.write(cr, uid, ids, data) - self.__history(cr, uid, cases, 'escalate') - self._action(cr, uid, cases, 'escalate') - return True - - - def case_open(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - self.__log(cr, uid, cases, 'open') - self.__history(cr, uid, cases, 'Open') - for case in cases: - data = {'state':'open', 'active':True} - if not case.user_id: - data['user_id'] = uid - self.write(cr, uid, ids, data) - self._action(cr,uid, cases, 'open') - return True - - def emails_get(self, cr, uid, id, context={}): - case = self.browse(cr, uid, id) - return ((case.user_id and case.user_id.address_id and case.user_id.address_id.email) or False, case.email_from, case.email_cc, case.priority) - - def case_cancel(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - cases[0].state # to fill the browse record cache - self.__log(cr, uid, cases, 'cancel') - self.__history(cr, uid, cases, 'Cancel') - self.write(cr, uid, ids, {'state':'cancel', 'active':True}) - self._action(cr,uid, cases, 'cancel') - return True - - def case_pending(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - cases[0].state # to fill the browse record cache - self.__log(cr, uid, cases, 'pending') - self.__history(cr, uid, cases, 'Pending') - self.write(cr, uid, ids, {'state':'pending', 'active':True}) - self._action(cr,uid, cases, 'pending') - return True - - def case_reset(self, cr, uid, ids, *args): - cases = self.browse(cr, uid, ids) - cases[0].state # to fill the browse record cache - self.__log(cr, uid, cases, 'draft') - self.__history(cr, uid, cases, 'Draft') - self.write(cr, uid, ids, {'state':'draft', 'active':True}) - self._action(cr, uid, cases, 'draft') - return True + _name = "crm.case" + _description = "Case" + + def _email_last(self, cursor, user, ids, name, arg, context=None): + res = {} + for case in self.browse(cursor, user, ids): + if case.history_line: + res[case.id] = case.history_line[0].description + else: + res[case.id] = False + return res + def copy(self, cr, uid, id, default=None, context={}): + if not default: default = {} + default.update( {'state':'draft', 'id':False, 'history_line':[],'log_ids':[]}) + return super(crm_case, self).copy(cr, uid, id, default, context) + + _columns = { + 'id': fields.integer('ID', readonly=True), + 'name': fields.char('Description',size=64, required=True), + 'priority': fields.selection(AVAILABLE_PRIORITIES, 'Priority'), + 'active': fields.boolean('Active'), + 'description': fields.text('Your action'), + 'section_id': fields.many2one('crm.case.section', 'Section', required=True, select=True), + 'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',section_id)]"), + 'planned_revenue': fields.float('Planned Revenue'), + 'planned_cost': fields.float('Planned Costs'), + 'probability': fields.float('Probability (0.50)'), + 'email_from': fields.char('Partner Email', size=128), + 'email_cc': fields.char('Watchers Emails', size=252), + 'email_last': fields.function(_email_last, method=True, + string='Latest E-Mail', type='text'), + 'partner_id': fields.many2one('res.partner', 'Partner'), + 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', domain="[('partner_id','=',partner_id)]"), + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'date': fields.datetime('Date'), + 'create_date': fields.datetime('Created' ,readonly=True), + 'date_deadline': fields.datetime('Deadline'), + 'date_closed': fields.datetime('Closed', readonly=True), + 'canal_id': fields.many2one('res.partner.canal', 'Channel'), + 'user_id': fields.many2one('res.users', 'User Responsible'), + 'history_line': fields.one2many('crm.case.history', 'case_id', 'Communication'), + 'log_ids': fields.one2many('crm.case.log', 'case_id', 'Logs History'), + 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), + 'ref' : fields.reference('Reference', selection=_links_get, size=128), + 'ref2' : fields.reference('Reference 2', selection=_links_get, size=128), + + 'date_action_last': fields.datetime('Last Action', readonly=1), + 'date_action_next': fields.datetime('Next Action', readonly=1), + } + def _get_default_partner_address(self, cr, uid, context): + if not context.get('portal',False): + return False + return self.pool.get('res.users').browse(cr, uid, uid, context).address_id.id + def _get_default_partner(self, cr, uid, context): + if not context.get('portal',False): + return False + user = self.pool.get('res.users').browse(cr, uid, uid, context) + if not user.address_id: + return False + return user.address_id.partner_id.id + def _get_default_email(self, cr, uid, context): + if not context.get('portal',False): + return False + user = self.pool.get('res.users').browse(cr, uid, uid, context) + if not user.address_id: + return False + return user.address_id.email + def _get_default_user(self, cr, uid, context): + if context.get('portal', False): + return False + return uid + _defaults = { + 'active': lambda *a: 1, + 'user_id': _get_default_user, + 'partner_id': _get_default_partner, + 'partner_address_id': _get_default_partner_address, + 'email_from': _get_default_email, + 'state': lambda *a: 'draft', + 'priority': lambda *a: AVAILABLE_PRIORITIES[2][0], + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + _order = 'priority, date_deadline desc, id desc' + + def _action(self, cr, uid, cases, state_to, scrit=None, context={}): + if not scrit: + scrit = [] + action_ids = self.pool.get('crm.case.rule').search(cr, uid, scrit) + level = MAX_LEVEL + while len(action_ids) and level: + newactions = [] + actions = self.pool.get('crm.case.rule').browse(cr, uid, action_ids, context) + for case in cases: + for action in actions: + ok = True + ok = ok and (not action.trg_state_from or action.trg_state_from==case.state) + ok = ok and (not action.trg_state_to or action.trg_state_to==state_to) + ok = ok and (not action.trg_section_id or action.trg_section_id.id==case.section_id.id) + ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==case.categ_id.id) + ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==case.user_id.id) + ok = ok and (not action.trg_partner_id.id or action.trg_partner_id.id==case.partner_id.id) + ok = ok and ( + not action.trg_partner_categ_id.id or + ( + case.partner_id.id and + (action.trg_partner_categ_id.id in map(lambda x: x.id, case.partner_id.category_id or [])) + ) + ) + ok = ok and (not action.trg_priority_from or action.trg_priority_from>=case.priority) + ok = ok and (not action.trg_priority_to or action.trg_priority_to<=case.priority) + if not ok: + continue + + base = False + if action.trg_date_type=='create': + base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='action_last': + if case.date_action_last: + base = mx.DateTime.strptime(case.date_action_last, '%Y-%m-%d %H:%M:%S') + else: + base = mx.DateTime.strptime(case.create_date[:19], '%Y-%m-%d %H:%M:%S') + elif action.trg_date_type=='deadline' and case.date_deadline: + base = mx.DateTime.strptime(case.date_deadline, '%Y-%m-%d') + if base: + fnct = { + 'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval), + 'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval), + 'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval), + 'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval), + } + d = base + fnct[action.trg_date_range_type](action.trg_date_range) + dt = d.strftime('%Y-%m-%d %H:%M:%S') + ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \ + ((not case.date_action_next) or \ + (dt >= case.date_action_next and \ + case.date_action_last < case.date_action_next)) + if not ok: + if not case.date_action_next or dt < case.date_action_next: + case.date_action_next = dt + self.write(cr, uid, [case.id], {'date_action_next': dt}, context) + + else: + ok = action.trg_date_type=='none' + + if ok: + write = {} + if action.act_state: + case.state = action.act_state + write['state'] = action.act_state + if action.act_section_id: + case.section_id = action.act_section_id + write['section_id'] = action.act_section_id.id + if action.act_user_id: + case.user_id = action.act_user_id + write['user_id'] = action.act_user_id.id + if action.act_priority: + case.priority = action.act_priority + write['priority'] = action.act_priority + if action.act_email_cc: + if '@' in (case.email_cc or ''): + write['email_cc'] = case.email_cc+','+action.act_email_cc + else: + write['email_cc'] = action.act_email_cc + write['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S') + self.write(cr, uid, [case.id], write, context) + caseobj = self.pool.get('crm.case') + if action.act_remind_user: + caseobj.remind_user(cr, uid, [case.id], context, attach=action.act_remind_attach) + if action.act_remind_partner: + caseobj.remind_partner(cr, uid, [case.id], context, attach=action.act_remind_attach) + if action.act_method: + getattr(caseobj, 'act_method')(cr, uid, [case.id], action, context) + emails = [] + if action.act_mail_to_user: + if case.user_id and case.user_id.address_id: + emails.append(case.user_id.address_id.email) + if action.act_mail_to_partner: + emails.append(case.email_from) + if action.act_mail_to_watchers: + emails += (case.email_cc or '').split(',') + if action.act_mail_to_email: + emails += (action.act_mail_to_email or '').split(',') + emails = filter(None, emails) + if len(emails) and action.act_mail_body: + self.email_send(cr, uid, case, emails, action.act_mail_body) + break + action_ids = newactions + level -= 1 + return True + + def email_send(self, cr, uid, case, emails, body, context={}): + data = { + 'case_id': case.id, + 'case_subject': case.name, + 'case_date': case.date, + + 'case_user': case.user_id.name or '/', + 'case_user_email': (case.user_id and case.user_id.address_id and case.user_id.address_id.email) or '/', + 'case_user_phone': (case.user_id and case.user_id.address_id and case.user_id.address_id.phone) or '/', + + 'email_from': case.email_from, + 'partner': (case.partner_id and case.partner_id.name) or '/', + 'partner_email': (case.partner_address_id and case.partner_address_id.email) or '/', + } + body = body % data + if case.user_id and case.user_id.address_id and case.user_id.address_id.email: + emailfrom = case.user_id.address_id.email + else: + emailfrom = case.section_id.reply_to + tools.email_send(emailfrom, emails, '['+str(case.id)+'] '+case.name, body, reply_to=case.section_id.reply_to, tinycrm=str(case.id)) + return True + def __log(self, cr, uid, cases, keyword, context={}): + if not self.pool.get('res.partner.event.type').check(cr, uid, 'crm_case_'+keyword): + return False + for case in cases: + if case.partner_id: + name = 'Case ' + keyword + ': ' + case.name + if isinstance(name, str): + name = unicode(name, 'utf-8') + if len(name) > 64: + name = name[:61] + '...' + self.pool.get('res.partner.event').create(cr, uid, { + 'name': name, + 'som':(case.som or False) and case.som.id, + 'description':case.description, + 'partner_id':case.partner_id.id, + 'date':time.strftime('%Y-%m-%d %H:%M:%S'), + 'canal_id':(case.canal_id or False) and case.canal_id.id, + 'user_id':uid, + 'document': 'crm.case,%i' % case.id, + }) + return True + + def __history(self, cr, uid, cases, keyword, history=False, email=False, context={}): + for case in cases: + data = { + 'name': keyword, + 'som': case.som.id, + 'canal_id': case.canal_id.id, + 'user_id': uid, + 'case_id': case.id + } + obj = self.pool.get('crm.case.log') + if history and case.description: + obj = self.pool.get('crm.case.history') + data['description'] = case.description + data['email'] = email or \ + (case.user_id and case.user_id.address_id and \ + case.user_id.address_id.email) or False + obj.create(cr, uid, data, context) + return True + + def create(self, cr, uid, *args, **argv): + res = super(crm_case, self).create(cr, uid, *args, **argv) + cases = self.browse(cr, uid, [res]) + cases[0].state # to fill the browse record cache + self.__log(cr,uid, cases, 'draft') + self._action(cr,uid, cases, 'draft') + return res + + def remind_partner(self, cr, uid, ids, context={}, attach=False): + return self.remind_user(cr, uid, ids, context, attach, + destination=False) + + def remind_user(self, cr, uid, ids, context={}, attach=False, + destination=True): + for case in self.browse(cr, uid, ids): + if case.user_id and case.user_id.address_id \ + and case.user_id.address_id.email \ + and case.email_from: + src = case.email_from + dest = case.user_id.address_id.email + body = case.email_last or case.description + if not destination: + src,dest = dest,src + if case.user_id.signature: + body += '\n\n%s' % (case.user_id.signature) + dest = [dest] + if not attach: + tools.email_send( + src, + dest, + 'Reminder: '+'['+str(case.id)+']'+' '+case.name, + body, + reply_to=case.section_id.reply_to, tinycrm=str(case.id) + ) + else: + attach_ids = self.pool.get('ir.attachment').search(cr, uid, + [('res_model', '=', 'crm.case'), + ('res_id', '=', case.id)]) + res = self.pool.get('ir.attachment').read(cr, uid, + attach_ids, ['datas_fname','datas']) + res = map(lambda x: (x['datas_fname'], + base64.decodestring(x['datas'])), res) + tools.email_send_attach( + src, + dest, + 'Reminder: '+'['+str(case.id)+']'+' '+case.name, + body, + reply_to=case.section_id.reply_to, + attach=res, tinycrm=str(case.id) + ) + return True + + def add_reply(self, cursor, user, ids, context=None): + for case in self.browse(cursor, user, ids, context=context): + if case.history_line: + description = case.history_line[0].description + self.write(cursor, user, case.id, { + 'description': '> ' + description.replace('\n','\n> '), + }, context=context) + return True + + def case_log(self, cr, uid, ids,context={}, email=False, *args): + cases = self.browse(cr, uid, ids) + self.__history(cr, uid, cases, 'Historize', history=True, email=email) + return self.write(cr, uid, ids, {'description': False, 'som': False, + 'canal_id': False}) + + def case_log_reply(self, cr, uid, ids, context={}, email=False, *args): + cases = self.browse(cr, uid, ids) + for case in cases: + if not case.email_from: + raise osv.except_osv(_('Error!'), + _('You must put a Partner eMail to use this action!')) + self.__history(cr, uid, cases, 'Send', history=True, email=False) + for case in cases: + self.write(cr, uid, [case.id], { + 'description': False, + 'som': False, + 'canal_id': False, + }) + emails = [case.email_from] + (case.email_cc or '').split(',') + emails = filter(None, emails) + body = case.description + if case.user_id.signature: + body += '\n\n%s' % (case.user_id.signature) + tools.email_send(case.user_id.address_id.email, emails, + '['+str(case.id)+'] '+case.name, body, + reply_to=case.section_id.reply_to, tinycrm=str(case.id)) + return True + + def onchange_partner_id(self, cr, uid, ids, part, email=False): + if not part: + return {'value':{'partner_address_id': False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) + data = {'partner_address_id':addr['contact']} + if addr['contact'] and not email: + data['email_from'] = self.pool.get('res.partner.address').browse(cr, uid, addr['contact']).email + return {'value':data} + + def onchange_categ_id(self, cr, uid, ids, categ, context={}): + if not categ: + return {'value':{}} + cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability + return {'value':{'probability':cat}} + + + def onchange_partner_address_id(self, cr, uid, ids, part, email=False): + if not part: + return {'value':{}} + data = {} + if not email: + data['email_from'] = self.pool.get('res.partner.address').browse(cr, uid, part).email + return {'value':data} + + def case_close(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + cases[0].state # to fill the browse record cache + self.__log(cr,uid, cases, 'done') + self.__history(cr, uid, cases, 'Close') + self.write(cr, uid, ids, {'state':'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')}) + # + # We use the cache of cases to keep the old case state + # + self._action(cr,uid, cases, 'done') + return True + + def case_escalate(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + for case in cases: + data = {'active':True, 'user_id': False} + if case.section_id.parent_id: + data['section_id'] = case.section_id.parent_id.id + if case.section_id.parent_id.user_id: + data['user_id'] = case.section_id.parent_id.user_id.id + else: + raise osv.except_osv(_('Error !'), _('You can not escalate this case.\nYou are already at the top level.')) + self.write(cr, uid, ids, data) + self.__history(cr, uid, cases, 'escalate') + self._action(cr, uid, cases, 'escalate') + return True + + + def case_open(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + self.__log(cr, uid, cases, 'open') + self.__history(cr, uid, cases, 'Open') + for case in cases: + data = {'state':'open', 'active':True} + if not case.user_id: + data['user_id'] = uid + self.write(cr, uid, ids, data) + self._action(cr,uid, cases, 'open') + return True + + def emails_get(self, cr, uid, id, context={}): + case = self.browse(cr, uid, id) + return ((case.user_id and case.user_id.address_id and case.user_id.address_id.email) or False, case.email_from, case.email_cc, case.priority) + + def case_cancel(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + cases[0].state # to fill the browse record cache + self.__log(cr, uid, cases, 'cancel') + self.__history(cr, uid, cases, 'Cancel') + self.write(cr, uid, ids, {'state':'cancel', 'active':True}) + self._action(cr,uid, cases, 'cancel') + return True + + def case_pending(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + cases[0].state # to fill the browse record cache + self.__log(cr, uid, cases, 'pending') + self.__history(cr, uid, cases, 'Pending') + self.write(cr, uid, ids, {'state':'pending', 'active':True}) + self._action(cr,uid, cases, 'pending') + return True + + def case_reset(self, cr, uid, ids, *args): + cases = self.browse(cr, uid, ids) + cases[0].state # to fill the browse record cache + self.__log(cr, uid, cases, 'draft') + self.__history(cr, uid, cases, 'Draft') + self.write(cr, uid, ids, {'state':'draft', 'active':True}) + self._action(cr, uid, cases, 'draft') + return True crm_case() class crm_case_log(osv.osv): - _name = "crm.case.log" - _description = "Case communication history" - _order = "id desc" - _columns = { - 'name': fields.char('Action', size=64), - 'som': fields.many2one('res.partner.som', 'State of Mind'), - 'date': fields.datetime('Date'), - 'canal_id': fields.many2one('res.partner.canal', 'Channel'), - 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), - 'case_id': fields.many2one('crm.case', 'Case', required=True, ondelete='cascade') - } - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } + _name = "crm.case.log" + _description = "Case communication history" + _order = "id desc" + _columns = { + 'name': fields.char('Action', size=64), + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'date': fields.datetime('Date'), + 'canal_id': fields.many2one('res.partner.canal', 'Channel'), + 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), + 'case_id': fields.many2one('crm.case', 'Case', required=True, ondelete='cascade') + } + _defaults = { + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } crm_case_log() class crm_case_history(osv.osv): - _name = "crm.case.history" - _description = "Case history" - _order = "id desc" - _inherits = {'crm.case.log':"log_id"} - def _note_get(self, cursor, user, ids, name, arg, context=None): - res = {} - for hist in self.browse(cursor, user, ids, context or {}): - res[hist.id] = (hist.email or '/') + ' (' + str(hist.date) + ')\n' - res[hist.id] += (hist.description or '') - return res - _columns = { - 'description': fields.text('Description'), - 'note': fields.function(_note_get, method=True, string="Description", type="text"), - 'email': fields.char('Email', size=84), - 'log_id': fields.many2one('crm.case.log','Log',ondelete='cascade'), - } + _name = "crm.case.history" + _description = "Case history" + _order = "id desc" + _inherits = {'crm.case.log':"log_id"} + def _note_get(self, cursor, user, ids, name, arg, context=None): + res = {} + for hist in self.browse(cursor, user, ids, context or {}): + res[hist.id] = (hist.email or '/') + ' (' + str(hist.date) + ')\n' + res[hist.id] += (hist.description or '') + return res + _columns = { + 'description': fields.text('Description'), + 'note': fields.function(_note_get, method=True, string="Description", type="text"), + 'email': fields.char('Email', size=84), + 'log_id': fields.many2one('crm.case.log','Log',ondelete='cascade'), + } crm_case_history() diff --git a/addons/crm/crm_operators.py b/addons/crm/crm_operators.py index 5fed4650c70..723b3c43f23 100644 --- a/addons/crm/crm_operators.py +++ b/addons/crm/crm_operators.py @@ -30,37 +30,37 @@ import time def som(cr, uid, partner_id, args): - result = args['som_interval_default'] - max = args['som_interval_max'] or 4 - factor = args['som_interval_decrease'] - date_start=time.time() - args['som_interval']*3600*24*max - for i in range(max): - next_date = date_start + args['som_interval']*3600*24 - cr.execute( - ''' - select s.factor from res_partner_event e - left join res_partner_som s - on (e.som=s.id) where partner_id=%d and date>=%s and date<%s''', - (partner_id, - time.strftime('%Y-%m-%d', time.gmtime(date_start)), - time.strftime('%Y-%m-%d', time.gmtime(next_date)))) + result = args['som_interval_default'] + max = args['som_interval_max'] or 4 + factor = args['som_interval_decrease'] + date_start=time.time() - args['som_interval']*3600*24*max + for i in range(max): + next_date = date_start + args['som_interval']*3600*24 + cr.execute( + ''' + select s.factor from res_partner_event e + left join res_partner_som s + on (e.som=s.id) where partner_id=%d and date>=%s and date<%s''', + (partner_id, + time.strftime('%Y-%m-%d', time.gmtime(date_start)), + time.strftime('%Y-%m-%d', time.gmtime(next_date)))) - soms = cr.fetchall() - if len(soms): - c = 0 - nbr = 0.0 - for som in soms: - if som[0]: - c+=1 - nbr+=som[0] - if c: - avg = nbr/c - else: - avg = result - result = result*(1-factor) + (avg*factor) - else: - avg = args['som_interval_default'] - date_start = next_date - return result + soms = cr.fetchall() + if len(soms): + c = 0 + nbr = 0.0 + for som in soms: + if som[0]: + c+=1 + nbr+=som[0] + if c: + avg = nbr/c + else: + avg = result + result = result*(1-factor) + (avg*factor) + else: + avg = args['som_interval_default'] + date_start = next_date + return result diff --git a/addons/crm/crm_segmentation.py b/addons/crm/crm_segmentation.py index ddf887f13ea..383bf86b3d7 100644 --- a/addons/crm/crm_segmentation.py +++ b/addons/crm/crm_segmentation.py @@ -33,136 +33,136 @@ import crm_operators class crm_segmentation(osv.osv): - ''' - A segmentation is a tool to automatically assign categories on partners. - These assignations are based on criterions. - ''' - _name = "crm.segmentation" - _description = "Partner Segmentation" - _columns = { - 'name': fields.char('Name', size=64, required=True, help='The name of the segmentation.'), - 'description': fields.text('Description'), - 'categ_id': fields.many2one('res.partner.category', 'Partner Category', required=True, help='The partner category that will be added to partners that match the segmentation criterions after computation.'), - 'exclusif': fields.boolean('Exclusive', help='Check if the category is limited to partners that match the segmentation criterions. If checked, remove the category from partners that doesn\'t match segmentation criterions'), - 'state': fields.selection([('not running','Not Running'),('running','Running')], 'Execution State', readonly=True), - 'partner_id': fields.integer('Max Partner ID processed'), - 'segmentation_line': fields.one2many('crm.segmentation.line', 'segmentation_id', 'Criteria', required=True), - 'som_interval': fields.integer('Days per Periode', help="A period is the average number of days between two cycle of sale or purchase for this segmentation. It's mainly used to detect if a partner has not purchased or buy for a too long time, so we suppose that his state of mind has decreased because he probably bought goods to another supplier. Use this functionnality for recurring businesses."), - 'som_interval_max': fields.integer('Max Interval', help="The computation is made on all events that occured during this interval, the past X periods."), - 'som_interval_decrease': fields.float('Decrease (0>1)', help="If the partner has not purchased (or buied) during a period, decrease the state of mind by this factor. It\'s a multiplication"), - 'som_interval_default': fields.float('Default (0=None)', help="Default state of mind for period preceeding the 'Max Interval' computation. This is the starting state of mind by default if the partner has no event."), - 'sales_purchase_active': fields.boolean('Use The Sales Purchase Rules', help='Check if you want to use this tab as part of the segmentation rule. If not checked, the criteria beneath will be ignored') - } - _defaults = { - 'partner_id': lambda *a: 0, - 'state': lambda *a: 'not running', - 'som_interval_max': lambda *a: 3, - 'som_interval_decrease': lambda *a: 0.8, - 'som_interval_default': lambda *a: 0.5 - } - - def process_continue(self, cr, uid, ids, start=False): - categs = self.read(cr,uid,ids,['categ_id','exclusif','partner_id', 'sales_purchase_active', 'profiling_active']) - for categ in categs: - if start: - if categ['exclusif']: - cr.execute('delete from res_partner_category_rel where category_id=%d', (categ['categ_id'][0],)) - - id = categ['id'] - - cr.execute('select id from res_partner order by id ') - partners = [x[0] for x in cr.fetchall()] - - if categ['sales_purchase_active']: - to_remove_list=[] - cr.execute('select id from crm_segmentation_line where segmentation_id=%d', (id,)) - line_ids = [x[0] for x in cr.fetchall()] - - for pid in partners: - if (not self.pool.get('crm.segmentation.line').test(cr, uid, line_ids, pid)): - to_remove_list.append(pid) - for pid in to_remove_list: - partners.remove(pid) - - for partner_id in partners: - cr.execute('insert into res_partner_category_rel (category_id,partner_id) values (%d,%d)', (categ['categ_id'][0],partner_id)) - cr.commit() - - self.write(cr, uid, [id], {'state':'not running', 'partner_id':0}) - cr.commit() - return True - - def process_stop(self, cr, uid, ids, *args): - return self.write(cr, uid, ids, {'state':'not running', 'partner_id':0}) - - def process_start(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'running', 'partner_id':0}) - return self.process_continue(cr, uid, ids, start=True) + ''' + A segmentation is a tool to automatically assign categories on partners. + These assignations are based on criterions. + ''' + _name = "crm.segmentation" + _description = "Partner Segmentation" + _columns = { + 'name': fields.char('Name', size=64, required=True, help='The name of the segmentation.'), + 'description': fields.text('Description'), + 'categ_id': fields.many2one('res.partner.category', 'Partner Category', required=True, help='The partner category that will be added to partners that match the segmentation criterions after computation.'), + 'exclusif': fields.boolean('Exclusive', help='Check if the category is limited to partners that match the segmentation criterions. If checked, remove the category from partners that doesn\'t match segmentation criterions'), + 'state': fields.selection([('not running','Not Running'),('running','Running')], 'Execution State', readonly=True), + 'partner_id': fields.integer('Max Partner ID processed'), + 'segmentation_line': fields.one2many('crm.segmentation.line', 'segmentation_id', 'Criteria', required=True), + 'som_interval': fields.integer('Days per Periode', help="A period is the average number of days between two cycle of sale or purchase for this segmentation. It's mainly used to detect if a partner has not purchased or buy for a too long time, so we suppose that his state of mind has decreased because he probably bought goods to another supplier. Use this functionnality for recurring businesses."), + 'som_interval_max': fields.integer('Max Interval', help="The computation is made on all events that occured during this interval, the past X periods."), + 'som_interval_decrease': fields.float('Decrease (0>1)', help="If the partner has not purchased (or buied) during a period, decrease the state of mind by this factor. It\'s a multiplication"), + 'som_interval_default': fields.float('Default (0=None)', help="Default state of mind for period preceeding the 'Max Interval' computation. This is the starting state of mind by default if the partner has no event."), + 'sales_purchase_active': fields.boolean('Use The Sales Purchase Rules', help='Check if you want to use this tab as part of the segmentation rule. If not checked, the criteria beneath will be ignored') + } + _defaults = { + 'partner_id': lambda *a: 0, + 'state': lambda *a: 'not running', + 'som_interval_max': lambda *a: 3, + 'som_interval_decrease': lambda *a: 0.8, + 'som_interval_default': lambda *a: 0.5 + } + + def process_continue(self, cr, uid, ids, start=False): + categs = self.read(cr,uid,ids,['categ_id','exclusif','partner_id', 'sales_purchase_active', 'profiling_active']) + for categ in categs: + if start: + if categ['exclusif']: + cr.execute('delete from res_partner_category_rel where category_id=%d', (categ['categ_id'][0],)) + + id = categ['id'] + + cr.execute('select id from res_partner order by id ') + partners = [x[0] for x in cr.fetchall()] + + if categ['sales_purchase_active']: + to_remove_list=[] + cr.execute('select id from crm_segmentation_line where segmentation_id=%d', (id,)) + line_ids = [x[0] for x in cr.fetchall()] + + for pid in partners: + if (not self.pool.get('crm.segmentation.line').test(cr, uid, line_ids, pid)): + to_remove_list.append(pid) + for pid in to_remove_list: + partners.remove(pid) + + for partner_id in partners: + cr.execute('insert into res_partner_category_rel (category_id,partner_id) values (%d,%d)', (categ['categ_id'][0],partner_id)) + cr.commit() + + self.write(cr, uid, [id], {'state':'not running', 'partner_id':0}) + cr.commit() + return True + + def process_stop(self, cr, uid, ids, *args): + return self.write(cr, uid, ids, {'state':'not running', 'partner_id':0}) + + def process_start(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'running', 'partner_id':0}) + return self.process_continue(cr, uid, ids, start=True) crm_segmentation() class crm_segmentation_line(osv.osv): - _name = "crm.segmentation.line" - _description = "Segmentation line" - _columns = { - 'name': fields.char('Rule Name', size=64, required=True), - 'segmentation_id': fields.many2one('crm.segmentation', 'Segmentation'), - 'expr_name': fields.selection([('sale','Sale Amount'),('som','State of Mind'),('purchase','Purchase Amount')], 'Control Variable', size=64, required=True), - 'expr_operator': fields.selection([('<','<'),('=','='),('>','>')], 'Operator', required=True), - 'expr_value': fields.float('Value', required=True), - 'operator': fields.selection([('and','Mandatory Expression'),('or','Optional Expression')],'Mandatory / Optionnal', required=True), - } - _defaults = { - 'expr_name': lambda *a: 'sale', - 'expr_operator': lambda *a: '>', - 'operator': lambda *a: 'and' - } - def test(self, cr, uid, ids, partner_id): - expression = {'<': lambda x,y: x<y, '=':lambda x,y:x==y, '>':lambda x,y:x>y} - ok = False - lst = self.read(cr, uid, ids) - for l in lst: - cr.execute('select * from ir_module_module where name=%s and state=%s', ('account','installed')) - if cr.fetchone(): - if l['expr_name']=='som': - datas = self.pool.get('crm.segmentation').read(cr, uid, [l['segmentation_id'][0]], - ['som','som_interval','som_interval_max','som_interval_default', 'som_interval_decrease']) - value = crm_operators.som(cr, uid, partner_id, datas[0]) - elif l['expr_name']=='sale': - cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ - 'FROM account_invoice_line l, account_invoice i ' \ - 'WHERE (l.invoice_id = i.id) ' \ - 'AND i.partner_id = %d '\ - 'AND i.type = \'out_invoice\'', - (partner_id,)) - value = cr.fetchone()[0] or 0.0 - cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ - 'FROM account_invoice_line l, account_invoice i ' \ - 'WHERE (l.invoice_id = i.id) ' \ - 'AND i.partner_id = %d '\ - 'AND i.type = \'out_refund\'', - (partner_id,)) - value -= cr.fetchone()[0] or 0.0 - elif l['expr_name']=='purchase': - cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ - 'FROM account_invoice_line l, account_invoice i ' \ - 'WHERE (l.invoice_id = i.id) ' \ - 'AND i.partner_id = %d '\ - 'AND i.type = \'in_invoice\'', - (partner_id,)) - value = cr.fetchone()[0] or 0.0 - cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ - 'FROM account_invoice_line l, account_invoice i ' \ - 'WHERE (l.invoice_id = i.id) ' \ - 'AND i.partner_id = %d '\ - 'AND i.type = \'in_refund\'', - (partner_id,)) - value -= cr.fetchone()[0] or 0.0 - res = expression[l['expr_operator']](value, l['expr_value']) - if (not res) and (l['operator']=='and'): - return False - if res: - return True - return True + _name = "crm.segmentation.line" + _description = "Segmentation line" + _columns = { + 'name': fields.char('Rule Name', size=64, required=True), + 'segmentation_id': fields.many2one('crm.segmentation', 'Segmentation'), + 'expr_name': fields.selection([('sale','Sale Amount'),('som','State of Mind'),('purchase','Purchase Amount')], 'Control Variable', size=64, required=True), + 'expr_operator': fields.selection([('<','<'),('=','='),('>','>')], 'Operator', required=True), + 'expr_value': fields.float('Value', required=True), + 'operator': fields.selection([('and','Mandatory Expression'),('or','Optional Expression')],'Mandatory / Optionnal', required=True), + } + _defaults = { + 'expr_name': lambda *a: 'sale', + 'expr_operator': lambda *a: '>', + 'operator': lambda *a: 'and' + } + def test(self, cr, uid, ids, partner_id): + expression = {'<': lambda x,y: x<y, '=':lambda x,y:x==y, '>':lambda x,y:x>y} + ok = False + lst = self.read(cr, uid, ids) + for l in lst: + cr.execute('select * from ir_module_module where name=%s and state=%s', ('account','installed')) + if cr.fetchone(): + if l['expr_name']=='som': + datas = self.pool.get('crm.segmentation').read(cr, uid, [l['segmentation_id'][0]], + ['som','som_interval','som_interval_max','som_interval_default', 'som_interval_decrease']) + value = crm_operators.som(cr, uid, partner_id, datas[0]) + elif l['expr_name']=='sale': + cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ + 'FROM account_invoice_line l, account_invoice i ' \ + 'WHERE (l.invoice_id = i.id) ' \ + 'AND i.partner_id = %d '\ + 'AND i.type = \'out_invoice\'', + (partner_id,)) + value = cr.fetchone()[0] or 0.0 + cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ + 'FROM account_invoice_line l, account_invoice i ' \ + 'WHERE (l.invoice_id = i.id) ' \ + 'AND i.partner_id = %d '\ + 'AND i.type = \'out_refund\'', + (partner_id,)) + value -= cr.fetchone()[0] or 0.0 + elif l['expr_name']=='purchase': + cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ + 'FROM account_invoice_line l, account_invoice i ' \ + 'WHERE (l.invoice_id = i.id) ' \ + 'AND i.partner_id = %d '\ + 'AND i.type = \'in_invoice\'', + (partner_id,)) + value = cr.fetchone()[0] or 0.0 + cr.execute('SELECT SUM(l.price_unit * l.quantity) ' \ + 'FROM account_invoice_line l, account_invoice i ' \ + 'WHERE (l.invoice_id = i.id) ' \ + 'AND i.partner_id = %d '\ + 'AND i.type = \'in_refund\'', + (partner_id,)) + value -= cr.fetchone()[0] or 0.0 + res = expression[l['expr_operator']](value, l['expr_value']) + if (not res) and (l['operator']=='and'): + return False + if res: + return True + return True crm_segmentation_line() diff --git a/addons/crm/report/report_businessopp.py b/addons/crm/report/report_businessopp.py index 280840f7c4d..33519ea4721 100644 --- a/addons/crm/report/report_businessopp.py +++ b/addons/crm/report/report_businessopp.py @@ -46,134 +46,134 @@ theme.use_color = 1 random.seed(0) class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + + def _render(self): + return self.pdf class report_custom(report_int): - def create(self, cr, uid, ids, datas, context={}): - assert len(ids), 'You should provide some ids!' - responsible_data = {} - responsible_names = {} - data = [] - minbenef = 999999999999999999999 - maxbenef = 0 - - cr.execute('select probability, planned_revenue, planned_cost, user_id, res_users.name as name from crm_case left join res_users on (crm_case.user_id=res_users.id) where crm_case.id in ('+','.join(map(str,ids))+') order by user_id') - res = cr.dictfetchall() - - for row in res: - proba = row['probability'] or 0 - cost = row['planned_cost'] or 0 - revenue = row['planned_revenue'] or 0 - userid = row['user_id'] or 0 - - benefit = revenue - cost - if benefit > maxbenef: - maxbenef = benefit - if benefit < minbenef: - minbenef = benefit - - tuple = (proba * 100, benefit) - responsible_data.setdefault(userid, []) - responsible_data[userid].append(tuple) - - tuple = (proba * 100, cost, benefit) - data.append(tuple) - - responsible_names[userid] = (row['name'] or '/').replace('/','//') - - minbenef -= maxbenef * 0.05 - maxbenef *= 1.2 - - ratio = 0.5 - minmaxdiff2 = (maxbenef - minbenef)/2 - - for l in responsible_data.itervalues(): - for i in range(len(l)): - percent, benef = l[i] - proba = percent/100 - - current_ratio = 1 + (ratio-1) * proba - - newbenef = minmaxdiff2 + ((benef - minbenef - minmaxdiff2) * current_ratio) - - l[i] = (percent, newbenef) + def create(self, cr, uid, ids, datas, context={}): + assert len(ids), 'You should provide some ids!' + responsible_data = {} + responsible_names = {} + data = [] + minbenef = 999999999999999999999 + maxbenef = 0 + + cr.execute('select probability, planned_revenue, planned_cost, user_id, res_users.name as name from crm_case left join res_users on (crm_case.user_id=res_users.id) where crm_case.id in ('+','.join(map(str,ids))+') order by user_id') + res = cr.dictfetchall() + + for row in res: + proba = row['probability'] or 0 + cost = row['planned_cost'] or 0 + revenue = row['planned_revenue'] or 0 + userid = row['user_id'] or 0 + + benefit = revenue - cost + if benefit > maxbenef: + maxbenef = benefit + if benefit < minbenef: + minbenef = benefit + + tuple = (proba * 100, benefit) + responsible_data.setdefault(userid, []) + responsible_data[userid].append(tuple) + + tuple = (proba * 100, cost, benefit) + data.append(tuple) + + responsible_names[userid] = (row['name'] or '/').replace('/','//') + + minbenef -= maxbenef * 0.05 + maxbenef *= 1.2 + + ratio = 0.5 + minmaxdiff2 = (maxbenef - minbenef)/2 + + for l in responsible_data.itervalues(): + for i in range(len(l)): + percent, benef = l[i] + proba = percent/100 + + current_ratio = 1 + (ratio-1) * proba + + newbenef = minmaxdiff2 + ((benef - minbenef - minmaxdiff2) * current_ratio) + + l[i] = (percent, newbenef) #TODO: #-group by "categorie de probabilites ds graphe du haut" #-echelle variable - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - - chart_object.set_defaults(line_plot.T, line_style=None) - - xaxis = axis.X(label=None, format="%d%%", tic_interval=20) - yaxis = axis.Y() - - ar = area.T( - size = (300,200), - y_grid_interval = 10000, - y_grid_style=None, - x_range = (0,100), - y_range = (minbenef, maxbenef), - x_axis = xaxis, - y_axis = None, - legend = legend.T() - ) - - for k, d in responsible_data.iteritems(): - fill = fill_style.Plain(bgcolor=color.T(r=random.random(), g=random.random(), b=random.random())) - tick = tick_mark.Square(size=6, fill_style=fill) - ar.add_plot(line_plot.T(label=responsible_names[k], data=d, tick_mark=tick)) - - ar.draw(can) - - # second graph (top right) - ar = area.T(legend = legend.T(), - size = (200,100), - loc=(100,250), - x_grid_interval = lambda min, max: [40,60,80,100], - x_grid_style=line_style.gray70_dash1, - x_range = (33, 100), - x_axis = axis.X(label=None, minor_tic_interval=lambda min,max: [50, 70, 90], format=lambda x: ""), - y_axis=axis.Y(label="Planned amounts")) - - bar_plot.fill_styles.reset(); - plot1=bar_plot.T(label="Cost", data=data, fill_style=fill_style.red) - plot2=bar_plot.T(label="Revenue", data=data, hcol=2, stack_on = plot1, fill_style=fill_style.blue) - - ar.add_plot(plot1, plot2) - - ar.draw(can) - - # diagonal "pipeline" lines - can.line(line_style.black, 0, 200, 300, 150) - can.line(line_style.black, 0, 0, 300, 50) - - # vertical lines - ls = line_style.T(width=0.4, color=color.gray70, dash=(2,2)) - for x in range(120, 300, 60): - can.line(ls, x, 0, x, 250) - - # draw arrows to the right - a = arrow.fat1 - for y in range(60,150,10): - a.draw([(285,y), (315,y)], can=can) - - # close canvas so that the file is written to "disk" - can.close() - - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - - pdf_string.close() - return (self.obj.pdf, 'pdf') + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + + chart_object.set_defaults(line_plot.T, line_style=None) + + xaxis = axis.X(label=None, format="%d%%", tic_interval=20) + yaxis = axis.Y() + + ar = area.T( + size = (300,200), + y_grid_interval = 10000, + y_grid_style=None, + x_range = (0,100), + y_range = (minbenef, maxbenef), + x_axis = xaxis, + y_axis = None, + legend = legend.T() + ) + + for k, d in responsible_data.iteritems(): + fill = fill_style.Plain(bgcolor=color.T(r=random.random(), g=random.random(), b=random.random())) + tick = tick_mark.Square(size=6, fill_style=fill) + ar.add_plot(line_plot.T(label=responsible_names[k], data=d, tick_mark=tick)) + + ar.draw(can) + + # second graph (top right) + ar = area.T(legend = legend.T(), + size = (200,100), + loc=(100,250), + x_grid_interval = lambda min, max: [40,60,80,100], + x_grid_style=line_style.gray70_dash1, + x_range = (33, 100), + x_axis = axis.X(label=None, minor_tic_interval=lambda min,max: [50, 70, 90], format=lambda x: ""), + y_axis=axis.Y(label="Planned amounts")) + + bar_plot.fill_styles.reset(); + plot1=bar_plot.T(label="Cost", data=data, fill_style=fill_style.red) + plot2=bar_plot.T(label="Revenue", data=data, hcol=2, stack_on = plot1, fill_style=fill_style.blue) + + ar.add_plot(plot1, plot2) + + ar.draw(can) + + # diagonal "pipeline" lines + can.line(line_style.black, 0, 200, 300, 150) + can.line(line_style.black, 0, 0, 300, 50) + + # vertical lines + ls = line_style.T(width=0.4, color=color.gray70, dash=(2,2)) + for x in range(120, 300, 60): + can.line(ls, x, 0, x, 250) + + # draw arrows to the right + a = arrow.fat1 + for y in range(60,150,10): + a.draw([(285,y), (315,y)], can=can) + + # close canvas so that the file is written to "disk" + can.close() + + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + + pdf_string.close() + return (self.obj.pdf, 'pdf') report_custom('report.crm.case') diff --git a/addons/crm/scripts/tinyerp-mailgate/tinyerp-mailgate.py b/addons/crm/scripts/tinyerp-mailgate/tinyerp-mailgate.py index 54844e0347e..20471826184 100755..100644 --- a/addons/crm/scripts/tinyerp-mailgate/tinyerp-mailgate.py +++ b/addons/crm/scripts/tinyerp-mailgate/tinyerp-mailgate.py @@ -39,284 +39,284 @@ import binascii import time, socket email_re = re.compile(r""" - ([a-zA-Z][\w\.-]*[a-zA-Z0-9] # username part - @ # mandatory @ sign - [a-zA-Z0-9][\w\.-]* # domain must start with a letter ... Ged> why do we include a 0-9 then? - \. - [a-z]{2,3} # TLD - ) - """, re.VERBOSE) + ([a-zA-Z][\w\.-]*[a-zA-Z0-9] # username part + @ # mandatory @ sign + [a-zA-Z0-9][\w\.-]* # domain must start with a letter ... Ged> why do we include a 0-9 then? + \. + [a-z]{2,3} # TLD + ) + """, re.VERBOSE) case_re = re.compile(r"\[([0-9]+)\]", re.UNICODE) command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE) reference_re = re.compile("<.*-tinycrm-(\\d+)@(.*)>", re.UNICODE) priorities = { - '1': '1 (Highest)', - '2': '2 (High)', - '3': '3 (Normal)', - '4': '4 (Low)', - '5': '5 (Lowest)', + '1': '1 (Highest)', + '2': '2 (High)', + '3': '3 (Normal)', + '4': '4 (Low)', + '5': '5 (Lowest)', } class rpc_proxy(object): - def __init__(self, uid, passwd, host='localhost', port=8069, path='object', dbname='terp'): - self.rpc = xmlrpclib.ServerProxy('http://%s:%s/%s' % (host, port, path)) - self.user_id = uid - self.passwd = passwd - self.dbname = dbname + def __init__(self, uid, passwd, host='localhost', port=8069, path='object', dbname='terp'): + self.rpc = xmlrpclib.ServerProxy('http://%s:%s/%s' % (host, port, path)) + self.user_id = uid + self.passwd = passwd + self.dbname = dbname - def __call__(self, *request): - return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request) + def __call__(self, *request): + return self.rpc.execute(self.dbname, self.user_id, self.passwd, *request) class email_parser(object): - def __init__(self, uid, password, section, email, email_default, dbname): - self.rpc = rpc_proxy(uid, password, dbname=dbname) - try: - self.section_id = int(section) - except: - self.section_id = self.rpc('crm.case.section', 'search', [('code','=',section)])[0] - self.email = email - self.email_default = email_default - self.canal_id = False - - def email_get(self, email_from): - res = email_re.search(email_from) - return res and res.group(1) - - def partner_get(self, email): - mail = self.email_get(email) - adr_ids = self.rpc('res.partner.address', 'search', [('email', '=', mail)]) - if not adr_ids: - return {} - adr = self.rpc('res.partner.address', 'read', adr_ids, ['partner_id']) - return { - 'partner_address_id': adr[0]['id'], - 'partner_id': adr[0]['partner_id'][0] - } - - def _decode_header(self, s): - from email.Header import decode_header - s = decode_header(s) - return ''.join(map(lambda x:x[0].decode(x[1] or 'ascii', 'replace'), s)) - - def msg_new(self, msg): - message = self.msg_body_get(msg) - data = { - 'name': self._decode_header(msg['Subject'][:64]), - 'section_id': self.section_id, - 'email_from': self._decode_header(msg['From'])[:128], - 'email_cc': self._decode_header(msg['Cc'] or '')[:252], - 'canal_id': self.canal_id, - 'user_id': False, - 'history_line': [(0, 0, {'description': message['body'], 'email': msg['From'][:84] })], - } - try: - data.update(self.partner_get(self._decode_header(msg['From']))) - except Exception, e: - print e - #end try - - id = self.rpc('crm.case', 'create', data) - attachments = message['attachment'] - - for attach in attachments or []: - data_attach = { - 'name': str(attach)[:64], - 'datas':binascii.b2a_base64(str(attachments[attach])), - 'datas_fname': str(attach)[:64], - 'description': 'Mail attachment', - 'res_model': 'crm.case', - 'res_id': id - } - self.rpc('ir.attachment', 'create', data_attach) - #end for - - return id - -# #change the return type format to dictionary -# { -# 'body':'body part', -# 'attachment':{ -# 'file_name':'file data', -# 'file_name':'file data', -# 'file_name':'file data', -# } -# } -# # - def msg_body_get(self, msg): - message = {}; - message['body'] = u''; - message['attachment'] = {}; - attachment = message['attachment']; - counter = 1; - - for part in msg.walk(): - if part.get_content_maintype() == 'multipart': - continue - - if part.get_content_maintype()=='text' and part.get_content_subtype() == 'plain': - buf = part.get_payload(decode=True) - if buf: - message['body'] += buf.decode(part.get_charsets()[0] or 'ascii', 'replace') - elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text': - filename = part.get_filename(); - if filename : - attachment[filename] = part.get_payload(decode=True); - else: - filename = 'attach_file'+str(counter); - counter += 1; - attachment[filename] = part.get_payload(decode=True); - #end if - #end if - message['attachment'] = attachment - #end for - return message - #end def - - def msg_user(self, msg, id): - body = self.msg_body_get(msg) - - # handle email body commands (ex: Set-State: Draft) - actions = {} - body_data='' - for line in body['body'].split('\n'): - res = command_re.match(line) - if res: - actions[res.group(1).lower()] = res.group(2).lower() - else: - body_data += line+'\n' - body['body'] = body_data - - data = { - 'history_line': [(0, 0, {'description': body['body'], 'email': msg['From'][:84]})], - } - - act = 'case_close' - if 'state' in actions: - if actions['state'] in ['draft','close','cancel','open','pending']: - act = 'case_' + actions['state'] - - for k1,k2 in [('cost','planned_cost'),('revenue','planned_revenue'),('probability','probability')]: - try: - data[k2] = float(actions[k1]) - except: - pass - - if 'priority' in actions: - if actions['priority'] in ('1','2','3','4','5'): - data['priority'] = actions['priority'] - - if 'partner' in actions: - data['email_from'] = actions['partner'][:128] - - if 'user' in actions: - uids = self.rpc('res.users', 'name_search', actions['user']) - if uids: - data['user_id'] = uids[0][0] - - self.rpc('crm.case', act, [id]) - self.rpc('crm.case', 'write', [id], data) - return id - - def msg_send(self, msg, emails, priority=None): - if not len(emails): - return False - del msg['To'] - msg['To'] = emails[0] - if len(emails)>1: - if 'Cc' in msg: - del msg['Cc'] - msg['Cc'] = ','.join(emails[1:]) - del msg['Reply-To'] - msg['Reply-To'] = self.email - if priority: - msg['X-Priority'] = priorities.get(priority, '3 (Normal)') - s = smtplib.SMTP() - s.connect() - s.sendmail(self.email, emails, msg.as_string()) - s.close() - return True - - def msg_partner(self, msg, id): - message = self.msg_body_get(msg) - body = message['body'] - act = 'case_open' - self.rpc('crm.case', act, [id]) - body2 = '\n'.join(map(lambda l: '> '+l, (body or '').split('\n'))) - data = { - 'history_line': [(0, 0, {'description': body, 'email': msg['From'][:84]})], - } - self.rpc('crm.case', 'write', [id], data) - return id - - def msg_test(self, msg, case_str): - if not case_str: - return (False, False) - emails = self.rpc('crm.case', 'emails_get', int(case_str)) - return (int(case_str), emails) - - def parse(self, msg): - case_str = reference_re.search(msg.get('References', '')) - if case_str: - case_str = case_str.group(1) - else: - case_str = case_re.search(msg.get('Subject', '')) - if case_str: - case_str = case_str.group(1) - (case_id, emails) = self.msg_test(msg, case_str) - if case_id: - if emails[0] and self.email_get(emails[0])==self.email_get(self._decode_header(msg['From'])): - self.msg_user(msg, case_id) - else: - self.msg_partner(msg, case_id) - else: - case_id = self.msg_new(msg) - subject = self._decode_header(msg['subject']) - if msg.get('Subject', ''): - del msg['Subject'] - msg['Subject'] = '['+str(case_id)+'] '+subject - msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(case_id)+'@'+socket.gethostname()+'>' - - emails = self.rpc('crm.case', 'emails_get', case_id) - priority = emails[3] - em = [emails[0], emails[1]] + (emails[2] or '').split(',') - emails = map(self.email_get, filter(None, em)) - - mm = [self._decode_header(msg['From']), self._decode_header(msg['To'])]+self._decode_header(msg.get('Cc','')).split(',') - msg_mails = map(self.email_get, filter(None, mm)) - - emails = filter(lambda m: m and m not in msg_mails, emails) - try: - self.msg_send(msg, emails, priority) - except: - if self.email_default: - a = self._decode_header(msg['Subject']) - del msg['Subject'] - msg['Subject'] = '[TinyERP-CaseError] ' + a - self.msg_send(msg, self.email_default.split(',')) - return emails + def __init__(self, uid, password, section, email, email_default, dbname): + self.rpc = rpc_proxy(uid, password, dbname=dbname) + try: + self.section_id = int(section) + except: + self.section_id = self.rpc('crm.case.section', 'search', [('code','=',section)])[0] + self.email = email + self.email_default = email_default + self.canal_id = False + + def email_get(self, email_from): + res = email_re.search(email_from) + return res and res.group(1) + + def partner_get(self, email): + mail = self.email_get(email) + adr_ids = self.rpc('res.partner.address', 'search', [('email', '=', mail)]) + if not adr_ids: + return {} + adr = self.rpc('res.partner.address', 'read', adr_ids, ['partner_id']) + return { + 'partner_address_id': adr[0]['id'], + 'partner_id': adr[0]['partner_id'][0] + } + + def _decode_header(self, s): + from email.Header import decode_header + s = decode_header(s) + return ''.join(map(lambda x:x[0].decode(x[1] or 'ascii', 'replace'), s)) + + def msg_new(self, msg): + message = self.msg_body_get(msg) + data = { + 'name': self._decode_header(msg['Subject'][:64]), + 'section_id': self.section_id, + 'email_from': self._decode_header(msg['From'])[:128], + 'email_cc': self._decode_header(msg['Cc'] or '')[:252], + 'canal_id': self.canal_id, + 'user_id': False, + 'history_line': [(0, 0, {'description': message['body'], 'email': msg['From'][:84] })], + } + try: + data.update(self.partner_get(self._decode_header(msg['From']))) + except Exception, e: + print e + #end try + + id = self.rpc('crm.case', 'create', data) + attachments = message['attachment'] + + for attach in attachments or []: + data_attach = { + 'name': str(attach)[:64], + 'datas':binascii.b2a_base64(str(attachments[attach])), + 'datas_fname': str(attach)[:64], + 'description': 'Mail attachment', + 'res_model': 'crm.case', + 'res_id': id + } + self.rpc('ir.attachment', 'create', data_attach) + #end for + + return id + +# #change the return type format to dictionary +# { +# 'body':'body part', +# 'attachment':{ +# 'file_name':'file data', +# 'file_name':'file data', +# 'file_name':'file data', +# } +# } +# # + def msg_body_get(self, msg): + message = {}; + message['body'] = u''; + message['attachment'] = {}; + attachment = message['attachment']; + counter = 1; + + for part in msg.walk(): + if part.get_content_maintype() == 'multipart': + continue + + if part.get_content_maintype()=='text' and part.get_content_subtype() == 'plain': + buf = part.get_payload(decode=True) + if buf: + message['body'] += buf.decode(part.get_charsets()[0] or 'ascii', 'replace') + elif part.get_content_maintype()=='application' or part.get_content_maintype()=='image' or part.get_content_maintype()=='text': + filename = part.get_filename(); + if filename : + attachment[filename] = part.get_payload(decode=True); + else: + filename = 'attach_file'+str(counter); + counter += 1; + attachment[filename] = part.get_payload(decode=True); + #end if + #end if + message['attachment'] = attachment + #end for + return message + #end def + + def msg_user(self, msg, id): + body = self.msg_body_get(msg) + + # handle email body commands (ex: Set-State: Draft) + actions = {} + body_data='' + for line in body['body'].split('\n'): + res = command_re.match(line) + if res: + actions[res.group(1).lower()] = res.group(2).lower() + else: + body_data += line+'\n' + body['body'] = body_data + + data = { + 'history_line': [(0, 0, {'description': body['body'], 'email': msg['From'][:84]})], + } + + act = 'case_close' + if 'state' in actions: + if actions['state'] in ['draft','close','cancel','open','pending']: + act = 'case_' + actions['state'] + + for k1,k2 in [('cost','planned_cost'),('revenue','planned_revenue'),('probability','probability')]: + try: + data[k2] = float(actions[k1]) + except: + pass + + if 'priority' in actions: + if actions['priority'] in ('1','2','3','4','5'): + data['priority'] = actions['priority'] + + if 'partner' in actions: + data['email_from'] = actions['partner'][:128] + + if 'user' in actions: + uids = self.rpc('res.users', 'name_search', actions['user']) + if uids: + data['user_id'] = uids[0][0] + + self.rpc('crm.case', act, [id]) + self.rpc('crm.case', 'write', [id], data) + return id + + def msg_send(self, msg, emails, priority=None): + if not len(emails): + return False + del msg['To'] + msg['To'] = emails[0] + if len(emails)>1: + if 'Cc' in msg: + del msg['Cc'] + msg['Cc'] = ','.join(emails[1:]) + del msg['Reply-To'] + msg['Reply-To'] = self.email + if priority: + msg['X-Priority'] = priorities.get(priority, '3 (Normal)') + s = smtplib.SMTP() + s.connect() + s.sendmail(self.email, emails, msg.as_string()) + s.close() + return True + + def msg_partner(self, msg, id): + message = self.msg_body_get(msg) + body = message['body'] + act = 'case_open' + self.rpc('crm.case', act, [id]) + body2 = '\n'.join(map(lambda l: '> '+l, (body or '').split('\n'))) + data = { + 'history_line': [(0, 0, {'description': body, 'email': msg['From'][:84]})], + } + self.rpc('crm.case', 'write', [id], data) + return id + + def msg_test(self, msg, case_str): + if not case_str: + return (False, False) + emails = self.rpc('crm.case', 'emails_get', int(case_str)) + return (int(case_str), emails) + + def parse(self, msg): + case_str = reference_re.search(msg.get('References', '')) + if case_str: + case_str = case_str.group(1) + else: + case_str = case_re.search(msg.get('Subject', '')) + if case_str: + case_str = case_str.group(1) + (case_id, emails) = self.msg_test(msg, case_str) + if case_id: + if emails[0] and self.email_get(emails[0])==self.email_get(self._decode_header(msg['From'])): + self.msg_user(msg, case_id) + else: + self.msg_partner(msg, case_id) + else: + case_id = self.msg_new(msg) + subject = self._decode_header(msg['subject']) + if msg.get('Subject', ''): + del msg['Subject'] + msg['Subject'] = '['+str(case_id)+'] '+subject + msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(case_id)+'@'+socket.gethostname()+'>' + + emails = self.rpc('crm.case', 'emails_get', case_id) + priority = emails[3] + em = [emails[0], emails[1]] + (emails[2] or '').split(',') + emails = map(self.email_get, filter(None, em)) + + mm = [self._decode_header(msg['From']), self._decode_header(msg['To'])]+self._decode_header(msg.get('Cc','')).split(',') + msg_mails = map(self.email_get, filter(None, mm)) + + emails = filter(lambda m: m and m not in msg_mails, emails) + try: + self.msg_send(msg, emails, priority) + except: + if self.email_default: + a = self._decode_header(msg['Subject']) + del msg['Subject'] + msg['Subject'] = '[TinyERP-CaseError] ' + a + self.msg_send(msg, self.email_default.split(',')) + return emails if __name__ == '__main__': - import sys, optparse - parser = optparse.OptionParser( - usage='usage: %prog [options]', - version='%prog v1.0') + import sys, optparse + parser = optparse.OptionParser( + usage='usage: %prog [options]', + version='%prog v1.0') - group = optparse.OptionGroup(parser, "Note", - "This program parse a mail from standard input and communicate " - "with the Tiny ERP server for case management in the CRM module.") - parser.add_option_group(group) + group = optparse.OptionGroup(parser, "Note", + "This program parse a mail from standard input and communicate " + "with the Tiny ERP server for case management in the CRM module.") + parser.add_option_group(group) - parser.add_option("-u", "--user", dest="userid", help="ID of the user in Tiny ERP", default=3, type='int') - parser.add_option("-p", "--password", dest="password", help="Password of the user in Tiny ERP", default='admin') - parser.add_option("-e", "--email", dest="email", help="Email address used in the From field of outgoing messages") - parser.add_option("-s", "--section", dest="section", help="ID or code of the case section", default="support") - parser.add_option("-m", "--default", dest="default", help="Default eMail in case of any trouble.", default=None) - parser.add_option("-d", "--dbname", dest="dbname", help="Database name (default: terp)", default='terp') + parser.add_option("-u", "--user", dest="userid", help="ID of the user in Tiny ERP", default=3, type='int') + parser.add_option("-p", "--password", dest="password", help="Password of the user in Tiny ERP", default='admin') + parser.add_option("-e", "--email", dest="email", help="Email address used in the From field of outgoing messages") + parser.add_option("-s", "--section", dest="section", help="ID or code of the case section", default="support") + parser.add_option("-m", "--default", dest="default", help="Default eMail in case of any trouble.", default=None) + parser.add_option("-d", "--dbname", dest="dbname", help="Database name (default: terp)", default='terp') - (options, args) = parser.parse_args() - parser = email_parser(options.userid, options.password, options.section, options.email, options.default, dbname=options.dbname) + (options, args) = parser.parse_args() + parser = email_parser(options.userid, options.password, options.section, options.email, options.default, dbname=options.dbname) - msg_txt = email.message_from_file(sys.stdin) + msg_txt = email.message_from_file(sys.stdin) - parser.parse(msg_txt) + parser.parse(msg_txt) diff --git a/addons/crm/wizard/crm_wizard.py b/addons/crm/wizard/crm_wizard.py index b49bae3e6ac..b5b0a77d8e3 100644 --- a/addons/crm/wizard/crm_wizard.py +++ b/addons/crm/wizard/crm_wizard.py @@ -35,106 +35,106 @@ import pooler section_form = '''<?xml version="1.0"?> <form string="Create Menus For Cases"> - <separator string="Base Information" colspan="4"/> - <field name="menu_name"/> - <field name="menu_parent_id"/> - <field name="section_id"/> - <separator string="Select Views (empty for default)" colspan="4"/> - <field name="view_form"/> - <field name="view_tree"/> - <field name="view_calendar"/> + <separator string="Base Information" colspan="4"/> + <field name="menu_name"/> + <field name="menu_parent_id"/> + <field name="section_id"/> + <separator string="Select Views (empty for default)" colspan="4"/> + <field name="view_form"/> + <field name="view_tree"/> + <field name="view_calendar"/> </form>''' section_fields = { - 'menu_name': {'string':'Base Menu Name', 'type':'char', 'required':True, 'size':64}, - 'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True}, - 'section_id': {'string':'Case Section', 'type':'many2one', 'relation':'crm.case.section', 'required':True}, - 'view_form': {'string':'Form View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','form'),('model','=','crm.case')] }, - 'view_tree': {'string':'Tree View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','tree'),('model','=','crm.case')] }, - 'view_calendar': {'string':'Calendar View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','calendar'),('model','=','crm.case')] } + 'menu_name': {'string':'Base Menu Name', 'type':'char', 'required':True, 'size':64}, + 'menu_parent_id': {'string':'Parent Menu', 'type':'many2one', 'relation':'ir.ui.menu', 'required':True}, + 'section_id': {'string':'Case Section', 'type':'many2one', 'relation':'crm.case.section', 'required':True}, + 'view_form': {'string':'Form View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','form'),('model','=','crm.case')] }, + 'view_tree': {'string':'Tree View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','tree'),('model','=','crm.case')] }, + 'view_calendar': {'string':'Calendar View', 'type':'many2one', 'relation':'ir.ui.view', 'domain':[('type','=','calendar'),('model','=','crm.case')] } } menu_lst = [ - (1,'My ',"[('section_id','=',SECTION_ID),('user_id','=',uid)]", 0, 'form,tree'), - (2,'My Unclosed ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','<>','cancel'), ('state','<>','done')]", 1, 'tree,form'), - (5,'My Open ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','open')]", 2, 'tree,form'), - (6,'My Pending ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','pending')]", 2, 'tree,form'), - (7,'My Draft ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','draft')]", 2, 'tree,form'), - (3,'My Late ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 1, 'tree,form'), - (4,'My Canceled ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','cancel')]", 1, 'no'), - (8,'All ',"[('section_id','=',SECTION_ID),]", 0, 'tree,form'), - (9,'All Unassigned ',"[('section_id','=',SECTION_ID),('user_id','=',False)]", 8, 'no'), - (10,'All Late ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 8, 'no'), - (11,'All Canceled ',"[('section_id','=',SECTION_ID),('state','=','cancel')]", 8, 'no'), - (12,'All Unclosed ',"[('section_id','=',SECTION_ID),('state','<>','cancel'), ('state','<>','done')]", 8, 'tree,form'), - (13,'All Open ',"[('section_id','=',SECTION_ID),('state','=','open')]", 12, 'tree,form'), - (14,'All Pending ',"[('section_id','=',SECTION_ID),('state','=','pending')]", 12, 'tree,form'), - (15,'All Draft ',"[('section_id','=',SECTION_ID),('state','=','draft')]", 12, 'tree,form'), - (16,'All Unclosed and Unassigned ',"[('section_id','=',SECTION_ID),('user_id','=',False),('state','<>','cancel'),('state','<>','done')]", 12, 'no') + (1,'My ',"[('section_id','=',SECTION_ID),('user_id','=',uid)]", 0, 'form,tree'), + (2,'My Unclosed ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','<>','cancel'), ('state','<>','done')]", 1, 'tree,form'), + (5,'My Open ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','open')]", 2, 'tree,form'), + (6,'My Pending ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','pending')]", 2, 'tree,form'), + (7,'My Draft ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','draft')]", 2, 'tree,form'), + (3,'My Late ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 1, 'tree,form'), + (4,'My Canceled ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('state','=','cancel')]", 1, 'no'), + (8,'All ',"[('section_id','=',SECTION_ID),]", 0, 'tree,form'), + (9,'All Unassigned ',"[('section_id','=',SECTION_ID),('user_id','=',False)]", 8, 'no'), + (10,'All Late ',"[('section_id','=',SECTION_ID),('user_id','=',uid), ('date_deadline','<=',time.strftime('%Y-%m-%d')), ('state','<>','cancel'), ('state','<>','done')]", 8, 'no'), + (11,'All Canceled ',"[('section_id','=',SECTION_ID),('state','=','cancel')]", 8, 'no'), + (12,'All Unclosed ',"[('section_id','=',SECTION_ID),('state','<>','cancel'), ('state','<>','done')]", 8, 'tree,form'), + (13,'All Open ',"[('section_id','=',SECTION_ID),('state','=','open')]", 12, 'tree,form'), + (14,'All Pending ',"[('section_id','=',SECTION_ID),('state','=','pending')]", 12, 'tree,form'), + (15,'All Draft ',"[('section_id','=',SECTION_ID),('state','=','draft')]", 12, 'tree,form'), + (16,'All Unclosed and Unassigned ',"[('section_id','=',SECTION_ID),('user_id','=',False),('state','<>','cancel'),('state','<>','done')]", 12, 'no') ] section_menu_form = '''<?xml version="1.0"?> <form string="Created Menus" width="800"> - <separator string="Update The Proposed Menus To Be Created" colspan="4"/> + <separator string="Update The Proposed Menus To Be Created" colspan="4"/> ''' + '\n'.join(map(lambda x: '\t<field name="menu%d" colspan="3"/> <field name="menu%d_option" nolabel="1"/>' % (x[0],x[0]), menu_lst)) + '''\n</form>''' section_menu_fields = { } for menu in menu_lst: - section_menu_fields['menu'+str(menu[0])] = { - 'string': menu[1], - 'type': 'char', - 'size': 64, - 'required': True - } - section_menu_fields['menu'+str(menu[0])+'_option'] = { - 'string': menu[1], - 'type': 'selection', - 'selection': [ - ('no',"Don't Create"), - ('form,tree','New Form'), - ('form,tree,calendar','New With Calendar'), - ('tree,form','List'), - ('tree,form,calendar','List With Calendar'), - ('calendar,tree,form','Calendar'), - ], - 'size': 64, - 'required': True, - } + section_menu_fields['menu'+str(menu[0])] = { + 'string': menu[1], + 'type': 'char', + 'size': 64, + 'required': True + } + section_menu_fields['menu'+str(menu[0])+'_option'] = { + 'string': menu[1], + 'type': 'selection', + 'selection': [ + ('no',"Don't Create"), + ('form,tree','New Form'), + ('form,tree,calendar','New With Calendar'), + ('tree,form','List'), + ('tree,form,calendar','List With Calendar'), + ('calendar,tree,form','Calendar'), + ], + 'size': 64, + 'required': True, + } def case_menu_create(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - pool.get('crm.case.section').menu_create_data(cr, uid, data['form'], menu_lst, context) - return {} + pool = pooler.get_pool(cr.dbname) + pool.get('crm.case.section').menu_create_data(cr, uid, data['form'], menu_lst, context) + return {} def _menu_default(self, cr, uid, data, context): - result = {} - for menu in menu_lst: - result['menu'+str(menu[0])] = menu[1] + data['form']['menu_name'] - result['menu'+str(menu[0])+'_option'] = menu[4] - return result + result = {} + for menu in menu_lst: + result['menu'+str(menu[0])] = menu[1] + data['form']['menu_name'] + result['menu'+str(menu[0])+'_option'] = menu[4] + return result class wizard_section_menu_create(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('design_menu','Create menu Entries')]} - }, - 'design_menu': { - 'actions': [_menu_default], - 'result': { - 'type':'form', - 'arch':section_menu_form, - 'fields':section_menu_fields, - 'state':[ - ('end','Cancel'), - ('create','Create menu Entries') - ] - } - }, - 'create': { - 'actions': [case_menu_create], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':section_form, 'fields':section_fields, 'state':[('end','Cancel'),('design_menu','Create menu Entries')]} + }, + 'design_menu': { + 'actions': [_menu_default], + 'result': { + 'type':'form', + 'arch':section_menu_form, + 'fields':section_menu_fields, + 'state':[ + ('end','Cancel'), + ('create','Create menu Entries') + ] + } + }, + 'create': { + 'actions': [case_menu_create], + 'result': {'type':'state', 'state':'end'} + } + } wizard_section_menu_create('crm.case.section.menu') diff --git a/addons/custom/__terp__.py b/addons/custom/__terp__.py index 6e100aca14b..13147a7b22c 100644 --- a/addons/custom/__terp__.py +++ b/addons/custom/__terp__.py @@ -29,15 +29,15 @@ # Use the custom module to put your specific code in a separate module. # { - "name" : "Module for custom developments", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Others", - "website": "http://www.tinyerp.com", - "description": "Sample custom module where you can put your customer specific developments.", - "depends" : ["base"], - "init_xml" : [], - "update_xml" : ["custom_view.xml"], - "active": False, - "installable": True + "name" : "Module for custom developments", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Others", + "website": "http://www.tinyerp.com", + "description": "Sample custom module where you can put your customer specific developments.", + "depends" : ["base"], + "init_xml" : [], + "update_xml" : ["custom_view.xml"], + "active": False, + "installable": True } diff --git a/addons/custom/custom.py b/addons/custom/custom.py index c667aec7c61..5f2265fcd99 100644 --- a/addons/custom/custom.py +++ b/addons/custom/custom.py @@ -30,12 +30,12 @@ from osv import osv, fields #class custom_material(osv.osv): -# _name = 'network.material' -# _inherit = 'network.material' -# _columns = { -# } -# _defaults = { -# } +# _name = 'network.material' +# _inherit = 'network.material' +# _columns = { +# } +# _defaults = { +# } #custom_material() diff --git a/addons/delivery/__terp__.py b/addons/delivery/__terp__.py index fa533805a0a..43c7910b278 100644 --- a/addons/delivery/__terp__.py +++ b/addons/delivery/__terp__.py @@ -26,15 +26,15 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Carriers and deliveries", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Sales & Purchases", - "description": "Allows to add delivery methods in sales order and packings. You can define your own carrier and delivery grids for prices. When creating invoices from pickings, Tiny ERP is able to add and compute the shipping line.", - "depends" : ["sale","purchase", "stock",], - "init_xml" : ["delivery_data.xml"], - "demo_xml" : ["delivery_demo.xml"], - "update_xml" : ["delivery_view.xml","delivery_wizard.xml"], - "active": False, - "installable": True, + "name" : "Carriers and deliveries", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Sales & Purchases", + "description": "Allows to add delivery methods in sales order and packings. You can define your own carrier and delivery grids for prices. When creating invoices from pickings, Tiny ERP is able to add and compute the shipping line.", + "depends" : ["sale","purchase", "stock",], + "init_xml" : ["delivery_data.xml"], + "demo_xml" : ["delivery_demo.xml"], + "update_xml" : ["delivery_view.xml","delivery_wizard.xml"], + "active": False, + "installable": True, } diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py index 61d94874a91..5a45ce583b5 100644 --- a/addons/delivery/delivery.py +++ b/addons/delivery/delivery.py @@ -32,119 +32,119 @@ from osv import fields,osv,orm from tools.translate import _ class delivery_carrier(osv.osv): - _name = "delivery.carrier" - _description = "Carrier and delivery grids" - _columns = { - 'name': fields.char('Carrier', size=64, required=True), - 'partner_id': fields.many2one('res.partner', 'Carrier partner', required=True), - 'product_id': fields.many2one('product.product', 'Delivery product', required=True), - 'grids_id': fields.one2many('delivery.grid', 'carrier_id', 'Delivery grids'), - 'active': fields.boolean('Active') - } - _defaults = { - 'active': lambda *args:1 - } - def grid_get(self, cr, uid, ids, contact_id, context={}): - contact = self.pool.get('res.partner.address').browse(cr, uid, [contact_id])[0] - for carrier in self.browse(cr, uid, ids): - for grid in carrier.grids_id: - get_id = lambda x: x.id - country_ids = map(get_id, grid.country_ids) - state_ids = map(get_id, grid.state_ids) - if country_ids and not contact.country_id.id in country_ids: - continue - if state_ids and not contact.state_id.id in state_ids: - continue - if grid.zip_from and (contact.zip or '')< grid.zip_from: - continue - if grid.zip_to and (contact.zip or '')> grid.zip_to: - continue - return grid.id - return False + _name = "delivery.carrier" + _description = "Carrier and delivery grids" + _columns = { + 'name': fields.char('Carrier', size=64, required=True), + 'partner_id': fields.many2one('res.partner', 'Carrier partner', required=True), + 'product_id': fields.many2one('product.product', 'Delivery product', required=True), + 'grids_id': fields.one2many('delivery.grid', 'carrier_id', 'Delivery grids'), + 'active': fields.boolean('Active') + } + _defaults = { + 'active': lambda *args:1 + } + def grid_get(self, cr, uid, ids, contact_id, context={}): + contact = self.pool.get('res.partner.address').browse(cr, uid, [contact_id])[0] + for carrier in self.browse(cr, uid, ids): + for grid in carrier.grids_id: + get_id = lambda x: x.id + country_ids = map(get_id, grid.country_ids) + state_ids = map(get_id, grid.state_ids) + if country_ids and not contact.country_id.id in country_ids: + continue + if state_ids and not contact.state_id.id in state_ids: + continue + if grid.zip_from and (contact.zip or '')< grid.zip_from: + continue + if grid.zip_to and (contact.zip or '')> grid.zip_to: + continue + return grid.id + return False delivery_carrier() class delivery_grid(osv.osv): - _name = "delivery.grid" - _description = "Delivery grid" - _columns = { - 'name': fields.char('Grid Name', size=64, required=True), - 'sequence': fields.integer('Sequence', size=64, required=True), - 'carrier_id': fields.many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade'), - 'country_ids': fields.many2many('res.country', 'delivery_grid_country_rel', 'grid_id', 'country_id', 'Countries'), - 'state_ids': fields.many2many('res.country.state', 'delivery_grid_state_rel', 'grid_id', 'state_id', 'States'), - 'zip_from': fields.char('Start Zip', size=12), - 'zip_to': fields.char('To Zip', size=12), - 'line_ids': fields.one2many('delivery.grid.line', 'grid_id', 'Grid Line'), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - 'sequence': lambda *a: 1, - } - _order = 'sequence' - - - def get_price(self, cr, uid, id, order, dt, context): - - total = 0 - weight = 0 - volume = 0 - - for line in order.order_line: - if not line.product_id: - continue - total += line.price_subtotal or 0.0 - weight += (line.product_id.weight or 0.0) * line.product_uom_qty - volume += (line.product_id.volume or 0.0) * line.product_uom_qty - - - return self.get_price_from_picking(cr, uid, id, total,weight, volume, context) - - def get_price_from_picking(self, cr, uid, id, total, weight, volume, context={}): - grid = self.browse(cr, uid, id, context) - - price = 0.0 - ok = False - - for line in grid.line_ids: - price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight} - test = eval(line.type+line.operator+str(line.max_value), price_dict) - if test: - if line.price_type=='variable': - price = line.list_price * price_dict[line.variable_factor] - else: - price = line.list_price - ok = True - break - if not ok: - raise except_osv(_('No price avaible !'), _('No line matched this order in the choosed delivery grids !')) - - return price + _name = "delivery.grid" + _description = "Delivery grid" + _columns = { + 'name': fields.char('Grid Name', size=64, required=True), + 'sequence': fields.integer('Sequence', size=64, required=True), + 'carrier_id': fields.many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade'), + 'country_ids': fields.many2many('res.country', 'delivery_grid_country_rel', 'grid_id', 'country_id', 'Countries'), + 'state_ids': fields.many2many('res.country.state', 'delivery_grid_state_rel', 'grid_id', 'state_id', 'States'), + 'zip_from': fields.char('Start Zip', size=12), + 'zip_to': fields.char('To Zip', size=12), + 'line_ids': fields.one2many('delivery.grid.line', 'grid_id', 'Grid Line'), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1, + 'sequence': lambda *a: 1, + } + _order = 'sequence' + + + def get_price(self, cr, uid, id, order, dt, context): + + total = 0 + weight = 0 + volume = 0 + + for line in order.order_line: + if not line.product_id: + continue + total += line.price_subtotal or 0.0 + weight += (line.product_id.weight or 0.0) * line.product_uom_qty + volume += (line.product_id.volume or 0.0) * line.product_uom_qty + + + return self.get_price_from_picking(cr, uid, id, total,weight, volume, context) + + def get_price_from_picking(self, cr, uid, id, total, weight, volume, context={}): + grid = self.browse(cr, uid, id, context) + + price = 0.0 + ok = False + + for line in grid.line_ids: + price_dict = {'price': total, 'volume':volume, 'weight': weight, 'wv':volume*weight} + test = eval(line.type+line.operator+str(line.max_value), price_dict) + if test: + if line.price_type=='variable': + price = line.list_price * price_dict[line.variable_factor] + else: + price = line.list_price + ok = True + break + if not ok: + raise except_osv(_('No price avaible !'), _('No line matched this order in the choosed delivery grids !')) + + return price delivery_grid() class delivery_grid_line(osv.osv): - _name = "delivery.grid.line" - _description = "Delivery line of grid" - _columns = { - 'name': fields.char('Name', size=32, required=True), - 'grid_id': fields.many2one('delivery.grid', 'Grid',required=True), - 'type': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price')], 'Variable', required=True), - 'operator': fields.selection([('=','='),('<=','<='),('>=','>=')], 'Operator', required=True), - 'max_value': fields.float('Maximum Value', required=True), - 'price_type': fields.selection([('fixed','Fixed'),('variable','Variable')], 'Price Type', required=True), - 'variable_factor': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price')], 'Variable Factor', required=True), - 'list_price': fields.float('Sale Price', required=True), - 'standard_price': fields.float('Cost Price', required=True), - } - _defaults = { - 'type': lambda *args: 'weight', - 'operator': lambda *args: '<=', - 'price_type': lambda *args: 'fixed', - 'variable_factor': lambda *args: 'weight', - } - _order = 'list_price' + _name = "delivery.grid.line" + _description = "Delivery line of grid" + _columns = { + 'name': fields.char('Name', size=32, required=True), + 'grid_id': fields.many2one('delivery.grid', 'Grid',required=True), + 'type': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price')], 'Variable', required=True), + 'operator': fields.selection([('=','='),('<=','<='),('>=','>=')], 'Operator', required=True), + 'max_value': fields.float('Maximum Value', required=True), + 'price_type': fields.selection([('fixed','Fixed'),('variable','Variable')], 'Price Type', required=True), + 'variable_factor': fields.selection([('weight','Weight'),('volume','Volume'),('wv','Weight * Volume'), ('price','Price')], 'Variable Factor', required=True), + 'list_price': fields.float('Sale Price', required=True), + 'standard_price': fields.float('Cost Price', required=True), + } + _defaults = { + 'type': lambda *args: 'weight', + 'operator': lambda *args: '<=', + 'price_type': lambda *args: 'fixed', + 'variable_factor': lambda *args: 'weight', + } + _order = 'list_price' delivery_grid_line() diff --git a/addons/delivery/partner.py b/addons/delivery/partner.py index caf6848bdd1..acfa8dc223e 100644 --- a/addons/delivery/partner.py +++ b/addons/delivery/partner.py @@ -31,18 +31,18 @@ from osv import fields, osv class res_partner(osv.osv): - _inherit = 'res.partner' - _columns = { - 'property_delivery_carrier': fields.property( - 'delivery.carrier', - type='many2one', - relation='delivery.carrier', - string="Delivery Method", - method=True, - view_load=True, - group_name="Delivery Properties", - help="This carrier will be used, instead of the default one, for delivering goods to the current partner"), - } + _inherit = 'res.partner' + _columns = { + 'property_delivery_carrier': fields.property( + 'delivery.carrier', + type='many2one', + relation='delivery.carrier', + string="Delivery Method", + method=True, + view_load=True, + group_name="Delivery Properties", + help="This carrier will be used, instead of the default one, for delivering goods to the current partner"), + } res_partner() diff --git a/addons/delivery/sale.py b/addons/delivery/sale.py index 787eb6feb2f..58f0f90928f 100644 --- a/addons/delivery/sale.py +++ b/addons/delivery/sale.py @@ -31,29 +31,29 @@ from osv import fields,osv # Overloaded sale_order to manage carriers : class sale_order(osv.osv): - _name = "sale.order" - _inherit = 'sale.order' - _description = "Sale Order" - - _columns = { - 'carrier_id':fields.many2one("delivery.carrier","Delivery method", help="Complete this field if you plan to invoice the shipping based on packings made."), - } - - def onchange_partner_id(self, cr, uid, ids, part): - result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part) - if part: - dtype = self.pool.get('res.partner').browse(cr, uid, part).property_delivery_carrier.id - result['value']['carrier_id'] = dtype - return result - - def action_ship_create(self, cr, uid, ids, *args): - result = super(sale_order, self).action_ship_create(cr, uid, ids, *args) - for order in self.browse(cr, uid, ids, context={}): - pids = [ x.id for x in order.picking_ids] - self.pool.get('stock.picking').write(cr, uid, pids, { - 'carrier_id':order.carrier_id.id, - }) - return result + _name = "sale.order" + _inherit = 'sale.order' + _description = "Sale Order" + + _columns = { + 'carrier_id':fields.many2one("delivery.carrier","Delivery method", help="Complete this field if you plan to invoice the shipping based on packings made."), + } + + def onchange_partner_id(self, cr, uid, ids, part): + result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part) + if part: + dtype = self.pool.get('res.partner').browse(cr, uid, part).property_delivery_carrier.id + result['value']['carrier_id'] = dtype + return result + + def action_ship_create(self, cr, uid, ids, *args): + result = super(sale_order, self).action_ship_create(cr, uid, ids, *args) + for order in self.browse(cr, uid, ids, context={}): + pids = [ x.id for x in order.picking_ids] + self.pool.get('stock.picking').write(cr, uid, pids, { + 'carrier_id':order.carrier_id.id, + }) + return result sale_order() diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index e83c1f0480c..7e1c30afecc 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -32,67 +32,67 @@ from tools.translate import _ # Overloaded stock_picking to manage carriers : class stock_picking(osv.osv): - _name = "stock.picking" - _description = "Picking list" - _inherit = 'stock.picking' - _columns = { - 'carrier_id':fields.many2one("delivery.carrier","Carrier"), - 'volume': fields.float('Volume'), - 'weight': fields.float('Weight'), - } + _name = "stock.picking" + _description = "Picking list" + _inherit = 'stock.picking' + _columns = { + 'carrier_id':fields.many2one("delivery.carrier","Carrier"), + 'volume': fields.float('Volume'), + 'weight': fields.float('Weight'), + } - def action_invoice_create(self, cursor, user, ids, journal_id=False, - group=False, type='out_invoice', context=None): - invoice_obj = self.pool.get('account.invoice') - picking_obj = self.pool.get('stock.picking') - carrier_obj = self.pool.get('delivery.carrier') - grid_obj = self.pool.get('delivery.grid') - invoice_line_obj = self.pool.get('account.invoice.line') + def action_invoice_create(self, cursor, user, ids, journal_id=False, + group=False, type='out_invoice', context=None): + invoice_obj = self.pool.get('account.invoice') + picking_obj = self.pool.get('stock.picking') + carrier_obj = self.pool.get('delivery.carrier') + grid_obj = self.pool.get('delivery.grid') + invoice_line_obj = self.pool.get('account.invoice.line') - result = super(stock_picking, self).action_invoice_create(cursor, user, - ids, journal_id=journal_id, group=group, type=type, - context=context) + result = super(stock_picking, self).action_invoice_create(cursor, user, + ids, journal_id=journal_id, group=group, type=type, + context=context) - picking_ids = result.keys() - invoice_ids = result.values() + picking_ids = result.keys() + invoice_ids = result.values() - invoices = {} - for invoice in invoice_obj.browse(cursor, user, invoice_ids, - context=context): - invoices[invoice.id] = invoice + invoices = {} + for invoice in invoice_obj.browse(cursor, user, invoice_ids, + context=context): + invoices[invoice.id] = invoice - for picking in picking_obj.browse(cursor, user, picking_ids, - context=context): - if not picking.carrier_id: - continue - grid_id = carrier_obj.grid_get(cursor, user, [picking.carrier_id.id], - picking.address_id.id, context=context) - if not grid_id: - raise osv.except_osv(_('Warning'), - _('The carrier %s (id: %d) has no delivery grid!') \ - % (picking.carrier_id.name, - picking.carrier_id.id)) - invoice = invoices[result[picking.id]] - price = grid_obj.get_price_from_picking(cursor, user, grid_id, - invoice.amount_untaxed, picking.weight, picking.volume, - context=context) - account_id = picking.carrier_id.product_id.product_tmpl_id\ - .property_account_income.id - if not account_id: - account_id = picking.carrier_id.product_id.categ_id\ - .property_account_income_categ.id - invoice_line_obj.create(cursor, user, { - 'name': picking.carrier_id.name, - 'invoice_id': invoice.id, - 'uos_id': picking.carrier_id.product_id.uos_id.id, - 'product_id': picking.carrier_id.product_id.id, - 'account_id': account_id, - 'price_unit': price, - 'quantity': 1, - 'invoice_line_tax_id': [(6, 0, - [x.id for x in (picking.carrier_id.product_id.taxes_id - or [])])], - }) - return result + for picking in picking_obj.browse(cursor, user, picking_ids, + context=context): + if not picking.carrier_id: + continue + grid_id = carrier_obj.grid_get(cursor, user, [picking.carrier_id.id], + picking.address_id.id, context=context) + if not grid_id: + raise osv.except_osv(_('Warning'), + _('The carrier %s (id: %d) has no delivery grid!') \ + % (picking.carrier_id.name, + picking.carrier_id.id)) + invoice = invoices[result[picking.id]] + price = grid_obj.get_price_from_picking(cursor, user, grid_id, + invoice.amount_untaxed, picking.weight, picking.volume, + context=context) + account_id = picking.carrier_id.product_id.product_tmpl_id\ + .property_account_income.id + if not account_id: + account_id = picking.carrier_id.product_id.categ_id\ + .property_account_income_categ.id + invoice_line_obj.create(cursor, user, { + 'name': picking.carrier_id.name, + 'invoice_id': invoice.id, + 'uos_id': picking.carrier_id.product_id.uos_id.id, + 'product_id': picking.carrier_id.product_id.id, + 'account_id': account_id, + 'price_unit': price, + 'quantity': 1, + 'invoice_line_tax_id': [(6, 0, + [x.id for x in (picking.carrier_id.product_id.taxes_id + or [])])], + }) + return result stock_picking() diff --git a/addons/delivery/wizard/delivery_sale_order.py b/addons/delivery/wizard/delivery_sale_order.py index 256e7a60a5e..b0b7eb544e3 100644 --- a/addons/delivery/wizard/delivery_sale_order.py +++ b/addons/delivery/wizard/delivery_sale_order.py @@ -35,57 +35,57 @@ from tools.translate import _ delivery_form = """<?xml version="1.0"?> <form string="Create deliveries"> - <separator colspan="4" string="Delivery Method" /> - <field name="carrier_id" /> + <separator colspan="4" string="Delivery Method" /> + <field name="carrier_id" /> </form> """ delivery_fields = { - 'carrier_id' : {'string':'Delivery Method', 'type':'many2one', 'relation': 'delivery.carrier','required':True} + 'carrier_id' : {'string':'Delivery Method', 'type':'many2one', 'relation': 'delivery.carrier','required':True} } def _delivery_default(self, cr, uid, data, context): - order_obj = pooler.get_pool(cr.dbname).get('sale.order') - order = order_obj.browse(cr, uid, data['ids'])[0] - if not order.state in ('draft'): - raise wizard.except_wizard(_('Order not in draft state !'), _('The order state have to be draft to add delivery lines.')) + order_obj = pooler.get_pool(cr.dbname).get('sale.order') + order = order_obj.browse(cr, uid, data['ids'])[0] + if not order.state in ('draft'): + raise wizard.except_wizard(_('Order not in draft state !'), _('The order state have to be draft to add delivery lines.')) - carrier_id = order.partner_id.property_delivery_carrier.id - return {'carrier_id': carrier_id} + carrier_id = order.partner_id.property_delivery_carrier.id + return {'carrier_id': carrier_id} def _delivery_set(self, cr, uid, data, context): - order_obj = pooler.get_pool(cr.dbname).get('sale.order') - line_obj = pooler.get_pool(cr.dbname).get('sale.order.line') - order_objs = order_obj.browse(cr, uid, data['ids'], context) + order_obj = pooler.get_pool(cr.dbname).get('sale.order') + line_obj = pooler.get_pool(cr.dbname).get('sale.order.line') + order_objs = order_obj.browse(cr, uid, data['ids'], context) - for order in order_objs: - grid_id = pooler.get_pool(cr.dbname).get('delivery.carrier').grid_get(cr, uid, [data['form']['carrier_id']],order.partner_shipping_id.id) - if not grid_id: - raise wizard.except_wizard(_('No grid avaible !'), _('No grid matching for this carrier !')) - grid = pooler.get_pool(cr.dbname).get('delivery.grid').browse(cr, uid, [grid_id])[0] + for order in order_objs: + grid_id = pooler.get_pool(cr.dbname).get('delivery.carrier').grid_get(cr, uid, [data['form']['carrier_id']],order.partner_shipping_id.id) + if not grid_id: + raise wizard.except_wizard(_('No grid avaible !'), _('No grid matching for this carrier !')) + grid = pooler.get_pool(cr.dbname).get('delivery.grid').browse(cr, uid, [grid_id])[0] - line_obj.create(cr, uid, { - 'order_id': order.id, - 'name': grid.carrier_id.name, - 'product_uom_qty': 1, - 'product_uom': grid.carrier_id.product_id.uom_id.id, - 'product_id': grid.carrier_id.product_id.id, - 'price_unit': grid.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context), - 'tax_id': [(6,0,[ x.id for x in grid.carrier_id.product_id.taxes_id])], - 'type': 'make_to_stock' - }) + line_obj.create(cr, uid, { + 'order_id': order.id, + 'name': grid.carrier_id.name, + 'product_uom_qty': 1, + 'product_uom': grid.carrier_id.product_id.uom_id.id, + 'product_id': grid.carrier_id.product_id.id, + 'price_unit': grid.get_price(cr, uid, grid.id, order, time.strftime('%Y-%m-%d'), context), + 'tax_id': [(6,0,[ x.id for x in grid.carrier_id.product_id.taxes_id])], + 'type': 'make_to_stock' + }) - return {} + return {} class make_delivery(wizard.interface): - states = { - 'init' : { - 'actions' : [_delivery_default], - 'result' : {'type' : 'form', 'arch' : delivery_form, 'fields' : delivery_fields, 'state' : [('end', 'Cancel'),('delivery', 'Create delivery line') ]} - }, - 'delivery' : { - 'actions' : [_delivery_set], - 'result' : {'type' : 'state', 'state' : 'end'} - }, - } + states = { + 'init' : { + 'actions' : [_delivery_default], + 'result' : {'type' : 'form', 'arch' : delivery_form, 'fields' : delivery_fields, 'state' : [('end', 'Cancel'),('delivery', 'Create delivery line') ]} + }, + 'delivery' : { + 'actions' : [_delivery_set], + 'result' : {'type' : 'state', 'state' : 'end'} + }, + } make_delivery("delivery.sale.order") diff --git a/addons/hr/__terp__.py b/addons/hr/__terp__.py index 29604f17dc2..dbbd59a4105 100644 --- a/addons/hr/__terp__.py +++ b/addons/hr/__terp__.py @@ -26,34 +26,34 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Human Resources", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "website" : "http://tinyerp.com/module_hr.html", - "description": """ - Module for human resource management. You can manage: - * Employees and hierarchies - * Work hours sheets - * Attendances and sign in/out system - * Holidays + "name" : "Human Resources", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "website" : "http://tinyerp.com/module_hr.html", + "description": """ + Module for human resource management. You can manage: + * Employees and hierarchies + * Work hours sheets + * Attendances and sign in/out system + * Holidays - Different reports are also provided, mainly for attendance statistics. - """, - "depends" : ["base"], - "init_xml" : [], - "demo_xml" : [ - "hr_demo.xml", - "hr_bel_holidays_2005.xml", - "hr_department_demo.xml", - ], - "update_xml" : [ - "hr_view.xml", - "hr_report.xml", - "hr_wizard.xml", - "hr_department_view.xml", - "hr_security.xml", - ], - "active": False, - "installable": True + Different reports are also provided, mainly for attendance statistics. + """, + "depends" : ["base"], + "init_xml" : [], + "demo_xml" : [ + "hr_demo.xml", + "hr_bel_holidays_2005.xml", + "hr_department_demo.xml", + ], + "update_xml" : [ + "hr_view.xml", + "hr_report.xml", + "hr_wizard.xml", + "hr_department_view.xml", + "hr_security.xml", + ], + "active": False, + "installable": True } diff --git a/addons/hr/hr.py b/addons/hr/hr.py index e1c5edaf2d1..bdea50c6749 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -34,239 +34,239 @@ from osv import fields, osv from tools.translate import _ class hr_timesheet_group(osv.osv): - _name = "hr.timesheet.group" - _description = "Working Time" - _columns = { - 'name' : fields.char("Group name", size=64, required=True), - 'timesheet_id' : fields.one2many('hr.timesheet', 'tgroup_id', 'Working Time'), - 'manager' : fields.many2one('res.users', 'Workgroup manager'), - } - # - # TODO: improve; very slow ! - # bug if transition to another period - # - def interval_get(self, cr, uid, id, dt_from, hours, byday=True): - if not id: - return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=int(hours)*3))] - todo = hours - cycle = 0 - result = [] - while todo>0: - cr.execute('select hour_from,hour_to from hr_timesheet where dayofweek=%d and tgroup_id=%d order by hour_from', (dt_from.day_of_week,id)) - for (hour_from,hour_to) in cr.fetchall(): - h1,m1 = map(int,hour_from.split(':')) - h2,m2 = map(int,hour_to.split(':')) - d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h1,m1) - d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h2,m2) - if dt_from<d2: - date1 = max(dt_from,d1) - if date1+DateTime.RelativeDateTime(hours=todo)<=d2: - result.append((date1, date1+DateTime.RelativeDateTime(hours=todo))) - todo = 0 - else: - todo -= (d2-date1).hours - result.append((date1, d2)) - dt_from = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day)+DateTime.RelativeDateTime(days=1) - cycle+=1 - if cycle>7 and todo==hours: - return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=hours*3))] - if byday: - i = 1 - while i<len(result): - if (result[i][0]-result[i-1][1]).days<1: - result[i-1]=(result[i-1][0],result[i][1]) - del result[i] - else: - i+=1 - return result + _name = "hr.timesheet.group" + _description = "Working Time" + _columns = { + 'name' : fields.char("Group name", size=64, required=True), + 'timesheet_id' : fields.one2many('hr.timesheet', 'tgroup_id', 'Working Time'), + 'manager' : fields.many2one('res.users', 'Workgroup manager'), + } + # + # TODO: improve; very slow ! + # bug if transition to another period + # + def interval_get(self, cr, uid, id, dt_from, hours, byday=True): + if not id: + return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=int(hours)*3))] + todo = hours + cycle = 0 + result = [] + while todo>0: + cr.execute('select hour_from,hour_to from hr_timesheet where dayofweek=%d and tgroup_id=%d order by hour_from', (dt_from.day_of_week,id)) + for (hour_from,hour_to) in cr.fetchall(): + h1,m1 = map(int,hour_from.split(':')) + h2,m2 = map(int,hour_to.split(':')) + d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h1,m1) + d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,h2,m2) + if dt_from<d2: + date1 = max(dt_from,d1) + if date1+DateTime.RelativeDateTime(hours=todo)<=d2: + result.append((date1, date1+DateTime.RelativeDateTime(hours=todo))) + todo = 0 + else: + todo -= (d2-date1).hours + result.append((date1, d2)) + dt_from = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day)+DateTime.RelativeDateTime(days=1) + cycle+=1 + if cycle>7 and todo==hours: + return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=hours*3))] + if byday: + i = 1 + while i<len(result): + if (result[i][0]-result[i-1][1]).days<1: + result[i-1]=(result[i-1][0],result[i][1]) + del result[i] + else: + i+=1 + return result hr_timesheet_group() class hr_employee_category(osv.osv): - _name = "hr.employee.category" - _description = "Employee Category" - _columns = { - 'name' : fields.char("Category", size=64, required=True), - 'parent_id': fields.many2one('hr.employee.category', 'Parent category', select=True), - 'child_ids': fields.one2many('hr.employee.category', 'parent_id', 'Childs Categories') - } + _name = "hr.employee.category" + _description = "Employee Category" + _columns = { + 'name' : fields.char("Category", size=64, required=True), + 'parent_id': fields.many2one('hr.employee.category', 'Parent category', select=True), + 'child_ids': fields.one2many('hr.employee.category', 'parent_id', 'Childs Categories') + } hr_employee_category() class hr_employee(osv.osv): - _name = "hr.employee" - _description = "Employee" + _name = "hr.employee" + _description = "Employee" - def _state(self, cr, uid, ids, name, args, context={}): - result = {} - for id in ids: - result[id] = 'absent' - cr.execute('SELECT hr_attendance.action, hr_attendance.employee_id \ - FROM ( \ - SELECT MAX(name) AS name, employee_id \ - FROM hr_attendance \ - WHERE action in (\'sign_in\', \'sign_out\') \ - GROUP BY employee_id \ - ) AS foo \ - LEFT JOIN hr_attendance \ - ON (hr_attendance.employee_id = foo.employee_id \ - AND hr_attendance.name = foo.name) \ - WHERE hr_attendance.employee_id \ - in (' + ','.join([str(x) for x in ids]) + ')') - for res in cr.fetchall(): - result[res[1]] = res[0] == 'sign_in' and 'present' or 'absent' - return result + def _state(self, cr, uid, ids, name, args, context={}): + result = {} + for id in ids: + result[id] = 'absent' + cr.execute('SELECT hr_attendance.action, hr_attendance.employee_id \ + FROM ( \ + SELECT MAX(name) AS name, employee_id \ + FROM hr_attendance \ + WHERE action in (\'sign_in\', \'sign_out\') \ + GROUP BY employee_id \ + ) AS foo \ + LEFT JOIN hr_attendance \ + ON (hr_attendance.employee_id = foo.employee_id \ + AND hr_attendance.name = foo.name) \ + WHERE hr_attendance.employee_id \ + in (' + ','.join([str(x) for x in ids]) + ')') + for res in cr.fetchall(): + result[res[1]] = res[0] == 'sign_in' and 'present' or 'absent' + return result - _columns = { - 'name' : fields.char("Employee", size=128, required=True), - 'active' : fields.boolean('Active'), - 'company_id': fields.many2one('res.company', 'Company'), - 'address_id': fields.many2one('res.partner.address', 'Contact address'), - 'state': fields.function(_state, method=True, type='selection', selection=[('absent', 'Absent'), ('present', 'Present')], string='Attendance'), - 'started' : fields.date("Started on"), - 'notes': fields.text('Notes'), - 'attendances' : fields.one2many('hr.attendance', 'employee_id', "Employee's attendances"), - 'holidays' : fields.one2many('hr.holidays', 'employee_id', "Employee's holidays"), - 'workgroups' : fields.many2many('hr.timesheet.group', 'hr_timesheet_employee_rel', 'emp_id', 'tgroup_id', "Employee's work team"), - 'user_id' : fields.many2one('res.users', 'Tiny ERP User'), - 'category_id' : fields.many2one('hr.employee.category', 'Category'), - 'regime' : fields.float('Workhours by week'), - 'holiday_max' : fields.integer("Number of holidays"), - 'parent_id': fields.many2one('hr.employee', 'Boss', select=True), - 'child_ids': fields.one2many('hr.employee', 'parent_id','Subordinates'), - } - _defaults = { - 'active' : lambda *a: True, - 'state' : lambda *a: 'absent', - } - def sign_change(self, cr, uid, ids, context={}, dt=False): - for emp in self.browse(cr, uid, ids): - if not self._action_check(cr, uid, emp.id, dt, context): - raise osv.except_osv(_('Warning'), _('You tried to sign with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) - res = {'action':'action', 'employee_id':emp.id} - if dt: - res['name'] = dt - att_id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) - return True + _columns = { + 'name' : fields.char("Employee", size=128, required=True), + 'active' : fields.boolean('Active'), + 'company_id': fields.many2one('res.company', 'Company'), + 'address_id': fields.many2one('res.partner.address', 'Contact address'), + 'state': fields.function(_state, method=True, type='selection', selection=[('absent', 'Absent'), ('present', 'Present')], string='Attendance'), + 'started' : fields.date("Started on"), + 'notes': fields.text('Notes'), + 'attendances' : fields.one2many('hr.attendance', 'employee_id', "Employee's attendances"), + 'holidays' : fields.one2many('hr.holidays', 'employee_id', "Employee's holidays"), + 'workgroups' : fields.many2many('hr.timesheet.group', 'hr_timesheet_employee_rel', 'emp_id', 'tgroup_id', "Employee's work team"), + 'user_id' : fields.many2one('res.users', 'Tiny ERP User'), + 'category_id' : fields.many2one('hr.employee.category', 'Category'), + 'regime' : fields.float('Workhours by week'), + 'holiday_max' : fields.integer("Number of holidays"), + 'parent_id': fields.many2one('hr.employee', 'Boss', select=True), + 'child_ids': fields.one2many('hr.employee', 'parent_id','Subordinates'), + } + _defaults = { + 'active' : lambda *a: True, + 'state' : lambda *a: 'absent', + } + def sign_change(self, cr, uid, ids, context={}, dt=False): + for emp in self.browse(cr, uid, ids): + if not self._action_check(cr, uid, emp.id, dt, context): + raise osv.except_osv(_('Warning'), _('You tried to sign with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) + res = {'action':'action', 'employee_id':emp.id} + if dt: + res['name'] = dt + att_id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) + return True - def sign_out(self, cr, uid, ids, context={}, dt=False, *args): - id = False - for emp in self.browse(cr, uid, ids): - if not self._action_check(cr, uid, emp.id, dt, context): - raise osv.except_osv(_('Warning'), _('You tried to sign out with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) - res = {'action':'sign_out', 'employee_id':emp.id} - if dt: - res['name'] = dt - att_id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) - id = att_id - return id + def sign_out(self, cr, uid, ids, context={}, dt=False, *args): + id = False + for emp in self.browse(cr, uid, ids): + if not self._action_check(cr, uid, emp.id, dt, context): + raise osv.except_osv(_('Warning'), _('You tried to sign out with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) + res = {'action':'sign_out', 'employee_id':emp.id} + if dt: + res['name'] = dt + att_id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) + id = att_id + return id - def _action_check(self, cr, uid, emp_id, dt=False,context={}): - cr.execute('select max(name) from hr_attendance where employee_id=%d', (emp_id,)) - res = cr.fetchone() - return not (res and (res[0]>=(dt or time.strftime('%Y-%m-%d %H:%M:%S')))) + def _action_check(self, cr, uid, emp_id, dt=False,context={}): + cr.execute('select max(name) from hr_attendance where employee_id=%d', (emp_id,)) + res = cr.fetchone() + return not (res and (res[0]>=(dt or time.strftime('%Y-%m-%d %H:%M:%S')))) - def sign_in(self, cr, uid, ids, context={}, dt=False, *args): - id = False - for emp in self.browse(cr, uid, ids): - if not self._action_check(cr, uid, emp.id, dt, context): - raise osv.except_osv(_('Warning'), _('You tried to sign in with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) - res = {'action':'sign_in', 'employee_id':emp.id} - if dt: - res['name'] = dt - id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) - return id + def sign_in(self, cr, uid, ids, context={}, dt=False, *args): + id = False + for emp in self.browse(cr, uid, ids): + if not self._action_check(cr, uid, emp.id, dt, context): + raise osv.except_osv(_('Warning'), _('You tried to sign in with a date anterior to another event !\nTry to contact the administrator to correct attendances.')) + res = {'action':'sign_in', 'employee_id':emp.id} + if dt: + res['name'] = dt + id = self.pool.get('hr.attendance').create(cr, uid, res, context=context) + return id hr_employee() class hr_timesheet(osv.osv): - _name = "hr.timesheet" - _description = "Timesheet Line" - _columns = { - 'name' : fields.char("Name", size=64, required=True), - 'dayofweek': fields.selection([('0','Monday'),('1','Tuesday'),('2','Wednesday'),('3','Thursday'),('4','Friday'),('5','Saturday'),('6','Sunday')], 'Day of week'), - 'date_from' : fields.date('Starting date'), - 'hour_from' : fields.char('Work from', size=8, required=True), - 'hour_to' : fields.char("Work to", size=8, required=True), - 'tgroup_id' : fields.many2one("hr.timesheet.group", "Employee's timesheet group", select=True), - } - _order = 'dayofweek, hour_from' + _name = "hr.timesheet" + _description = "Timesheet Line" + _columns = { + 'name' : fields.char("Name", size=64, required=True), + 'dayofweek': fields.selection([('0','Monday'),('1','Tuesday'),('2','Wednesday'),('3','Thursday'),('4','Friday'),('5','Saturday'),('6','Sunday')], 'Day of week'), + 'date_from' : fields.date('Starting date'), + 'hour_from' : fields.char('Work from', size=8, required=True), + 'hour_to' : fields.char("Work to", size=8, required=True), + 'tgroup_id' : fields.many2one("hr.timesheet.group", "Employee's timesheet group", select=True), + } + _order = 'dayofweek, hour_from' hr_timesheet() class hr_action_reason(osv.osv): - _name = "hr.action.reason" - _description = "Action reason" - _columns = { - 'name' : fields.char('Reason', size=64, required=True), - 'action_type' : fields.selection([('sign_in', 'Sign in'), ('sign_out', 'Sign out')], "Action's type"), - } - _defaults = { - 'action_type' : lambda *a: 'sign_in', - } + _name = "hr.action.reason" + _description = "Action reason" + _columns = { + 'name' : fields.char('Reason', size=64, required=True), + 'action_type' : fields.selection([('sign_in', 'Sign in'), ('sign_out', 'Sign out')], "Action's type"), + } + _defaults = { + 'action_type' : lambda *a: 'sign_in', + } hr_action_reason() def _employee_get(obj,cr,uid,context={}): - ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)]) - if ids: - return ids[0] - return False + ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)]) + if ids: + return ids[0] + return False class hr_attendance(osv.osv): - _name = "hr.attendance" - _description = "Attendance" - _columns = { - 'name' : fields.datetime('Date', required=True), - 'action' : fields.selection([('sign_in', 'Sign In'), ('sign_out', 'Sign Out'),('action','Action')], 'Action', required=True), - 'action_desc' : fields.many2one("hr.action.reason", "Action reason", domain="[('action_type', '=', action)]"), - 'employee_id' : fields.many2one('hr.employee', 'Employee', required=True, select=True), - } - _defaults = { - 'name' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'employee_id' : _employee_get, - } - - def _altern_si_so(self, cr, uid, ids): - for id in ids: - sql = ''' - select action, name - from hr_attendance as att - where employee_id = (select employee_id from hr_attendance where id=%s) - and action in ('sign_in','sign_out') - and name <= (select name from hr_attendance where id=%s) - order by name desc - limit 2 - ''' % (id, id) - cr.execute(sql) - atts = cr.fetchall() - if not ((len(atts)==1 and atts[0][0] == 'sign_in') or (atts[0][0] != atts[1][0] and atts[0][1] != atts[1][1])): - return False - return True - - _constraints = [(_altern_si_so, 'Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])] - _order = 'name desc' + _name = "hr.attendance" + _description = "Attendance" + _columns = { + 'name' : fields.datetime('Date', required=True), + 'action' : fields.selection([('sign_in', 'Sign In'), ('sign_out', 'Sign Out'),('action','Action')], 'Action', required=True), + 'action_desc' : fields.many2one("hr.action.reason", "Action reason", domain="[('action_type', '=', action)]"), + 'employee_id' : fields.many2one('hr.employee', 'Employee', required=True, select=True), + } + _defaults = { + 'name' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'employee_id' : _employee_get, + } + + def _altern_si_so(self, cr, uid, ids): + for id in ids: + sql = ''' + select action, name + from hr_attendance as att + where employee_id = (select employee_id from hr_attendance where id=%s) + and action in ('sign_in','sign_out') + and name <= (select name from hr_attendance where id=%s) + order by name desc + limit 2 + ''' % (id, id) + cr.execute(sql) + atts = cr.fetchall() + if not ((len(atts)==1 and atts[0][0] == 'sign_in') or (atts[0][0] != atts[1][0] and atts[0][1] != atts[1][1])): + return False + return True + + _constraints = [(_altern_si_so, 'Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])] + _order = 'name desc' hr_attendance() class hr_holidays_status(osv.osv): - _name = "hr.holidays.status" - _description = "Holidays Status" - _columns = { - 'name' : fields.char('Holiday Status', size=64, required=True, translate=True), - } + _name = "hr.holidays.status" + _description = "Holidays Status" + _columns = { + 'name' : fields.char('Holiday Status', size=64, required=True, translate=True), + } hr_holidays_status() class hr_holidays(osv.osv): - _name = "hr.holidays" - _description = "Holidays" - _columns = { - 'name' : fields.char('Description', required=True, size=64), - 'date_from' : fields.datetime('Vacation start day', required=True), - 'date_to' : fields.datetime('Vacation end day'), - 'holiday_status' : fields.many2one("hr.holidays.status", "Holiday's Status"), - 'employee_id' : fields.many2one('hr.employee', 'Employee', select=True), - } - _defaults = { - 'employee_id' : _employee_get - } - _order = 'date_from desc' + _name = "hr.holidays" + _description = "Holidays" + _columns = { + 'name' : fields.char('Description', required=True, size=64), + 'date_from' : fields.datetime('Vacation start day', required=True), + 'date_to' : fields.datetime('Vacation end day'), + 'holiday_status' : fields.many2one("hr.holidays.status", "Holiday's Status"), + 'employee_id' : fields.many2one('hr.employee', 'Employee', select=True), + } + _defaults = { + 'employee_id' : _employee_get + } + _order = 'date_from desc' hr_holidays() # vim:tw=0:noexpandtab diff --git a/addons/hr/hr_department.py b/addons/hr/hr_department.py index d36258e57d5..615d5ae2826 100644 --- a/addons/hr/hr_department.py +++ b/addons/hr/hr_department.py @@ -31,57 +31,57 @@ from osv import fields,osv import tools class hr_department(osv.osv): - _name = "hr.department" - _columns = { - 'name': fields.char('Department Name', size=64, required=True), - 'company_id': fields.many2one('res.company', 'Company', select=True, required=True), - 'parent_id': fields.many2one('hr.department', 'Parent Department', select=True), - 'child_ids': fields.one2many('hr.department', 'parent_id', 'Childs Departments'), - 'note': fields.text('Note'), - 'manager_id': fields.many2one('res.users', 'Manager', required=True), - 'member_ids': fields.many2many('res.users', 'hr_department_user_rel', 'department_id', 'user_id', 'Members'), - } - def _get_members(self,cr, uid, context={}): - mids = self.search(cr, uid, [('manager_id','=',uid)]) - result = {uid:1} - for m in self.browse(cr, uid, mids, context): - for user in m.member_ids: - result[user.id] = 1 - return result.keys() - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from hr_department where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - _constraints = [ - (_check_recursion, 'Error! You can not create recursive departments.', ['parent_id']) - ] + _name = "hr.department" + _columns = { + 'name': fields.char('Department Name', size=64, required=True), + 'company_id': fields.many2one('res.company', 'Company', select=True, required=True), + 'parent_id': fields.many2one('hr.department', 'Parent Department', select=True), + 'child_ids': fields.one2many('hr.department', 'parent_id', 'Childs Departments'), + 'note': fields.text('Note'), + 'manager_id': fields.many2one('res.users', 'Manager', required=True), + 'member_ids': fields.many2many('res.users', 'hr_department_user_rel', 'department_id', 'user_id', 'Members'), + } + def _get_members(self,cr, uid, context={}): + mids = self.search(cr, uid, [('manager_id','=',uid)]) + result = {uid:1} + for m in self.browse(cr, uid, mids, context): + for user in m.member_ids: + result[user.id] = 1 + return result.keys() + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from hr_department where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + _constraints = [ + (_check_recursion, 'Error! You can not create recursive departments.', ['parent_id']) + ] hr_department() class ir_action_window(osv.osv): - _inherit = 'ir.actions.act_window' + _inherit = 'ir.actions.act_window' - def read(self, cr, uid, ids, fields=None, context=None, - load='_classic_read'): - select = ids - if isinstance(ids, (int, long)): - select = [ids] - res = super(ir_action_window, self).read(cr, uid, select, fields=fields, - context=context, load=load) - for r in res: - mystring = 'department_users_get()' - if mystring in (r.get('domain', '[]') or ''): - r['domain'] = r['domain'].replace(mystring, str( - self.pool.get('hr.department')._get_members(cr, uid))) - if isinstance(ids, (int, long)): - return res[0] - return res + def read(self, cr, uid, ids, fields=None, context=None, + load='_classic_read'): + select = ids + if isinstance(ids, (int, long)): + select = [ids] + res = super(ir_action_window, self).read(cr, uid, select, fields=fields, + context=context, load=load) + for r in res: + mystring = 'department_users_get()' + if mystring in (r.get('domain', '[]') or ''): + r['domain'] = r['domain'].replace(mystring, str( + self.pool.get('hr.department')._get_members(cr, uid))) + if isinstance(ids, (int, long)): + return res[0] + return res ir_action_window() diff --git a/addons/hr/report/attendance_errors.py b/addons/hr/report/attendance_errors.py index 4f6cd57ce63..a6549ce5f5c 100644 --- a/addons/hr/report/attendance_errors.py +++ b/addons/hr/report/attendance_errors.py @@ -32,53 +32,53 @@ import time from report import report_sxw class attendance_print(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(attendance_print, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'lst': self._lst, - 'total': self._lst_total, - }) + def __init__(self, cr, uid, name, context): + super(attendance_print, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'lst': self._lst, + 'total': self._lst_total, + }) - def _sign(self, dt): - if abs(dt.days)>1: - format = '%d day'+(((abs(dt.days)>=2) and 's') or '')+' %H:%M:%S' - else: - format = '%H:%M:%S' - if dt.seconds<0: - return dt.strftime('- '+format) - else: - return dt.strftime(format) + def _sign(self, dt): + if abs(dt.days)>1: + format = '%d day'+(((abs(dt.days)>=2) and 's') or '')+' %H:%M:%S' + else: + format = '%H:%M:%S' + if dt.seconds<0: + return dt.strftime('- '+format) + else: + return dt.strftime(format) - def _lst(self, employee_id, dt_from, dt_to, max, *args): - self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s) order by name', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) - res = self.cr.dictfetchall() - for r in res: - if r['action']=='sign_out': - r['delay'] = - r['delay'] - temp = r['delay'].seconds - r['delay'] = self._sign(r['delay']) - if abs(temp) < max*60: - r['delay2'] = r['delay'] - else: - r['delay2'] = '/' - return res + def _lst(self, employee_id, dt_from, dt_to, max, *args): + self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s) order by name', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) + res = self.cr.dictfetchall() + for r in res: + if r['action']=='sign_out': + r['delay'] = - r['delay'] + temp = r['delay'].seconds + r['delay'] = self._sign(r['delay']) + if abs(temp) < max*60: + r['delay2'] = r['delay'] + else: + r['delay2'] = '/' + return res - def _lst_total(self, employee_id, dt_from, dt_to, max, *args): - self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s)', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) - res = self.cr.dictfetchall() - if not res: - return ('/','/') - total = 0 - total2 = 0 - for r in res: - if r['action']=='sign_out': - r['delay'] = - r['delay'] - total += r['delay'] - if abs(r['delay'].seconds) < max*60: - total2 += r['delay'] + def _lst_total(self, employee_id, dt_from, dt_to, max, *args): + self.cr.execute('select name as date, create_date, action, create_date-name as delay from hr_attendance where employee_id=%d and name<=%s and name>=%s and action in (%s,%s)', (employee_id, dt_to, dt_from, 'sign_in', 'sign_out')) + res = self.cr.dictfetchall() + if not res: + return ('/','/') + total = 0 + total2 = 0 + for r in res: + if r['action']=='sign_out': + r['delay'] = - r['delay'] + total += r['delay'] + if abs(r['delay'].seconds) < max*60: + total2 += r['delay'] - return (self._sign(total),total2 and self._sign(total2)) + return (self._sign(total),total2 and self._sign(total2)) report_sxw.report_sxw('report.hr.timesheet.attendance.error', 'hr.employee', 'addons/hr/report/attendance_errors.rml',parser=attendance_print) diff --git a/addons/hr/report/bymonth.py b/addons/hr/report/bymonth.py index 2c3c9e3751d..52cda6820a3 100644 --- a/addons/hr/report/bymonth.py +++ b/addons/hr/report/bymonth.py @@ -41,128 +41,128 @@ one_day = DateTime.RelativeDateTime(days=1) month2name = [0,'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] def hour2str(h): - hours = int(h) - minutes = int(round((h - hours) * 60, 0)) - return '%02dh%02d' % (hours, minutes) + hours = int(h) + minutes = int(round((h - hours) * 60, 0)) + return '%02dh%02d' % (hours, minutes) class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context): - service = netsvc.LocalService('object_proxy') + def create_xml(self, cr, uid, ids, datas, context): + service = netsvc.LocalService('object_proxy') - month = DateTime.DateTime(datas['form']['year'], datas['form']['month'], 1) - - user_xml = ['<month>%s</month>' % month2name[month.month], '<year>%s</year>' % month.year] - - # Public holidays - jf_sql = """select hol.date_from, hol.date_to from hr_holidays as hol, hr_holidays_status as stat - where hol.holiday_status = stat.id and stat.name = 'Public holidays' """ - cr.execute(jf_sql) - jfs = [] - jfs = [(DateTime.strptime(l['date_from'], '%Y-%m-%d %H:%M:%S'), DateTime.strptime(l['date_to'], '%Y-%m-%d %H:%M:%S')) for l in cr.dictfetchall()] - - for employee_id in ids: - emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id])[0] - stop, days_xml = False, [] - user_repr = ''' - <user> - <name>%s</name> - <regime>%s</regime> - <holiday>%s</holiday> - %%s - </user> - ''' % (toxml(emp['name']),emp['regime'],emp['holiday_max']) - today, tomor = month, month + one_day - while today.month == month.month: - #### Work hour calculation - sql = ''' - select action, att.name - from hr_employee as emp inner join hr_attendance as att - on emp.id = att.employee_id - where att.name between '%s' and '%s' and emp.id = %s - order by att.name - ''' - cr.execute(sql, (today, tomor, employee_id)) - attendences = cr.dictfetchall() - wh = 0 - if attendences and attendences[0]['action'] == 'sign_out': - attendences.insert(0, {'name': today.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'}) - if attendences and attendences[-1]['action'] == 'sign_in': - attendences.append({'name' : tomor.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'}) - for att in attendences: - dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S') - if att['action'] == 'sign_out': - wh += (dt - ldt).hours - ldt = dt + month = DateTime.DateTime(datas['form']['year'], datas['form']['month'], 1) + + user_xml = ['<month>%s</month>' % month2name[month.month], '<year>%s</year>' % month.year] + + # Public holidays + jf_sql = """select hol.date_from, hol.date_to from hr_holidays as hol, hr_holidays_status as stat + where hol.holiday_status = stat.id and stat.name = 'Public holidays' """ + cr.execute(jf_sql) + jfs = [] + jfs = [(DateTime.strptime(l['date_from'], '%Y-%m-%d %H:%M:%S'), DateTime.strptime(l['date_to'], '%Y-%m-%d %H:%M:%S')) for l in cr.dictfetchall()] + + for employee_id in ids: + emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id])[0] + stop, days_xml = False, [] + user_repr = ''' + <user> + <name>%s</name> + <regime>%s</regime> + <holiday>%s</holiday> + %%s + </user> + ''' % (toxml(emp['name']),emp['regime'],emp['holiday_max']) + today, tomor = month, month + one_day + while today.month == month.month: + #### Work hour calculation + sql = ''' + select action, att.name + from hr_employee as emp inner join hr_attendance as att + on emp.id = att.employee_id + where att.name between '%s' and '%s' and emp.id = %s + order by att.name + ''' + cr.execute(sql, (today, tomor, employee_id)) + attendences = cr.dictfetchall() + wh = 0 + if attendences and attendences[0]['action'] == 'sign_out': + attendences.insert(0, {'name': today.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'}) + if attendences and attendences[-1]['action'] == 'sign_in': + attendences.append({'name' : tomor.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'}) + for att in attendences: + dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S') + if att['action'] == 'sign_out': + wh += (dt - ldt).hours + ldt = dt - #### Theoretical workhour calculation - sql = ''' - select t.hour_from, t.hour_to - from hr_timesheet as t - inner join (hr_timesheet_group as g inner join hr_timesheet_employee_rel as rel - on rel.tgroup_id = g.id and rel.emp_id = %s) - on t.tgroup_id = g.id - where dayofweek = %s - and date_from = (select max(date_from) - from hr_timesheet inner join (hr_timesheet_employee_rel - inner join hr_timesheet_group - on hr_timesheet_group.id = hr_timesheet_employee_rel.tgroup_id - and hr_timesheet_employee_rel.emp_id = %s) - on hr_timesheet.tgroup_id = hr_timesheet_group.id - where dayofweek = %s and date_from <= '%s') - order by date_from desc - ''' - isPH = False - for jf_start, jf_end in jfs: - if jf_start <= today <= jf_end: - isPH = True - break - if isPH: - twh = 0 - else: - cr.execute(sql, (emp['id'], today.day_of_week, emp['id'], today.day_of_week, today)) - ths = cr.dictfetchall() - twh = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,ths, 0) + #### Theoretical workhour calculation + sql = ''' + select t.hour_from, t.hour_to + from hr_timesheet as t + inner join (hr_timesheet_group as g inner join hr_timesheet_employee_rel as rel + on rel.tgroup_id = g.id and rel.emp_id = %s) + on t.tgroup_id = g.id + where dayofweek = %s + and date_from = (select max(date_from) + from hr_timesheet inner join (hr_timesheet_employee_rel + inner join hr_timesheet_group + on hr_timesheet_group.id = hr_timesheet_employee_rel.tgroup_id + and hr_timesheet_employee_rel.emp_id = %s) + on hr_timesheet.tgroup_id = hr_timesheet_group.id + where dayofweek = %s and date_from <= '%s') + order by date_from desc + ''' + isPH = False + for jf_start, jf_end in jfs: + if jf_start <= today <= jf_end: + isPH = True + break + if isPH: + twh = 0 + else: + cr.execute(sql, (emp['id'], today.day_of_week, emp['id'], today.day_of_week, today)) + ths = cr.dictfetchall() + twh = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,ths, 0) - #### Holiday calculation - hh = 0 - sql = ''' - select hol.date_from, hol.date_to, stat.name as status - from hr_employee as emp - inner join (hr_holidays as hol left join hr_holidays_status as stat - on hol.holiday_status = stat.id) - on emp.id = hol.employee_id - where ((hol.date_from <= '%s' and hol.date_to >= '%s') - or (hol.date_from < '%s' and hol.date_to >= '%s') - or (hol.date_from > '%s' and hol.date_to < '%s')) - and emp.id = %s - order by hol.date_from - ''' - cr.execute(sql, (today, today, tomor, tomor, today, tomor, employee_id)) - holidays = cr.dictfetchall() - for hol in holidays: - df = DateTime.strptime(hol['date_from'], '%Y-%m-%d %H:%M:%S') - dt = DateTime.strptime(hol['date_to'], '%Y-%m-%d %H:%M:%S') - if (df.year, df.month, df.day) <= (today.year, today.month, today.day) <= (dt.year, dt.month, dt.day): - if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day): - hh += (dt - df).hours - else: - hh = twh - - # Week xml representation - twh, wh, hh = map(hour2str, (twh, wh, hh)) - today_xml = '<day num="%s"><th>%s</th><wh>%s</wh><hh>%s</hh></day>' % ((today - month).days+1, twh, wh, hh) - days_xml.append(today_xml) - today, tomor = tomor, tomor + one_day - - user_xml.append(user_repr % '\n'.join(days_xml)) - - xml = '''<?xml version="1.0" encoding="UTF-8" ?> - <report> - %s - </report> - ''' % '\n'.join(user_xml) + #### Holiday calculation + hh = 0 + sql = ''' + select hol.date_from, hol.date_to, stat.name as status + from hr_employee as emp + inner join (hr_holidays as hol left join hr_holidays_status as stat + on hol.holiday_status = stat.id) + on emp.id = hol.employee_id + where ((hol.date_from <= '%s' and hol.date_to >= '%s') + or (hol.date_from < '%s' and hol.date_to >= '%s') + or (hol.date_from > '%s' and hol.date_to < '%s')) + and emp.id = %s + order by hol.date_from + ''' + cr.execute(sql, (today, today, tomor, tomor, today, tomor, employee_id)) + holidays = cr.dictfetchall() + for hol in holidays: + df = DateTime.strptime(hol['date_from'], '%Y-%m-%d %H:%M:%S') + dt = DateTime.strptime(hol['date_to'], '%Y-%m-%d %H:%M:%S') + if (df.year, df.month, df.day) <= (today.year, today.month, today.day) <= (dt.year, dt.month, dt.day): + if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day): + hh += (dt - df).hours + else: + hh = twh + + # Week xml representation + twh, wh, hh = map(hour2str, (twh, wh, hh)) + today_xml = '<day num="%s"><th>%s</th><wh>%s</wh><hh>%s</hh></day>' % ((today - month).days+1, twh, wh, hh) + days_xml.append(today_xml) + today, tomor = tomor, tomor + one_day + + user_xml.append(user_repr % '\n'.join(days_xml)) + + xml = '''<?xml version="1.0" encoding="UTF-8" ?> + <report> + %s + </report> + ''' % '\n'.join(user_xml) - return xml + return xml report_custom('report.hr.timesheet.bymonth', 'hr.employee', '', 'addons/hr/report/bymonth.xsl') # vim:noexpandtab:tw=0 diff --git a/addons/hr/report/timesheet.py b/addons/hr/report/timesheet.py index c1c18f7f24d..ec1f93d3006 100644 --- a/addons/hr/report/timesheet.py +++ b/addons/hr/report/timesheet.py @@ -40,149 +40,149 @@ one_week = DateTime.RelativeDateTime(days=7) num2day = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] def to_hour(h): - return int(h), int(round((h - int(h)) * 60, 0)) + return int(h), int(round((h - int(h)) * 60, 0)) class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context): - service = netsvc.LocalService('object_proxy') + def create_xml(self, cr, uid, ids, datas, context): + service = netsvc.LocalService('object_proxy') - start_date = DateTime.strptime(datas['form']['init_date'], '%Y-%m-%d') - end_date = DateTime.strptime(datas['form']['end_date'], '%Y-%m-%d') - first_monday = start_date - DateTime.RelativeDateTime(days=start_date.day_of_week) - last_monday = end_date + DateTime.RelativeDateTime(days=7 - end_date.day_of_week) + start_date = DateTime.strptime(datas['form']['init_date'], '%Y-%m-%d') + end_date = DateTime.strptime(datas['form']['end_date'], '%Y-%m-%d') + first_monday = start_date - DateTime.RelativeDateTime(days=start_date.day_of_week) + last_monday = end_date + DateTime.RelativeDateTime(days=7 - end_date.day_of_week) - if last_monday < first_monday: - first_monday, last_monday = last_monday, first_monday + if last_monday < first_monday: + first_monday, last_monday = last_monday, first_monday - user_xml = [] - - jf_sql = """select hol.date_from, hol.date_to from hr_holidays as hol, hr_holidays_status as stat - where hol.holiday_status = stat.id and stat.name = 'Public holidays' """ - cr.execute(jf_sql) - jfs = [] - jfs = [(DateTime.strptime(l['date_from'], '%Y-%m-%d %H:%M:%S'), DateTime.strptime(l['date_to'], '%Y-%m-%d %H:%M:%S')) for l in cr.dictfetchall()] - - for employee_id in ids: - emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id], ['id', 'name'])[0] - monday, n_monday = first_monday, first_monday + one_week - stop, week_xml = False, [] - user_repr = ''' - <user> - <name>%s</name> - %%s - </user> - ''' % toxml(emp['name']) - while monday != last_monday: - #### Work hour calculation - sql = ''' - select action, att.name - from hr_employee as emp inner join hr_attendance as att - on emp.id = att.employee_id - where att.name between '%s' and '%s' and emp.id = %s - order by att.name - ''' - for idx in range(7): - cr.execute(sql, (monday, monday + DateTime.RelativeDateTime(days=idx+1), employee_id)) - attendences = cr.dictfetchall() - week_wh = {} - if attendences and attendences[0]['action'] == 'sign_out': - attendences.insert(0, {'name': monday.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'}) - if attendences and attendences[-1]['action'] == 'sign_in': - attendences.append({'name' : n_monday.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'}) - for att in attendences: - dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S') - if att['action'] == 'sign_out': - week_wh[ldt.day_of_week] = week_wh.get(ldt.day_of_week, 0) + (dt - ldt).hours - ldt = dt + user_xml = [] + + jf_sql = """select hol.date_from, hol.date_to from hr_holidays as hol, hr_holidays_status as stat + where hol.holiday_status = stat.id and stat.name = 'Public holidays' """ + cr.execute(jf_sql) + jfs = [] + jfs = [(DateTime.strptime(l['date_from'], '%Y-%m-%d %H:%M:%S'), DateTime.strptime(l['date_to'], '%Y-%m-%d %H:%M:%S')) for l in cr.dictfetchall()] + + for employee_id in ids: + emp = service.execute(cr.dbname, uid, 'hr.employee', 'read', [employee_id], ['id', 'name'])[0] + monday, n_monday = first_monday, first_monday + one_week + stop, week_xml = False, [] + user_repr = ''' + <user> + <name>%s</name> + %%s + </user> + ''' % toxml(emp['name']) + while monday != last_monday: + #### Work hour calculation + sql = ''' + select action, att.name + from hr_employee as emp inner join hr_attendance as att + on emp.id = att.employee_id + where att.name between '%s' and '%s' and emp.id = %s + order by att.name + ''' + for idx in range(7): + cr.execute(sql, (monday, monday + DateTime.RelativeDateTime(days=idx+1), employee_id)) + attendences = cr.dictfetchall() + week_wh = {} + if attendences and attendences[0]['action'] == 'sign_out': + attendences.insert(0, {'name': monday.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_in'}) + if attendences and attendences[-1]['action'] == 'sign_in': + attendences.append({'name' : n_monday.strftime('%Y-%m-%d %H:%M:%S'), 'action':'sign_out'}) + for att in attendences: + dt = DateTime.strptime(att['name'], '%Y-%m-%d %H:%M:%S') + if att['action'] == 'sign_out': + week_wh[ldt.day_of_week] = week_wh.get(ldt.day_of_week, 0) + (dt - ldt).hours + ldt = dt - #### Theoretical workhour calculation - week_twh = {} - sql = ''' - select t.hour_from, t.hour_to - from hr_timesheet as t - inner join (hr_timesheet_group as g inner join hr_timesheet_employee_rel as rel - on rel.tgroup_id = g.id and rel.emp_id = %s) - on t.tgroup_id = g.id - where dayofweek = %s - and date_from = (select max(date_from) - from hr_timesheet inner join (hr_timesheet_employee_rel - inner join hr_timesheet_group - on hr_timesheet_group.id = hr_timesheet_employee_rel.tgroup_id - and hr_timesheet_employee_rel.emp_id = %s) - on hr_timesheet.tgroup_id = hr_timesheet_group.id - where dayofweek = %s and date_from <= '%s') - order by date_from desc - ''' - for idx in range(7): - day = monday + DateTime.RelativeDateTime(days=idx+1) - # Is this a public holiday ? - isPH = False - for jf_start, jf_end in jfs: - if jf_start <= day < jf_end: - isPH = True - break - if isPH: - week_twh[idx] = 0 - else: - cr.execute(sql, (emp['id'], day.day_of_week, emp['id'], day.day_of_week, day)) - dhs = cr.dictfetchall() - week_twh[idx] = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,dhs, 0) + #### Theoretical workhour calculation + week_twh = {} + sql = ''' + select t.hour_from, t.hour_to + from hr_timesheet as t + inner join (hr_timesheet_group as g inner join hr_timesheet_employee_rel as rel + on rel.tgroup_id = g.id and rel.emp_id = %s) + on t.tgroup_id = g.id + where dayofweek = %s + and date_from = (select max(date_from) + from hr_timesheet inner join (hr_timesheet_employee_rel + inner join hr_timesheet_group + on hr_timesheet_group.id = hr_timesheet_employee_rel.tgroup_id + and hr_timesheet_employee_rel.emp_id = %s) + on hr_timesheet.tgroup_id = hr_timesheet_group.id + where dayofweek = %s and date_from <= '%s') + order by date_from desc + ''' + for idx in range(7): + day = monday + DateTime.RelativeDateTime(days=idx+1) + # Is this a public holiday ? + isPH = False + for jf_start, jf_end in jfs: + if jf_start <= day < jf_end: + isPH = True + break + if isPH: + week_twh[idx] = 0 + else: + cr.execute(sql, (emp['id'], day.day_of_week, emp['id'], day.day_of_week, day)) + dhs = cr.dictfetchall() + week_twh[idx] = reduce(lambda x,y:x+(DateTime.strptime(y['hour_to'], '%H:%M:%S') - DateTime.strptime(y['hour_from'], '%H:%M:%S')).hours,dhs, 0) - #### Holiday calculation - sql = ''' - select hol.date_from, hol.date_to, stat.name as status - from hr_employee as emp - inner join (hr_holidays as hol left join hr_holidays_status as stat - on hol.holiday_status = stat.id) - on emp.id = hol.employee_id - where ((hol.date_from <= '%s' and hol.date_to >= '%s') - or (hol.date_from < '%s' and hol.date_to >= '%s') - or (hol.date_from > '%s' and hol.date_to < '%s') - and stat.name != 'Public holidays') and emp.id = %s - order by hol.date_from - ''' - cr.execute(sql, (monday, monday, n_monday, n_monday, monday, n_monday, employee_id)) - holidays = cr.dictfetchall() - week_hol = {} - for hol in holidays: - df = DateTime.strptime(hol['date_from'], '%Y-%m-%d %H:%M:%S') - dt = DateTime.strptime(hol['date_to'], '%Y-%m-%d %H:%M:%S') - for idx in range(7): - day = monday + DateTime.RelativeDateTime(days=idx+1) - if (df.year, df.month, df.day) <= (day.year, day.month, day.day) <= (dt.year, dt.month, dt.day): - if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day): - week_hol[idx] = {'status' : hol['status'], 'hours' : (dt - df).hours} - else: - week_hol[idx] = {'status' : hol['status'], 'hours' : week_twh[idx]} - - # Week xml representation - week_repr = ['<week>', '<weekstart>%s</weekstart>' % monday.strftime('%Y-%m-%d'), '<weekend>%s</weekend>' % n_monday.strftime('%Y-%m-%d')] - for idx in range(7): - week_repr.append('<%s>' % num2day[idx]) - week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(week_twh[idx])) - if idx in week_wh: - week_repr.append('<workhours>%sh%02d</workhours>' % to_hour(week_wh[idx])) - if idx in week_hol and week_hol[idx]['hours']: - week_repr.append('<holidayhours type="%(status)s">%(hours)s</holidayhours>' % week_hol[idx]) - week_repr.append('</%s>' % num2day[idx]) - week_repr.append('<total>') - week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(reduce(lambda x,y:x+y, week_twh.values(), 0))) - week_repr.append('<worked>%sh%02d</worked>' % to_hour(reduce(lambda x,y:x+y, week_wh.values(), 0))) - week_repr.append('<holiday>%sh%02d</holiday>' % to_hour(reduce(lambda x,y:x+y, [day['hours'] for day in week_hol.values()], 0))) - week_repr.append('</total>') - week_repr.append('</week>') - if len(week_repr) > 30: # 30 = minimal length of week_repr - week_xml.append('\n'.join(week_repr)) - - monday, n_monday = n_monday, n_monday + one_week - user_xml.append(user_repr % '\n'.join(week_xml)) - - xml = '''<?xml version="1.0" encoding="UTF-8" ?> - <report> - %s - </report> - ''' % '\n'.join(user_xml) - return self.post_process_xml_data(cr, uid, xml, context) + #### Holiday calculation + sql = ''' + select hol.date_from, hol.date_to, stat.name as status + from hr_employee as emp + inner join (hr_holidays as hol left join hr_holidays_status as stat + on hol.holiday_status = stat.id) + on emp.id = hol.employee_id + where ((hol.date_from <= '%s' and hol.date_to >= '%s') + or (hol.date_from < '%s' and hol.date_to >= '%s') + or (hol.date_from > '%s' and hol.date_to < '%s') + and stat.name != 'Public holidays') and emp.id = %s + order by hol.date_from + ''' + cr.execute(sql, (monday, monday, n_monday, n_monday, monday, n_monday, employee_id)) + holidays = cr.dictfetchall() + week_hol = {} + for hol in holidays: + df = DateTime.strptime(hol['date_from'], '%Y-%m-%d %H:%M:%S') + dt = DateTime.strptime(hol['date_to'], '%Y-%m-%d %H:%M:%S') + for idx in range(7): + day = monday + DateTime.RelativeDateTime(days=idx+1) + if (df.year, df.month, df.day) <= (day.year, day.month, day.day) <= (dt.year, dt.month, dt.day): + if (df.year, df.month, df.day) == (dt.year, dt.month, dt.day): + week_hol[idx] = {'status' : hol['status'], 'hours' : (dt - df).hours} + else: + week_hol[idx] = {'status' : hol['status'], 'hours' : week_twh[idx]} + + # Week xml representation + week_repr = ['<week>', '<weekstart>%s</weekstart>' % monday.strftime('%Y-%m-%d'), '<weekend>%s</weekend>' % n_monday.strftime('%Y-%m-%d')] + for idx in range(7): + week_repr.append('<%s>' % num2day[idx]) + week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(week_twh[idx])) + if idx in week_wh: + week_repr.append('<workhours>%sh%02d</workhours>' % to_hour(week_wh[idx])) + if idx in week_hol and week_hol[idx]['hours']: + week_repr.append('<holidayhours type="%(status)s">%(hours)s</holidayhours>' % week_hol[idx]) + week_repr.append('</%s>' % num2day[idx]) + week_repr.append('<total>') + week_repr.append('<theoretical>%sh%02d</theoretical>' % to_hour(reduce(lambda x,y:x+y, week_twh.values(), 0))) + week_repr.append('<worked>%sh%02d</worked>' % to_hour(reduce(lambda x,y:x+y, week_wh.values(), 0))) + week_repr.append('<holiday>%sh%02d</holiday>' % to_hour(reduce(lambda x,y:x+y, [day['hours'] for day in week_hol.values()], 0))) + week_repr.append('</total>') + week_repr.append('</week>') + if len(week_repr) > 30: # 30 = minimal length of week_repr + week_xml.append('\n'.join(week_repr)) + + monday, n_monday = n_monday, n_monday + one_week + user_xml.append(user_repr % '\n'.join(week_xml)) + + xml = '''<?xml version="1.0" encoding="UTF-8" ?> + <report> + %s + </report> + ''' % '\n'.join(user_xml) + return self.post_process_xml_data(cr, uid, xml, context) report_custom('report.hr.timesheet.allweeks', 'hr.employee', '', 'addons/hr/report/timesheet.xsl') # vim:noexpandtab:tw=0 diff --git a/addons/hr/wizard/print_attendance_error.py b/addons/hr/wizard/print_attendance_error.py index 9939ae1e136..3d74bd936b4 100644 --- a/addons/hr/wizard/print_attendance_error.py +++ b/addons/hr/wizard/print_attendance_error.py @@ -32,29 +32,29 @@ import time _date_form = '''<?xml version="1.0"?> <form string="Select a time span"> - <separator string="Analysis Information" colspan="4"/> - <field name="init_date"/> - <field name="end_date"/> - <field name="max_delay"/> - <label string="Bellow this delay, the error is considered as volunteer" colspan="2"/> + <separator string="Analysis Information" colspan="4"/> + <field name="init_date"/> + <field name="end_date"/> + <field name="max_delay"/> + <label string="Bellow this delay, the error is considered as volunteer" colspan="2"/> </form>''' _date_fields = { - 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, - 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, - 'max_delay': {'string':'Max. Delay (Min)', 'type':'integer', 'default':lambda *a: 120, 'required':True}, + 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, + 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, + 'max_delay': {'string':'Max. Delay (Min)', 'type':'integer', 'default':lambda *a: 120, 'required':True}, } class wiz_attendance(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Attendance Report'),('end','Cancel') ]} - }, - 'print': { - 'actions': [], - 'result': {'type': 'print', 'report': 'hr.timesheet.attendance.error', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Attendance Report'),('end','Cancel') ]} + }, + 'print': { + 'actions': [], + 'result': {'type': 'print', 'report': 'hr.timesheet.attendance.error', 'state':'end'} + } + } wiz_attendance('hr.timesheet.attendance.report') diff --git a/addons/hr/wizard/print_bymonth.py b/addons/hr/wizard/print_bymonth.py index 454fe58e7cd..3b0df25e9ba 100644 --- a/addons/hr/wizard/print_bymonth.py +++ b/addons/hr/wizard/print_bymonth.py @@ -32,26 +32,26 @@ import time _date_form = '''<?xml version="1.0"?> <form string="Select a month"> - <separator string="Select a month" colspan="4"/> - <field name="month"/> - <field name="year"/> + <separator string="Select a month" colspan="4"/> + <field name="month"/> + <field name="year"/> </form>''' _date_fields = { - 'month' : {'string' : 'Month', 'type' : 'selection', 'selection' : [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], 'required':True }, - 'year' : {'string' : 'Year', 'type' : 'integer', 'required':True, 'default' : lambda *a: time.gmtime()[0]}, + 'month' : {'string' : 'Month', 'type' : 'selection', 'selection' : [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], 'required':True }, + 'year' : {'string' : 'Year', 'type' : 'integer', 'required':True, 'default' : lambda *a: time.gmtime()[0]}, } class wiz_bymonth(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel')]} - }, - 'print': { - 'actions': [], - 'result': {'type': 'print', 'report': 'hr.timesheet.bymonth', 'state': 'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel')]} + }, + 'print': { + 'actions': [], + 'result': {'type': 'print', 'report': 'hr.timesheet.bymonth', 'state': 'end'} + } + } wiz_bymonth('hr.print_month') diff --git a/addons/hr/wizard/print_byweek.py b/addons/hr/wizard/print_byweek.py index 00b72f037f2..cab9bfa5584 100644 --- a/addons/hr/wizard/print_byweek.py +++ b/addons/hr/wizard/print_byweek.py @@ -32,27 +32,27 @@ import time _date_form = '''<?xml version="1.0"?> <form string="Select a time span"> - <separator string="Select a starting and a end date" colspan="4"/> - <field name="init_date"/> - <newline/> - <field name="end_date"/> + <separator string="Select a starting and a end date" colspan="4"/> + <field name="init_date"/> + <newline/> + <field name="end_date"/> </form>''' _date_fields = { - 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, - 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True} + 'init_date': {'string':'Starting Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True}, + 'end_date': {'string':'Ending Date', 'type':'date', 'default':lambda *a: time.strftime('%Y-%m-%d'), 'required':True} } class wiz_byweek(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel') ]} - }, - 'print': { - 'actions': [], - 'result': {'type': 'print', 'report': 'hr.timesheet.allweeks', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':_date_form, 'fields':_date_fields, 'state':[('print','Print Timesheet'),('end','Cancel') ]} + }, + 'print': { + 'actions': [], + 'result': {'type': 'print', 'report': 'hr.timesheet.allweeks', 'state':'end'} + } + } wiz_byweek('hr.print_week') diff --git a/addons/hr/wizard/sign_in_out.py b/addons/hr/wizard/sign_in_out.py index 70b909262a8..738a02641e1 100644 --- a/addons/hr/wizard/sign_in_out.py +++ b/addons/hr/wizard/sign_in_out.py @@ -34,155 +34,155 @@ from tools.translate import _ si_so_form ='''<?xml version="1.0"?> <form string="Sign in / Sign out"> - <separator string="You are now ready to sign in or out of the attendance follow up" colspan="4" /> - <field name="name" readonly="True" /> - <field name="state" readonly="True" /> + <separator string="You are now ready to sign in or out of the attendance follow up" colspan="4" /> + <field name="name" readonly="True" /> + <field name="state" readonly="True" /> </form>''' si_so_fields = { - 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, - 'state' : {'string' : "Current state", 'type' : 'char', 'required' : True, 'readonly': True}, + 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, + 'state' : {'string' : "Current state", 'type' : 'char', 'required' : True, 'readonly': True}, } si_form = '''<?xml version="1.0" ?> <form string="Sign in status"> - <seperator string="This is the status of your sign in request. Check it out maybe you were already signed in." colspan="4" /> - <field name="success" readonly="True" /> + <seperator string="This is the status of your sign in request. Check it out maybe you were already signed in." colspan="4" /> + <field name="success" readonly="True" /> </form>''' si_fields = { - 'success' : {'string' : "Sign in's status", 'type' : 'char', 'required' : True, 'readonly' : True}, + 'success' : {'string' : "Sign in's status", 'type' : 'char', 'required' : True, 'readonly' : True}, } so_form = '''<?xml version="1.0" ?> <form string="Sign in status"> - <seperator string="This is the status of your sign out request. Check it out maybe you were already signed out." colspan="4" /> - <field name="success" readonly="True" /> + <seperator string="This is the status of your sign out request. Check it out maybe you were already signed out." colspan="4" /> + <field name="success" readonly="True" /> </for>''' so_fields = { - 'success' : {'string' : "Sign out's status", 'type' : 'char', 'required' : True, 'readonly' : True}, + 'success' : {'string' : "Sign out's status", 'type' : 'char', 'required' : True, 'readonly' : True}, } def _get_empid(self, cr, uid, data, context): - service = netsvc.LocalService('object_proxy') - emp_id = service.execute(cr.dbname, uid, 'hr.employee', 'search', [('user_id', '=', uid)]) - if emp_id: - employee = service.execute(cr.dbname, uid, 'hr.employee', 'read', emp_id)[0] - return {'name': employee['name'], 'state': employee['state'], 'emp_id': emp_id[0]} - return {} + service = netsvc.LocalService('object_proxy') + emp_id = service.execute(cr.dbname, uid, 'hr.employee', 'search', [('user_id', '=', uid)]) + if emp_id: + employee = service.execute(cr.dbname, uid, 'hr.employee', 'read', emp_id)[0] + return {'name': employee['name'], 'state': employee['state'], 'emp_id': emp_id[0]} + return {} def _sign_in(self, cr, uid, data, context): - service = netsvc.LocalService('object_proxy') - emp_id = data['form']['emp_id'] - if 'last_time' in data['form'] : - if data['form']['last_time'] > time.strftime('%Y-%m-%d'): - raise wizard.except_wizard(_('UserError'), _('The sign-out date must be in the past')) - return {'success': False} - service.execute(cr.dbname, uid, 'hr.attendance', 'create', { - 'name': data['form']['last_time'], - 'action': 'sign_out', - 'employee_id': emp_id - }) - try: - success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_in', [emp_id]) - print success - except: - raise wizard.except_wizard(_('UserError'), _('A sign-in must be right after a sign-out !')) - return {'success': success} + service = netsvc.LocalService('object_proxy') + emp_id = data['form']['emp_id'] + if 'last_time' in data['form'] : + if data['form']['last_time'] > time.strftime('%Y-%m-%d'): + raise wizard.except_wizard(_('UserError'), _('The sign-out date must be in the past')) + return {'success': False} + service.execute(cr.dbname, uid, 'hr.attendance', 'create', { + 'name': data['form']['last_time'], + 'action': 'sign_out', + 'employee_id': emp_id + }) + try: + success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_in', [emp_id]) + print success + except: + raise wizard.except_wizard(_('UserError'), _('A sign-in must be right after a sign-out !')) + return {'success': success} def _sign_out(self, cr, uid, data, context): - service = netsvc.LocalService('object_proxy') - emp_id = data['form']['emp_id'] - if 'last_time' in data['form'] : - if data['form']['last_time'] > time.strftime('%Y-%m-%d'): - raise wizard.except_wizard(_('UserError'), _('The Sign-in date must be in the past')) - return {'success': False} - service.execute(cr.dbname, uid, 'hr.attendance', 'create', {'name':data['form']['last_time'], 'action':'sign_in', 'employee_id':emp_id}) - try: - success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_out', [emp_id]) - except: - raise wizard.except_wizard(_('UserError'), _('A sign-out must be right after a sign-in !')) - - return {'success' : success} + service = netsvc.LocalService('object_proxy') + emp_id = data['form']['emp_id'] + if 'last_time' in data['form'] : + if data['form']['last_time'] > time.strftime('%Y-%m-%d'): + raise wizard.except_wizard(_('UserError'), _('The Sign-in date must be in the past')) + return {'success': False} + service.execute(cr.dbname, uid, 'hr.attendance', 'create', {'name':data['form']['last_time'], 'action':'sign_in', 'employee_id':emp_id}) + try: + success = service.execute(cr.dbname, uid, 'hr.employee', 'sign_out', [emp_id]) + except: + raise wizard.except_wizard(_('UserError'), _('A sign-out must be right after a sign-in !')) + + return {'success' : success} so_ask_form ='''<?xml version="1.0"?> <form string="Sign in / Sign out"> - <separator string="You did not signed out the last time. Please enter the date and time you signed out." colspan="4" /> - <field name="name" readonly="True" /> - <field name="last_time" /> + <separator string="You did not signed out the last time. Please enter the date and time you signed out." colspan="4" /> + <field name="name" readonly="True" /> + <field name="last_time" /> </form>''' so_ask_fields = { - 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, - 'last_time' : {'string' : "Your last sign out", 'type' : 'datetime', 'required' : True}, + 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, + 'last_time' : {'string' : "Your last sign out", 'type' : 'datetime', 'required' : True}, } def _si_check(self, cr, uid, data, context): - states = {True : 'si', False: 'si_ask_so'} - service = netsvc.LocalService('object_proxy') - emp_id = data['form']['emp_id'] - att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc') - last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id) - if last_att: - last_att = last_att[0] - cond = not last_att or last_att['action'] == 'sign_out' - return states[cond] + states = {True : 'si', False: 'si_ask_so'} + service = netsvc.LocalService('object_proxy') + emp_id = data['form']['emp_id'] + att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc') + last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id) + if last_att: + last_att = last_att[0] + cond = not last_att or last_att['action'] == 'sign_out' + return states[cond] si_ask_form ='''<?xml version="1.0"?> <form string="Sign in / Sign out"> - <separator string="You did not signed in the last time. Please enter the date and time you signed in." colspan="4" /> - <field name="name" readonly="True" /> - <field name="last_time" /> + <separator string="You did not signed in the last time. Please enter the date and time you signed in." colspan="4" /> + <field name="name" readonly="True" /> + <field name="last_time" /> </form>''' si_ask_fields = { - 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, - 'last_time' : {'string' : "Your last sign in", 'type' : 'datetime', 'required' : True}, + 'name' : {'string' : "Employee's name", 'type':'char', 'required':True, 'readonly':True}, + 'last_time' : {'string' : "Your last sign in", 'type' : 'datetime', 'required' : True}, } def _so_check(self, cr, uid, data, context): - states = {True : 'so', False: 'so_ask_si'} - service = netsvc.LocalService('object_proxy') - emp_id = data['form']['emp_id'] - att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc') - last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id) - if last_att: - last_att = last_att[0] - cond = last_att and last_att['action'] == 'sign_in' - return states[cond] + states = {True : 'so', False: 'so_ask_si'} + service = netsvc.LocalService('object_proxy') + emp_id = data['form']['emp_id'] + att_id = service.execute(cr.dbname, uid, 'hr.attendance', 'search', [('employee_id', '=', emp_id)], limit=1, order='name desc') + last_att = service.execute(cr.dbname, uid, 'hr.attendance', 'read', att_id) + if last_att: + last_att = last_att[0] + cond = last_att and last_att['action'] == 'sign_in' + return states[cond] class wiz_si_so(wizard.interface): - states = { - 'init' : { - 'actions' : [_get_empid], - 'result' : {'type' : 'form', 'arch' : si_so_form, 'fields' : si_so_fields, 'state' : [('end', 'Cancel'),('si_test', 'Sign in'),('so_test', 'Sign out')] } - }, - 'si_test' : { - 'actions' : [], - 'result' : {'type' : 'choice', 'next_state': _si_check} - }, - 'si_ask_so' : { - 'actions' : [], - 'result' : {'type' : 'form', 'arch' : so_ask_form, 'fields' : so_ask_fields, 'state' : [('end', 'Cancel'),('si', 'Sign in') ] } - }, - 'si' : { - 'actions' : [_sign_in], - 'result' : {'type' : 'state', 'state':'end'} - }, - 'so_test' : { - 'actions' : [], - 'result' : {'type' : 'choice', 'next_state': _so_check } - }, - 'so_ask_si' : { - 'actions' : [], - 'result' : {'type' : 'form', 'arch' : si_ask_form, 'fields' : si_ask_fields, 'state' : [('end', 'Cancel'),('so', 'Sign out')] } - }, - 'so' : { - 'actions' : [_sign_out], - 'result' : {'type' : 'state', 'state':'end'} - }, - } + states = { + 'init' : { + 'actions' : [_get_empid], + 'result' : {'type' : 'form', 'arch' : si_so_form, 'fields' : si_so_fields, 'state' : [('end', 'Cancel'),('si_test', 'Sign in'),('so_test', 'Sign out')] } + }, + 'si_test' : { + 'actions' : [], + 'result' : {'type' : 'choice', 'next_state': _si_check} + }, + 'si_ask_so' : { + 'actions' : [], + 'result' : {'type' : 'form', 'arch' : so_ask_form, 'fields' : so_ask_fields, 'state' : [('end', 'Cancel'),('si', 'Sign in') ] } + }, + 'si' : { + 'actions' : [_sign_in], + 'result' : {'type' : 'state', 'state':'end'} + }, + 'so_test' : { + 'actions' : [], + 'result' : {'type' : 'choice', 'next_state': _so_check } + }, + 'so_ask_si' : { + 'actions' : [], + 'result' : {'type' : 'form', 'arch' : si_ask_form, 'fields' : si_ask_fields, 'state' : [('end', 'Cancel'),('so', 'Sign out')] } + }, + 'so' : { + 'actions' : [_sign_out], + 'result' : {'type' : 'state', 'state':'end'} + }, + } wiz_si_so('hr.si_so') # vim:noexpandtab:tw=0 diff --git a/addons/hr_contract/__terp__.py b/addons/hr_contract/__terp__.py index 02fe14a665e..b93ca0e83e7 100644 --- a/addons/hr_contract/__terp__.py +++ b/addons/hr_contract/__terp__.py @@ -26,27 +26,27 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Human Resources Contracts", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "website" : "http://tinyerp.com/module_hr.html", - "depends" : ["hr"], - "module": "", - "description": """ - Add all information on the employee form to manage contracts: - * Martial status, - * Security number, - * Place of birth, birth date, ... + "name" : "Human Resources Contracts", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "website" : "http://tinyerp.com/module_hr.html", + "depends" : ["hr"], + "module": "", + "description": """ + Add all information on the employee form to manage contracts: + * Martial status, + * Security number, + * Place of birth, birth date, ... - You can assign several contracts per employee. - """, - "init_xml" : ["hr_contract_data.xml", ], - "demo_xml" : [], - "update_xml" : [ - "hr_contract_view.xml", - "hr_contract_security.xml" - ], - "active": False, - "installable": True + You can assign several contracts per employee. + """, + "init_xml" : ["hr_contract_data.xml", ], + "demo_xml" : [], + "update_xml" : [ + "hr_contract_view.xml", + "hr_contract_security.xml" + ], + "active": False, + "installable": True } diff --git a/addons/hr_contract/hr_contract.py b/addons/hr_contract/hr_contract.py index bfa77a6062c..a00dfe089c2 100644 --- a/addons/hr_contract/hr_contract.py +++ b/addons/hr_contract/hr_contract.py @@ -33,78 +33,78 @@ from osv import fields, osv import time class hr_employee_marital_status(osv.osv): - _name = "hr.employee.marital.status" - _description = "Employee Marital Status" - _columns = { - 'name' : fields.char('Marital Status', size=30, required=True), - 'description' : fields.text('Status Description'), - } + _name = "hr.employee.marital.status" + _description = "Employee Marital Status" + _columns = { + 'name' : fields.char('Marital Status', size=30, required=True), + 'description' : fields.text('Status Description'), + } hr_employee_marital_status() class hr_employee(osv.osv): - _name = "hr.employee" - _description = "Employee" - _inherit = "hr.employee" - _columns = { - 'manager' : fields.boolean('Manager'), - 'soc_security' : fields.char('Social security number', size=50, select=True), - 'medic_exam' : fields.date('Medical examination date'), - 'audiens_num' : fields.char('AUDIENS Number', size=30), - 'nationality' : fields.many2one('res.country', 'Nationality'), - 'birth_date' : fields.date('Birth Date'), - 'place_of_birth' : fields.char('Place of Birth', size=30), - 'marital_status' : fields.many2one('hr.employee.marital.status', 'Marital Status'), - 'children' : fields.integer('Number of children'), - 'contract_ids' : fields.one2many('hr.contract', 'employee_id', 'Contracts'), - } + _name = "hr.employee" + _description = "Employee" + _inherit = "hr.employee" + _columns = { + 'manager' : fields.boolean('Manager'), + 'soc_security' : fields.char('Social security number', size=50, select=True), + 'medic_exam' : fields.date('Medical examination date'), + 'audiens_num' : fields.char('AUDIENS Number', size=30), + 'nationality' : fields.many2one('res.country', 'Nationality'), + 'birth_date' : fields.date('Birth Date'), + 'place_of_birth' : fields.char('Place of Birth', size=30), + 'marital_status' : fields.many2one('hr.employee.marital.status', 'Marital Status'), + 'children' : fields.integer('Number of children'), + 'contract_ids' : fields.one2many('hr.contract', 'employee_id', 'Contracts'), + } hr_employee() #Contract wage type period name class hr_contract_wage_type_period(osv.osv): - _name='hr.contract.wage.type.period' - _description='Wage Period' - _columns = { - 'name' : fields.char('Period Name', size=50, required=True, select=True), - 'factor_days': fields.float('Hours in the period', digits=(12,4), required=True, help='This field is used by the timesheet system to compute the price of an hour of work wased on the contract of the employee') - } - _defaults = { - 'factor_days': lambda *args: 168.0 - } + _name='hr.contract.wage.type.period' + _description='Wage Period' + _columns = { + 'name' : fields.char('Period Name', size=50, required=True, select=True), + 'factor_days': fields.float('Hours in the period', digits=(12,4), required=True, help='This field is used by the timesheet system to compute the price of an hour of work wased on the contract of the employee') + } + _defaults = { + 'factor_days': lambda *args: 168.0 + } hr_contract_wage_type_period() #Contract wage type (hourly, daily, monthly, ...) class hr_contract_wage_type(osv.osv): - _name = 'hr.contract.wage.type' - _description = 'Wage Type' - _columns = { - 'name' : fields.char('Wage Type Name', size=50, required=True, select=True), - 'period_id' : fields.many2one('hr.contract.wage.type.period', 'Wage Period', required=True), - 'type' : fields.selection([('gross','Gross'), ('net','Net')], 'Type', required=True), - 'factor_type': fields.float('Factor for hour cost', digits=(12,4), required=True, help='This field is used by the timesheet system to compute the price of an hour of work wased on the contract of the employee') - } - _defaults = { - 'type' : lambda *a : 'gross', - 'factor_type': lambda *args: 1.8 - } + _name = 'hr.contract.wage.type' + _description = 'Wage Type' + _columns = { + 'name' : fields.char('Wage Type Name', size=50, required=True, select=True), + 'period_id' : fields.many2one('hr.contract.wage.type.period', 'Wage Period', required=True), + 'type' : fields.selection([('gross','Gross'), ('net','Net')], 'Type', required=True), + 'factor_type': fields.float('Factor for hour cost', digits=(12,4), required=True, help='This field is used by the timesheet system to compute the price of an hour of work wased on the contract of the employee') + } + _defaults = { + 'type' : lambda *a : 'gross', + 'factor_type': lambda *args: 1.8 + } hr_contract_wage_type() class hr_contract(osv.osv): - _name = 'hr.contract' - _description = 'Contract' - _columns = { - 'name' : fields.char('Contract Name', size=30, required=True), - 'employee_id' : fields.many2one('hr.employee', 'Employee', required=True), - 'function' : fields.many2one('res.partner.function', 'Function'), - 'date_start' : fields.date('Start Date', required=True), - 'date_end' : fields.date('End Date'), - 'working_hours_per_day' : fields.integer('Working hours per day'), - 'wage_type_id' : fields.many2one('hr.contract.wage.type', 'Wage Type', required=True), - 'wage' : fields.float('Wage', required=True), - 'notes' : fields.text('Notes'), - } - _defaults = { - 'date_start' : lambda *a : time.strftime("%Y-%m-%d"), - 'working_hours_per_day' : lambda *a : 8, - } + _name = 'hr.contract' + _description = 'Contract' + _columns = { + 'name' : fields.char('Contract Name', size=30, required=True), + 'employee_id' : fields.many2one('hr.employee', 'Employee', required=True), + 'function' : fields.many2one('res.partner.function', 'Function'), + 'date_start' : fields.date('Start Date', required=True), + 'date_end' : fields.date('End Date'), + 'working_hours_per_day' : fields.integer('Working hours per day'), + 'wage_type_id' : fields.many2one('hr.contract.wage.type', 'Wage Type', required=True), + 'wage' : fields.float('Wage', required=True), + 'notes' : fields.text('Notes'), + } + _defaults = { + 'date_start' : lambda *a : time.strftime("%Y-%m-%d"), + 'working_hours_per_day' : lambda *a : 8, + } hr_contract() diff --git a/addons/hr_expense/__terp__.py b/addons/hr_expense/__terp__.py index 3140775120d..e23efc93c60 100644 --- a/addons/hr_expense/__terp__.py +++ b/addons/hr_expense/__terp__.py @@ -26,29 +26,29 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Human Resources Expenses Tracking", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "website" : "http://tinyerp.com/module_hr.html", - "depends" : ["hr","account", "account_tax_include",], - "description": """ - This module aims to manage employee's expenses. + "name" : "Human Resources Expenses Tracking", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "website" : "http://tinyerp.com/module_hr.html", + "depends" : ["hr","account", "account_tax_include",], + "description": """ + This module aims to manage employee's expenses. - The whole workflow is implemented: - * Draft expense - * Confirmation of the sheet by the employee - * Validation by his manager - * Validation by the accountant and invoice creation - * Payment of the invoice to the employee + The whole workflow is implemented: + * Draft expense + * Confirmation of the sheet by the employee + * Validation by his manager + * Validation by the accountant and invoice creation + * Payment of the invoice to the employee - This module also use the analytic accounting and is compatible with - the invoice on timesheet module so that you will be able to automatcally - re-invoice your customer's expenses if your work by project. - """, - "init_xml" : [], - "demo_xml" : ["hr_expense_demo.xml", "hr.expense.expense.csv"], - "update_xml" : ["hr_expense_sequence.xml", "hr_expense_workflow.xml", "hr_expense_view.xml", "hr_expense_report.xml",], - "active": False, - "installable": True + This module also use the analytic accounting and is compatible with + the invoice on timesheet module so that you will be able to automatcally + re-invoice your customer's expenses if your work by project. + """, + "init_xml" : [], + "demo_xml" : ["hr_expense_demo.xml", "hr.expense.expense.csv"], + "update_xml" : ["hr_expense_sequence.xml", "hr_expense_workflow.xml", "hr_expense_view.xml", "hr_expense_report.xml",], + "active": False, + "installable": True } diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index adbb12e0bee..8d5434ec35b 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -34,187 +34,187 @@ from osv import fields, osv from tools.translate import _ def _employee_get(obj,cr,uid,context={}): - ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)]) - if ids: - return ids[0] - return False + ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id','=', uid)]) + if ids: + return ids[0] + return False class hr_expense_expense(osv.osv): - def copy(self, cr, uid, id, default=None, context={}): - if not default: default = {} - default.update( {'invoice_id':False,'date_confirm':False,'date_valid':False,'user_valid':False}) - return super(hr_expense_expense, self).copy(cr, uid, id, default, context) - - def _amount(self, cr, uid, ids, field_name, arg, context): - id_set = ",".join(map(str, ids)) - cr.execute("SELECT s.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_expense s LEFT OUTER JOIN hr_expense_line l ON (s.id=l.expense_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") - res = dict(cr.fetchall()) - return res - - def _get_currency(self, cr, uid, context): - user = self.pool.get('res.users').browse(cr, uid, [uid])[0] - if user.company_id: - return user.company_id.currency_id.id - else: - return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] - - _name = "hr.expense.expense" - _description = "Expense" - _columns = { - 'name': fields.char('Expense Sheet', size=128, required=True), - 'id': fields.integer('Sheet ID', readonly=True), - 'ref': fields.char('Reference', size=32), - 'date': fields.date('Date'), - 'journal_id': fields.many2one('account.journal', 'Force Journal'), - 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), - 'user_id': fields.many2one('res.users', 'User', required=True), - 'date_confirm': fields.date('Date Confirmed'), - 'date_valid': fields.date('Date Valided'), - 'user_valid': fields.many2one('res.users', 'Validation User'), - 'account_move_id': fields.many2one('account.move', 'Account Move'), - 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines'), - 'note': fields.text('Note'), - 'amount': fields.function(_amount, method=True, string='Total Amount'), - 'invoice_id': fields.many2one('account.invoice', 'Invoice'), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True), - - 'state': fields.selection([ - ('draft', 'Draft'), - ('confirm', 'Waiting confirmation'), - ('accepted', 'Accepted'), - ('invoiced', 'Invoiced'), - ('paid', 'Reimbursed'), - ('canceled', 'Canceled')], - 'State', readonly=True), - } - _defaults = { - 'date' : lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'draft', - 'employee_id' : _employee_get, - 'user_id' : lambda cr,uid,id,c={}: id, - 'currency_id': _get_currency, - } - def expense_confirm(self, cr, uid, ids, *args): - #for exp in self.browse(cr, uid, ids): - self.write(cr, uid, ids, { - 'state':'confirm', - 'date_confirm': time.strftime('%Y-%m-%d') - }) - return True - - def expense_accept(self, cr, uid, ids, *args): - self.write(cr, uid, ids, { - 'state':'accepted', - 'date_valid':time.strftime('%Y-%m-%d'), - 'user_valid': uid, - }) - return True - - def expense_canceled(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'canceled'}) - return True - - def expense_paid(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'paid'}) - return True - - def action_invoice_create(self, cr, uid, ids): - res = False - invoice_obj = self.pool.get('account.invoice') - for exp in self.browse(cr, uid, ids): - lines = [] - for l in exp.line_ids: - tax_id = [] - if l.product_id: - acc = l.product_id.product_tmpl_id.property_account_expense.id - if not acc: - acc = l.product_id.categ_id.property_account_expense_categ.id - tax_id = [x.id for x in l.product_id.supplier_taxes_id] - else: - acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category') - lines.append((0, False, { - 'name': l.name, - 'account_id': acc, - 'price_unit': l.unit_amount, - 'quantity': l.unit_quantity, - 'uos_id': l.uom_id.id, - 'product_id': l.product_id and l.product_id.id or False, - 'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False, - 'account_analytic_id': l.analytic_account.id, - })) - if not exp.employee_id.address_id: - raise osv.except_osv(_('Error !'), _('The employee must have a contact address')) - acc = exp.employee_id.address_id.partner_id.property_account_payable.id - payment_term_id = exp.employee_id.address_id.partner_id.property_payment_term.id - inv = { - 'name': exp.name, - 'reference': self.pool.get('ir.sequence').get(cr, uid, 'hr.expense.invoice'), - 'account_id': acc, - 'type': 'in_invoice', - 'partner_id': exp.employee_id.address_id.partner_id.id, - 'address_invoice_id': exp.employee_id.address_id.id, - 'address_contact_id': exp.employee_id.address_id.id, - 'origin': exp.name, - 'invoice_line': lines, - 'price_type': 'tax_included', - 'currency_id': exp.currency_id.id, - 'payment_term': payment_term_id, - } - if payment_term_id: - to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [], - payment_term_id, None) - if to_update: - inv.update(to_update['value']) - if exp.journal_id: - inv['journal_id']=exp.journal_id.id - inv_id = invoice_obj.create(cr, uid, inv, {'type':'in_invoice'}) - invoice_obj.button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, - set_total=True) - self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'}) - res = inv_id - return res + def copy(self, cr, uid, id, default=None, context={}): + if not default: default = {} + default.update( {'invoice_id':False,'date_confirm':False,'date_valid':False,'user_valid':False}) + return super(hr_expense_expense, self).copy(cr, uid, id, default, context) + + def _amount(self, cr, uid, ids, field_name, arg, context): + id_set = ",".join(map(str, ids)) + cr.execute("SELECT s.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_expense s LEFT OUTER JOIN hr_expense_line l ON (s.id=l.expense_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") + res = dict(cr.fetchall()) + return res + + def _get_currency(self, cr, uid, context): + user = self.pool.get('res.users').browse(cr, uid, [uid])[0] + if user.company_id: + return user.company_id.currency_id.id + else: + return self.pool.get('res.currency').search(cr, uid, [('rate','=',1.0)])[0] + + _name = "hr.expense.expense" + _description = "Expense" + _columns = { + 'name': fields.char('Expense Sheet', size=128, required=True), + 'id': fields.integer('Sheet ID', readonly=True), + 'ref': fields.char('Reference', size=32), + 'date': fields.date('Date'), + 'journal_id': fields.many2one('account.journal', 'Force Journal'), + 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), + 'user_id': fields.many2one('res.users', 'User', required=True), + 'date_confirm': fields.date('Date Confirmed'), + 'date_valid': fields.date('Date Valided'), + 'user_valid': fields.many2one('res.users', 'Validation User'), + 'account_move_id': fields.many2one('account.move', 'Account Move'), + 'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines'), + 'note': fields.text('Note'), + 'amount': fields.function(_amount, method=True, string='Total Amount'), + 'invoice_id': fields.many2one('account.invoice', 'Invoice'), + 'currency_id': fields.many2one('res.currency', 'Currency', required=True), + + 'state': fields.selection([ + ('draft', 'Draft'), + ('confirm', 'Waiting confirmation'), + ('accepted', 'Accepted'), + ('invoiced', 'Invoiced'), + ('paid', 'Reimbursed'), + ('canceled', 'Canceled')], + 'State', readonly=True), + } + _defaults = { + 'date' : lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'employee_id' : _employee_get, + 'user_id' : lambda cr,uid,id,c={}: id, + 'currency_id': _get_currency, + } + def expense_confirm(self, cr, uid, ids, *args): + #for exp in self.browse(cr, uid, ids): + self.write(cr, uid, ids, { + 'state':'confirm', + 'date_confirm': time.strftime('%Y-%m-%d') + }) + return True + + def expense_accept(self, cr, uid, ids, *args): + self.write(cr, uid, ids, { + 'state':'accepted', + 'date_valid':time.strftime('%Y-%m-%d'), + 'user_valid': uid, + }) + return True + + def expense_canceled(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'canceled'}) + return True + + def expense_paid(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'paid'}) + return True + + def action_invoice_create(self, cr, uid, ids): + res = False + invoice_obj = self.pool.get('account.invoice') + for exp in self.browse(cr, uid, ids): + lines = [] + for l in exp.line_ids: + tax_id = [] + if l.product_id: + acc = l.product_id.product_tmpl_id.property_account_expense.id + if not acc: + acc = l.product_id.categ_id.property_account_expense_categ.id + tax_id = [x.id for x in l.product_id.supplier_taxes_id] + else: + acc = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category') + lines.append((0, False, { + 'name': l.name, + 'account_id': acc, + 'price_unit': l.unit_amount, + 'quantity': l.unit_quantity, + 'uos_id': l.uom_id.id, + 'product_id': l.product_id and l.product_id.id or False, + 'invoice_line_tax_id': tax_id and [(6, 0, tax_id)] or False, + 'account_analytic_id': l.analytic_account.id, + })) + if not exp.employee_id.address_id: + raise osv.except_osv(_('Error !'), _('The employee must have a contact address')) + acc = exp.employee_id.address_id.partner_id.property_account_payable.id + payment_term_id = exp.employee_id.address_id.partner_id.property_payment_term.id + inv = { + 'name': exp.name, + 'reference': self.pool.get('ir.sequence').get(cr, uid, 'hr.expense.invoice'), + 'account_id': acc, + 'type': 'in_invoice', + 'partner_id': exp.employee_id.address_id.partner_id.id, + 'address_invoice_id': exp.employee_id.address_id.id, + 'address_contact_id': exp.employee_id.address_id.id, + 'origin': exp.name, + 'invoice_line': lines, + 'price_type': 'tax_included', + 'currency_id': exp.currency_id.id, + 'payment_term': payment_term_id, + } + if payment_term_id: + to_update = invoice_obj.onchange_payment_term_date_invoice(cr, uid, [], + payment_term_id, None) + if to_update: + inv.update(to_update['value']) + if exp.journal_id: + inv['journal_id']=exp.journal_id.id + inv_id = invoice_obj.create(cr, uid, inv, {'type':'in_invoice'}) + invoice_obj.button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, + set_total=True) + self.write(cr, uid, [exp.id], {'invoice_id': inv_id, 'state': 'invoiced'}) + res = inv_id + return res hr_expense_expense() class hr_expense_line(osv.osv): - _name = "hr.expense.line" - _description = "Expense Line" - def _amount(self, cr, uid, ids, field_name, arg, context): - if not len(ids): - return {} - id_set = ",".join(map(str, ids)) - cr.execute("SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN ("+id_set+") GROUP BY l.id ") - res = dict(cr.fetchall()) - return res - - _columns = { - 'name': fields.char('Short Description', size=128, required=True), - 'date_value': fields.date('Date', required=True), - 'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True), - 'total_amount': fields.function(_amount, method=True, string='Total'), - 'unit_amount': fields.float('Unit Price', readonly=True, states={'draft':[('readonly',False)]}), - 'unit_quantity': fields.float('Quantities', readonly=True, states={'draft':[('readonly',False)]}), - 'product_id': fields.many2one('product.product', 'Product', readonly=True, states={'draft':[('readonly',False)]}), - 'uom_id': fields.many2one('product.uom', 'UoM', readonly=True, states={'draft':[('readonly',False)]}), - 'description': fields.text('Description'), - 'analytic_account': fields.many2one('account.analytic.account','Analytic account'), - 'ref': fields.char('Reference', size=32), - 'sequence' : fields.integer('Sequence'), - } - _defaults = { - 'unit_quantity': lambda *a: 1, - 'date_value' : lambda *a: time.strftime('%Y-%m-%d'), - } - _order = "sequence" - def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}): - v={} - if product_id: - product=self.pool.get('product.product').browse(cr,uid,product_id, context=context) - v['name']=product.name - v['unit_amount']=product.standard_price - if not uom_id: - v['uom_id']=product.uom_id.id - return {'value':v} + _name = "hr.expense.line" + _description = "Expense Line" + def _amount(self, cr, uid, ids, field_name, arg, context): + if not len(ids): + return {} + id_set = ",".join(map(str, ids)) + cr.execute("SELECT l.id,COALESCE(SUM(l.unit_amount*l.unit_quantity),0) AS amount FROM hr_expense_line l WHERE id IN ("+id_set+") GROUP BY l.id ") + res = dict(cr.fetchall()) + return res + + _columns = { + 'name': fields.char('Short Description', size=128, required=True), + 'date_value': fields.date('Date', required=True), + 'expense_id': fields.many2one('hr.expense.expense', 'Expense', ondelete='cascade', select=True), + 'total_amount': fields.function(_amount, method=True, string='Total'), + 'unit_amount': fields.float('Unit Price', readonly=True, states={'draft':[('readonly',False)]}), + 'unit_quantity': fields.float('Quantities', readonly=True, states={'draft':[('readonly',False)]}), + 'product_id': fields.many2one('product.product', 'Product', readonly=True, states={'draft':[('readonly',False)]}), + 'uom_id': fields.many2one('product.uom', 'UoM', readonly=True, states={'draft':[('readonly',False)]}), + 'description': fields.text('Description'), + 'analytic_account': fields.many2one('account.analytic.account','Analytic account'), + 'ref': fields.char('Reference', size=32), + 'sequence' : fields.integer('Sequence'), + } + _defaults = { + 'unit_quantity': lambda *a: 1, + 'date_value' : lambda *a: time.strftime('%Y-%m-%d'), + } + _order = "sequence" + def onchange_product_id(self, cr, uid, ids, product_id, uom_id, context={}): + v={} + if product_id: + product=self.pool.get('product.product').browse(cr,uid,product_id, context=context) + v['name']=product.name + v['unit_amount']=product.standard_price + if not uom_id: + v['uom_id']=product.uom_id.id + return {'value':v} hr_expense_line() diff --git a/addons/hr_expense/report/expense.py b/addons/hr_expense/report/expense.py index cf9924a087c..c573261342a 100644 --- a/addons/hr_expense/report/expense.py +++ b/addons/hr_expense/report/expense.py @@ -32,11 +32,11 @@ from report import report_sxw import datetime class expense(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(expense, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'time': time, - }) + def __init__(self, cr, uid, name, context): + super(expense, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'time': time, + }) report_sxw.report_sxw('report.hr.expense', 'hr.expense.expense', 'addons/hr_expense/report/expense.rml',parser=expense) diff --git a/addons/hr_timesheet/__terp__.py b/addons/hr_timesheet/__terp__.py index 03b8c8ceec1..4308b8a1887 100644 --- a/addons/hr_timesheet/__terp__.py +++ b/addons/hr_timesheet/__terp__.py @@ -26,12 +26,12 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Human Resources (Timesheet encoding)", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "website" : "http://tinyerp.com/module_hr.html", - "description": """ + "name" : "Human Resources (Timesheet encoding)", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "website" : "http://tinyerp.com/module_hr.html", + "description": """ This module implement a timesheet system. Each employee can encode and track their time spent on the different projects. A project is an analytic account and the time spent on a project generate costs on @@ -41,11 +41,11 @@ Lots of reporting on time and employee tracking are provided. It is completly integrated with the cost accounting module. It allows you to set up a management by affair. - """, - "depends" : ["account", "hr", "base",], - "init_xml" : ["hr_timesheet_data.xml"], - "demo_xml" : ["hr_timesheet_demo.xml",], - "update_xml" : ["hr_timesheet_view.xml", "hr_timesheet_report.xml","hr_timesheet_wizard.xml"], - "active": False, - "installable": True + """, + "depends" : ["account", "hr", "base",], + "init_xml" : ["hr_timesheet_data.xml"], + "demo_xml" : ["hr_timesheet_demo.xml",], + "update_xml" : ["hr_timesheet_view.xml", "hr_timesheet_report.xml","hr_timesheet_wizard.xml"], + "active": False, + "installable": True } diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index 8ef4b78ece6..9c9b47a901e 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -33,99 +33,99 @@ from osv import osv class hr_employee(osv.osv): - _name = "hr.employee" - _inherit = "hr.employee" - _columns = { - 'product_id': fields.many2one('product.product', 'Product'), - 'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal') - } + _name = "hr.employee" + _inherit = "hr.employee" + _columns = { + 'product_id': fields.many2one('product.product', 'Product'), + 'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal') + } hr_employee() class hr_analytic_timesheet(osv.osv): - _name = "hr.analytic.timesheet" - _table = 'hr_analytic_timesheet' - _description = "Timesheet line" - _inherits = {'account.analytic.line': 'line_id'} - _order = "id desc" - _columns = { - 'line_id' : fields.many2one('account.analytic.line', 'Analytic line', ondelete='cascade'), - } - - def unlink(self, cr, uid, ids, context={}): - toremove = {} - for obj in self.browse(cr, uid, ids, context): - toremove[obj.line_id.id] = True - self.pool.get('account.analytic.line').unlink(cr, uid, toremove.keys(), context) - return super(hr_analytic_timesheet, self).unlink(cr, uid, ids, context) - - - def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, unit, context={}): - res = {} - if prod_id and unit_amount: - res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,unit, context) - return res - - def _getEmployeeProduct(self, cr, uid, context): - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) - if emp_id: - emp=emp_obj.browse(cr, uid, emp_id[0], context) - if emp.product_id: - return emp.product_id.id - return False - - def _getEmployeeUnit(self, cr, uid, context): - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) - if emp_id: - emp=emp_obj.browse(cr, uid, emp_id[0], context) - if emp.product_id: - return emp.product_id.uom_id.id - return False - - def _getGeneralAccount(self, cr, uid, context): - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) - if emp_id: - emp = self.pool.get('hr.employee').browse(cr, uid, emp_id[0], context=context) - if bool(emp.product_id): - a = emp.product_id.product_tmpl_id.property_account_expense.id - if not a: - a = emp.product_id.categ_id.property_account_expense_categ.id - if a: - return a - return False - - def _getAnalyticJournal(self, cr, uid, context): - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) - if emp_id: - emp = self.pool.get('hr.employee').browse(cr, uid, emp_id[0], context=context) - if emp.journal_id: - return emp.journal_id.id - return False - - - _defaults = { - 'product_uom_id' : _getEmployeeUnit, - 'product_id' : _getEmployeeProduct, - 'general_account_id' : _getGeneralAccount, - 'journal_id' : _getAnalyticJournal, - 'date' : lambda self,cr,uid,ctx: ctx.get('date', time.strftime('%Y-%m-%d')), - 'user_id' : lambda obj, cr, uid, ctx : ctx.get('user_id', uid), - } - - - def on_change_user_id(self, cr, uid, ids, user_id): - if not user_id: - return {} - return {'value' : { - 'product_id' : self._getEmployeeProduct(cr,user_id, context= {}), - 'product_uom_id' : self._getEmployeeUnit(cr, user_id, context= {}), - 'general_account_id' :self. _getGeneralAccount(cr, user_id, context= {}), - 'journal_id' : self._getAnalyticJournal(cr, user_id, context= {}), - }} + _name = "hr.analytic.timesheet" + _table = 'hr_analytic_timesheet' + _description = "Timesheet line" + _inherits = {'account.analytic.line': 'line_id'} + _order = "id desc" + _columns = { + 'line_id' : fields.many2one('account.analytic.line', 'Analytic line', ondelete='cascade'), + } + + def unlink(self, cr, uid, ids, context={}): + toremove = {} + for obj in self.browse(cr, uid, ids, context): + toremove[obj.line_id.id] = True + self.pool.get('account.analytic.line').unlink(cr, uid, toremove.keys(), context) + return super(hr_analytic_timesheet, self).unlink(cr, uid, ids, context) + + + def on_change_unit_amount(self, cr, uid, id, prod_id, unit_amount, unit, context={}): + res = {} + if prod_id and unit_amount: + res = self.pool.get('account.analytic.line').on_change_unit_amount(cr, uid, id, prod_id, unit_amount,unit, context) + return res + + def _getEmployeeProduct(self, cr, uid, context): + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) + if emp_id: + emp=emp_obj.browse(cr, uid, emp_id[0], context) + if emp.product_id: + return emp.product_id.id + return False + + def _getEmployeeUnit(self, cr, uid, context): + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) + if emp_id: + emp=emp_obj.browse(cr, uid, emp_id[0], context) + if emp.product_id: + return emp.product_id.uom_id.id + return False + + def _getGeneralAccount(self, cr, uid, context): + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) + if emp_id: + emp = self.pool.get('hr.employee').browse(cr, uid, emp_id[0], context=context) + if bool(emp.product_id): + a = emp.product_id.product_tmpl_id.property_account_expense.id + if not a: + a = emp.product_id.categ_id.property_account_expense_categ.id + if a: + return a + return False + + def _getAnalyticJournal(self, cr, uid, context): + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', context.get('user_id', uid))]) + if emp_id: + emp = self.pool.get('hr.employee').browse(cr, uid, emp_id[0], context=context) + if emp.journal_id: + return emp.journal_id.id + return False + + + _defaults = { + 'product_uom_id' : _getEmployeeUnit, + 'product_id' : _getEmployeeProduct, + 'general_account_id' : _getGeneralAccount, + 'journal_id' : _getAnalyticJournal, + 'date' : lambda self,cr,uid,ctx: ctx.get('date', time.strftime('%Y-%m-%d')), + 'user_id' : lambda obj, cr, uid, ctx : ctx.get('user_id', uid), + } + + + def on_change_user_id(self, cr, uid, ids, user_id): + if not user_id: + return {} + return {'value' : { + 'product_id' : self._getEmployeeProduct(cr,user_id, context= {}), + 'product_uom_id' : self._getEmployeeUnit(cr, user_id, context= {}), + 'general_account_id' :self. _getGeneralAccount(cr, user_id, context= {}), + 'journal_id' : self._getAnalyticJournal(cr, user_id, context= {}), + }} diff --git a/addons/hr_timesheet/report/user_timesheet.py b/addons/hr_timesheet/report/user_timesheet.py index 45b21385b14..fa2633fa5a1 100644 --- a/addons/hr_timesheet/report/user_timesheet.py +++ b/addons/hr_timesheet/report/user_timesheet.py @@ -35,66 +35,66 @@ from report.interface import toxml import pooler def lengthmonth(year, month): - if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))): - return 29 - return [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] + if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))): + return 29 + return [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] class report_custom(report_rml): - def create_xml(self, cr, uid, ids, data, context): + def create_xml(self, cr, uid, ids, data, context): - # Computing the dates (start of month: som, and end of month: eom) - som = datetime.date(data['form']['year'], data['form']['month'], 1) - eom = som + datetime.timedelta(lengthmonth(som.year, som.month)) - date_xml = ['<date month="%s" year="%d" />' % (som.strftime('%B'), som.year), '<days>'] - date_xml += ['<day number="%d" name="%s" />' % (x, som.replace(day=x).strftime('%a')) for x in range(1, lengthmonth(som.year, som.month)+1)] - date_xml.append('</days>') - date_xml.append('<cols>2.5cm%s,2cm</cols>\n' % (',0.7cm' * lengthmonth(som.year, som.month))) - - # Computing the attendence by analytical account - cr.execute( - "select line.date, (unit_amount * unit.factor) as amount, account_id, account.name "\ - "from account_analytic_line as line, hr_analytic_timesheet as hr, "\ - "account_analytic_account as account, product_uom as unit "\ - "where hr.line_id=line.id and line.account_id=account.id "\ - "and product_uom_id = unit.id "\ - "and line.user_id=%d and line.date >= %s and line.date < %s " - "order by line.date", - (data['form']['user_id'], som.strftime('%Y-%m-%d'), eom.strftime('%Y-%m-%d'))) + # Computing the dates (start of month: som, and end of month: eom) + som = datetime.date(data['form']['year'], data['form']['month'], 1) + eom = som + datetime.timedelta(lengthmonth(som.year, som.month)) + date_xml = ['<date month="%s" year="%d" />' % (som.strftime('%B'), som.year), '<days>'] + date_xml += ['<day number="%d" name="%s" />' % (x, som.replace(day=x).strftime('%a')) for x in range(1, lengthmonth(som.year, som.month)+1)] + date_xml.append('</days>') + date_xml.append('<cols>2.5cm%s,2cm</cols>\n' % (',0.7cm' * lengthmonth(som.year, som.month))) + + # Computing the attendence by analytical account + cr.execute( + "select line.date, (unit_amount * unit.factor) as amount, account_id, account.name "\ + "from account_analytic_line as line, hr_analytic_timesheet as hr, "\ + "account_analytic_account as account, product_uom as unit "\ + "where hr.line_id=line.id and line.account_id=account.id "\ + "and product_uom_id = unit.id "\ + "and line.user_id=%d and line.date >= %s and line.date < %s " + "order by line.date", + (data['form']['user_id'], som.strftime('%Y-%m-%d'), eom.strftime('%Y-%m-%d'))) - # Sum attendence by account, then by day - accounts = {} - for presence in cr.dictfetchall(): - day = int(presence['date'][-2:]) - account = accounts.setdefault((presence['account_id'], presence['name']), {}) - account[day] = account.get(day, 0.0) + presence['amount'] - - xml = ''' - <time-element date="%s"> - <amount>%.2f</amount> - </time-element> - ''' - account_xml = [] - for account, telems in accounts.iteritems(): - aid, aname = account - aname = pooler.get_pool(cr.dbname).get('account.analytic.account').name_get(cr, uid, [aid], context) - aname = aname[0][1] + # Sum attendence by account, then by day + accounts = {} + for presence in cr.dictfetchall(): + day = int(presence['date'][-2:]) + account = accounts.setdefault((presence['account_id'], presence['name']), {}) + account[day] = account.get(day, 0.0) + presence['amount'] + + xml = ''' + <time-element date="%s"> + <amount>%.2f</amount> + </time-element> + ''' + account_xml = [] + for account, telems in accounts.iteritems(): + aid, aname = account + aname = pooler.get_pool(cr.dbname).get('account.analytic.account').name_get(cr, uid, [aid], context) + aname = aname[0][1] - account_xml.append('<account id="%d" name="%s">' % (aid, toxml(aname))) - account_xml.append('\n'.join([xml % (day, amount) for day, amount in telems.iteritems()])) - account_xml.append('</account>') + account_xml.append('<account id="%d" name="%s">' % (aid, toxml(aname))) + account_xml.append('\n'.join([xml % (day, amount) for day, amount in telems.iteritems()])) + account_xml.append('</account>') - # Computing the employee - cr.execute("select name from res_users where id=%d", (data['form']['user_id'],)) - emp = cr.fetchone()[0] + # Computing the employee + cr.execute("select name from res_users where id=%d", (data['form']['user_id'],)) + emp = cr.fetchone()[0] - # Computing the xml - xml = '''<?xml version="1.0" encoding="UTF-8" ?> - <report> - <employee>%s</employee> - %s - </report> - ''' % (toxml(emp), '\n'.join(date_xml) + '\n'.join(account_xml)) - return xml + # Computing the xml + xml = '''<?xml version="1.0" encoding="UTF-8" ?> + <report> + <employee>%s</employee> + %s + </report> + ''' % (toxml(emp), '\n'.join(date_xml) + '\n'.join(account_xml)) + return xml report_custom('report.hr.analytical.timesheet', 'hr.employee', '', 'addons/hr_timesheet/report/user_timesheet.xsl') diff --git a/addons/hr_timesheet/report/users_timesheet.py b/addons/hr_timesheet/report/users_timesheet.py index 0216576076f..228d473d2f5 100644 --- a/addons/hr_timesheet/report/users_timesheet.py +++ b/addons/hr_timesheet/report/users_timesheet.py @@ -32,69 +32,69 @@ from report.interface import report_rml from report.interface import toxml def lengthmonth(year, month): - if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))): - return 29 - return [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] + if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))): + return 29 + return [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month] def emp_create_xml(cr, id, som, eom): - # Computing the attendence by analytical account - cr.execute( - "select line.date, (unit_amount * unit.factor) as amount "\ - "from account_analytic_line as line, hr_analytic_timesheet as hr, "\ - "product_uom as unit "\ - "where hr.line_id=line.id "\ - "and product_uom_id = unit.id "\ - "and line.user_id=%d and line.date >= %s and line.date < %s " - "order by line.date", - (id, som.strftime('%Y-%m-%d'), eom.strftime('%Y-%m-%d'))) - - # Sum by day - month = {} - for presence in cr.dictfetchall(): - day = int(presence['date'][-2:]) - month[day] = month.get(day, 0.0) + presence['amount'] - - xml = ''' - <time-element date="%s"> - <amount>%.2f</amount> - </time-element> - ''' - time_xml = ([xml % (day, amount) for day, amount in month.iteritems()]) - - # Computing the employee - cr.execute("select name from res_users where id=%d", (id,)) - emp = cr.fetchone()[0] - - # Computing the xml - xml = ''' - <employee id="%d" name="%s"> - %s - </employee> - ''' % (id, toxml(emp), '\n'.join(time_xml)) - return xml + # Computing the attendence by analytical account + cr.execute( + "select line.date, (unit_amount * unit.factor) as amount "\ + "from account_analytic_line as line, hr_analytic_timesheet as hr, "\ + "product_uom as unit "\ + "where hr.line_id=line.id "\ + "and product_uom_id = unit.id "\ + "and line.user_id=%d and line.date >= %s and line.date < %s " + "order by line.date", + (id, som.strftime('%Y-%m-%d'), eom.strftime('%Y-%m-%d'))) + + # Sum by day + month = {} + for presence in cr.dictfetchall(): + day = int(presence['date'][-2:]) + month[day] = month.get(day, 0.0) + presence['amount'] + + xml = ''' + <time-element date="%s"> + <amount>%.2f</amount> + </time-element> + ''' + time_xml = ([xml % (day, amount) for day, amount in month.iteritems()]) + + # Computing the employee + cr.execute("select name from res_users where id=%d", (id,)) + emp = cr.fetchone()[0] + + # Computing the xml + xml = ''' + <employee id="%d" name="%s"> + %s + </employee> + ''' % (id, toxml(emp), '\n'.join(time_xml)) + return xml class report_custom(report_rml): - def create_xml(self, cr, uid, ids, data, context): - - # Computing the dates (start of month: som, and end of month: eom) - som = datetime.date(data['form']['year'], data['form']['month'], 1) - eom = som + datetime.timedelta(lengthmonth(som.year, som.month)) - date_xml = ['<date month="%s" year="%d" />' % (som.strftime('%B'), som.year), '<days>'] - date_xml += ['<day number="%d" name="%s" />' % (x, som.replace(day=x).strftime('%a')) for x in range(1, lengthmonth(som.year, som.month)+1)] - date_xml.append('</days>') - date_xml.append('<cols>2.5cm%s,2cm</cols>\n' % (',0.7cm' * lengthmonth(som.year, som.month))) - - emp_xml='' - for id in data['form']['user_ids'][0][2]: - emp_xml += emp_create_xml(cr, id, som, eom) + def create_xml(self, cr, uid, ids, data, context): + + # Computing the dates (start of month: som, and end of month: eom) + som = datetime.date(data['form']['year'], data['form']['month'], 1) + eom = som + datetime.timedelta(lengthmonth(som.year, som.month)) + date_xml = ['<date month="%s" year="%d" />' % (som.strftime('%B'), som.year), '<days>'] + date_xml += ['<day number="%d" name="%s" />' % (x, som.replace(day=x).strftime('%a')) for x in range(1, lengthmonth(som.year, som.month)+1)] + date_xml.append('</days>') + date_xml.append('<cols>2.5cm%s,2cm</cols>\n' % (',0.7cm' * lengthmonth(som.year, som.month))) + + emp_xml='' + for id in data['form']['user_ids'][0][2]: + emp_xml += emp_create_xml(cr, id, som, eom) - # Computing the xml - xml='''<?xml version="1.0" encoding="UTF-8" ?> - <report> - %s - %s - </report> - ''' % (date_xml, emp_xml) - return xml + # Computing the xml + xml='''<?xml version="1.0" encoding="UTF-8" ?> + <report> + %s + %s + </report> + ''' % (date_xml, emp_xml) + return xml report_custom('report.hr.analytical.timesheet_users', 'hr.employee', '', 'addons/hr_timesheet/report/users_timesheet.xsl') diff --git a/addons/hr_timesheet/wizard/sign_in_out.py b/addons/hr_timesheet/wizard/sign_in_out.py index acda5153f24..27e2fcdf93d 100644 --- a/addons/hr_timesheet/wizard/sign_in_out.py +++ b/addons/hr_timesheet/wizard/sign_in_out.py @@ -35,141 +35,141 @@ from tools.translate import _ si_form ='''<?xml version="1.0"?> <form string="Sign in / Sign out"> - <separator string="Sign in" colspan="4"/> - <field name="name" readonly="True" /> - <field name="state" readonly="True" /> - <field name="server_date"/> - <label string="(local time on the server side)" colspan="2"/> - <field name="date"/> - <label string="(Keep empty for current time)" colspan="2"/> + <separator string="Sign in" colspan="4"/> + <field name="name" readonly="True" /> + <field name="state" readonly="True" /> + <field name="server_date"/> + <label string="(local time on the server side)" colspan="2"/> + <field name="date"/> + <label string="(Keep empty for current time)" colspan="2"/> </form>''' si_fields = { - 'name': {'string': "Employee's name", 'type':'char', 'required':True, 'readonly':True}, - 'state': {'string': "Current state", 'type' : 'char', 'required' : True, 'readonly': True}, - 'date': {'string':"Starting Date", 'type':'datetime'}, - 'server_date': {'string':"Current Date", 'type':'datetime', 'readonly':True}, + 'name': {'string': "Employee's name", 'type':'char', 'required':True, 'readonly':True}, + 'state': {'string': "Current state", 'type' : 'char', 'required' : True, 'readonly': True}, + 'date': {'string':"Starting Date", 'type':'datetime'}, + 'server_date': {'string':"Current Date", 'type':'datetime', 'readonly':True}, } so_form = '''<?xml version="1.0" ?> <form string="Sign in status"> - <separator string="General Information" colspan="4" /> - <field name="name" readonly="True" /> - <field name="state" readonly="True" /> - <field name="date_start"/> - <field name="server_date"/> - <separator string="Work done in the last period" colspan="4" /> - <field name="account_id" colspan="3"/> - <field name="info" colspan="3"/> - <field name="date"/> - <label string="(Keep empty for current_time)" colspan="2"/> - <field name="analytic_amount"/> + <separator string="General Information" colspan="4" /> + <field name="name" readonly="True" /> + <field name="state" readonly="True" /> + <field name="date_start"/> + <field name="server_date"/> + <separator string="Work done in the last period" colspan="4" /> + <field name="account_id" colspan="3"/> + <field name="info" colspan="3"/> + <field name="date"/> + <label string="(Keep empty for current_time)" colspan="2"/> + <field name="analytic_amount"/> </form>''' so_fields = { - 'name': {'string':"Employee's name", 'type':'char', 'required':True, 'readonly':True}, - 'state': {'string':"Current state", 'type':'char', 'required':True, 'readonly':True}, - 'account_id': {'string':"Analytic Account", 'type':'many2one', 'relation':'account.analytic.account', 'required':True, 'domain':"[('type','=','normal')]"}, - 'info': {'string':"Work Description", 'type':'char', 'size':256, 'required':True}, - 'date': {'string':"Closing Date", 'type':'datetime'}, - 'date_start': {'string':"Starting Date", 'type':'datetime', 'readonly':True}, - 'server_date': {'string':"Current Server Date", 'type':'datetime', 'readonly':True}, - 'analytic_amount': {'string':"Minimum Analytic Amount", 'type':'float'}, + 'name': {'string':"Employee's name", 'type':'char', 'required':True, 'readonly':True}, + 'state': {'string':"Current state", 'type':'char', 'required':True, 'readonly':True}, + 'account_id': {'string':"Analytic Account", 'type':'many2one', 'relation':'account.analytic.account', 'required':True, 'domain':"[('type','=','normal')]"}, + 'info': {'string':"Work Description", 'type':'char', 'size':256, 'required':True}, + 'date': {'string':"Closing Date", 'type':'datetime'}, + 'date_start': {'string':"Starting Date", 'type':'datetime', 'readonly':True}, + 'server_date': {'string':"Current Server Date", 'type':'datetime', 'readonly':True}, + 'analytic_amount': {'string':"Minimum Analytic Amount", 'type':'float'}, } def _get_empid(self, cr, uid, data, context): - emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) - if emp_id: - employee = emp_obj.read(cr, uid, emp_id)[0] - return {'name': employee['name'], 'state': employee['state'], 'emp_id': emp_id[0], 'date':False, 'server_date':time.strftime('%Y-%m-%d %H:%M:%S')} - raise wizard.except_wizard(_('UserError'), _('No employee defined for your user !')) + emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) + if emp_id: + employee = emp_obj.read(cr, uid, emp_id)[0] + return {'name': employee['name'], 'state': employee['state'], 'emp_id': emp_id[0], 'date':False, 'server_date':time.strftime('%Y-%m-%d %H:%M:%S')} + raise wizard.except_wizard(_('UserError'), _('No employee defined for your user !')) def _get_empid2(self, cr, uid, data, context): - res = _get_empid(self,cr, uid, data, context) - cr.execute('select name,action from hr_attendance where employee_id=%d order by name desc limit 1', (res['emp_id'],)) - res['server_date'] = time.strftime('%Y-%m-%d %H:%M:%S') - res['date_start'] = cr.fetchone()[0] - res['info'] = '' - res['account_id'] = False - return res + res = _get_empid(self,cr, uid, data, context) + cr.execute('select name,action from hr_attendance where employee_id=%d order by name desc limit 1', (res['emp_id'],)) + res['server_date'] = time.strftime('%Y-%m-%d %H:%M:%S') + res['date_start'] = cr.fetchone()[0] + res['info'] = '' + res['account_id'] = False + return res def _sign_in_result(self, cr, uid, data, context): - emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') - emp_id = data['form']['emp_id'] - from osv.osv import except_osv - try: - success = emp_obj.sign_in(cr, uid, [emp_id], dt=data['form']['date'] or False) - except except_osv, e: - raise wizard.except_wizard(e.name, e.value) - return {} + emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') + emp_id = data['form']['emp_id'] + from osv.osv import except_osv + try: + success = emp_obj.sign_in(cr, uid, [emp_id], dt=data['form']['date'] or False) + except except_osv, e: + raise wizard.except_wizard(e.name, e.value) + return {} def _write(self, cr, uid, data, emp_id, context): - timesheet_obj = pooler.get_pool(cr.dbname).get('hr.analytic.timesheet') - hour = (time.mktime(time.strptime(data['form']['date'] or time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')) - - time.mktime(time.strptime(data['form']['date_start'], '%Y-%m-%d %H:%M:%S'))) / 3600.0 - minimum = data['form']['analytic_amount'] - if minimum: - hour = round(round((hour + minimum / 2) / minimum) * minimum, 2) - res = timesheet_obj.default_get(cr, uid, ['product_id','product_uom_id']) - if not res['product_uom_id']: - raise wizard.except_wizard(_('UserError'), _('No cost unit defined for this employee !')) - up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, res['product_uom_id'])['value'] - res['name'] = data['form']['info'] - res['account_id'] = data['form']['account_id'] - res['unit_amount'] = hour - res.update(up) - return timesheet_obj.create(cr, uid, res, context) + timesheet_obj = pooler.get_pool(cr.dbname).get('hr.analytic.timesheet') + hour = (time.mktime(time.strptime(data['form']['date'] or time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')) - + time.mktime(time.strptime(data['form']['date_start'], '%Y-%m-%d %H:%M:%S'))) / 3600.0 + minimum = data['form']['analytic_amount'] + if minimum: + hour = round(round((hour + minimum / 2) / minimum) * minimum, 2) + res = timesheet_obj.default_get(cr, uid, ['product_id','product_uom_id']) + if not res['product_uom_id']: + raise wizard.except_wizard(_('UserError'), _('No cost unit defined for this employee !')) + up = timesheet_obj.on_change_unit_amount(cr, uid, False, res['product_id'], hour, res['product_uom_id'])['value'] + res['name'] = data['form']['info'] + res['account_id'] = data['form']['account_id'] + res['unit_amount'] = hour + res.update(up) + return timesheet_obj.create(cr, uid, res, context) def _sign_out_result_end(self, cr, uid, data, context): - emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') - emp_id = data['form']['emp_id'] - emp_obj.sign_out(cr, uid, [emp_id], dt=data['form']['date']) - _write(self, cr, uid, data, emp_id, context) - return {} + emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') + emp_id = data['form']['emp_id'] + emp_obj.sign_out(cr, uid, [emp_id], dt=data['form']['date']) + _write(self, cr, uid, data, emp_id, context) + return {} def _sign_out_result(self, cr, uid, data, context): - emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') - emp_id = data['form']['emp_id'] - emp_obj.sign_change(cr, uid, [emp_id], dt=data['form']['date']) - _write(self, cr, uid, data, emp_id, context) - return {} + emp_obj = pooler.get_pool(cr.dbname).get('hr.employee') + emp_id = data['form']['emp_id'] + emp_obj.sign_change(cr, uid, [emp_id], dt=data['form']['date']) + _write(self, cr, uid, data, emp_id, context) + return {} def _state_check(self, cr, uid, data, context): - emp_id = _get_empid(self, cr, uid, data, context)['emp_id'] - # get the latest action (sign_in or out) for this employee - cr.execute('select action from hr_attendance where employee_id=%d and action in (\'sign_in\',\'sign_out\') order by name desc limit 1', (emp_id,)) - res = (cr.fetchone() or ('sign_out',))[0] + emp_id = _get_empid(self, cr, uid, data, context)['emp_id'] + # get the latest action (sign_in or out) for this employee + cr.execute('select action from hr_attendance where employee_id=%d and action in (\'sign_in\',\'sign_out\') order by name desc limit 1', (emp_id,)) + res = (cr.fetchone() or ('sign_out',))[0] #TODO: invert sign_in et sign_out - return res + return res class wiz_si_so(wizard.interface): - states = { - 'init' : { - 'actions' : [], - 'result' : {'type' : 'choice', 'next_state': _state_check} - }, - 'sign_out' : { # this means sign_in... - 'actions' : [_get_empid], - 'result' : {'type':'form', 'arch':si_form, 'fields' : si_fields, 'state':[('end', 'Cancel'),('si_result', 'Start Working') ] } - }, - 'si_result' : { - 'actions' : [_sign_in_result], - 'result' : {'type':'state', 'state':'end'} - }, - 'sign_in' : { # this means sign_out... - 'actions' : [_get_empid2], - 'result' : {'type':'form', 'arch':so_form, 'fields':so_fields, 'state':[('end', 'Cancel'),('so_result', 'Change Work'),('so_result_end', 'Stop Working') ] } - }, - 'so_result' : { - 'actions' : [_sign_out_result], - 'result' : {'type':'state', 'state':'end'} - }, - 'so_result_end' : { - 'actions' : [_sign_out_result_end], - 'result' : {'type':'state', 'state':'end'} - }, - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type' : 'choice', 'next_state': _state_check} + }, + 'sign_out' : { # this means sign_in... + 'actions' : [_get_empid], + 'result' : {'type':'form', 'arch':si_form, 'fields' : si_fields, 'state':[('end', 'Cancel'),('si_result', 'Start Working') ] } + }, + 'si_result' : { + 'actions' : [_sign_in_result], + 'result' : {'type':'state', 'state':'end'} + }, + 'sign_in' : { # this means sign_out... + 'actions' : [_get_empid2], + 'result' : {'type':'form', 'arch':so_form, 'fields':so_fields, 'state':[('end', 'Cancel'),('so_result', 'Change Work'),('so_result_end', 'Stop Working') ] } + }, + 'so_result' : { + 'actions' : [_sign_out_result], + 'result' : {'type':'state', 'state':'end'} + }, + 'so_result_end' : { + 'actions' : [_sign_out_result_end], + 'result' : {'type':'state', 'state':'end'} + }, + } wiz_si_so('hr_timesheet.si_so') # vim:noexpandtab:tw=0 diff --git a/addons/hr_timesheet/wizard/wizard_print_timesheet.py b/addons/hr_timesheet/wizard/wizard_print_timesheet.py index fcec43ac3da..4a40d06665d 100644 --- a/addons/hr_timesheet/wizard/wizard_print_timesheet.py +++ b/addons/hr_timesheet/wizard/wizard_print_timesheet.py @@ -34,52 +34,52 @@ import netsvc dates_form = '''<?xml version="1.0"?> <form string="Choose your month"> - <field name="month" /> - <field name="year" /> - <field name="user_id" colspan="3" /> + <field name="month" /> + <field name="year" /> + <field name="user_id" colspan="3" /> </form>''' dates_form_ro = '''<?xml version="1.0"?> <form string="Choose your month"> - <field name="month" /> - <field name="year" /> - <field name="user_id" colspan="3" readonly="1"/> + <field name="month" /> + <field name="year" /> + <field name="user_id" colspan="3" readonly="1"/> </form>''' dates_fields = { - 'month': dict(string=u'Month', type='selection', required=True, selection=[(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)]), - 'year': dict(string=u'Year', type=u'integer', required=True), - 'user_id' : dict(string=u'User', type='many2one', relation='res.users', required=True), + 'month': dict(string=u'Month', type='selection', required=True, selection=[(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)]), + 'year': dict(string=u'Year', type=u'integer', required=True), + 'user_id' : dict(string=u'User', type='many2one', relation='res.users', required=True), } def _get_value(self, cr, uid, data, context): - today = datetime.date.today() - return dict(month=today.month, year=today.year, user_id=uid) + today = datetime.date.today() + return dict(month=today.month, year=today.year, user_id=uid) class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [_get_value], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[ ('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'hr.analytical.timesheet', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_value], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[ ('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'hr.analytical.timesheet', 'state':'end'} + } + } wizard_report('hr.analytical.timesheet') class wizard_report_my(wizard.interface): - states = { - 'init': { - 'actions': [_get_value], - 'result': {'type':'form', 'arch':dates_form_ro, 'fields':dates_fields, 'state':[ ('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'hr.analytical.timesheet', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_value], + 'result': {'type':'form', 'arch':dates_form_ro, 'fields':dates_fields, 'state':[ ('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'hr.analytical.timesheet', 'state':'end'} + } + } wizard_report_my('hr.analytical.timesheet.my') diff --git a/addons/hr_timesheet/wizard/wizard_print_timesheet_users.py b/addons/hr_timesheet/wizard/wizard_print_timesheet_users.py index 8bdc7d211a5..a2fbef67f09 100644 --- a/addons/hr_timesheet/wizard/wizard_print_timesheet_users.py +++ b/addons/hr_timesheet/wizard/wizard_print_timesheet_users.py @@ -32,30 +32,30 @@ import datetime form='''<?xml version="1.0"?> <form string="Choose Users"> - <field name="month"/> - <field name="year"/> - <field name="user_ids" colspan="3"/> + <field name="month"/> + <field name="year"/> + <field name="user_ids" colspan="3"/> </form>''' fields = { - 'month': dict(string=u'Month', type='selection', required=True, selection=[(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)]), - 'year': dict(string=u'Year', type='integer', required=True), - 'user_ids': dict(string=u'Users', type='many2many', relation='res.users', required=True), + 'month': dict(string=u'Month', type='selection', required=True, selection=[(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)]), + 'year': dict(string=u'Year', type='integer', required=True), + 'user_ids': dict(string=u'Users', type='many2many', relation='res.users', required=True), } def _get_value(self, cr, uid, data, context): - today=datetime.date.today() - return dict(month=today.month, year=today.year) + today=datetime.date.today() + return dict(month=today.month, year=today.year) class wizard_report(wizard.interface): - states={ - 'init':{ - 'actions':[_get_value], - 'result':{'type':'form', 'arch':form, 'fields':fields, 'state':[('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} - }, - 'report':{ - 'actions':[], - 'result':{'type':'print', 'report':'hr.analytical.timesheet_users', 'state':'end'} - } - } + states={ + 'init':{ + 'actions':[_get_value], + 'result':{'type':'form', 'arch':form, 'fields':fields, 'state':[('end','Cancel','gtk-cancel'),('report','Print','gtk-print')]} + }, + 'report':{ + 'actions':[], + 'result':{'type':'print', 'report':'hr.analytical.timesheet_users', 'state':'end'} + } + } wizard_report('hr.analytical.timesheet_users') diff --git a/addons/hr_timesheet_invoice/__terp__.py b/addons/hr_timesheet_invoice/__terp__.py index 12acb5736e9..3fe60871361 100644 --- a/addons/hr_timesheet_invoice/__terp__.py +++ b/addons/hr_timesheet_invoice/__terp__.py @@ -26,25 +26,25 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Invoice on analytic lines", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Accounting", - "website" : "http://tinyerp.com/", - "depends" : ["account",'hr_timesheet'], - "description": """ + "name" : "Invoice on analytic lines", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Accounting", + "website" : "http://tinyerp.com/", + "depends" : ["account",'hr_timesheet'], + "description": """ Module to generate invoices based on costs (human ressources, expenses, ...). You can define pricelists in analytic account, make some theorical revenue reports, eso.""", - "init_xml" : [], - "demo_xml" : [ - 'hr_timesheet_invoice_demo.xml' - ], - "update_xml" : [ - 'hr_timesheet_invoice_view.xml', - 'hr_timesheet_invoice_wizard.xml', - 'hr_timesheet_invoice_report.xml' - ], - "active": False, - "installable": True + "init_xml" : [], + "demo_xml" : [ + 'hr_timesheet_invoice_demo.xml' + ], + "update_xml" : [ + 'hr_timesheet_invoice_view.xml', + 'hr_timesheet_invoice_wizard.xml', + 'hr_timesheet_invoice_report.xml' + ], + "active": False, + "installable": True } diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 8629f587e32..69e72829b3a 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -33,117 +33,117 @@ from osv import fields, osv from tools.translate import _ class hr_timesheet_invoice_factor(osv.osv): - _name = "hr_timesheet_invoice.factor" - _description = "Invoice rate" - _columns = { - 'name': fields.char('Internal name', size=128, required=True), - 'customer_name': fields.char('Visible name', size=128), - 'factor': fields.float('Discount (%)', required=True), - } - _defaults = { - 'factor': lambda *a: 0.0, - } + _name = "hr_timesheet_invoice.factor" + _description = "Invoice rate" + _columns = { + 'name': fields.char('Internal name', size=128, required=True), + 'customer_name': fields.char('Visible name', size=128), + 'factor': fields.float('Discount (%)', required=True), + } + _defaults = { + 'factor': lambda *a: 0.0, + } hr_timesheet_invoice_factor() class account_analytic_account(osv.osv): - def _invoiced_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - for account in self.browse(cr, uid, ids): - invoiced = {} - cr.execute('select distinct l.invoice_id from hr_analytic_timesheet h left join account_analytic_line l on (h.line_id=l.id) where account_id=1', (account.id,)) - invoice_ids = filter(None, map(lambda x: x[0], cr.fetchall())) - for invoice in self.pool.get('account.invoice').browse(cr, uid, invoice_ids, context): - res.setdefault(account.id, 0.0) - res[account.id] += invoice.amount_untaxed - for id in ids: - res[id] = round(res.get(id, 0.0),2) - return res - - _inherit = "account.analytic.account" - _columns = { - 'pricelist_id' : fields.many2one('product.pricelist', 'Sale Pricelist'), - 'amount_max': fields.float('Max. Invoice Price'), - 'amount_invoiced': fields.function(_invoiced_calc, method=True, string='Invoiced Amount'), - 'to_invoice': fields.many2one('hr_timesheet_invoice.factor','Invoicing'), - } + def _invoiced_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + for account in self.browse(cr, uid, ids): + invoiced = {} + cr.execute('select distinct l.invoice_id from hr_analytic_timesheet h left join account_analytic_line l on (h.line_id=l.id) where account_id=1', (account.id,)) + invoice_ids = filter(None, map(lambda x: x[0], cr.fetchall())) + for invoice in self.pool.get('account.invoice').browse(cr, uid, invoice_ids, context): + res.setdefault(account.id, 0.0) + res[account.id] += invoice.amount_untaxed + for id in ids: + res[id] = round(res.get(id, 0.0),2) + return res + + _inherit = "account.analytic.account" + _columns = { + 'pricelist_id' : fields.many2one('product.pricelist', 'Sale Pricelist'), + 'amount_max': fields.float('Max. Invoice Price'), + 'amount_invoiced': fields.function(_invoiced_calc, method=True, string='Invoiced Amount'), + 'to_invoice': fields.many2one('hr_timesheet_invoice.factor','Invoicing'), + } account_analytic_account() class account_analytic_line(osv.osv): - _inherit = 'account.analytic.line' - _columns = { - 'invoice_id': fields.many2one('account.invoice', 'Invoice'), - 'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Invoicing'), - } - - def unlink(self, cursor, user, ids, context=None): - self._check(cursor, user, ids) - return super(account_analytic_line,self).unlink(cursor, user, ids, - context=context) - - def write(self, cr, uid, ids, vals, context=None): - self._check(cr, uid, ids) - return super(account_analytic_line,self).write(cr, uid, ids, vals, - context=context) - - def _check(self, cr, uid, ids): - select = ids - if isinstance(select, (int, long)): - select = [ids] - for line in self.browse(cr, uid, select): - if line.invoice_id: - raise osv.except_osv(_('Error !'), - _('You can not modify an invoiced analytic line!')) - return True - - def copy(self, cursor, user, obj_id, default=None, context=None): - if default is None: - default = {} - default = default.copy() - default.update({'invoice_id': False}) - return super(account_analytic_line, self).copy(cursor, user, obj_id, - default, context) + _inherit = 'account.analytic.line' + _columns = { + 'invoice_id': fields.many2one('account.invoice', 'Invoice'), + 'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Invoicing'), + } + + def unlink(self, cursor, user, ids, context=None): + self._check(cursor, user, ids) + return super(account_analytic_line,self).unlink(cursor, user, ids, + context=context) + + def write(self, cr, uid, ids, vals, context=None): + self._check(cr, uid, ids) + return super(account_analytic_line,self).write(cr, uid, ids, vals, + context=context) + + def _check(self, cr, uid, ids): + select = ids + if isinstance(select, (int, long)): + select = [ids] + for line in self.browse(cr, uid, select): + if line.invoice_id: + raise osv.except_osv(_('Error !'), + _('You can not modify an invoiced analytic line!')) + return True + + def copy(self, cursor, user, obj_id, default=None, context=None): + if default is None: + default = {} + default = default.copy() + default.update({'invoice_id': False}) + return super(account_analytic_line, self).copy(cursor, user, obj_id, + default, context) account_analytic_line() class hr_analytic_timesheet(osv.osv): - _inherit = "hr.analytic.timesheet" - def on_change_account_id(self, cr, uid, ids, account_id): - res = {} - if not account_id: - return res - res.setdefault('value',{}) - st = self.pool.get('account.analytic.account').browse(cr, uid, - account_id).to_invoice.id - res['value']['to_invoice'] = st or False - return res - - def copy(self, cursor, user, obj_id, default=None, context=None): - if default is None: - default = {} - default = default.copy() - default.update({'invoice_id': False}) - return super(hr_analytic_timesheet, self).copy(cursor, user, obj_id, - default, context) + _inherit = "hr.analytic.timesheet" + def on_change_account_id(self, cr, uid, ids, account_id): + res = {} + if not account_id: + return res + res.setdefault('value',{}) + st = self.pool.get('account.analytic.account').browse(cr, uid, + account_id).to_invoice.id + res['value']['to_invoice'] = st or False + return res + + def copy(self, cursor, user, obj_id, default=None, context=None): + if default is None: + default = {} + default = default.copy() + default.update({'invoice_id': False}) + return super(hr_analytic_timesheet, self).copy(cursor, user, obj_id, + default, context) hr_analytic_timesheet() class account_invoice(osv.osv): - _inherit = "account.invoice" - - def _get_analityc_lines(self, cr, uid, id): - iml = super(account_invoice, self)._get_analityc_lines(cr, uid, id) - - inv = self.browse(cr, uid, [id])[0] - if inv.type == 'in_invoice': - for il in iml: - if il['account_analytic_id']: - to_invoice = self.pool.get('account.analytic.account').read(cr, uid, [il['account_analytic_id']], ['to_invoice'])[0]['to_invoice'] - if to_invoice: - il['analytic_lines'][0][2]['to_invoice'] = to_invoice[0] - return iml + _inherit = "account.invoice" + + def _get_analityc_lines(self, cr, uid, id): + iml = super(account_invoice, self)._get_analityc_lines(cr, uid, id) + + inv = self.browse(cr, uid, [id])[0] + if inv.type == 'in_invoice': + for il in iml: + if il['account_analytic_id']: + to_invoice = self.pool.get('account.analytic.account').read(cr, uid, [il['account_analytic_id']], ['to_invoice'])[0]['to_invoice'] + if to_invoice: + il['analytic_lines'][0][2]['to_invoice'] = to_invoice[0] + return iml account_invoice() diff --git a/addons/hr_timesheet_invoice/report/account_analytic_profit.py b/addons/hr_timesheet_invoice/report/account_analytic_profit.py index 04b1a21ef46..9289c5b2611 100644 --- a/addons/hr_timesheet_invoice/report/account_analytic_profit.py +++ b/addons/hr_timesheet_invoice/report/account_analytic_profit.py @@ -31,103 +31,103 @@ from report import report_sxw import pooler class account_analytic_profit(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_profit, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'lines': self._lines, - 'user_ids': self._user_ids, - 'journal_ids': self._journal_ids, - 'line': self._line, - }) - def _user_ids(self, lines): - user_obj=pooler.get_pool(self.cr.dbname).get('res.users') - ids=list(set([b.user_id.id for b in lines])) - res=user_obj.browse(self.cr, self.uid, ids) - return res + def __init__(self, cr, uid, name, context): + super(account_analytic_profit, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'lines': self._lines, + 'user_ids': self._user_ids, + 'journal_ids': self._journal_ids, + 'line': self._line, + }) + def _user_ids(self, lines): + user_obj=pooler.get_pool(self.cr.dbname).get('res.users') + ids=list(set([b.user_id.id for b in lines])) + res=user_obj.browse(self.cr, self.uid, ids) + return res - def _journal_ids(self, form, user_id): - line_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.line') - journal_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.journal') - line_ids=line_obj.search(self.cr, self.uid, [ - ('date', '>=', form['date_from']), - ('date', '<=', form['date_to']), - ('journal_id', 'in', form['journal_ids'][0][2]), - ('user_id', '=', user_id), - ]) - ids=list(set([b.journal_id.id for b in line_obj.browse(self.cr, self.uid, line_ids)])) - res=journal_obj.browse(self.cr, self.uid, ids) - return res + def _journal_ids(self, form, user_id): + line_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.line') + journal_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.journal') + line_ids=line_obj.search(self.cr, self.uid, [ + ('date', '>=', form['date_from']), + ('date', '<=', form['date_to']), + ('journal_id', 'in', form['journal_ids'][0][2]), + ('user_id', '=', user_id), + ]) + ids=list(set([b.journal_id.id for b in line_obj.browse(self.cr, self.uid, line_ids)])) + res=journal_obj.browse(self.cr, self.uid, ids) + return res - def _line(self, form, journal_ids, user_ids): - pool=pooler.get_pool(self.cr.dbname) - line_obj=pool.get('account.analytic.line') - product_obj=pool.get('product.product') - price_obj=pool.get('product.pricelist') - ids=line_obj.search(self.cr, self.uid, [ - ('date', '>=', form['date_from']), - ('date', '<=', form['date_to']), - ('journal_id', 'in', journal_ids), - ('user_id', 'in', user_ids), - ]) - res={} - for line in line_obj.browse(self.cr, self.uid, ids): - if line.account_id.pricelist_id: - if line.account_id.to_invoice: - if line.to_invoice: - id=line.to_invoice.id - name=line.to_invoice.name - discount=line.to_invoice.factor - else: - name="/" - discount=1.0 - id = -1 - else: - name="Fixed" - discount=0.0 - id=0 - pl=line.account_id.pricelist_id.id - price=price_obj.price_get(self.cr, self.uid, [pl], line.product_id.id, line.unit_amount or 1.0, line.account_id.partner_id.id)[pl] - else: - name="/" - discount=1.0 - id = -1 - price=0.0 - if id not in res: - res[id]={'name': name, 'amount': 0, 'cost':0, 'unit_amount':0,'amount_th':0} - xxx = round(price * line.unit_amount * (1-(discount or 0.0)), 2) - res[id]['amount_th']+=xxx - if line.invoice_id: - self.cr.execute('select id from account_analytic_line where invoice_id=%d', (line.invoice_id.id,)) - tot = 0 - for lid in self.cr.fetchall(): - lid2 = line_obj.browse(self.cr, self.uid, lid[0]) - pl=lid2.account_id.pricelist_id.id - price=price_obj.price_get(self.cr, self.uid, [pl], lid2.product_id.id, lid2.unit_amount or 1.0, lid2.account_id.partner_id.id)[pl] - tot += price * lid2.unit_amount * (1-(discount or 0.0)) - if tot: - procent = line.invoice_id.amount_untaxed / tot - res[id]['amount'] += xxx * procent - else: - res[id]['amount'] += xxx - else: - res[id]['amount'] += xxx + def _line(self, form, journal_ids, user_ids): + pool=pooler.get_pool(self.cr.dbname) + line_obj=pool.get('account.analytic.line') + product_obj=pool.get('product.product') + price_obj=pool.get('product.pricelist') + ids=line_obj.search(self.cr, self.uid, [ + ('date', '>=', form['date_from']), + ('date', '<=', form['date_to']), + ('journal_id', 'in', journal_ids), + ('user_id', 'in', user_ids), + ]) + res={} + for line in line_obj.browse(self.cr, self.uid, ids): + if line.account_id.pricelist_id: + if line.account_id.to_invoice: + if line.to_invoice: + id=line.to_invoice.id + name=line.to_invoice.name + discount=line.to_invoice.factor + else: + name="/" + discount=1.0 + id = -1 + else: + name="Fixed" + discount=0.0 + id=0 + pl=line.account_id.pricelist_id.id + price=price_obj.price_get(self.cr, self.uid, [pl], line.product_id.id, line.unit_amount or 1.0, line.account_id.partner_id.id)[pl] + else: + name="/" + discount=1.0 + id = -1 + price=0.0 + if id not in res: + res[id]={'name': name, 'amount': 0, 'cost':0, 'unit_amount':0,'amount_th':0} + xxx = round(price * line.unit_amount * (1-(discount or 0.0)), 2) + res[id]['amount_th']+=xxx + if line.invoice_id: + self.cr.execute('select id from account_analytic_line where invoice_id=%d', (line.invoice_id.id,)) + tot = 0 + for lid in self.cr.fetchall(): + lid2 = line_obj.browse(self.cr, self.uid, lid[0]) + pl=lid2.account_id.pricelist_id.id + price=price_obj.price_get(self.cr, self.uid, [pl], lid2.product_id.id, lid2.unit_amount or 1.0, lid2.account_id.partner_id.id)[pl] + tot += price * lid2.unit_amount * (1-(discount or 0.0)) + if tot: + procent = line.invoice_id.amount_untaxed / tot + res[id]['amount'] += xxx * procent + else: + res[id]['amount'] += xxx + else: + res[id]['amount'] += xxx - res[id]['cost']+=line.amount - res[id]['unit_amount']+=line.unit_amount - for id in res: - res[id]['profit']=res[id]['amount']+res[id]['cost'] - res[id]['eff']='%d' % (-res[id]['amount'] / res[id]['cost'] * 100,) - return res.values() + res[id]['cost']+=line.amount + res[id]['unit_amount']+=line.unit_amount + for id in res: + res[id]['profit']=res[id]['amount']+res[id]['cost'] + res[id]['eff']='%d' % (-res[id]['amount'] / res[id]['cost'] * 100,) + return res.values() - def _lines(self, form): - line_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.line') - ids=line_obj.search(self.cr, self.uid, [ - ('date', '>=', form['date_from']), - ('date', '<=', form['date_to']), - ('journal_id', 'in', form['journal_ids'][0][2]), - ('user_id', 'in', form['employee_ids'][0][2]), - ]) - res=line_obj.browse(self.cr, self.uid, ids) - return res + def _lines(self, form): + line_obj=pooler.get_pool(self.cr.dbname).get('account.analytic.line') + ids=line_obj.search(self.cr, self.uid, [ + ('date', '>=', form['date_from']), + ('date', '<=', form['date_to']), + ('journal_id', 'in', form['journal_ids'][0][2]), + ('user_id', 'in', form['employee_ids'][0][2]), + ]) + res=line_obj.browse(self.cr, self.uid, ids) + return res report_sxw.report_sxw('report.account.analytic.profit', 'account.analytic.line', 'addons/hr_timesheet_invoice/report/account_analytic_profit.rml', parser=account_analytic_profit) diff --git a/addons/hr_timesheet_invoice/report/cost_ledger.py b/addons/hr_timesheet_invoice/report/cost_ledger.py index 2253bc5a41e..9782c66b453 100644 --- a/addons/hr_timesheet_invoice/report/cost_ledger.py +++ b/addons/hr_timesheet_invoice/report/cost_ledger.py @@ -33,147 +33,147 @@ from report import report_sxw class account_analytic_cost_ledger(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(account_analytic_cost_ledger, self).__init__(cr, uid, name, context) - self.sum_revenue={} - self.account_sum_revenue={} - self.localcontext.update( { - 'time': time, - 'lines_g': self._lines_g, - 'lines_a': self._lines_a, - 'account_sum_debit': self._account_sum_debit, - 'account_sum_credit': self._account_sum_credit, - 'account_sum_balance': self._account_sum_balance, - 'account_sum_qty': self._account_sum_qty, - 'account_sum_revenue': self._account_sum_revenue, - 'account_g_sum_revenue': self._account_g_sum_revenue, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_balance': self._sum_balance, - 'sum_qty': self._sum_qty, - 'sum_revenue': self._sum_revenue, - }) - - def _lines_g(self, account_id, date1, date2): - self.cr.execute("SELECT sum(aal.amount) AS balance, aa.code AS code, aa.name AS name, aa.id AS id, sum(aal.unit_amount) AS quantity \ - FROM account_account AS aa, account_analytic_line AS aal \ - WHERE (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) AND (aal.general_account_id=aa.id) AND aa.active \ - GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", (account_id, date1, date2)) - res = self.cr.dictfetchall() - - for r in res: - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res - - def _lines_a(self, general_account_id, account_id, date1, date2): - self.cr.execute("SELECT aal.id AS id, aal.name AS name, aal.code AS code, aal.amount AS balance, aal.date AS date, aaj.code AS cj, aal.unit_amount AS quantity \ - FROM account_analytic_line AS aal, account_analytic_journal AS aaj \ - WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) \ - AND (aal.journal_id=aaj.id) \ - ORDER BY aal.date, aaj.code, aal.code", (general_account_id, account_id, date1, date2)) - res = self.cr.dictfetchall() - - line_obj = self.pool.get('account.analytic.line') - price_obj = self.pool.get('product.pricelist') - lines = {} - for l in line_obj.browse(self.cr, self.uid, [ x['id'] for x in res]): - lines[l.id] = l - if not account_id in self.sum_revenue: - self.sum_revenue[account_id] = 0.0 - if not general_account_id in self.account_sum_revenue: - self.account_sum_revenue[general_account_id] = 0.0 - for r in res: - id = r['id'] - revenue = 0.0 - if lines[id].amount < 0 and lines[id].product_id and lines[id].product_uom_id and lines[id].account_id.pricelist_id: - ctx = {'uom': lines[id].product_uom_id.id} - price = price_obj.price_get(self.cr, self.uid, [lines[id].pricelist_id.id], lines[id].product_id.id, lines[id].unit_amount, ctx)[lines[id].pricelist_id.id] - revenue = round(price * lines[id].unit_amount, 2) - r['revenue'] = revenue - self.sum_revenue[account_id] += revenue - self.account_sum_revenue[general_account_id] += revenue - if r['balance'] > 0: - r['debit'] = r['balance'] - r['credit'] = 0.0 - elif r['balance'] < 0: - r['debit'] = 0.0 - r['credit'] = -r['balance'] - else: - r['debit'] = 0.0 - r['credit'] = 0.0 - return res - - def _account_sum_debit(self, account_id, date1, date2): - self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount>0", (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _account_sum_credit(self, account_id, date1, date2): - self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount<0", (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _account_sum_balance(self, account_id, date1, date2): - debit = self._account_sum_debit(account_id, date1, date2) - credit = self._account_sum_credit(account_id, date1, date2) - return (debit-credit) - - def _account_sum_qty(self, account_id, date1, date2): - self.cr.execute("SELECT sum(unit_amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s", (account_id, date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _account_sum_revenue(self, account_id): - return self.sum_revenue.get(account_id, 0.0) - - def _account_g_sum_revenue(self, general_account_id): - return self.account_sum_revenue.get(general_account_id, 0.0) - - def _sum_debit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_credit(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_balance(self, accounts, date1, date2): - debit = self._sum_debit(accounts, date1, date2) or 0.0 - credit = self._sum_credit(accounts, date1, date2) or 0.0 - return (debit-credit) - - def _sum_qty(self, accounts, date1, date2): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - ids = map(lambda x: x.id, accounts) - self.cr.execute("SELECT sum(unit_amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s", (date1, date2)) - return self.cr.fetchone()[0] or 0.0 - - def _sum_revenue(self, accounts): - ids = map(lambda x: x.id, accounts) - if not len(ids): - return 0.0 - res = 0.0 - for id in ids: - res += self.sum_revenue.get(id, 0.0) - return res + def __init__(self, cr, uid, name, context): + super(account_analytic_cost_ledger, self).__init__(cr, uid, name, context) + self.sum_revenue={} + self.account_sum_revenue={} + self.localcontext.update( { + 'time': time, + 'lines_g': self._lines_g, + 'lines_a': self._lines_a, + 'account_sum_debit': self._account_sum_debit, + 'account_sum_credit': self._account_sum_credit, + 'account_sum_balance': self._account_sum_balance, + 'account_sum_qty': self._account_sum_qty, + 'account_sum_revenue': self._account_sum_revenue, + 'account_g_sum_revenue': self._account_g_sum_revenue, + 'sum_debit': self._sum_debit, + 'sum_credit': self._sum_credit, + 'sum_balance': self._sum_balance, + 'sum_qty': self._sum_qty, + 'sum_revenue': self._sum_revenue, + }) + + def _lines_g(self, account_id, date1, date2): + self.cr.execute("SELECT sum(aal.amount) AS balance, aa.code AS code, aa.name AS name, aa.id AS id, sum(aal.unit_amount) AS quantity \ + FROM account_account AS aa, account_analytic_line AS aal \ + WHERE (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) AND (aal.general_account_id=aa.id) AND aa.active \ + GROUP BY aa.code, aa.name, aa.id ORDER BY aa.code", (account_id, date1, date2)) + res = self.cr.dictfetchall() + + for r in res: + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res + + def _lines_a(self, general_account_id, account_id, date1, date2): + self.cr.execute("SELECT aal.id AS id, aal.name AS name, aal.code AS code, aal.amount AS balance, aal.date AS date, aaj.code AS cj, aal.unit_amount AS quantity \ + FROM account_analytic_line AS aal, account_analytic_journal AS aaj \ + WHERE (aal.general_account_id=%d) AND (aal.account_id=%d) AND (aal.date>=%s) AND (aal.date<=%s) \ + AND (aal.journal_id=aaj.id) \ + ORDER BY aal.date, aaj.code, aal.code", (general_account_id, account_id, date1, date2)) + res = self.cr.dictfetchall() + + line_obj = self.pool.get('account.analytic.line') + price_obj = self.pool.get('product.pricelist') + lines = {} + for l in line_obj.browse(self.cr, self.uid, [ x['id'] for x in res]): + lines[l.id] = l + if not account_id in self.sum_revenue: + self.sum_revenue[account_id] = 0.0 + if not general_account_id in self.account_sum_revenue: + self.account_sum_revenue[general_account_id] = 0.0 + for r in res: + id = r['id'] + revenue = 0.0 + if lines[id].amount < 0 and lines[id].product_id and lines[id].product_uom_id and lines[id].account_id.pricelist_id: + ctx = {'uom': lines[id].product_uom_id.id} + price = price_obj.price_get(self.cr, self.uid, [lines[id].pricelist_id.id], lines[id].product_id.id, lines[id].unit_amount, ctx)[lines[id].pricelist_id.id] + revenue = round(price * lines[id].unit_amount, 2) + r['revenue'] = revenue + self.sum_revenue[account_id] += revenue + self.account_sum_revenue[general_account_id] += revenue + if r['balance'] > 0: + r['debit'] = r['balance'] + r['credit'] = 0.0 + elif r['balance'] < 0: + r['debit'] = 0.0 + r['credit'] = -r['balance'] + else: + r['debit'] = 0.0 + r['credit'] = 0.0 + return res + + def _account_sum_debit(self, account_id, date1, date2): + self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount>0", (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _account_sum_credit(self, account_id, date1, date2): + self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s AND amount<0", (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _account_sum_balance(self, account_id, date1, date2): + debit = self._account_sum_debit(account_id, date1, date2) + credit = self._account_sum_credit(account_id, date1, date2) + return (debit-credit) + + def _account_sum_qty(self, account_id, date1, date2): + self.cr.execute("SELECT sum(unit_amount) FROM account_analytic_line WHERE account_id=%d AND date>=%s AND date<=%s", (account_id, date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _account_sum_revenue(self, account_id): + return self.sum_revenue.get(account_id, 0.0) + + def _account_g_sum_revenue(self, general_account_id): + return self.account_sum_revenue.get(general_account_id, 0.0) + + def _sum_debit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + self.cr.execute("SELECT sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount>0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT -sum(amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s AND amount<0", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_balance(self, accounts, date1, date2): + debit = self._sum_debit(accounts, date1, date2) or 0.0 + credit = self._sum_credit(accounts, date1, date2) or 0.0 + return (debit-credit) + + def _sum_qty(self, accounts, date1, date2): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + ids = map(lambda x: x.id, accounts) + self.cr.execute("SELECT sum(unit_amount) FROM account_analytic_line WHERE account_id IN ("+','.join(map(str, ids))+") AND date>=%s AND date<=%s", (date1, date2)) + return self.cr.fetchone()[0] or 0.0 + + def _sum_revenue(self, accounts): + ids = map(lambda x: x.id, accounts) + if not len(ids): + return 0.0 + res = 0.0 + for id in ids: + res += self.sum_revenue.get(id, 0.0) + return res report_sxw.report_sxw( - 'report.hr.timesheet.invoice.account.analytic.account.cost_ledger', - 'account.analytic.account', - 'addons/hr_timesheet_invoice/report/cost_ledger.rml', - parser=account_analytic_cost_ledger, header=False) + 'report.hr.timesheet.invoice.account.analytic.account.cost_ledger', + 'account.analytic.account', + 'addons/hr_timesheet_invoice/report/cost_ledger.rml', + parser=account_analytic_cost_ledger, header=False) diff --git a/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py b/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py index ad5265b06e6..4b6dea74b91 100644 --- a/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py +++ b/addons/hr_timesheet_invoice/wizard/account_analytic_profit.py @@ -32,53 +32,53 @@ import datetime form='''<?xml version="1.0"?> <form string="Choose"> - <field name="date_from"/> - <field name="date_to"/> - <field name="journal_ids" colspan="4"/> - <field name="employee_ids" colspan="4"/> + <field name="date_from"/> + <field name="date_to"/> + <field name="journal_ids" colspan="4"/> + <field name="employee_ids" colspan="4"/> </form>''' class wizard_report(wizard.interface): - def _date_from(*a): - return datetime.datetime.today().strftime('%Y-%m-1') - def _date_to(*a): - return datetime.datetime.today().strftime('%Y-%m-%d') + def _date_from(*a): + return datetime.datetime.today().strftime('%Y-%m-1') + def _date_to(*a): + return datetime.datetime.today().strftime('%Y-%m-%d') - fields={ - 'date_from':{ - 'string':'From', - 'type':'date', - 'required':True, - 'default':_date_from, - }, - 'date_to':{ - 'string':'To', - 'type':'date', - 'required':True, - 'default':_date_to, - }, - 'journal_ids':{ - 'string':'Journal', - 'type':'many2many', - 'relation':'account.analytic.journal', - 'required':True, - }, - 'employee_ids':{ - 'string':'Employee', - 'type':'many2many', - 'relation':'res.users', - 'required':True, - }, - } + fields={ + 'date_from':{ + 'string':'From', + 'type':'date', + 'required':True, + 'default':_date_from, + }, + 'date_to':{ + 'string':'To', + 'type':'date', + 'required':True, + 'default':_date_to, + }, + 'journal_ids':{ + 'string':'Journal', + 'type':'many2many', + 'relation':'account.analytic.journal', + 'required':True, + }, + 'employee_ids':{ + 'string':'Employee', + 'type':'many2many', + 'relation':'res.users', + 'required':True, + }, + } - states={ - 'init':{ - 'actions':[], - 'result':{'type':'form', 'arch':form, 'fields':fields, 'state':[('end', 'Cancel'), ('report', 'Print')]} - }, - 'report':{ - 'actions':[], - 'result':{'type':'print', 'report':'account.analytic.profit', 'state':'end'} - } - } + states={ + 'init':{ + 'actions':[], + 'result':{'type':'form', 'arch':form, 'fields':fields, 'state':[('end', 'Cancel'), ('report', 'Print')]} + }, + 'report':{ + 'actions':[], + 'result':{'type':'print', 'report':'account.analytic.profit', 'state':'end'} + } + } wizard_report('account.analytic.profit') diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py index 5bd6f541d30..798fcf801dc 100644 --- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py +++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_final_invoice_create.py @@ -40,228 +40,228 @@ from tools.translate import _ # TODO: check unit of measure !!! # class final_invoice_create(wizard.interface): - def _get_defaults(self, cr, uid, data, context): - if not data['ids']: - return {} - account = pooler.get_pool(cr.dbname).get('account.analytic.account').browse(cr, uid, data['ids'], context)[0] - return {'use_amount_max': bool(account.amount_max)} + def _get_defaults(self, cr, uid, data, context): + if not data['ids']: + return {} + account = pooler.get_pool(cr.dbname).get('account.analytic.account').browse(cr, uid, data['ids'], context)[0] + return {'use_amount_max': bool(account.amount_max)} - def _do_create(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - analytic_account_obj = pool.get('account.analytic.account') - res_partner_obj = pool.get('res.partner') - account_payment_term_obj = pool.get('account.payment.term') + def _do_create(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + analytic_account_obj = pool.get('account.analytic.account') + res_partner_obj = pool.get('res.partner') + account_payment_term_obj = pool.get('account.payment.term') - account_ids = data['ids'] - invoices = [] - for account in analytic_account_obj.browse(cr, uid, account_ids, context): - partner = account.partner_id - amount_total=0.0 - if (not partner) or not (account.pricelist_id): - raise wizard.except_wizard(_('Analytic account incomplete'), - _('Please fill in the partner and pricelist field ' - 'in the analytic account:\n%s') % (account.name,)) + account_ids = data['ids'] + invoices = [] + for account in analytic_account_obj.browse(cr, uid, account_ids, context): + partner = account.partner_id + amount_total=0.0 + if (not partner) or not (account.pricelist_id): + raise wizard.except_wizard(_('Analytic account incomplete'), + _('Please fill in the partner and pricelist field ' + 'in the analytic account:\n%s') % (account.name,)) - date_due = False - if partner.property_payment_term: - pterm_list= account_payment_term_obj.compute(cr, uid, - partner.property_payment_term.id, value=1, - date_ref=time.strftime('%Y-%m-%d')) - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - date_due = pterm_list[-1] + date_due = False + if partner.property_payment_term: + pterm_list= account_payment_term_obj.compute(cr, uid, + partner.property_payment_term.id, value=1, + date_ref=time.strftime('%Y-%m-%d')) + if pterm_list: + pterm_list = [line[0] for line in pterm_list] + pterm_list.sort() + date_due = pterm_list[-1] - curr_invoice = { - 'name': time.strftime('%D')+' - '+account.name, - 'partner_id': account.partner_id.id, - 'address_contact_id': pool.get('res.partner').address_get(cr, uid, [account.partner_id.id], adr_pref=['contact'])['contact'], - 'address_invoice_id': pool.get('res.partner').address_get(cr, uid, [account.partner_id.id], adr_pref=['invoice'])['invoice'], - 'payment_term': partner.property_payment_term.id or False, - 'account_id': partner.property_account_receivable.id, - 'currency_id': account.pricelist_id.currency_id.id, - 'date_due': date_due, - } - last_invoice = pool.get('account.invoice').create(cr, uid, curr_invoice) - invoices.append(last_invoice) + curr_invoice = { + 'name': time.strftime('%D')+' - '+account.name, + 'partner_id': account.partner_id.id, + 'address_contact_id': pool.get('res.partner').address_get(cr, uid, [account.partner_id.id], adr_pref=['contact'])['contact'], + 'address_invoice_id': pool.get('res.partner').address_get(cr, uid, [account.partner_id.id], adr_pref=['invoice'])['invoice'], + 'payment_term': partner.property_payment_term.id or False, + 'account_id': partner.property_account_receivable.id, + 'currency_id': account.pricelist_id.currency_id.id, + 'date_due': date_due, + } + last_invoice = pool.get('account.invoice').create(cr, uid, curr_invoice) + invoices.append(last_invoice) - context2=context.copy() - context2['lang'] = partner.lang - cr.execute("SELECT product_id, to_invoice, sum(unit_amount) " \ - "FROM account_analytic_line as line " \ - "WHERE account_id = %d " \ - "AND to_invoice IS NOT NULL " \ - "GROUP BY product_id, to_invoice", (account.id,)) - for product_id,factor_id,qty in cr.fetchall(): - product = pool.get('product.product').browse(cr, uid, product_id, context2) - factor_name = '' - factor = pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context2) - if factor.customer_name: - factor_name = product.name+' - '+factor.customer_name - else: - factor_name = product.name - if account.pricelist_id: - pl = account.pricelist_id.id - price = pool.get('product.pricelist').price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id)[pl] - else: - price = 0.0 + context2=context.copy() + context2['lang'] = partner.lang + cr.execute("SELECT product_id, to_invoice, sum(unit_amount) " \ + "FROM account_analytic_line as line " \ + "WHERE account_id = %d " \ + "AND to_invoice IS NOT NULL " \ + "GROUP BY product_id, to_invoice", (account.id,)) + for product_id,factor_id,qty in cr.fetchall(): + product = pool.get('product.product').browse(cr, uid, product_id, context2) + factor_name = '' + factor = pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context2) + if factor.customer_name: + factor_name = product.name+' - '+factor.customer_name + else: + factor_name = product.name + if account.pricelist_id: + pl = account.pricelist_id.id + price = pool.get('product.pricelist').price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id)[pl] + else: + price = 0.0 - taxes = product.taxes_id - taxep = account.partner_id.property_account_tax - if not taxep.id: - tax = [x.id for x in taxes or []] - else: - tax = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - tax.append(t.id) + taxes = product.taxes_id + taxep = account.partner_id.property_account_tax + if not taxep.id: + tax = [x.id for x in taxes or []] + else: + tax = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + tax.append(t.id) - account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id + account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id - curr_line = { - 'price_unit': price, - 'quantity': qty, - 'discount':factor.factor, - 'invoice_line_tax_id': [(6,0,tax )], - 'invoice_id': last_invoice, - 'name': factor_name, - 'product_id': product_id, - 'uos_id': product.uom_id.id, - 'account_id': account_id, - 'account_analytic_id': account.id, - } + curr_line = { + 'price_unit': price, + 'quantity': qty, + 'discount':factor.factor, + 'invoice_line_tax_id': [(6,0,tax )], + 'invoice_id': last_invoice, + 'name': factor_name, + 'product_id': product_id, + 'uos_id': product.uom_id.id, + 'account_id': account_id, + 'account_analytic_id': account.id, + } - amount_total += round((price * ( 1.0 - (factor.factor or 0.0)/100.0)), 2) * qty + amount_total += round((price * ( 1.0 - (factor.factor or 0.0)/100.0)), 2) * qty - # - # Compute for lines - # - cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %d AND product_id=%d and to_invoice=%d" % (account.id, product_id, factor_id)) - line_ids = cr.dictfetchall() - note = [] - for line in line_ids: - # set invoice_line_note - details = [] - if data['form']['date']: - details.append(line['date']) - if data['form']['time']: - details.append("%s %s" % (line['unit_amount'], pool.get('product.uom').browse(cr, uid, [line['product_uom_id']])[0].name)) - if data['form']['name']: - details.append(line['name']) - #if data['form']['price']: - # details.append(abs(line['amount'])) - note.append(' - '.join(map(str,details))) + # + # Compute for lines + # + cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %d AND product_id=%d and to_invoice=%d" % (account.id, product_id, factor_id)) + line_ids = cr.dictfetchall() + note = [] + for line in line_ids: + # set invoice_line_note + details = [] + if data['form']['date']: + details.append(line['date']) + if data['form']['time']: + details.append("%s %s" % (line['unit_amount'], pool.get('product.uom').browse(cr, uid, [line['product_uom_id']])[0].name)) + if data['form']['name']: + details.append(line['name']) + #if data['form']['price']: + # details.append(abs(line['amount'])) + note.append(' - '.join(map(str,details))) - curr_line['note'] = "\n".join(map(str,note)) - pool.get('account.invoice.line').create(cr, uid, curr_line) - cr.execute("update account_analytic_line set invoice_id=%d WHERE account_id = %d and invoice_id is null" % (last_invoice,account.id,)) + curr_line['note'] = "\n".join(map(str,note)) + pool.get('account.invoice.line').create(cr, uid, curr_line) + cr.execute("update account_analytic_line set invoice_id=%d WHERE account_id = %d and invoice_id is null" % (last_invoice,account.id,)) - cr.execute("SELECT line.product_id, sum(line.amount), line.account_id, line.product_uom_id, move_line.ref FROM account_analytic_line as line, account_move_line as move_line WHERE line.account_id = %d AND line.move_id IS NOT NULL AND move_line.id = line.move_id GROUP BY line.product_id, line.account_id, line.product_uom_id, move_line.ref" % (account.id)) - for product_id, amount, account_id, product_uom_id, ref in cr.fetchall(): - product = pool.get('product.product').browse(cr, uid, product_id, context2) + cr.execute("SELECT line.product_id, sum(line.amount), line.account_id, line.product_uom_id, move_line.ref FROM account_analytic_line as line, account_move_line as move_line WHERE line.account_id = %d AND line.move_id IS NOT NULL AND move_line.id = line.move_id GROUP BY line.product_id, line.account_id, line.product_uom_id, move_line.ref" % (account.id)) + for product_id, amount, account_id, product_uom_id, ref in cr.fetchall(): + product = pool.get('product.product').browse(cr, uid, product_id, context2) - if product: - taxes = product.taxes_id - else: - taxes = [] - taxep = account.partner_id.property_account_tax - if not taxep.id: - tax = [x.id for x in taxes or []] - else: - tax = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - tax.append(t.id) + if product: + taxes = product.taxes_id + else: + taxes = [] + taxep = account.partner_id.property_account_tax + if not taxep.id: + tax = [x.id for x in taxes or []] + else: + tax = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + tax.append(t.id) - curr_line = { - 'price_unit': -amount, - 'quantity': 1.0, - 'discount': 0.0, - 'invoice_line_tax_id': [(6,0,tax)], - 'invoice_id': last_invoice, - 'name': ref+(product and ' - '+product.name or ''), - 'product_id': product_id, - 'uos_id': product_uom_id, - 'account_id': account_id, - } - pool.get('account.invoice.line').create(cr, uid, curr_line) + curr_line = { + 'price_unit': -amount, + 'quantity': 1.0, + 'discount': 0.0, + 'invoice_line_tax_id': [(6,0,tax)], + 'invoice_id': last_invoice, + 'name': ref+(product and ' - '+product.name or ''), + 'product_id': product_id, + 'uos_id': product_uom_id, + 'account_id': account_id, + } + pool.get('account.invoice.line').create(cr, uid, curr_line) - if data['form']['use_amount_max']: - if abs(account.amount_max - amount_total) > data['form']['balance_amount'] : - if not data['form']['balance_product']: - raise wizard.except_wizard(_('Balance product needed'), _('Please fill a Balance product in the wizard')) - product = pool.get('product.product').browse(cr, uid, data['form']['balance_product'], context2) + if data['form']['use_amount_max']: + if abs(account.amount_max - amount_total) > data['form']['balance_amount'] : + if not data['form']['balance_product']: + raise wizard.except_wizard(_('Balance product needed'), _('Please fill a Balance product in the wizard')) + product = pool.get('product.product').browse(cr, uid, data['form']['balance_product'], context2) - taxes = product.taxes_id - taxep = account.partner_id.property_account_tax - if not taxep.id: - tax = [x.id for x in taxes or []] - else: - tax = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - tax.append(t.id) - - account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id + taxes = product.taxes_id + taxep = account.partner_id.property_account_tax + if not taxep.id: + tax = [x.id for x in taxes or []] + else: + tax = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + tax.append(t.id) + + account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id - curr_line = { - 'price_unit': account.amount_max - amount_total, - 'quantity': 1.0, - 'discount': 0.0, - 'invoice_line_tax_id': [(6,0,tax)], - 'invoice_id': last_invoice, - 'name': product.name, - 'product_id': product_id, - 'uos_id': product.uom_id.id, - 'account_id': account_id, - } - pool.get('account.invoice.line').create(cr, uid, curr_line) - if account.amount_max < amount_total: - pool.get('account.invoice').write(cr, uid, [last_invoice], {'type': 'out_refund',}) + curr_line = { + 'price_unit': account.amount_max - amount_total, + 'quantity': 1.0, + 'discount': 0.0, + 'invoice_line_tax_id': [(6,0,tax)], + 'invoice_id': last_invoice, + 'name': product.name, + 'product_id': product_id, + 'uos_id': product.uom_id.id, + 'account_id': account_id, + } + pool.get('account.invoice.line').create(cr, uid, curr_line) + if account.amount_max < amount_total: + pool.get('account.invoice').write(cr, uid, [last_invoice], {'type': 'out_refund',}) - return { - 'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]", - 'name': 'Invoices', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.invoice', - 'view_id': False, - 'context': "{'type':'out_invoice'}", - 'type': 'ir.actions.act_window' - } + return { + 'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]", + 'name': 'Invoices', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.invoice', + 'view_id': False, + 'context': "{'type':'out_invoice'}", + 'type': 'ir.actions.act_window' + } - _create_form = """<?xml version="1.0"?> - <form title="Final invoice for analytic account"> - <separator string="Do you want details for each line of the invoices ?" colspan="4"/> - <field name="date"/> - <field name="time"/> - <field name="name"/> - <field name="price"/> - <separator string="Invoice Balance amount" colspan="4"/> - <field name="use_amount_max"/> - <field name="balance_amount"/> - <field name="balance_product"/> - </form>""" + _create_form = """<?xml version="1.0"?> + <form title="Final invoice for analytic account"> + <separator string="Do you want details for each line of the invoices ?" colspan="4"/> + <field name="date"/> + <field name="time"/> + <field name="name"/> + <field name="price"/> + <separator string="Invoice Balance amount" colspan="4"/> + <field name="use_amount_max"/> + <field name="balance_amount"/> + <field name="balance_product"/> + </form>""" - _create_fields = { - 'date': {'string':'Date', 'type':'boolean'}, - 'time': {'string':'Time spent', 'type':'boolean'}, - 'name': {'string':'Name of entry', 'type':'boolean'}, - 'price': {'string':'Cost', 'type':'boolean'}, - 'use_amount_max': {'string':'Use Max. Invoice Price', 'type':'boolean'}, - 'balance_amount': {'string':'Balance amount', 'type': 'float'}, - 'balance_product': {'string':'Balance product', 'type': 'many2one', 'relation':'product.product'}, - } + _create_fields = { + 'date': {'string':'Date', 'type':'boolean'}, + 'time': {'string':'Time spent', 'type':'boolean'}, + 'name': {'string':'Name of entry', 'type':'boolean'}, + 'price': {'string':'Cost', 'type':'boolean'}, + 'use_amount_max': {'string':'Use Max. Invoice Price', 'type':'boolean'}, + 'balance_amount': {'string':'Balance amount', 'type': 'float'}, + 'balance_product': {'string':'Balance product', 'type': 'many2one', 'relation':'product.product'}, + } - states = { - 'init' : { - 'actions' : [_get_defaults], - 'result' : {'type':'form', 'arch':_create_form, 'fields':_create_fields, 'state': [('end','Cancel'),('create','Create invoices')]}, - }, - 'create' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_do_create, 'state':'end'}, - }, - } + states = { + 'init' : { + 'actions' : [_get_defaults], + 'result' : {'type':'form', 'arch':_create_form, 'fields':_create_fields, 'state': [('end','Cancel'),('create','Create invoices')]}, + }, + 'create' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_do_create, 'state':'end'}, + }, + } final_invoice_create('hr.timesheet.final.invoice.create') diff --git a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py index 3130bd53f5f..a32fedb4bf4 100644 --- a/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py +++ b/addons/hr_timesheet_invoice/wizard/hr_timesheet_invoice_create.py @@ -40,168 +40,168 @@ from tools.translate import _ # TODO: check unit of measure !!! # class invoice_create(wizard.interface): - def _get_accounts(self, cr, uid, data, context): - if not len(data['ids']): - return {} - cr.execute("SELECT distinct(account_id) from account_analytic_line where id IN (%s)"% (','.join(map(str,data['ids'])),)) - account_ids = cr.fetchall() - return {'accounts': [x[0] for x in account_ids]} - - def _do_create(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - analytic_account_obj = pool.get('account.analytic.account') - res_partner_obj = pool.get('res.partner') - account_payment_term_obj = pool.get('account.payment.term') - - account_ids = data['form']['accounts'][0][2] - invoices = [] - for account in analytic_account_obj.browse(cr, uid, account_ids, context): - partner = account.partner_id - if (not partner) or not (account.pricelist_id): - raise wizard.except_wizard(_('Analytic account incomplete'), - _('Please fill in the partner and pricelist field ' - 'in the analytic account:\n%s') % (account.name,)) - - date_due = False - if partner.property_payment_term: - pterm_list= account_payment_term_obj.compute(cr, uid, - partner.property_payment_term.id, value=1, - date_ref=time.strftime('%Y-%m-%d')) - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - date_due = pterm_list[-1] - - curr_invoice = { - 'name': time.strftime('%D')+' - '+account.name, - 'partner_id': account.partner_id.id, - 'address_contact_id': res_partner_obj.address_get(cr, uid, - [account.partner_id.id], adr_pref=['contact'])['contact'], - 'address_invoice_id': res_partner_obj.address_get(cr, uid, - [account.partner_id.id], adr_pref=['invoice'])['invoice'], - 'payment_term': partner.property_payment_term.id or False, - 'account_id': partner.property_account_receivable.id, - 'currency_id': account.pricelist_id.currency_id.id, - 'date_due': date_due, - } - last_invoice = pool.get('account.invoice').create(cr, uid, curr_invoice) - invoices.append(last_invoice) - - context2=context.copy() - context2['lang'] = partner.lang - cr.execute("SELECT product_id, to_invoice, sum(unit_amount) " \ - "FROM account_analytic_line as line " \ - "WHERE account_id = %d " \ - "AND id IN (" + ','.join([str(x) for x in data['ids']]) + ") " \ - "AND to_invoice IS NOT NULL " \ - "GROUP BY product_id,to_invoice", (account.id,)) - for product_id,factor_id,qty in cr.fetchall(): - product = pool.get('product.product').browse(cr, uid, product_id, context2) - if not product: - raise wizard.except_wizard(_('Error'), _('At least on line have no product !')) - factor_name = '' - factor = pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context2) - if factor.customer_name: - factor_name = product.name+' - '+factor.customer_name - else: - factor_name = product.name - if account.pricelist_id: - pl = account.pricelist_id.id - price = pool.get('product.pricelist').price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id)[pl] - else: - price = 0.0 - - taxes = product.taxes_id - taxep = account.partner_id.property_account_tax - if not taxep.id: - tax = [x.id for x in taxes or []] - else: - tax = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - tax.append(t.id) - - account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id - - curr_line = { - 'price_unit': price, - 'quantity': qty, - 'discount':factor.factor, - 'invoice_line_tax_id': [(6,0,tax )], - 'invoice_id': last_invoice, - 'name': factor_name, - 'product_id': data['form']['product'] or product_id, - 'invoice_line_tax_id': [(6,0,tax)], - 'uos_id': product.uom_id.id, - 'account_id': account_id, - 'account_analytic_id': account.id, - } - - # - # Compute for lines - # - cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %d and id IN (%s) AND product_id=%d and to_invoice=%d" % (account.id, ','.join(map(str,data['ids'])), product_id, factor_id)) - line_ids = cr.dictfetchall() - note = [] - for line in line_ids: - # set invoice_line_note - details = [] - if data['form']['date']: - details.append(line['date']) - if data['form']['time']: - details.append("%s %s" % (line['unit_amount'], pool.get('product.uom').browse(cr, uid, [line['product_uom_id']])[0].name)) - if data['form']['name']: - details.append(line['name']) - #if data['form']['price']: - # details.append(abs(line['amount'])) - note.append(' - '.join(map(str,details))) - - curr_line['note'] = "\n".join(map(str,note)) - pool.get('account.invoice.line').create(cr, uid, curr_line) - cr.execute("update account_analytic_line set invoice_id=%d WHERE account_id = %d and id IN (%s)" % (last_invoice,account.id, ','.join(map(str,data['ids'])))) - - return { - 'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]", - 'name': 'Invoices', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.invoice', - 'view_id': False, - 'context': "{'type':'out_invoice'}", - 'type': 'ir.actions.act_window' - } - - - _create_form = """<?xml version="1.0"?> - <form title="Invoice on analytic entries"> - <separator string="Do you want details for each line of the invoices ?" colspan="4"/> - <field name="date"/> - <field name="time"/> - <field name="name"/> - <field name="price"/> - <separator string="Choose accounts you want to invoice" colspan="4"/> - <field name="accounts" colspan="4"/> - <separator string="Choose a product for intermediary invoice" colspan="4"/> - <field name="product"/> - </form>""" - - _create_fields = { - 'accounts': {'string':'Analytic Accounts', 'type':'many2many', 'required':'true', 'relation':'account.analytic.account'}, - 'date': {'string':'Date', 'type':'boolean'}, - 'time': {'string':'Time spent', 'type':'boolean'}, - 'name': {'string':'Name of entry', 'type':'boolean'}, - 'price': {'string':'Cost', 'type':'boolean'}, - 'product': {'string':'Product', 'type':'many2one', 'relation': 'product.product'}, - } - - states = { - 'init' : { - 'actions' : [_get_accounts], - 'result' : {'type':'form', 'arch':_create_form, 'fields':_create_fields, 'state': [('end','Cancel'),('create','Create invoices')]}, - }, - 'create' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_do_create, 'state':'end'}, - }, - } + def _get_accounts(self, cr, uid, data, context): + if not len(data['ids']): + return {} + cr.execute("SELECT distinct(account_id) from account_analytic_line where id IN (%s)"% (','.join(map(str,data['ids'])),)) + account_ids = cr.fetchall() + return {'accounts': [x[0] for x in account_ids]} + + def _do_create(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + analytic_account_obj = pool.get('account.analytic.account') + res_partner_obj = pool.get('res.partner') + account_payment_term_obj = pool.get('account.payment.term') + + account_ids = data['form']['accounts'][0][2] + invoices = [] + for account in analytic_account_obj.browse(cr, uid, account_ids, context): + partner = account.partner_id + if (not partner) or not (account.pricelist_id): + raise wizard.except_wizard(_('Analytic account incomplete'), + _('Please fill in the partner and pricelist field ' + 'in the analytic account:\n%s') % (account.name,)) + + date_due = False + if partner.property_payment_term: + pterm_list= account_payment_term_obj.compute(cr, uid, + partner.property_payment_term.id, value=1, + date_ref=time.strftime('%Y-%m-%d')) + if pterm_list: + pterm_list = [line[0] for line in pterm_list] + pterm_list.sort() + date_due = pterm_list[-1] + + curr_invoice = { + 'name': time.strftime('%D')+' - '+account.name, + 'partner_id': account.partner_id.id, + 'address_contact_id': res_partner_obj.address_get(cr, uid, + [account.partner_id.id], adr_pref=['contact'])['contact'], + 'address_invoice_id': res_partner_obj.address_get(cr, uid, + [account.partner_id.id], adr_pref=['invoice'])['invoice'], + 'payment_term': partner.property_payment_term.id or False, + 'account_id': partner.property_account_receivable.id, + 'currency_id': account.pricelist_id.currency_id.id, + 'date_due': date_due, + } + last_invoice = pool.get('account.invoice').create(cr, uid, curr_invoice) + invoices.append(last_invoice) + + context2=context.copy() + context2['lang'] = partner.lang + cr.execute("SELECT product_id, to_invoice, sum(unit_amount) " \ + "FROM account_analytic_line as line " \ + "WHERE account_id = %d " \ + "AND id IN (" + ','.join([str(x) for x in data['ids']]) + ") " \ + "AND to_invoice IS NOT NULL " \ + "GROUP BY product_id,to_invoice", (account.id,)) + for product_id,factor_id,qty in cr.fetchall(): + product = pool.get('product.product').browse(cr, uid, product_id, context2) + if not product: + raise wizard.except_wizard(_('Error'), _('At least on line have no product !')) + factor_name = '' + factor = pool.get('hr_timesheet_invoice.factor').browse(cr, uid, factor_id, context2) + if factor.customer_name: + factor_name = product.name+' - '+factor.customer_name + else: + factor_name = product.name + if account.pricelist_id: + pl = account.pricelist_id.id + price = pool.get('product.pricelist').price_get(cr,uid,[pl], product_id, qty or 1.0, account.partner_id.id)[pl] + else: + price = 0.0 + + taxes = product.taxes_id + taxep = account.partner_id.property_account_tax + if not taxep.id: + tax = [x.id for x in taxes or []] + else: + tax = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + tax.append(t.id) + + account_id = product.product_tmpl_id.property_account_income.id or product.categ_id.property_account_income_categ.id + + curr_line = { + 'price_unit': price, + 'quantity': qty, + 'discount':factor.factor, + 'invoice_line_tax_id': [(6,0,tax )], + 'invoice_id': last_invoice, + 'name': factor_name, + 'product_id': data['form']['product'] or product_id, + 'invoice_line_tax_id': [(6,0,tax)], + 'uos_id': product.uom_id.id, + 'account_id': account_id, + 'account_analytic_id': account.id, + } + + # + # Compute for lines + # + cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %d and id IN (%s) AND product_id=%d and to_invoice=%d" % (account.id, ','.join(map(str,data['ids'])), product_id, factor_id)) + line_ids = cr.dictfetchall() + note = [] + for line in line_ids: + # set invoice_line_note + details = [] + if data['form']['date']: + details.append(line['date']) + if data['form']['time']: + details.append("%s %s" % (line['unit_amount'], pool.get('product.uom').browse(cr, uid, [line['product_uom_id']])[0].name)) + if data['form']['name']: + details.append(line['name']) + #if data['form']['price']: + # details.append(abs(line['amount'])) + note.append(' - '.join(map(str,details))) + + curr_line['note'] = "\n".join(map(str,note)) + pool.get('account.invoice.line').create(cr, uid, curr_line) + cr.execute("update account_analytic_line set invoice_id=%d WHERE account_id = %d and id IN (%s)" % (last_invoice,account.id, ','.join(map(str,data['ids'])))) + + return { + 'domain': "[('id','in', ["+','.join(map(str,invoices))+"])]", + 'name': 'Invoices', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.invoice', + 'view_id': False, + 'context': "{'type':'out_invoice'}", + 'type': 'ir.actions.act_window' + } + + + _create_form = """<?xml version="1.0"?> + <form title="Invoice on analytic entries"> + <separator string="Do you want details for each line of the invoices ?" colspan="4"/> + <field name="date"/> + <field name="time"/> + <field name="name"/> + <field name="price"/> + <separator string="Choose accounts you want to invoice" colspan="4"/> + <field name="accounts" colspan="4"/> + <separator string="Choose a product for intermediary invoice" colspan="4"/> + <field name="product"/> + </form>""" + + _create_fields = { + 'accounts': {'string':'Analytic Accounts', 'type':'many2many', 'required':'true', 'relation':'account.analytic.account'}, + 'date': {'string':'Date', 'type':'boolean'}, + 'time': {'string':'Time spent', 'type':'boolean'}, + 'name': {'string':'Name of entry', 'type':'boolean'}, + 'price': {'string':'Cost', 'type':'boolean'}, + 'product': {'string':'Product', 'type':'many2one', 'relation': 'product.product'}, + } + + states = { + 'init' : { + 'actions' : [_get_accounts], + 'result' : {'type':'form', 'arch':_create_form, 'fields':_create_fields, 'state': [('end','Cancel'),('create','Create invoices')]}, + }, + 'create' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_do_create, 'state':'end'}, + }, + } invoice_create('hr.timesheet.invoice.create') diff --git a/addons/hr_timesheet_invoice/wizard/wizard_account_analytic_cost_ledger_report.py b/addons/hr_timesheet_invoice/wizard/wizard_account_analytic_cost_ledger_report.py index 6954d50e1e2..866c44326a7 100644 --- a/addons/hr_timesheet_invoice/wizard/wizard_account_analytic_cost_ledger_report.py +++ b/addons/hr_timesheet_invoice/wizard/wizard_account_analytic_cost_ledger_report.py @@ -32,26 +32,26 @@ import wizard dates_form = '''<?xml version="1.0"?> <form string="Select period"> - <field name="date1"/> - <field name="date2"/> + <field name="date1"/> + <field name="date2"/> </form>''' dates_fields = { - 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, - 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, + 'date1': {'string':'Start of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-01-01')}, + 'date2': {'string':'End of period', 'type':'date', 'required':True, 'default': lambda *a: time.strftime('%Y-%m-%d')}, } class wizard_report(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'hr.timesheet.invoice.account.analytic.account.cost_ledger', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':dates_form, 'fields':dates_fields, 'state':[('end','Cancel'),('report','Print')]} + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'hr.timesheet.invoice.account.analytic.account.cost_ledger', 'state':'end'} + } + } wizard_report('hr.timesheet.invoice.account.analytic.account.cost_ledger.report') diff --git a/addons/hr_timesheet_project/__terp__.py b/addons/hr_timesheet_project/__terp__.py index 6fc8fe6be17..c00e2f48675 100644 --- a/addons/hr_timesheet_project/__terp__.py +++ b/addons/hr_timesheet_project/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Human Resources (Timesheet encoding from project tasks)", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "description": """Auto-complete timesheet based on tasks made on the project management module.""", - "website" : "http://tinyerp.com/module_hr.html", - "depends" : ["project", "hr_timesheet"], - "update_xml" : ["hr_timesheet_project_view.xml"], - "active": False, - "installable": True + "name" : "Human Resources (Timesheet encoding from project tasks)", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "description": """Auto-complete timesheet based on tasks made on the project management module.""", + "website" : "http://tinyerp.com/module_hr.html", + "depends" : ["project", "hr_timesheet"], + "update_xml" : ["hr_timesheet_project_view.xml"], + "active": False, + "installable": True } diff --git a/addons/hr_timesheet_project/wizard/timesheet_hour_encode.py b/addons/hr_timesheet_project/wizard/timesheet_hour_encode.py index 422e34f4db1..8900832f167 100644 --- a/addons/hr_timesheet_project/wizard/timesheet_hour_encode.py +++ b/addons/hr_timesheet_project/wizard/timesheet_hour_encode.py @@ -36,65 +36,65 @@ import pooler from osv import osv def _action_line_create(self, cr, uid, data, context): - tw = pooler.get_pool(cr.dbname).get('project.task.work') - ids = tw.search(cr, uid, [('user_id','=',uid), ('date','>=',time.strftime('%Y-%m-%d 00:00:00')), ('date','<=',time.strftime('%Y-%m-%d 23:59:59'))]) - ts = pooler.get_pool(cr.dbname).get('hr.analytic.timesheet') + tw = pooler.get_pool(cr.dbname).get('project.task.work') + ids = tw.search(cr, uid, [('user_id','=',uid), ('date','>=',time.strftime('%Y-%m-%d 00:00:00')), ('date','<=',time.strftime('%Y-%m-%d 23:59:59'))]) + ts = pooler.get_pool(cr.dbname).get('hr.analytic.timesheet') - for work in tw.browse(cr, uid, ids, context): - if not work.task_id.project_id: - continue - proj = work.task_id.project_id - aa = proj.category_id - while proj and not aa: - proj = proj.parent_id - aa = proj.category_id - if aa: - unit_id = ts._getEmployeeUnit(cr, uid, context) - product_id = ts._getEmployeeProduct(cr, uid, context) - res = { - 'name': work.name or work.task_id.name, - 'date': time.strftime('%Y-%m-%d'), - 'unit_amount': work.hours, - 'product_uom_id': unit_id, - 'product_id': product_id, - 'amount': work.hours or 0.0, - 'account_id': aa.id - } - res2 = ts.on_change_unit_amount(cr, uid, False, product_id, work.hours or 0.0,unit_id, context) - if res2: - res.update(res2['value']) - if hasattr(ts, 'on_change_account_id'): - res2 = ts.on_change_account_id(cr, uid, False, aa.id) - if res2: - res.update(res2['value']) - id = ts.create(cr, uid, res, context) + for work in tw.browse(cr, uid, ids, context): + if not work.task_id.project_id: + continue + proj = work.task_id.project_id + aa = proj.category_id + while proj and not aa: + proj = proj.parent_id + aa = proj.category_id + if aa: + unit_id = ts._getEmployeeUnit(cr, uid, context) + product_id = ts._getEmployeeProduct(cr, uid, context) + res = { + 'name': work.name or work.task_id.name, + 'date': time.strftime('%Y-%m-%d'), + 'unit_amount': work.hours, + 'product_uom_id': unit_id, + 'product_id': product_id, + 'amount': work.hours or 0.0, + 'account_id': aa.id + } + res2 = ts.on_change_unit_amount(cr, uid, False, product_id, work.hours or 0.0,unit_id, context) + if res2: + res.update(res2['value']) + if hasattr(ts, 'on_change_account_id'): + res2 = ts.on_change_account_id(cr, uid, False, aa.id) + if res2: + res.update(res2['value']) + id = ts.create(cr, uid, res, context) - value = { - 'domain': "[('user_id','=',%d),('date','>=','%s'), ('date','<=','%s')]" % (uid, time.strftime('%Y-%m-%d 00:00:00'), time.strftime('%Y-%m-%d 23:59:59')), - 'name': 'Create Analytic Line', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr.analytic.timesheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - return value + value = { + 'domain': "[('user_id','=',%d),('date','>=','%s'), ('date','<=','%s')]" % (uid, time.strftime('%Y-%m-%d 00:00:00'), time.strftime('%Y-%m-%d 23:59:59')), + 'name': 'Create Analytic Line', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'hr.analytic.timesheet', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + return value class wiz_hr_timesheet_project(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_line_create, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_line_create, 'state':'end'} + } + } wiz_hr_timesheet_project('hr_timesheet_project.encode.hour') class wiz_hr_timesheet_project_noopen(wizard.interface): - states = { - 'init': { - 'actions': [_action_line_create], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_action_line_create], + 'result': {'type': 'state', 'state':'end'} + } + } wiz_hr_timesheet_project_noopen('hr_timesheet_project.encode.hour.noopen') diff --git a/addons/hr_timesheet_sheet/__terp__.py b/addons/hr_timesheet_sheet/__terp__.py index f7ae02e254c..747d7064b90 100644 --- a/addons/hr_timesheet_sheet/__terp__.py +++ b/addons/hr_timesheet_sheet/__terp__.py @@ -26,12 +26,12 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Timesheets", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Human Resources", - "website" : "http://tinyerp.com/module_hr.html", - "description": """ + "name" : "Timesheets", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Human Resources", + "website" : "http://tinyerp.com/module_hr.html", + "description": """ This module help you easily encode and validate timesheet and attendances within the same view. The upper part of the view is for attendances and track (sign in/sign out) events. The lower part is for timesheet. @@ -49,11 +49,11 @@ This module also implement a complete timesheet validation process: The validation can be configured in te company: * Period size (day, week, month, year) * Maximal difference between timesheet and attendances - """, - "depends" : ["hr_timesheet", "hr_timesheet_invoice"], - "init_xml" : [], - "demo_xml" : ["hr_timesheet_sheet_demo.xml",], - "update_xml" : ["hr_timesheet_sheet_view.xml", "hr_timesheet_workflow.xml"], - "active": False, - "installable": True + """, + "depends" : ["hr_timesheet", "hr_timesheet_invoice"], + "init_xml" : [], + "demo_xml" : ["hr_timesheet_sheet_demo.xml",], + "update_xml" : ["hr_timesheet_sheet_view.xml", "hr_timesheet_workflow.xml"], + "active": False, + "installable": True } diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index d4a0fccd949..69c8d13ae82 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -37,636 +37,636 @@ from tools.translate import _ class one2many_mod2(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): - res = {} - for id in ids: - res[id] = [] - - res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context) - res6 = {} - for r in res5: - res6[r['id']] = (r['date_current'], r['user_id'][0]) - - ids2 = [] - for id in ids: - dom = [] - if id in res6: - dom = [('name', '>=', res6[id][0] + ' 00:00:00'), - ('name', '<=', res6[id][0] + ' 23:59:59'), - ('employee_id.user_id', '=', res6[id][1])] - ids2.extend(obj.pool.get(self._obj).search(cr, user, - dom, limit=self._limit)) - - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_write'): - if r[self._fields_id]: - res.setdefault(r[self._fields_id][0], []).append(r['id']) - - return res - - def set(self, cr, obj, id, field, values, user=None, context=None): - if context is None: - context = {} - context = context.copy() - context['sheet_id'] = id - return super(one2many_mod2, self).set(cr, obj, id, field, values, user=user, - context=context) + def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): + res = {} + for id in ids: + res[id] = [] + + res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context) + res6 = {} + for r in res5: + res6[r['id']] = (r['date_current'], r['user_id'][0]) + + ids2 = [] + for id in ids: + dom = [] + if id in res6: + dom = [('name', '>=', res6[id][0] + ' 00:00:00'), + ('name', '<=', res6[id][0] + ' 23:59:59'), + ('employee_id.user_id', '=', res6[id][1])] + ids2.extend(obj.pool.get(self._obj).search(cr, user, + dom, limit=self._limit)) + + for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, + [self._fields_id], context=context, load='_classic_write'): + if r[self._fields_id]: + res.setdefault(r[self._fields_id][0], []).append(r['id']) + + return res + + def set(self, cr, obj, id, field, values, user=None, context=None): + if context is None: + context = {} + context = context.copy() + context['sheet_id'] = id + return super(one2many_mod2, self).set(cr, obj, id, field, values, user=user, + context=context) class one2many_mod(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): - res = {} - for id in ids: - res[id] = [] - - res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context) - res6 = {} - for r in res5: - res6[r['id']] = (r['date_current'], r['user_id'][0]) - - ids2 = [] - for id in ids: - dom = [] - if id in res6: - dom = [('date', '=', res6[id][0]), ('user_id', '=', res6[id][1])] - ids2.extend(obj.pool.get(self._obj).search(cr, user, - dom, limit=self._limit)) - - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_write'): - if r[self._fields_id]: - res.setdefault(r[self._fields_id][0], []).append(r['id']) - - return res + def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): + res = {} + for id in ids: + res[id] = [] + + res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context) + res6 = {} + for r in res5: + res6[r['id']] = (r['date_current'], r['user_id'][0]) + + ids2 = [] + for id in ids: + dom = [] + if id in res6: + dom = [('date', '=', res6[id][0]), ('user_id', '=', res6[id][1])] + ids2.extend(obj.pool.get(self._obj).search(cr, user, + dom, limit=self._limit)) + + for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, + [self._fields_id], context=context, load='_classic_write'): + if r[self._fields_id]: + res.setdefault(r[self._fields_id][0], []).append(r['id']) + + return res class hr_timesheet_sheet(osv.osv): - _name = "hr_timesheet_sheet.sheet" - _table = 'hr_timesheet_sheet_sheet' - _order = "id desc" - - def _total_day(self, cr, uid, ids, name, args, context): - field_name = name.strip('_day') - cr.execute('SELECT sheet.id, day.' + field_name +' \ - FROM hr_timesheet_sheet_sheet AS sheet \ - LEFT JOIN hr_timesheet_sheet_sheet_day AS day \ - ON (sheet.id = day.sheet_id \ - AND day.name = sheet.date_current) \ - WHERE sheet.id in (' + ','.join([str(x) for x in ids]) + ')') - return dict(cr.fetchall()) - - def _total(self, cr, uid, ids, name, args, context): - cr.execute('SELECT s.id, COALESCE(SUM(d.' + name + '),0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN hr_timesheet_sheet_sheet_day d \ - ON (s.id = d.sheet_id) \ - WHERE s.id in ('+ ','.join(map(str, ids)) + ') \ - GROUP BY s.id') - return dict(cr.fetchall()) - - def _state_attendance(self, cr, uid, ids, name, args, context): - result = {} - link_emp = {} - emp_ids = [] - emp_obj = self.pool.get('hr.employee') - - for sheet in self.browse(cr, uid, ids, context): - result[sheet.id] = 'none' - emp_ids2 = emp_obj.search(cr, uid, - [('user_id', '=', sheet.user_id.id)]) - if emp_ids2: - link_emp[emp_ids2[0]] = sheet.id - emp_ids.append(emp_ids2[0]) - for emp in emp_obj.browse(cr, uid, emp_ids, context=context): - if emp.id in link_emp: - sheet_id = link_emp[emp.id] - result[sheet_id] = emp.state - return result - - def button_confirm(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - di = sheet.user_id.company_id.timesheet_max_difference - if (abs(sheet.total_difference) < di) or not di: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'hr_timesheet_sheet.sheet', sheet.id, 'confirm', cr) - else: - raise osv.except_osv(_('Warning !'), _('Please verify that the total difference of the sheet is lower than %.2f !') %(di,)) - return True - - def date_today(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.now() <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) - elif DateTime.now() >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) - else: - self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}) - return True - - def date_previous(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) - else: - self.write(cr, uid, [sheet.id], { - 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=-1)).strftime('%Y-%m-%d'), - }) - return True - - def date_next(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) - else: - self.write(cr, uid, [sheet.id], { - 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d'), - }) - return True - - def button_dummy(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) - elif DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) - return True - - def sign_in(self, cr, uid, ids, context): - if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): - raise osv.except_osv(_('Error !'), _('You can not sign in from an other date than today')) - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) - context['sheet_id']=ids[0] - success = emp_obj.sign_in(cr, uid, emp_id, context=context) - return True - - def sign_out(self, cr, uid, ids, context): - if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): - raise osv.except_osv(_('Error !'), _('You can not sign out from an other date than today')) - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) - context['sheet_id']=ids[0] - success = emp_obj.sign_out(cr, uid, emp_id, context=context) - return True - - _columns = { - 'name': fields.char('Description', size=64, select=1), - 'user_id': fields.many2one('res.users', 'User', required=True, select=1), - 'date_from': fields.date('Date from', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), - 'date_to': fields.date('Date to', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), - 'date_current': fields.date('Current date', required=True), - 'timesheet_ids' : one2many_mod('hr.analytic.timesheet', 'sheet_id', - 'Timesheet lines', domain=[('date', '=', time.strftime('%Y-%m-%d'))], - readonly=True, states={ - 'draft': [('readonly', False)], - 'new': [('readonly', False)]} - ), - 'attendances_ids' : one2many_mod2('hr.attendance', 'sheet_id', 'Attendances', readonly=True, states={'draft':[('readonly',False)],'new':[('readonly',False)]}), - 'state' : fields.selection([('new', 'New'),('draft','Draft'),('confirm','Confirmed'),('done','Done')], 'State', select=True, required=True, readonly=True), - 'state_attendance' : fields.function(_state_attendance, method=True, type='selection', selection=[('absent', 'Absent'), ('present', 'Present'),('none','No employee defined')], string='Current state'), - 'total_attendance_day': fields.function(_total_day, method=True, string='Total Attendance'), - 'total_timesheet_day': fields.function(_total_day, method=True, string='Total Timesheet'), - 'total_difference_day': fields.function(_total_day, method=True, string='Difference'), - 'total_attendance': fields.function(_total, method=True, string='Total Attendance'), - 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet'), - 'total_difference': fields.function(_total, method=True, string='Difference'), - 'period_ids': fields.one2many('hr_timesheet_sheet.sheet.day', 'sheet_id', 'Period', readonly=True), - 'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True), - } - - def _default_date_from(self,cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context) - r = user.company_id and user.company_id.timesheet_range or 'month' - if r=='month': - return time.strftime('%Y-%m-01') - elif r=='week': - return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Monday,0))).strftime('%Y-%m-%d') - elif r=='year': - return time.strftime('%Y-01-01') - return time.strftime('%Y-%m-%d') - - def _default_date_to(self,cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context) - r = user.company_id and user.company_id.timesheet_range or 'month' - if r=='month': - return (DateTime.now() + DateTime.RelativeDateTime(months=+1,day=1,days=-1)).strftime('%Y-%m-%d') - elif r=='week': - return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Sunday,0))).strftime('%Y-%m-%d') - elif r=='year': - return time.strftime('%Y-12-31') - return time.strftime('%Y-%m-%d') - - _defaults = { - 'user_id': lambda self,cr,uid,c: uid, - 'date_from' : _default_date_from, - 'date_current' : lambda *a: time.strftime('%Y-%m-%d'), - 'date_to' : _default_date_to, - 'state': lambda *a: 'new', - } - - def _sheet_date(self, cr, uid, ids): - for sheet in self.browse(cr, uid, ids): - cr.execute('SELECT id \ - FROM hr_timesheet_sheet_sheet \ - WHERE (date_from < %s and %s < date_to) \ - AND user_id=%d \ - AND id <> %d', (sheet.date_to, sheet.date_from, - sheet.user_id.id, sheet.id)) - if cr.fetchall(): - return False - return True - - _constraints = [ - (_sheet_date, 'You can not have 2 timesheets that overlaps !', ['date_from','date_to']) - ] - - def action_set_to_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state': 'draft'}) - wf_service = netsvc.LocalService('workflow') - for id in ids: - wf_service.trg_create(uid, self._name, id, cr) - return True - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - return [(r['id'], r['date_from'] + ' - ' + r['date_to']) \ - for r in self.read(cr, uid, ids, ['date_from', 'date_to'], - context, load='_classic_write')] + _name = "hr_timesheet_sheet.sheet" + _table = 'hr_timesheet_sheet_sheet' + _order = "id desc" + + def _total_day(self, cr, uid, ids, name, args, context): + field_name = name.strip('_day') + cr.execute('SELECT sheet.id, day.' + field_name +' \ + FROM hr_timesheet_sheet_sheet AS sheet \ + LEFT JOIN hr_timesheet_sheet_sheet_day AS day \ + ON (sheet.id = day.sheet_id \ + AND day.name = sheet.date_current) \ + WHERE sheet.id in (' + ','.join([str(x) for x in ids]) + ')') + return dict(cr.fetchall()) + + def _total(self, cr, uid, ids, name, args, context): + cr.execute('SELECT s.id, COALESCE(SUM(d.' + name + '),0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN hr_timesheet_sheet_sheet_day d \ + ON (s.id = d.sheet_id) \ + WHERE s.id in ('+ ','.join(map(str, ids)) + ') \ + GROUP BY s.id') + return dict(cr.fetchall()) + + def _state_attendance(self, cr, uid, ids, name, args, context): + result = {} + link_emp = {} + emp_ids = [] + emp_obj = self.pool.get('hr.employee') + + for sheet in self.browse(cr, uid, ids, context): + result[sheet.id] = 'none' + emp_ids2 = emp_obj.search(cr, uid, + [('user_id', '=', sheet.user_id.id)]) + if emp_ids2: + link_emp[emp_ids2[0]] = sheet.id + emp_ids.append(emp_ids2[0]) + for emp in emp_obj.browse(cr, uid, emp_ids, context=context): + if emp.id in link_emp: + sheet_id = link_emp[emp.id] + result[sheet_id] = emp.state + return result + + def button_confirm(self, cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context): + di = sheet.user_id.company_id.timesheet_max_difference + if (abs(sheet.total_difference) < di) or not di: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'hr_timesheet_sheet.sheet', sheet.id, 'confirm', cr) + else: + raise osv.except_osv(_('Warning !'), _('Please verify that the total difference of the sheet is lower than %.2f !') %(di,)) + return True + + def date_today(self, cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context): + if DateTime.now() <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) + elif DateTime.now() >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + else: + self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}) + return True + + def date_previous(self, cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context): + if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) + else: + self.write(cr, uid, [sheet.id], { + 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=-1)).strftime('%Y-%m-%d'), + }) + return True + + def date_next(self, cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context): + if DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + else: + self.write(cr, uid, [sheet.id], { + 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d'), + }) + return True + + def button_dummy(self, cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context): + if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) + elif DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + return True + + def sign_in(self, cr, uid, ids, context): + if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): + raise osv.except_osv(_('Error !'), _('You can not sign in from an other date than today')) + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) + context['sheet_id']=ids[0] + success = emp_obj.sign_in(cr, uid, emp_id, context=context) + return True + + def sign_out(self, cr, uid, ids, context): + if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): + raise osv.except_osv(_('Error !'), _('You can not sign out from an other date than today')) + emp_obj = self.pool.get('hr.employee') + emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) + context['sheet_id']=ids[0] + success = emp_obj.sign_out(cr, uid, emp_id, context=context) + return True + + _columns = { + 'name': fields.char('Description', size=64, select=1), + 'user_id': fields.many2one('res.users', 'User', required=True, select=1), + 'date_from': fields.date('Date from', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), + 'date_to': fields.date('Date to', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), + 'date_current': fields.date('Current date', required=True), + 'timesheet_ids' : one2many_mod('hr.analytic.timesheet', 'sheet_id', + 'Timesheet lines', domain=[('date', '=', time.strftime('%Y-%m-%d'))], + readonly=True, states={ + 'draft': [('readonly', False)], + 'new': [('readonly', False)]} + ), + 'attendances_ids' : one2many_mod2('hr.attendance', 'sheet_id', 'Attendances', readonly=True, states={'draft':[('readonly',False)],'new':[('readonly',False)]}), + 'state' : fields.selection([('new', 'New'),('draft','Draft'),('confirm','Confirmed'),('done','Done')], 'State', select=True, required=True, readonly=True), + 'state_attendance' : fields.function(_state_attendance, method=True, type='selection', selection=[('absent', 'Absent'), ('present', 'Present'),('none','No employee defined')], string='Current state'), + 'total_attendance_day': fields.function(_total_day, method=True, string='Total Attendance'), + 'total_timesheet_day': fields.function(_total_day, method=True, string='Total Timesheet'), + 'total_difference_day': fields.function(_total_day, method=True, string='Difference'), + 'total_attendance': fields.function(_total, method=True, string='Total Attendance'), + 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet'), + 'total_difference': fields.function(_total, method=True, string='Difference'), + 'period_ids': fields.one2many('hr_timesheet_sheet.sheet.day', 'sheet_id', 'Period', readonly=True), + 'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True), + } + + def _default_date_from(self,cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context) + r = user.company_id and user.company_id.timesheet_range or 'month' + if r=='month': + return time.strftime('%Y-%m-01') + elif r=='week': + return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Monday,0))).strftime('%Y-%m-%d') + elif r=='year': + return time.strftime('%Y-01-01') + return time.strftime('%Y-%m-%d') + + def _default_date_to(self,cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context) + r = user.company_id and user.company_id.timesheet_range or 'month' + if r=='month': + return (DateTime.now() + DateTime.RelativeDateTime(months=+1,day=1,days=-1)).strftime('%Y-%m-%d') + elif r=='week': + return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Sunday,0))).strftime('%Y-%m-%d') + elif r=='year': + return time.strftime('%Y-12-31') + return time.strftime('%Y-%m-%d') + + _defaults = { + 'user_id': lambda self,cr,uid,c: uid, + 'date_from' : _default_date_from, + 'date_current' : lambda *a: time.strftime('%Y-%m-%d'), + 'date_to' : _default_date_to, + 'state': lambda *a: 'new', + } + + def _sheet_date(self, cr, uid, ids): + for sheet in self.browse(cr, uid, ids): + cr.execute('SELECT id \ + FROM hr_timesheet_sheet_sheet \ + WHERE (date_from < %s and %s < date_to) \ + AND user_id=%d \ + AND id <> %d', (sheet.date_to, sheet.date_from, + sheet.user_id.id, sheet.id)) + if cr.fetchall(): + return False + return True + + _constraints = [ + (_sheet_date, 'You can not have 2 timesheets that overlaps !', ['date_from','date_to']) + ] + + def action_set_to_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state': 'draft'}) + wf_service = netsvc.LocalService('workflow') + for id in ids: + wf_service.trg_create(uid, self._name, id, cr) + return True + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + return [(r['id'], r['date_from'] + ' - ' + r['date_to']) \ + for r in self.read(cr, uid, ids, ['date_from', 'date_to'], + context, load='_classic_write')] hr_timesheet_sheet() class hr_timesheet_line(osv.osv): - _inherit = "hr.analytic.timesheet" - - def _get_default_date(self, cr, uid, context={}): - if 'date' in context: - return context['date'] - return time.strftime('%Y-%m-%d') - - def _sheet(self, cursor, user, ids, name, args, context): - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - - cursor.execute('SELECT l.id, COALESCE(MAX(s.id), 0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_analytic_timesheet l \ - LEFT JOIN account_analytic_line al \ - ON (l.line_id = al.id)) \ - ON (s.date_to >= al.date \ - AND s.date_from <= al.date \ - AND s.user_id = al.user_id) \ - WHERE l.id in (' + ','.join([str(x) for x in ids]) + ') \ - GROUP BY l.id') - res = dict(cursor.fetchall()) - sheet_names = {} - for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), - context=context): - sheet_names[sheet_id] = name - - for line_id in {}.fromkeys(ids): - sheet_id = res.get(line_id, False) - if sheet_id: - res[line_id] = (sheet_id, sheet_names[sheet_id]) - else: - res[line_id] = False - return res - - def _sheet_search(self, cursor, user, obj, name, args): - if not len(args): - return [] - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - - i = 0 - while i < len(args): - fargs = args[i][0].split('.', 1) - if len(fargs) > 1: - args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, - [(fargs[1], args[i][1], args[i][2])])) - i += 1 - continue - if isinstance(args[i][2], basestring): - res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], - args[i][1]) - args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) - i += 1 - qu1, qu2 = [], [] - for x in args: - if x[1] != 'in': - if (x[2] is False) and (x[1] == '='): - qu1.append('(s.id IS NULL)') - elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): - qu1.append('(s.id IS NOT NULL)') - else: - qu1.append('(s.id %s %s)' % (x[1], '%d')) - qu2.append(x[2]) - elif x[1] == 'in': - if len(x[2]) > 0: - qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) - qu2 += x[2] - else: - qu1.append('(False)') - if len(qu1): - qu1 = ' WHERE ' + ' AND '.join(qu1) - else: - qu1 = '' - cursor.execute('SELECT l.id \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_analytic_timesheet l \ - LEFT JOIN account_analytic_line al \ - ON (l.line_id = al.id)) \ - ON (s.date_to >= al.date \ - AND s.date_from <= al.date \ - AND s.user_id = al.user_id)' + \ - qu1, qu2) - res = cursor.fetchall() - if not len(res): - return [('id', '=', '0')] - return [('id', 'in', [x[0] for x in res])] - - _columns = { - 'sheet_id': fields.function(_sheet, method=True, string='Sheet', - type='many2one', relation='hr_timesheet_sheet.sheet', - fnct_search=_sheet_search), - } - _defaults = { - 'date': _get_default_date, - } - - def create(self, cr, uid, vals, *args, **kwargs): - if 'sheet_id' in vals: - ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, vals['sheet_id']) - if not ts.state in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) - return super(hr_timesheet_line,self).create(cr, uid, vals, *args, **kwargs) - - def unlink(self, cr, uid, ids, *args, **kwargs): - self._check(cr, uid, ids) - return super(hr_timesheet_line,self).unlink(cr, uid, ids,*args, **kwargs) - - def write(self, cr, uid, ids, *args, **kwargs): - self._check(cr, uid, ids) - return super(hr_timesheet_line,self).write(cr, uid, ids,*args, **kwargs) - - def _check(self, cr, uid, ids): - for att in self.browse(cr, uid, ids): - if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) - return True + _inherit = "hr.analytic.timesheet" + + def _get_default_date(self, cr, uid, context={}): + if 'date' in context: + return context['date'] + return time.strftime('%Y-%m-%d') + + def _sheet(self, cursor, user, ids, name, args, context): + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + + cursor.execute('SELECT l.id, COALESCE(MAX(s.id), 0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_analytic_timesheet l \ + LEFT JOIN account_analytic_line al \ + ON (l.line_id = al.id)) \ + ON (s.date_to >= al.date \ + AND s.date_from <= al.date \ + AND s.user_id = al.user_id) \ + WHERE l.id in (' + ','.join([str(x) for x in ids]) + ') \ + GROUP BY l.id') + res = dict(cursor.fetchall()) + sheet_names = {} + for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), + context=context): + sheet_names[sheet_id] = name + + for line_id in {}.fromkeys(ids): + sheet_id = res.get(line_id, False) + if sheet_id: + res[line_id] = (sheet_id, sheet_names[sheet_id]) + else: + res[line_id] = False + return res + + def _sheet_search(self, cursor, user, obj, name, args): + if not len(args): + return [] + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + + i = 0 + while i < len(args): + fargs = args[i][0].split('.', 1) + if len(fargs) > 1: + args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, + [(fargs[1], args[i][1], args[i][2])])) + i += 1 + continue + if isinstance(args[i][2], basestring): + res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], + args[i][1]) + args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) + i += 1 + qu1, qu2 = [], [] + for x in args: + if x[1] != 'in': + if (x[2] is False) and (x[1] == '='): + qu1.append('(s.id IS NULL)') + elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): + qu1.append('(s.id IS NOT NULL)') + else: + qu1.append('(s.id %s %s)' % (x[1], '%d')) + qu2.append(x[2]) + elif x[1] == 'in': + if len(x[2]) > 0: + qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) + qu2 += x[2] + else: + qu1.append('(False)') + if len(qu1): + qu1 = ' WHERE ' + ' AND '.join(qu1) + else: + qu1 = '' + cursor.execute('SELECT l.id \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_analytic_timesheet l \ + LEFT JOIN account_analytic_line al \ + ON (l.line_id = al.id)) \ + ON (s.date_to >= al.date \ + AND s.date_from <= al.date \ + AND s.user_id = al.user_id)' + \ + qu1, qu2) + res = cursor.fetchall() + if not len(res): + return [('id', '=', '0')] + return [('id', 'in', [x[0] for x in res])] + + _columns = { + 'sheet_id': fields.function(_sheet, method=True, string='Sheet', + type='many2one', relation='hr_timesheet_sheet.sheet', + fnct_search=_sheet_search), + } + _defaults = { + 'date': _get_default_date, + } + + def create(self, cr, uid, vals, *args, **kwargs): + if 'sheet_id' in vals: + ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, vals['sheet_id']) + if not ts.state in ('draft', 'new'): + raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + return super(hr_timesheet_line,self).create(cr, uid, vals, *args, **kwargs) + + def unlink(self, cr, uid, ids, *args, **kwargs): + self._check(cr, uid, ids) + return super(hr_timesheet_line,self).unlink(cr, uid, ids,*args, **kwargs) + + def write(self, cr, uid, ids, *args, **kwargs): + self._check(cr, uid, ids) + return super(hr_timesheet_line,self).write(cr, uid, ids,*args, **kwargs) + + def _check(self, cr, uid, ids): + for att in self.browse(cr, uid, ids): + if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): + raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + return True hr_timesheet_line() class hr_attendance(osv.osv): - _inherit = "hr.attendance" - - def _get_default_date(self, cr, uid, context={}): - if 'name' in context: - return context['name'] + time.strftime(' %H:%M:%S') - return time.strftime('%Y-%m-%d %H:%M:%S') - - def _sheet(self, cursor, user, ids, name, args, context): - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - - cursor.execute('SELECT a.id, COALESCE(MAX(s.id), 0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_attendance a \ - LEFT JOIN hr_employee e \ - ON (a.employee_id = e.id)) \ - ON (s.date_to >= a.name \ - AND s.date_from <= a.name \ - AND s.user_id = e.user_id) \ - WHERE a.id in (' + ','.join([str(x) for x in ids]) + ') \ - GROUP BY a.id') - res = dict(cursor.fetchall()) - sheet_names = {} - for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), - context=context): - sheet_names[sheet_id] = name - - for line_id in {}.fromkeys(ids): - sheet_id = res.get(line_id, False) - if sheet_id: - res[line_id] = (sheet_id, sheet_names[sheet_id]) - else: - res[line_id] = False - return res - - def _sheet_search(self, cursor, user, obj, name, args): - if not len(args): - return [] - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - - i = 0 - while i < len(args): - fargs = args[i][0].split('.', 1) - if len(fargs) > 1: - args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, - [(fargs[1], args[i][1], args[i][2])])) - i += 1 - continue - if isinstance(args[i][2], basestring): - res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], - args[i][1]) - args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) - i += 1 - qu1, qu2 = [], [] - for x in args: - if x[1] != 'in': - if (x[2] is False) and (x[1] == '='): - qu1.append('(s.id IS NULL)') - elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): - qu1.append('(s.id IS NOT NULL)') - else: - qu1.append('(s.id %s %s)' % (x[1], '%d')) - qu2.append(x[2]) - elif x[1] == 'in': - if len(x[2]) > 0: - qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) - qu2 += x[2] - else: - qu1.append('(False)') - if len(qu1): - qu1 = ' WHERE ' + ' AND '.join(qu1) - else: - qu1 = '' - cursor.execute('SELECT a.id\ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_attendance a \ - LEFT JOIN hr_employee e \ - ON (a.employee_id = e.id)) \ - ON (s.date_to >= a.name \ - AND s.date_from <= a.name \ - AND s.user_id = e.user_id) ' + \ - qu1, qu2) - res = cursor.fetchall() - if not len(res): - return [('id', '=', '0')] - return [('id', 'in', [x[0] for x in res])] - - _columns = { - 'sheet_id': fields.function(_sheet, method=True, string='Sheet', - type='many2one', relation='hr_timesheet_sheet.sheet', - fnct_search=_sheet_search), - } - _defaults = { - 'name': _get_default_date, - } - - def create(self, cr, uid, vals, context={}): - if 'sheet_id' in context: - ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, context['sheet_id']) - if ts.state not in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) - res = super(hr_attendance,self).create(cr, uid, vals, context=context) - if 'sheet_id' in context: - if context['sheet_id'] != self.browse(cr, uid, res, context=context).sheet_id.id: - raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ - 'date outside the current timesheet dates!')) - return res - - def unlink(self, cr, uid, ids, *args, **kwargs): - self._check(cr, uid, ids) - return super(hr_attendance,self).unlink(cr, uid, ids,*args, **kwargs) - - def write(self, cr, uid, ids, vals, context={}): - self._check(cr, uid, ids) - res = super(hr_attendance,self).write(cr, uid, ids, vals, context=context) - if 'sheet_id' in context: - for attendance in self.browse(cr, uid, ids, context=context): - if context['sheet_id'] != attendance.sheet_id.id: - raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ - 'date outside the current timesheet dates!')) - return res - - def _check(self, cr, uid, ids): - for att in self.browse(cr, uid, ids): - if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) - return True + _inherit = "hr.attendance" + + def _get_default_date(self, cr, uid, context={}): + if 'name' in context: + return context['name'] + time.strftime(' %H:%M:%S') + return time.strftime('%Y-%m-%d %H:%M:%S') + + def _sheet(self, cursor, user, ids, name, args, context): + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + + cursor.execute('SELECT a.id, COALESCE(MAX(s.id), 0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_attendance a \ + LEFT JOIN hr_employee e \ + ON (a.employee_id = e.id)) \ + ON (s.date_to >= a.name \ + AND s.date_from <= a.name \ + AND s.user_id = e.user_id) \ + WHERE a.id in (' + ','.join([str(x) for x in ids]) + ') \ + GROUP BY a.id') + res = dict(cursor.fetchall()) + sheet_names = {} + for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), + context=context): + sheet_names[sheet_id] = name + + for line_id in {}.fromkeys(ids): + sheet_id = res.get(line_id, False) + if sheet_id: + res[line_id] = (sheet_id, sheet_names[sheet_id]) + else: + res[line_id] = False + return res + + def _sheet_search(self, cursor, user, obj, name, args): + if not len(args): + return [] + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + + i = 0 + while i < len(args): + fargs = args[i][0].split('.', 1) + if len(fargs) > 1: + args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, + [(fargs[1], args[i][1], args[i][2])])) + i += 1 + continue + if isinstance(args[i][2], basestring): + res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], + args[i][1]) + args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) + i += 1 + qu1, qu2 = [], [] + for x in args: + if x[1] != 'in': + if (x[2] is False) and (x[1] == '='): + qu1.append('(s.id IS NULL)') + elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): + qu1.append('(s.id IS NOT NULL)') + else: + qu1.append('(s.id %s %s)' % (x[1], '%d')) + qu2.append(x[2]) + elif x[1] == 'in': + if len(x[2]) > 0: + qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) + qu2 += x[2] + else: + qu1.append('(False)') + if len(qu1): + qu1 = ' WHERE ' + ' AND '.join(qu1) + else: + qu1 = '' + cursor.execute('SELECT a.id\ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_attendance a \ + LEFT JOIN hr_employee e \ + ON (a.employee_id = e.id)) \ + ON (s.date_to >= a.name \ + AND s.date_from <= a.name \ + AND s.user_id = e.user_id) ' + \ + qu1, qu2) + res = cursor.fetchall() + if not len(res): + return [('id', '=', '0')] + return [('id', 'in', [x[0] for x in res])] + + _columns = { + 'sheet_id': fields.function(_sheet, method=True, string='Sheet', + type='many2one', relation='hr_timesheet_sheet.sheet', + fnct_search=_sheet_search), + } + _defaults = { + 'name': _get_default_date, + } + + def create(self, cr, uid, vals, context={}): + if 'sheet_id' in context: + ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, context['sheet_id']) + if ts.state not in ('draft', 'new'): + raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + res = super(hr_attendance,self).create(cr, uid, vals, context=context) + if 'sheet_id' in context: + if context['sheet_id'] != self.browse(cr, uid, res, context=context).sheet_id.id: + raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ + 'date outside the current timesheet dates!')) + return res + + def unlink(self, cr, uid, ids, *args, **kwargs): + self._check(cr, uid, ids) + return super(hr_attendance,self).unlink(cr, uid, ids,*args, **kwargs) + + def write(self, cr, uid, ids, vals, context={}): + self._check(cr, uid, ids) + res = super(hr_attendance,self).write(cr, uid, ids, vals, context=context) + if 'sheet_id' in context: + for attendance in self.browse(cr, uid, ids, context=context): + if context['sheet_id'] != attendance.sheet_id.id: + raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ + 'date outside the current timesheet dates!')) + return res + + def _check(self, cr, uid, ids): + for att in self.browse(cr, uid, ids): + if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): + raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + return True hr_attendance() class hr_timesheet_sheet_sheet_day(osv.osv): - _name = "hr_timesheet_sheet.sheet.day" - _description = "Timesheets by period" - _auto = False - _order='name' - _columns = { - 'name': fields.date('Date', readonly=True), - 'sheet_id': fields.many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True, select="1"), - 'total_timesheet': fields.float('Project Timesheet', readonly=True), - 'total_attendance': fields.float('Attendance', readonly=True), - 'total_difference': fields.float('Difference', readonly=True), - } - - def init(self, cr): - cr.execute("""create or replace view hr_timesheet_sheet_sheet_day as - SELECT - id, - name, - sheet_id, - total_timesheet, - total_attendance, - (total_attendance - total_timesheet) AS total_difference - FROM - (( - SELECT - MAX(id) as id, - name, - sheet_id, - SUM(total_timesheet) as total_timesheet, - CASE WHEN SUM(total_attendance) < 0 - THEN (SUM(total_attendance) + - CASE WHEN current_date <> name - THEN 1440 - ELSE (EXTRACT(hour FROM current_time) * 60) + EXTRACT(minute FROM current_time) - END - ) - ELSE SUM(total_attendance) - END /60 as total_attendance - FROM - (( - select - min(hrt.id) as id, - l.date::date as name, - s.id as sheet_id, - sum(l.unit_amount) as total_timesheet, - 0.0 as total_attendance - from - hr_analytic_timesheet hrt - left join (account_analytic_line l - LEFT JOIN hr_timesheet_sheet_sheet s - ON (s.date_to >= l.date - AND s.date_from <= l.date - AND s.user_id = l.user_id)) - on (l.id = hrt.line_id) - group by l.date::date, s.id - ) union ( - select - -min(a.id) as id, - a.name::date as name, - s.id as sheet_id, - 0.0 as total_timesheet, - SUM(((EXTRACT(hour FROM a.name) * 60) + EXTRACT(minute FROM a.name)) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance - from - hr_attendance a - LEFT JOIN (hr_timesheet_sheet_sheet s - LEFT JOIN hr_employee e - ON (s.user_id = e.user_id)) - ON (a.employee_id = e.id - AND s.date_to >= a.name - AND s.date_from <= a.name) - WHERE action in ('sign_in', 'sign_out') - group by a.name::date, s.id - )) AS foo - GROUP BY name, sheet_id - )) AS bar""") + _name = "hr_timesheet_sheet.sheet.day" + _description = "Timesheets by period" + _auto = False + _order='name' + _columns = { + 'name': fields.date('Date', readonly=True), + 'sheet_id': fields.many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True, select="1"), + 'total_timesheet': fields.float('Project Timesheet', readonly=True), + 'total_attendance': fields.float('Attendance', readonly=True), + 'total_difference': fields.float('Difference', readonly=True), + } + + def init(self, cr): + cr.execute("""create or replace view hr_timesheet_sheet_sheet_day as + SELECT + id, + name, + sheet_id, + total_timesheet, + total_attendance, + (total_attendance - total_timesheet) AS total_difference + FROM + (( + SELECT + MAX(id) as id, + name, + sheet_id, + SUM(total_timesheet) as total_timesheet, + CASE WHEN SUM(total_attendance) < 0 + THEN (SUM(total_attendance) + + CASE WHEN current_date <> name + THEN 1440 + ELSE (EXTRACT(hour FROM current_time) * 60) + EXTRACT(minute FROM current_time) + END + ) + ELSE SUM(total_attendance) + END /60 as total_attendance + FROM + (( + select + min(hrt.id) as id, + l.date::date as name, + s.id as sheet_id, + sum(l.unit_amount) as total_timesheet, + 0.0 as total_attendance + from + hr_analytic_timesheet hrt + left join (account_analytic_line l + LEFT JOIN hr_timesheet_sheet_sheet s + ON (s.date_to >= l.date + AND s.date_from <= l.date + AND s.user_id = l.user_id)) + on (l.id = hrt.line_id) + group by l.date::date, s.id + ) union ( + select + -min(a.id) as id, + a.name::date as name, + s.id as sheet_id, + 0.0 as total_timesheet, + SUM(((EXTRACT(hour FROM a.name) * 60) + EXTRACT(minute FROM a.name)) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance + from + hr_attendance a + LEFT JOIN (hr_timesheet_sheet_sheet s + LEFT JOIN hr_employee e + ON (s.user_id = e.user_id)) + ON (a.employee_id = e.id + AND s.date_to >= a.name + AND s.date_from <= a.name) + WHERE action in ('sign_in', 'sign_out') + group by a.name::date, s.id + )) AS foo + GROUP BY name, sheet_id + )) AS bar""") hr_timesheet_sheet_sheet_day() class hr_timesheet_sheet_sheet_account(osv.osv): - _name = "hr_timesheet_sheet.sheet.account" - _description = "Timesheets by period" - _auto = False - _order='name' - _columns = { - 'name': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), - 'sheet_id': fields.many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True), - 'total': fields.float('Total Time', digits=(16,2), readonly=True), - 'invoice_rate': fields.many2one('hr_timesheet_invoice.factor', 'Invoice rate', readonly=True), - } - - def init(self, cr): - cr.execute("""create or replace view hr_timesheet_sheet_sheet_account as ( - select - min(hrt.id) as id, - l.account_id as name, - s.id as sheet_id, - sum(l.unit_amount) as total, - l.to_invoice as invoice_rate - from - hr_analytic_timesheet hrt - left join (account_analytic_line l - LEFT JOIN hr_timesheet_sheet_sheet s - ON (s.date_to >= l.date - AND s.date_from <= l.date - AND s.user_id = l.user_id)) - on (l.id = hrt.line_id) - group by l.account_id, s.id, l.to_invoice - )""") + _name = "hr_timesheet_sheet.sheet.account" + _description = "Timesheets by period" + _auto = False + _order='name' + _columns = { + 'name': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'sheet_id': fields.many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True), + 'total': fields.float('Total Time', digits=(16,2), readonly=True), + 'invoice_rate': fields.many2one('hr_timesheet_invoice.factor', 'Invoice rate', readonly=True), + } + + def init(self, cr): + cr.execute("""create or replace view hr_timesheet_sheet_sheet_account as ( + select + min(hrt.id) as id, + l.account_id as name, + s.id as sheet_id, + sum(l.unit_amount) as total, + l.to_invoice as invoice_rate + from + hr_analytic_timesheet hrt + left join (account_analytic_line l + LEFT JOIN hr_timesheet_sheet_sheet s + ON (s.date_to >= l.date + AND s.date_from <= l.date + AND s.user_id = l.user_id)) + on (l.id = hrt.line_id) + group by l.account_id, s.id, l.to_invoice + )""") hr_timesheet_sheet_sheet_account() class res_company(osv.osv): - _inherit = 'res.company' - _columns = { - 'timesheet_range': fields.selection([('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timeshet range'), - 'timesheet_max_difference': fields.float('Timesheet allowed difference', help="Allowed difference between the sign in/out and the timesheet computation for one sheet. Set this to 0 if you do not want any control."), - } - _defaults = { - 'timesheet_range': lambda *args: 'month', - 'timesheet_max_difference': lambda *args: 0.0 - } + _inherit = 'res.company' + _columns = { + 'timesheet_range': fields.selection([('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timeshet range'), + 'timesheet_max_difference': fields.float('Timesheet allowed difference', help="Allowed difference between the sign in/out and the timesheet computation for one sheet. Set this to 0 if you do not want any control."), + } + _defaults = { + 'timesheet_range': lambda *args: 'month', + 'timesheet_max_difference': lambda *args: 0.0 + } res_company() diff --git a/addons/hr_timesheet_sheet/wizard/wizard_timesheet_current.py b/addons/hr_timesheet_sheet/wizard/wizard_timesheet_current.py index f6685d7d96a..8331f228189 100644 --- a/addons/hr_timesheet_sheet/wizard/wizard_timesheet_current.py +++ b/addons/hr_timesheet_sheet/wizard/wizard_timesheet_current.py @@ -36,41 +36,41 @@ from osv import osv from tools.translate import _ class wiz_timesheet_open(wizard.interface): - def _open_timesheet(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - user_ids = pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)]) - if not len(user_ids): - raise wizard.except_wizard(_('Error !'), _('No employee defined for your user !')) - ts = pool.get('hr_timesheet_sheet.sheet') - ids = ts.search(cr, uid, [('user_id','=',uid),('state','=','draft'),('date_from','<=',time.strftime('%Y-%m-%d')), ('date_to','>=',time.strftime('%Y-%m-%d'))]) - view_type = 'form,tree' - if len(ids) > 1: - view_type = 'tree,form' - domain = "[('id','in',["+','.join(map(str,ids))+"]),('user_id', '=', uid)]" - elif len(ids)==1: - ts.write(cr, uid, ids, {'date_current': time.strftime('%Y-%m-%d')}) - domain = "[('user_id', '=', uid)]" - else: - domain = "[('user_id', '=', uid)]" - value = { - 'domain': domain, - 'name': 'Open timesheet', - 'view_type': 'form', - 'view_mode': view_type, - 'res_model': 'hr_timesheet_sheet.sheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - if len(ids) == 1: - value['res_id'] = ids[0] - return value + def _open_timesheet(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + user_ids = pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)]) + if not len(user_ids): + raise wizard.except_wizard(_('Error !'), _('No employee defined for your user !')) + ts = pool.get('hr_timesheet_sheet.sheet') + ids = ts.search(cr, uid, [('user_id','=',uid),('state','=','draft'),('date_from','<=',time.strftime('%Y-%m-%d')), ('date_to','>=',time.strftime('%Y-%m-%d'))]) + view_type = 'form,tree' + if len(ids) > 1: + view_type = 'tree,form' + domain = "[('id','in',["+','.join(map(str,ids))+"]),('user_id', '=', uid)]" + elif len(ids)==1: + ts.write(cr, uid, ids, {'date_current': time.strftime('%Y-%m-%d')}) + domain = "[('user_id', '=', uid)]" + else: + domain = "[('user_id', '=', uid)]" + value = { + 'domain': domain, + 'name': 'Open timesheet', + 'view_type': 'form', + 'view_mode': view_type, + 'res_model': 'hr_timesheet_sheet.sheet', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + if len(ids) == 1: + value['res_id'] = ids[0] + return value - states = { - 'init' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} - } - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} + } + } wiz_timesheet_open('hr_timesheet_sheet.current.open') # vim:noexpandtab:tw=0 diff --git a/addons/hr_timesheet_sheet/wizard/wizard_timesheet_department.py b/addons/hr_timesheet_sheet/wizard/wizard_timesheet_department.py index 40d347ff117..5cd0e30da94 100644 --- a/addons/hr_timesheet_sheet/wizard/wizard_timesheet_department.py +++ b/addons/hr_timesheet_sheet/wizard/wizard_timesheet_department.py @@ -35,85 +35,85 @@ import pooler from osv import osv class wiz_timesheet_open(wizard.interface): - def _open_timesheet(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) - user_ids = {} - for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): - for user in dep.member_ids: - user_ids[user.id] = 1 - value = { - 'domain': "[('user_id', 'in', "+str(user_ids.keys())+")]", - 'name': 'Timesheets', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr_timesheet_sheet.sheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - return value + def _open_timesheet(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) + user_ids = {} + for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): + for user in dep.member_ids: + user_ids[user.id] = 1 + value = { + 'domain': "[('user_id', 'in', "+str(user_ids.keys())+")]", + 'name': 'Timesheets', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'hr_timesheet_sheet.sheet', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + return value - states = { - 'init' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} - } - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} + } + } wiz_timesheet_open('hr_timesheet_sheet.department.open') class wiz_timesheet_confirm_open(wizard.interface): - def _open_timesheet(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) - user_ids = {} - for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): - for user in dep.member_ids: - user_ids[user.id] = 1 - value = { - 'domain': "[('user_id', 'in', "+str(user_ids.keys())+"),('state','=','draft')]", - 'name': 'Timesheets', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr_timesheet_sheet.sheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - return value + def _open_timesheet(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) + user_ids = {} + for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): + for user in dep.member_ids: + user_ids[user.id] = 1 + value = { + 'domain': "[('user_id', 'in', "+str(user_ids.keys())+"),('state','=','draft')]", + 'name': 'Timesheets', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'hr_timesheet_sheet.sheet', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + return value - states = { - 'init' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} - } - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} + } + } wiz_timesheet_confirm_open('hr_timesheet_sheet.department.confirm.open') class wiz_timesheet_validate_open(wizard.interface): - def _open_timesheet(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) - user_ids = {} - for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): - for user in dep.member_ids: - user_ids[user.id] = 1 - value = { - 'domain': "[('user_id', 'in', "+str(user_ids.keys())+"),('state','=','confirm')]", - 'name': 'Timesheets', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr_timesheet_sheet.sheet', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - return value + def _open_timesheet(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + dep_ids = pool.get('hr.department').search(cr, uid, [('manager_id','=',uid)]) + user_ids = {} + for dep in pool.get('hr.department').browse(cr, uid, dep_ids, context): + for user in dep.member_ids: + user_ids[user.id] = 1 + value = { + 'domain': "[('user_id', 'in', "+str(user_ids.keys())+"),('state','=','confirm')]", + 'name': 'Timesheets', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'hr_timesheet_sheet.sheet', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + return value - states = { - 'init' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} - } - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_open_timesheet, 'state':'end'} + } + } wiz_timesheet_validate_open('hr_timesheet_sheet.department.validate.open') diff --git a/addons/l10n_be/__terp__.py b/addons/l10n_be/__terp__.py index 8255419545d..009db8ce347 100644 --- a/addons/l10n_be/__terp__.py +++ b/addons/l10n_be/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Belgium - Plan Comptable Minimum Normalise", - "version" : "1.1", - "author" : "Tiny", - "category" : "Localisation/Account charts", - "depends" : ["account", "account_report", "base_vat", "base_iban", - "account_chart"], - "init_xml" : [], - "demo_xml" : ["account_demo.xml","account.report.report.csv"], - "update_xml" : ["../account_chart/account_chart.xml", "account_pcmn_belgium.xml"], - "installable": True + "name" : "Belgium - Plan Comptable Minimum Normalise", + "version" : "1.1", + "author" : "Tiny", + "category" : "Localisation/Account charts", + "depends" : ["account", "account_report", "base_vat", "base_iban", + "account_chart"], + "init_xml" : [], + "demo_xml" : ["account_demo.xml","account.report.report.csv"], + "update_xml" : ["../account_chart/account_chart.xml", "account_pcmn_belgium.xml"], + "installable": True } diff --git a/addons/l10n_chart_uk_minimal/__init__.py b/addons/l10n_chart_uk_minimal/__init__.py index 7e18ac6e656..7e18ac6e656 100755..100644 --- a/addons/l10n_chart_uk_minimal/__init__.py +++ b/addons/l10n_chart_uk_minimal/__init__.py diff --git a/addons/l10n_chart_uk_minimal/__terp__.py b/addons/l10n_chart_uk_minimal/__terp__.py index 89b07d4feac..f50dae69e1f 100755..100644 --- a/addons/l10n_chart_uk_minimal/__terp__.py +++ b/addons/l10n_chart_uk_minimal/__terp__.py @@ -63,15 +63,15 @@ # - COGS account type fixed # { - "name" : "United Kingdom - minimal", - "version" : "1.1", - "author" : "Seath Solutions Ltd", - "website": "http://www.seathsolutions.com", - "category" : "Localisation/Account charts", - "depends" : ["base", "account", "base_iban", "base_vat", "account_chart"], - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["../account_chart/account_chart.xml", "account_chart.xml", - "account_tax.xml"], - "installable": True + "name" : "United Kingdom - minimal", + "version" : "1.1", + "author" : "Seath Solutions Ltd", + "website": "http://www.seathsolutions.com", + "category" : "Localisation/Account charts", + "depends" : ["base", "account", "base_iban", "base_vat", "account_chart"], + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["../account_chart/account_chart.xml", "account_chart.xml", + "account_tax.xml"], + "installable": True } diff --git a/addons/l10n_fr/__terp__.py b/addons/l10n_fr/__terp__.py index 97f9cc01804..0278175aeef 100644 --- a/addons/l10n_fr/__terp__.py +++ b/addons/l10n_fr/__terp__.py @@ -33,15 +33,15 @@ # Mise en forme et paramétrage par http://sisalp.fr et http://nbconseil.net # { - "name" : "France - Plan comptable Societe - 99-03", - "version" : "1.1", - "author" : "SISalp-NBconseil", - "category" : "Localisation/Account charts", - "website": "http://erp-libre.info", - "depends" : ["base", "account", "account_chart", 'base_vat'], - "init_xml" : [], - "update_xml" : ["../account_chart/account_chart.xml", "types.xml", - "plan-99-03_societe.xml", "taxes.xml","fr_wizard.xml"], - "demo_xml" : [], - "installable": True + "name" : "France - Plan comptable Societe - 99-03", + "version" : "1.1", + "author" : "SISalp-NBconseil", + "category" : "Localisation/Account charts", + "website": "http://erp-libre.info", + "depends" : ["base", "account", "account_chart", 'base_vat'], + "init_xml" : [], + "update_xml" : ["../account_chart/account_chart.xml", "types.xml", + "plan-99-03_societe.xml", "taxes.xml","fr_wizard.xml"], + "demo_xml" : [], + "installable": True } diff --git a/addons/l10n_fr/wizard/__init__.py b/addons/l10n_fr/wizard/__init__.py index cc1913bae4a..cc1913bae4a 100755..100644 --- a/addons/l10n_fr/wizard/__init__.py +++ b/addons/l10n_fr/wizard/__init__.py diff --git a/addons/l10n_fr/wizard/pdf_ext.py b/addons/l10n_fr/wizard/pdf_ext.py index d4d88483d06..6fbd164bc3b 100755..100644 --- a/addons/l10n_fr/wizard/pdf_ext.py +++ b/addons/l10n_fr/wizard/pdf_ext.py @@ -74,9 +74,9 @@ def write_field(out, key, value): def write_fields(out, fields): out.write(HEAD) for key in fields: - value = fields[key] - write_field(out, key, value) -# write_field(out, key+"a", value) # pour copie-carbone sur autres pages + value = fields[key] + write_field(out, key, value) +# write_field(out, key+"a", value) # pour copie-carbone sur autres pages out.write(TAIL) def extract_keys_from_pdf(filename): diff --git a/addons/l10n_fr/wizard/wizard_chart_report.py b/addons/l10n_fr/wizard/wizard_chart_report.py index 0ff729c5036..0ff729c5036 100755..100644 --- a/addons/l10n_fr/wizard/wizard_chart_report.py +++ b/addons/l10n_fr/wizard/wizard_chart_report.py diff --git a/addons/mrp/__terp__.py b/addons/mrp/__terp__.py index 24c2a162d97..493fcedc6d9 100644 --- a/addons/mrp/__terp__.py +++ b/addons/mrp/__terp__.py @@ -26,48 +26,48 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Manufacturing Resource Planning", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_mrp.html", - "category" : "Generic Modules/Production", - "depends" : ["stock", "hr", "purchase", "product"], - "description": """ - This is the base module to manage the manufacturing process in Tiny ERP. + "name" : "Manufacturing Resource Planning", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_mrp.html", + "category" : "Generic Modules/Production", + "depends" : ["stock", "hr", "purchase", "product"], + "description": """ + This is the base module to manage the manufacturing process in Tiny ERP. - Features: - * Make to Stock / Make to Order (by line) - * Multi-level BoMs, no limit - * Multi-level routing, no limit - * Routing and workcenter integrated with analytic accounting - * Scheduler computation periodically / Just In Time module - * Multi-pos, multi-warehouse - * Different reordering policies - * Cost method by product: standard price, average price - * Easy analysis of troubles or needs - * Very flexible + Features: + * Make to Stock / Make to Order (by line) + * Multi-level BoMs, no limit + * Multi-level routing, no limit + * Routing and workcenter integrated with analytic accounting + * Scheduler computation periodically / Just In Time module + * Multi-pos, multi-warehouse + * Different reordering policies + * Cost method by product: standard price, average price + * Easy analysis of troubles or needs + * Very flexible - It supports complete integration and planification of stockable goods, - consumable of services. Services are completly integrated with the rest - of the software. For instance, you can set up a sub-contracting service - in a BoM to automatically purchase on order the assembly of your production. + It supports complete integration and planification of stockable goods, + consumable of services. Services are completly integrated with the rest + of the software. For instance, you can set up a sub-contracting service + in a BoM to automatically purchase on order the assembly of your production. - Reports provided by this module: - * Bill of Material structure and components - * Load forecast on workcenters - * Print a production order - * Stock forecasts - """, - "init_xml" : [], - "demo_xml" : ["mrp_demo.xml","mrp_order_point.xml"], - "update_xml" : [ - "mrp_workflow.xml", - "mrp_data.xml", - "mrp_view.xml", - "mrp_wizard.xml", - "mrp_report.xml", - "mrp_security.xml", - ], - "active": False, - "installable": True + Reports provided by this module: + * Bill of Material structure and components + * Load forecast on workcenters + * Print a production order + * Stock forecasts + """, + "init_xml" : [], + "demo_xml" : ["mrp_demo.xml","mrp_order_point.xml"], + "update_xml" : [ + "mrp_workflow.xml", + "mrp_data.xml", + "mrp_view.xml", + "mrp_wizard.xml", + "mrp_report.xml", + "mrp_security.xml", + ], + "active": False, + "installable": True } diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index d8dd99e562e..236ad832af7 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -45,607 +45,607 @@ from mx import DateTime # TODO: Work Center may be recursive ? # class mrp_workcenter(osv.osv): - _name = 'mrp.workcenter' - _description = 'Workcenter' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'active': fields.boolean('Active'), - 'type': fields.selection([('machine','Machine'),('hr','Human Resource'),('tool','Tool')], 'Type', required=True), - 'code': fields.char('Code', size=8), - 'timesheet_id': fields.many2one('hr.timesheet.group', 'Timesheet'), - 'note': fields.text('Description'), - - 'capacity_per_cycle': fields.float('Capacity per Cycle'), - - 'time_cycle': fields.float('Time for 1 cycle (hour)'), - 'time_start': fields.float('Time before prod.'), - 'time_stop': fields.float('Time after prod.'), - 'time_efficiency': fields.float('Time Efficiency'), - - 'costs_hour': fields.float('Cost per hour'), - 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', domain=[('type','=','expense')]), - 'costs_cycle': fields.float('Cost per cycle'), - 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', domain=[('type','=','expense')]), - 'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), - 'costs_general_account_id': fields.many2one('account.account', 'General Account'), - } - _defaults = { - 'active': lambda *a: 1, - 'type': lambda *a: 'machine', - 'time_efficiency': lambda *a: 1.0, - 'capacity_per_cycle': lambda *a: 1.0, - } + _name = 'mrp.workcenter' + _description = 'Workcenter' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'active': fields.boolean('Active'), + 'type': fields.selection([('machine','Machine'),('hr','Human Resource'),('tool','Tool')], 'Type', required=True), + 'code': fields.char('Code', size=8), + 'timesheet_id': fields.many2one('hr.timesheet.group', 'Timesheet'), + 'note': fields.text('Description'), + + 'capacity_per_cycle': fields.float('Capacity per Cycle'), + + 'time_cycle': fields.float('Time for 1 cycle (hour)'), + 'time_start': fields.float('Time before prod.'), + 'time_stop': fields.float('Time after prod.'), + 'time_efficiency': fields.float('Time Efficiency'), + + 'costs_hour': fields.float('Cost per hour'), + 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', domain=[('type','=','expense')]), + 'costs_cycle': fields.float('Cost per cycle'), + 'costs_cycle_account_id': fields.many2one('account.analytic.account', 'Cycle Account', domain=[('type','=','expense')]), + 'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), + 'costs_general_account_id': fields.many2one('account.account', 'General Account'), + } + _defaults = { + 'active': lambda *a: 1, + 'type': lambda *a: 'machine', + 'time_efficiency': lambda *a: 1.0, + 'capacity_per_cycle': lambda *a: 1.0, + } mrp_workcenter() class mrp_property_group(osv.osv): - _name = 'mrp.property.group' - _description = 'Property Group' - _columns = { - 'name': fields.char('Property Group', size=64, required=True), - 'description': fields.text('Description'), - } + _name = 'mrp.property.group' + _description = 'Property Group' + _columns = { + 'name': fields.char('Property Group', size=64, required=True), + 'description': fields.text('Description'), + } mrp_property_group() class mrp_property(osv.osv): - _name = 'mrp.property' - _description = 'Property' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True), - 'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True), - 'description': fields.text('Description'), - } - _defaults = { - 'composition': lambda *a: 'min', - } + _name = 'mrp.property' + _description = 'Property' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'composition': fields.selection([('min','min'),('max','max'),('plus','plus')], 'Properties composition', required=True), + 'group_id': fields.many2one('mrp.property.group', 'Property Group', required=True), + 'description': fields.text('Description'), + } + _defaults = { + 'composition': lambda *a: 'min', + } mrp_property() class mrp_routing(osv.osv): - _name = 'mrp.routing' - _description = 'Routing' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'active': fields.boolean('Active'), - 'code': fields.char('Code', size=8), - - 'note': fields.text('Description'), - 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Workcenters'), - - 'location_id': fields.many2one('stock.location', 'Production Location'), - } - _defaults = { - 'active': lambda *a: 1, - } + _name = 'mrp.routing' + _description = 'Routing' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'active': fields.boolean('Active'), + 'code': fields.char('Code', size=8), + + 'note': fields.text('Description'), + 'workcenter_lines': fields.one2many('mrp.routing.workcenter', 'routing_id', 'Workcenters'), + + 'location_id': fields.many2one('stock.location', 'Production Location'), + } + _defaults = { + 'active': lambda *a: 1, + } mrp_routing() class mrp_routing_workcenter(osv.osv): - _name = 'mrp.routing.workcenter' - _description = 'Routing workcenter usage' - _columns = { - 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), - 'name': fields.char('Name', size=64, required=True), - 'sequence': fields.integer('Sequence'), - 'cycle_nbr': fields.float('Number of cycle', required=True), - 'hour_nbr': fields.float('Number of hours', required=True), - 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True), - 'note': fields.text('Description') - } - _defaults = { - 'cycle_nbr': lambda *a: 1.0, - 'hour_nbr': lambda *a: 0.0, - } + _name = 'mrp.routing.workcenter' + _description = 'Routing workcenter usage' + _columns = { + 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), + 'name': fields.char('Name', size=64, required=True), + 'sequence': fields.integer('Sequence'), + 'cycle_nbr': fields.float('Number of cycle', required=True), + 'hour_nbr': fields.float('Number of hours', required=True), + 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True), + 'note': fields.text('Description') + } + _defaults = { + 'cycle_nbr': lambda *a: 1.0, + 'hour_nbr': lambda *a: 0.0, + } mrp_routing_workcenter() class mrp_bom(osv.osv): - _name = 'mrp.bom' - _description = 'Bill of Material' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'code': fields.char('Code', size=16), - 'active': fields.boolean('Active'), - 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', required=True, help="Use a phantom bill of material in lines that have a sub-bom and that have to be automatically computed in one line, without habing two production orders."), - 'date_start': fields.date('Valid from'), - 'date_stop': fields.date('Valid until'), - 'sequence': fields.integer('Sequence'), - 'position': fields.char('Internal Ref.', size=64), - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_uos_qty': fields.float('Product UOS Qty'), - 'product_uos': fields.many2one('product.uom', 'Product UOS'), - 'product_qty': fields.float('Product Qty', required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_rounding': fields.float('Product Rounding'), - 'product_efficiency': fields.float('Product Efficiency', required=True), - 'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'), - 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True), - 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of workcenters) to produce the finnished product. The routing is mainly used to compute workcenter costs during operations and to plan futur loads on workcenters based on production plannification."), - 'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id','property_id', 'Properties'), - 'revision_ids': fields.one2many('mrp.bom.revision', 'bom_id', 'BoM Revisions'), - 'revision_type': fields.selection([('numeric','numeric indices'),('alpha','alphabetical indices')], 'indice type') - } - _defaults = { - 'active': lambda *a: 1, - 'product_efficiency': lambda *a: 1.0, - 'product_qty': lambda *a: 1.0, - 'product_rounding': lambda *a: 1.0, - 'type': lambda *a: 'normal', - } - _order = "sequence" - _sql_constraints = [ - ('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0 !'), - ] - def _check_recursion(self, cr, uid, ids): - level = 500 - while len(ids): - cr.execute('select distinct bom_id from mrp_bom where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive BoM.', ['parent_id']) - ] - - - def onchange_product_id(self, cr, uid, ids, product_id, name, context={}): - if product_id: - prod=self.pool.get('product.product').browse(cr,uid,[product_id])[0] - v = {'product_uom':prod.uom_id.id} - if not name: - v['name'] = prod.name - return {'value': v} - return {} - - def _bom_find(self, cr, uid, product_id, product_uom, properties = []): - bom_result = False - # Why searching on BoM without parent ? - cr.execute('select id from mrp_bom where product_id=%d and bom_id is null order by sequence', (product_id,)) - ids = map(lambda x: x[0], cr.fetchall()) - max_prop = 0 - result = False - for bom in self.pool.get('mrp.bom').browse(cr, uid, ids): - prop = 0 - for prop_id in bom.property_ids: - if prop_id.id in properties: - prop+=1 - if (prop>max_prop) or ((max_prop==0) and not result): - result = bom.id - return result - - def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10): - factor = factor / (bom.product_efficiency or 1.0) - factor = rounding(factor, bom.product_rounding) - if factor<bom.product_rounding: - factor = bom.product_rounding - result = [] - result2 = [] - if bom.type=='phantom' and not bom.bom_lines: - newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties) - if newbom: - res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor*bom.product_qty, properties, addthis=True, level=level+10) - result = result + res[0] - result2 = result2 + res[1] - else: - return [],[] - else: - if addthis and not bom.bom_lines: - result.append( - { - 'name': bom.product_id.name, - 'product_id': bom.product_id.id, - 'product_qty': bom.product_qty * factor, - 'product_uom': bom.product_uom.id, - 'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False, - 'product_uos': bom.product_uos and bom.product_uos.id or False, - }) - if bom.routing_id: - for wc_use in bom.routing_id.workcenter_lines: - wc = wc_use.workcenter_id - d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) - cycle = (d + (m and 1.0 or 0.0)) * wc_use.cycle_nbr - result2.append({ - 'name': bom.routing_id.name, - 'workcenter_id': wc.id, - 'sequence': level, - 'cycle': cycle, - 'hour': wc_use.hour_nbr + (wc.time_start+wc.time_stop+cycle*wc.time_cycle) * (wc.time_efficiency or 1.0), - }) - for bom2 in bom.bom_lines: - res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level+10) - result = result + res[0] - result2 = result2 + res[1] - return result, result2 - - def set_indices(self, cr, uid, ids, context = {}): - if not ids or (ids and not ids[0]): - return True - res = self.read(cr, uid, ids, ['revision_ids', 'revision_type']) - rev_ids = res[0]['revision_ids'] - idx = 1 - new_idx = [] - for rev_id in rev_ids: - if res[0]['revision_type'] == 'numeric': - self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : idx}) - else: - self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : "%c"%(idx+96,)}) - idx+=1 - return True + _name = 'mrp.bom' + _description = 'Bill of Material' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=16), + 'active': fields.boolean('Active'), + 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', required=True, help="Use a phantom bill of material in lines that have a sub-bom and that have to be automatically computed in one line, without habing two production orders."), + 'date_start': fields.date('Valid from'), + 'date_stop': fields.date('Valid until'), + 'sequence': fields.integer('Sequence'), + 'position': fields.char('Internal Ref.', size=64), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_uos_qty': fields.float('Product UOS Qty'), + 'product_uos': fields.many2one('product.uom', 'Product UOS'), + 'product_qty': fields.float('Product Qty', required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_rounding': fields.float('Product Rounding'), + 'product_efficiency': fields.float('Product Efficiency', required=True), + 'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'), + 'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True), + 'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of workcenters) to produce the finnished product. The routing is mainly used to compute workcenter costs during operations and to plan futur loads on workcenters based on production plannification."), + 'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id','property_id', 'Properties'), + 'revision_ids': fields.one2many('mrp.bom.revision', 'bom_id', 'BoM Revisions'), + 'revision_type': fields.selection([('numeric','numeric indices'),('alpha','alphabetical indices')], 'indice type') + } + _defaults = { + 'active': lambda *a: 1, + 'product_efficiency': lambda *a: 1.0, + 'product_qty': lambda *a: 1.0, + 'product_rounding': lambda *a: 1.0, + 'type': lambda *a: 'normal', + } + _order = "sequence" + _sql_constraints = [ + ('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0 !'), + ] + def _check_recursion(self, cr, uid, ids): + level = 500 + while len(ids): + cr.execute('select distinct bom_id from mrp_bom where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive BoM.', ['parent_id']) + ] + + + def onchange_product_id(self, cr, uid, ids, product_id, name, context={}): + if product_id: + prod=self.pool.get('product.product').browse(cr,uid,[product_id])[0] + v = {'product_uom':prod.uom_id.id} + if not name: + v['name'] = prod.name + return {'value': v} + return {} + + def _bom_find(self, cr, uid, product_id, product_uom, properties = []): + bom_result = False + # Why searching on BoM without parent ? + cr.execute('select id from mrp_bom where product_id=%d and bom_id is null order by sequence', (product_id,)) + ids = map(lambda x: x[0], cr.fetchall()) + max_prop = 0 + result = False + for bom in self.pool.get('mrp.bom').browse(cr, uid, ids): + prop = 0 + for prop_id in bom.property_ids: + if prop_id.id in properties: + prop+=1 + if (prop>max_prop) or ((max_prop==0) and not result): + result = bom.id + return result + + def _bom_explode(self, cr, uid, bom, factor, properties, addthis=False, level=10): + factor = factor / (bom.product_efficiency or 1.0) + factor = rounding(factor, bom.product_rounding) + if factor<bom.product_rounding: + factor = bom.product_rounding + result = [] + result2 = [] + if bom.type=='phantom' and not bom.bom_lines: + newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties) + if newbom: + res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor*bom.product_qty, properties, addthis=True, level=level+10) + result = result + res[0] + result2 = result2 + res[1] + else: + return [],[] + else: + if addthis and not bom.bom_lines: + result.append( + { + 'name': bom.product_id.name, + 'product_id': bom.product_id.id, + 'product_qty': bom.product_qty * factor, + 'product_uom': bom.product_uom.id, + 'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False, + 'product_uos': bom.product_uos and bom.product_uos.id or False, + }) + if bom.routing_id: + for wc_use in bom.routing_id.workcenter_lines: + wc = wc_use.workcenter_id + d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle) + cycle = (d + (m and 1.0 or 0.0)) * wc_use.cycle_nbr + result2.append({ + 'name': bom.routing_id.name, + 'workcenter_id': wc.id, + 'sequence': level, + 'cycle': cycle, + 'hour': wc_use.hour_nbr + (wc.time_start+wc.time_stop+cycle*wc.time_cycle) * (wc.time_efficiency or 1.0), + }) + for bom2 in bom.bom_lines: + res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level+10) + result = result + res[0] + result2 = result2 + res[1] + return result, result2 + + def set_indices(self, cr, uid, ids, context = {}): + if not ids or (ids and not ids[0]): + return True + res = self.read(cr, uid, ids, ['revision_ids', 'revision_type']) + rev_ids = res[0]['revision_ids'] + idx = 1 + new_idx = [] + for rev_id in rev_ids: + if res[0]['revision_type'] == 'numeric': + self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : idx}) + else: + self.pool.get('mrp.bom.revision').write(cr, uid, [rev_id], {'indice' : "%c"%(idx+96,)}) + idx+=1 + return True mrp_bom() class mrp_bom_revision(osv.osv): - _name = 'mrp.bom.revision' - _description = 'Bill of material revisions' - _columns = { - 'name': fields.char('Modification name', size=64, required=True), - 'description': fields.text('Description'), - 'date': fields.date('Modification Date'), - 'indice': fields.char('Revision', size=16), - 'last_indice': fields.char('last indice', size=64), - 'author_id': fields.many2one('res.users', 'Author'), - 'bom_id': fields.many2one('mrp.bom', 'BoM', select=True), - } - - _defaults = { - 'author_id': lambda x,y,z,c: z, - 'date': lambda *a: time.strftime('%Y-%m-%d'), - } + _name = 'mrp.bom.revision' + _description = 'Bill of material revisions' + _columns = { + 'name': fields.char('Modification name', size=64, required=True), + 'description': fields.text('Description'), + 'date': fields.date('Modification Date'), + 'indice': fields.char('Revision', size=16), + 'last_indice': fields.char('last indice', size=64), + 'author_id': fields.many2one('res.users', 'Author'), + 'bom_id': fields.many2one('mrp.bom', 'BoM', select=True), + } + + _defaults = { + 'author_id': lambda x,y,z,c: z, + 'date': lambda *a: time.strftime('%Y-%m-%d'), + } mrp_bom_revision() def rounding(f, r): - if not r: - return f - return round(f / r) * r + if not r: + return f + return round(f / r) * r class mrp_production(osv.osv): - _name = 'mrp.production' - _description = 'Production' - _date_name = 'date_planned' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'origin': fields.char('Origin', size=64), - 'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority'), - - 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','<>','service')]), - 'product_qty': fields.float('Product Qty', required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_uos_qty': fields.float('Product Qty'), - 'product_uos': fields.many2one('product.uom', 'Product UOM'), - - 'location_src_id': fields.many2one('stock.location', 'Production Location', required=True), - 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), - - 'date_planned': fields.date('Scheduled date', required=True), - 'date_start': fields.datetime('Start Date'), - 'date_finnished': fields.datetime('End Date'), - - 'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id','=',False)]), - - 'picking_id': fields.many2one('stock.picking', 'Packing list', readonly=True), - 'move_prod_id': fields.many2one('stock.move', 'Move product', readonly=True), - 'move_lines': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products Consummed'), - - 'move_created_ids': fields.one2many('stock.move', 'production_id', 'Moves Created'), - 'product_lines': fields.one2many('mrp.production.product.line', 'production_id', 'Scheduled goods'), - 'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Workcenters Utilisation'), - - 'state': fields.selection([('draft','Draft'),('picking_except', 'Packing Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],'State', readonly=True) - } - _defaults = { - 'priority': lambda *a: '1', - 'state': lambda *a: 'draft', - 'product_qty': lambda *a: 1.0, - 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.production') or '/', - } - _order = 'date_planned asc, priority desc'; - - def product_id_change(self, cr, uid, ids, product): - if not product: - return {} - res = self.pool.get('product.product').read(cr, uid, [product], ['uom_id'])[0] - uom = res['uom_id'] and res['uom_id'][0] - result = {'product_uom':uom} - return {'value':result} - - def action_picking_except(self, cr, uid, ids): - self.write(cr, uid, ids, {'state':'picking_except'}) - return True - - def action_compute(self, cr, uid, ids, properties=[]): - results = [] - for production in self.browse(cr, uid, ids): - cr.execute('delete from mrp_production_product_line where production_id=%d', (production.id,)) - cr.execute('delete from mrp_production_workcenter_line where production_id=%d', (production.id,)) - bom_point = production.bom_id - bom_id = production.bom_id.id - if not bom_point: - bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties) - if bom_id: - self.write(cr, uid, [production.id], {'bom_id': bom_id}) - bom_point = self.pool.get('mrp.bom').browse(cr, uid, [bom_id])[0] - - if not bom_id: - raise osv.except_osv('Error', "Couldn't find bill of material for product") - - #if bom_point.routing_id and bom_point.routing_id.location_id: - # self.write(cr, uid, [production.id], {'location_src_id': bom_point.routing_id.location_id.id}) - - factor = production.product_qty * production.product_uom.factor / bom_point.product_uom.factor - res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties) - results = res[0] - results2 = res[1] - for line in results: - line['production_id'] = production.id - self.pool.get('mrp.production.product.line').create(cr, uid, line) - for line in results2: - line['production_id'] = production.id - self.pool.get('mrp.production.workcenter.line').create(cr, uid, line) - return len(results) - - def action_cancel(self, cr, uid, ids): - for production in self.browse(cr, uid, ids): - if production.move_created_ids: - self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_created_ids]) - self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_lines]) - self.write(cr, uid, ids, {'state':'cancel','move_lines':[(6,0,[])]}) - return True - - #XXX: may be a bug here; lot_lines are unreserved for a few seconds; - # between the end of the picking list and the call to this function - def action_ready(self, cr, uid, ids): - self.write(cr, uid, ids, {'state':'ready'}) - for production in self.browse(cr, uid, ids): - if production.move_prod_id: - self.pool.get('stock.move').write(cr, uid, [production.move_prod_id.id], - {'location_id':production.location_dest_id.id}) - return True - - #TODO Review materials in function in_prod and prod_end. - def action_production_end(self, cr, uid, ids): - move_ids = [] - for production in self.browse(cr, uid, ids): - for res in production.move_lines: - for move in production.move_created_ids: - #XXX must use the orm - cr.execute('INSERT INTO stock_move_history_ids \ - (parent_id, child_id) VALUES (%d,%d)', - (res.id, move.id)) - move_ids.append(res.id) - if production.move_created_ids: - #TODO There we should handle the residus move creation - vals= {'state':'confirmed'} - new_moves = [x.id for x in production.move_created_ids] - self.pool.get('stock.move').write(cr, uid, new_moves, vals) - else: - #XXX Why is it there ? Aren't we suppose to already have a created_move ? - source = production.product_id.product_tmpl_id.property_stock_production.id - vals = { - 'name':'PROD:'+production.name, - 'date_planned': production.date_planned, - 'product_id': production.product_id.id, - 'product_qty': production.product_qty, - 'product_uom': production.product_uom.id, - 'product_uos_qty': production.product_uos and production.product_uos_qty or False, - 'product_uos': production.product_uos and production.product_uos.id or False, - 'location_id': source, - 'location_dest_id': production.location_dest_id.id, - 'move_dest_id': production.move_prod_id.id, - 'state': 'confirmed' - } - new_moves = [self.pool.get('stock.move').create(cr, uid, vals)] - self.write(cr, uid, [production.id], - {'move_created_ids': [(6, 'WTF', new_moves)]}) - if not production.date_finnished: - self.write(cr, uid, [production.id], - {'date_finnished': time.strftime('%Y-%m-%d %H:%M:%S')}) - self.pool.get('stock.move').check_assign(cr, uid, new_moves) - self.pool.get('stock.move').action_done(cr, uid, new_moves) - self._costs_generate(cr, uid, production) - self.pool.get('stock.move').action_done(cr, uid, move_ids) - self.write(cr, uid, ids, {'state': 'done'}) - return True - - def _costs_generate(self, cr, uid, production): - amount = 0.0 - for wc_line in production.workcenter_lines: - wc = wc_line.workcenter_id - if wc.costs_journal_id and wc.costs_general_account_id: - value = wc_line.hour * wc.costs_hour - account = wc.costs_hour_account_id.id - if value and account: - amount += value - self.pool.get('account.analytic.line').create(cr, uid, { - 'name': wc_line.name+' (H)', - 'amount': value, - 'account_id': account, - 'general_account_id': wc.costs_general_account_id.id, - 'journal_id': wc.costs_journal_id.id, - 'code': wc.code - } ) - if wc.costs_journal_id and wc.costs_general_account_id: - value = wc_line.cycle * wc.costs_cycle - account = wc.costs_cycle_account_id.id - if value and account: - amount += value - self.pool.get('account.analytic.line').create(cr, uid, { - 'name': wc_line.name+' (C)', - 'amount': value, - 'account_id': account, - 'general_account_id': wc.costs_general_account_id.id, - 'journal_id': wc.costs_journal_id.id, - 'code': wc.code - } ) - return amount - - def action_in_production(self, cr, uid, ids): - move_ids = [] - for production in self.browse(cr, uid, ids): - for res in production.move_lines: - move_ids.append(res.id) - if not production.date_start: - self.write(cr, uid, [production.id], - {'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}) - self.pool.get('stock.move').action_done(cr, uid, move_ids) - self.write(cr, uid, ids, {'state': 'in_production'}) - return True - - def test_if_product(self, cr, uid, ids): - res = True - for production in self.browse(cr, uid, ids): - if not production.product_lines: - if not self.action_compute(cr, uid, [production.id]): - res = False - return res - - def _get_auto_picking(self, cr, uid, production): - return True - - def action_confirm(self, cr, uid, ids): - picking_id=False - for production in self.browse(cr, uid, ids): - if not production.product_lines: - self.action_compute(cr, uid, [production.id]) - production = self.browse(cr, uid, [production.id])[0] - routing_loc = None - pick_type = 'internal' - address_id = False - if production.bom_id.routing_id and production.bom_id.routing_id.location_id: - routing_loc = production.bom_id.routing_id.location_id - if routing_loc.usage<>'internal': - pick_type = 'out' - address_id = routing_loc.address_id and routing_loc.address_id.id or False - routing_loc = routing_loc.id - picking_id = self.pool.get('stock.picking').create(cr, uid, { - 'origin': (production.origin or '').split(':')[0] +':'+production.name, - 'type': pick_type, - 'move_type': 'one', - 'state': 'auto', - 'address_id': address_id, - 'auto_picking': self._get_auto_picking(cr, uid, production), - }) - toconfirm = True - - source = production.product_id.product_tmpl_id.property_stock_production.id - data = { - 'name':'PROD:'+production.name, - 'date_planned': production.date_planned, - 'product_id': production.product_id.id, - 'product_qty': production.product_qty, - 'product_uom': production.product_uom.id, - 'product_uos_qty': production.product_uos and production.product_uos_qty or False, - 'product_uos': production.product_uos and production.product_uos.id or False, - 'location_id': source, - 'location_dest_id': production.location_dest_id.id, - 'move_dest_id': production.move_prod_id.id, - 'state': 'waiting' - } - res_final_id = self.pool.get('stock.move').create(cr, uid, data) - - self.write(cr, uid, [production.id], {'move_created_ids': [(6, 'WTF', [res_final_id])]}) - moves = [] - for line in production.product_lines: - move_id=False - newdate = production.date_planned - if line.product_id.type in ('product', 'consu'): - res_dest_id = self.pool.get('stock.move').create(cr, uid, { - 'name':'PROD:'+production.name, - 'date_planned': production.date_planned, - 'product_id': line.product_id.id, - 'product_qty': line.product_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos and line.product_uos_qty or False, - 'product_uos': line.product_uos and line.product_uos.id or False, - 'location_id': routing_loc or production.location_src_id.id, - 'location_dest_id': source, - 'move_dest_id': res_final_id, - 'state': 'waiting', - }) - moves.append(res_dest_id) - move_id = self.pool.get('stock.move').create(cr, uid, { - 'name':'PROD:'+production.name, - 'picking_id':picking_id, - 'product_id': line.product_id.id, - 'product_qty': line.product_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos and line.product_uos_qty or False, - 'product_uos': line.product_uos and line.product_uos.id or False, - 'date_planned': newdate, - 'move_dest_id': res_dest_id, - 'location_id': production.location_src_id.id, - 'location_dest_id': routing_loc or production.location_src_id.id, - 'state': 'waiting', - }) - proc_id = self.pool.get('mrp.procurement').create(cr, uid, { - 'name': (production.origin or '').split(':')[0] + ':' + production.name, - 'origin': (production.origin or '').split(':')[0] + ':' + production.name, - 'date_planned': newdate, - 'product_id': line.product_id.id, - 'product_qty': line.product_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos and line.product_qty or False, - 'product_uos': line.product_uos and line.product_uos.id or False, - 'location_id': production.location_src_id.id, - 'procure_method': line.product_id.procure_method, - 'move_id': move_id, - }) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) - if toconfirm: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) - self.write(cr, uid, [production.id], {'picking_id':picking_id, 'move_lines': [(6,0,moves)], 'state':'confirmed'}) - return picking_id - - def force_production(self, cr, uid, ids, *args): - pick_obj = self.pool.get('stock.picking') - pick_obj.force_assign(cr, uid, [prod.picking_id.id for prod in self.browse(cr, uid, ids)]) - return True + _name = 'mrp.production' + _description = 'Production' + _date_name = 'date_planned' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'origin': fields.char('Origin', size=64), + 'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority'), + + 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','<>','service')]), + 'product_qty': fields.float('Product Qty', required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_uos_qty': fields.float('Product Qty'), + 'product_uos': fields.many2one('product.uom', 'Product UOM'), + + 'location_src_id': fields.many2one('stock.location', 'Production Location', required=True), + 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), + + 'date_planned': fields.date('Scheduled date', required=True), + 'date_start': fields.datetime('Start Date'), + 'date_finnished': fields.datetime('End Date'), + + 'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id','=',False)]), + + 'picking_id': fields.many2one('stock.picking', 'Packing list', readonly=True), + 'move_prod_id': fields.many2one('stock.move', 'Move product', readonly=True), + 'move_lines': fields.many2many('stock.move', 'mrp_production_move_ids', 'production_id', 'move_id', 'Products Consummed'), + + 'move_created_ids': fields.one2many('stock.move', 'production_id', 'Moves Created'), + 'product_lines': fields.one2many('mrp.production.product.line', 'production_id', 'Scheduled goods'), + 'workcenter_lines': fields.one2many('mrp.production.workcenter.line', 'production_id', 'Workcenters Utilisation'), + + 'state': fields.selection([('draft','Draft'),('picking_except', 'Packing Exception'),('confirmed','Waiting Goods'),('ready','Ready to Produce'),('in_production','In Production'),('cancel','Canceled'),('done','Done')],'State', readonly=True) + } + _defaults = { + 'priority': lambda *a: '1', + 'state': lambda *a: 'draft', + 'product_qty': lambda *a: 1.0, + 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.production') or '/', + } + _order = 'date_planned asc, priority desc'; + + def product_id_change(self, cr, uid, ids, product): + if not product: + return {} + res = self.pool.get('product.product').read(cr, uid, [product], ['uom_id'])[0] + uom = res['uom_id'] and res['uom_id'][0] + result = {'product_uom':uom} + return {'value':result} + + def action_picking_except(self, cr, uid, ids): + self.write(cr, uid, ids, {'state':'picking_except'}) + return True + + def action_compute(self, cr, uid, ids, properties=[]): + results = [] + for production in self.browse(cr, uid, ids): + cr.execute('delete from mrp_production_product_line where production_id=%d', (production.id,)) + cr.execute('delete from mrp_production_workcenter_line where production_id=%d', (production.id,)) + bom_point = production.bom_id + bom_id = production.bom_id.id + if not bom_point: + bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties) + if bom_id: + self.write(cr, uid, [production.id], {'bom_id': bom_id}) + bom_point = self.pool.get('mrp.bom').browse(cr, uid, [bom_id])[0] + + if not bom_id: + raise osv.except_osv('Error', "Couldn't find bill of material for product") + + #if bom_point.routing_id and bom_point.routing_id.location_id: + # self.write(cr, uid, [production.id], {'location_src_id': bom_point.routing_id.location_id.id}) + + factor = production.product_qty * production.product_uom.factor / bom_point.product_uom.factor + res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties) + results = res[0] + results2 = res[1] + for line in results: + line['production_id'] = production.id + self.pool.get('mrp.production.product.line').create(cr, uid, line) + for line in results2: + line['production_id'] = production.id + self.pool.get('mrp.production.workcenter.line').create(cr, uid, line) + return len(results) + + def action_cancel(self, cr, uid, ids): + for production in self.browse(cr, uid, ids): + if production.move_created_ids: + self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_created_ids]) + self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in production.move_lines]) + self.write(cr, uid, ids, {'state':'cancel','move_lines':[(6,0,[])]}) + return True + + #XXX: may be a bug here; lot_lines are unreserved for a few seconds; + # between the end of the picking list and the call to this function + def action_ready(self, cr, uid, ids): + self.write(cr, uid, ids, {'state':'ready'}) + for production in self.browse(cr, uid, ids): + if production.move_prod_id: + self.pool.get('stock.move').write(cr, uid, [production.move_prod_id.id], + {'location_id':production.location_dest_id.id}) + return True + + #TODO Review materials in function in_prod and prod_end. + def action_production_end(self, cr, uid, ids): + move_ids = [] + for production in self.browse(cr, uid, ids): + for res in production.move_lines: + for move in production.move_created_ids: + #XXX must use the orm + cr.execute('INSERT INTO stock_move_history_ids \ + (parent_id, child_id) VALUES (%d,%d)', + (res.id, move.id)) + move_ids.append(res.id) + if production.move_created_ids: + #TODO There we should handle the residus move creation + vals= {'state':'confirmed'} + new_moves = [x.id for x in production.move_created_ids] + self.pool.get('stock.move').write(cr, uid, new_moves, vals) + else: + #XXX Why is it there ? Aren't we suppose to already have a created_move ? + source = production.product_id.product_tmpl_id.property_stock_production.id + vals = { + 'name':'PROD:'+production.name, + 'date_planned': production.date_planned, + 'product_id': production.product_id.id, + 'product_qty': production.product_qty, + 'product_uom': production.product_uom.id, + 'product_uos_qty': production.product_uos and production.product_uos_qty or False, + 'product_uos': production.product_uos and production.product_uos.id or False, + 'location_id': source, + 'location_dest_id': production.location_dest_id.id, + 'move_dest_id': production.move_prod_id.id, + 'state': 'confirmed' + } + new_moves = [self.pool.get('stock.move').create(cr, uid, vals)] + self.write(cr, uid, [production.id], + {'move_created_ids': [(6, 'WTF', new_moves)]}) + if not production.date_finnished: + self.write(cr, uid, [production.id], + {'date_finnished': time.strftime('%Y-%m-%d %H:%M:%S')}) + self.pool.get('stock.move').check_assign(cr, uid, new_moves) + self.pool.get('stock.move').action_done(cr, uid, new_moves) + self._costs_generate(cr, uid, production) + self.pool.get('stock.move').action_done(cr, uid, move_ids) + self.write(cr, uid, ids, {'state': 'done'}) + return True + + def _costs_generate(self, cr, uid, production): + amount = 0.0 + for wc_line in production.workcenter_lines: + wc = wc_line.workcenter_id + if wc.costs_journal_id and wc.costs_general_account_id: + value = wc_line.hour * wc.costs_hour + account = wc.costs_hour_account_id.id + if value and account: + amount += value + self.pool.get('account.analytic.line').create(cr, uid, { + 'name': wc_line.name+' (H)', + 'amount': value, + 'account_id': account, + 'general_account_id': wc.costs_general_account_id.id, + 'journal_id': wc.costs_journal_id.id, + 'code': wc.code + } ) + if wc.costs_journal_id and wc.costs_general_account_id: + value = wc_line.cycle * wc.costs_cycle + account = wc.costs_cycle_account_id.id + if value and account: + amount += value + self.pool.get('account.analytic.line').create(cr, uid, { + 'name': wc_line.name+' (C)', + 'amount': value, + 'account_id': account, + 'general_account_id': wc.costs_general_account_id.id, + 'journal_id': wc.costs_journal_id.id, + 'code': wc.code + } ) + return amount + + def action_in_production(self, cr, uid, ids): + move_ids = [] + for production in self.browse(cr, uid, ids): + for res in production.move_lines: + move_ids.append(res.id) + if not production.date_start: + self.write(cr, uid, [production.id], + {'date_start': time.strftime('%Y-%m-%d %H:%M:%S')}) + self.pool.get('stock.move').action_done(cr, uid, move_ids) + self.write(cr, uid, ids, {'state': 'in_production'}) + return True + + def test_if_product(self, cr, uid, ids): + res = True + for production in self.browse(cr, uid, ids): + if not production.product_lines: + if not self.action_compute(cr, uid, [production.id]): + res = False + return res + + def _get_auto_picking(self, cr, uid, production): + return True + + def action_confirm(self, cr, uid, ids): + picking_id=False + for production in self.browse(cr, uid, ids): + if not production.product_lines: + self.action_compute(cr, uid, [production.id]) + production = self.browse(cr, uid, [production.id])[0] + routing_loc = None + pick_type = 'internal' + address_id = False + if production.bom_id.routing_id and production.bom_id.routing_id.location_id: + routing_loc = production.bom_id.routing_id.location_id + if routing_loc.usage<>'internal': + pick_type = 'out' + address_id = routing_loc.address_id and routing_loc.address_id.id or False + routing_loc = routing_loc.id + picking_id = self.pool.get('stock.picking').create(cr, uid, { + 'origin': (production.origin or '').split(':')[0] +':'+production.name, + 'type': pick_type, + 'move_type': 'one', + 'state': 'auto', + 'address_id': address_id, + 'auto_picking': self._get_auto_picking(cr, uid, production), + }) + toconfirm = True + + source = production.product_id.product_tmpl_id.property_stock_production.id + data = { + 'name':'PROD:'+production.name, + 'date_planned': production.date_planned, + 'product_id': production.product_id.id, + 'product_qty': production.product_qty, + 'product_uom': production.product_uom.id, + 'product_uos_qty': production.product_uos and production.product_uos_qty or False, + 'product_uos': production.product_uos and production.product_uos.id or False, + 'location_id': source, + 'location_dest_id': production.location_dest_id.id, + 'move_dest_id': production.move_prod_id.id, + 'state': 'waiting' + } + res_final_id = self.pool.get('stock.move').create(cr, uid, data) + + self.write(cr, uid, [production.id], {'move_created_ids': [(6, 'WTF', [res_final_id])]}) + moves = [] + for line in production.product_lines: + move_id=False + newdate = production.date_planned + if line.product_id.type in ('product', 'consu'): + res_dest_id = self.pool.get('stock.move').create(cr, uid, { + 'name':'PROD:'+production.name, + 'date_planned': production.date_planned, + 'product_id': line.product_id.id, + 'product_qty': line.product_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos and line.product_uos_qty or False, + 'product_uos': line.product_uos and line.product_uos.id or False, + 'location_id': routing_loc or production.location_src_id.id, + 'location_dest_id': source, + 'move_dest_id': res_final_id, + 'state': 'waiting', + }) + moves.append(res_dest_id) + move_id = self.pool.get('stock.move').create(cr, uid, { + 'name':'PROD:'+production.name, + 'picking_id':picking_id, + 'product_id': line.product_id.id, + 'product_qty': line.product_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos and line.product_uos_qty or False, + 'product_uos': line.product_uos and line.product_uos.id or False, + 'date_planned': newdate, + 'move_dest_id': res_dest_id, + 'location_id': production.location_src_id.id, + 'location_dest_id': routing_loc or production.location_src_id.id, + 'state': 'waiting', + }) + proc_id = self.pool.get('mrp.procurement').create(cr, uid, { + 'name': (production.origin or '').split(':')[0] + ':' + production.name, + 'origin': (production.origin or '').split(':')[0] + ':' + production.name, + 'date_planned': newdate, + 'product_id': line.product_id.id, + 'product_qty': line.product_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos and line.product_qty or False, + 'product_uos': line.product_uos and line.product_uos.id or False, + 'location_id': production.location_src_id.id, + 'procure_method': line.product_id.procure_method, + 'move_id': move_id, + }) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) + if toconfirm: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + self.write(cr, uid, [production.id], {'picking_id':picking_id, 'move_lines': [(6,0,moves)], 'state':'confirmed'}) + return picking_id + + def force_production(self, cr, uid, ids, *args): + pick_obj = self.pool.get('stock.picking') + pick_obj.force_assign(cr, uid, [prod.picking_id.id for prod in self.browse(cr, uid, ids)]) + return True mrp_production() class stock_move(osv.osv): - _name = 'stock.move' - _inherit = 'stock.move' + _name = 'stock.move' + _inherit = 'stock.move' - _columns = { - 'production_id': fields.many2one('mrp.production', 'Production', select=True), - } + _columns = { + 'production_id': fields.many2one('mrp.production', 'Production', select=True), + } stock_move() class mrp_production_workcenter_line(osv.osv): - _name = 'mrp.production.workcenter.line' - _description = 'Production workcenters used' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), - 'cycle': fields.float('Nbr of cycle'), - 'hour': fields.float('Nbr of hour'), - 'sequence': fields.integer('Sequence', required=True), - 'production_id': fields.many2one('mrp.production', 'Production Order', select=True), - } - _defaults = { - 'sequence': lambda *a: 1, - 'hour': lambda *a: 0, - 'cycle': lambda *a: 0, - } + _name = 'mrp.production.workcenter.line' + _description = 'Production workcenters used' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), + 'cycle': fields.float('Nbr of cycle'), + 'hour': fields.float('Nbr of hour'), + 'sequence': fields.integer('Sequence', required=True), + 'production_id': fields.many2one('mrp.production', 'Production Order', select=True), + } + _defaults = { + 'sequence': lambda *a: 1, + 'hour': lambda *a: 0, + 'cycle': lambda *a: 0, + } mrp_production_workcenter_line() class mrp_production_product_line(osv.osv): - _name = 'mrp.production.product.line' - _description = 'Production scheduled products' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_qty': fields.float('Product Qty', required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_uos_qty': fields.float('Product UOS Qty'), - 'product_uos': fields.many2one('product.uom', 'Product UOS'), - 'production_id': fields.many2one('mrp.production', 'Production Order', select=True), - } + _name = 'mrp.production.product.line' + _description = 'Production scheduled products' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_qty': fields.float('Product Qty', required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_uos_qty': fields.float('Product UOS Qty'), + 'product_uos': fields.many2one('product.uom', 'Product UOS'), + 'production_id': fields.many2one('mrp.production', 'Production Order', select=True), + } mrp_production_product_line() # ------------------------------------------------------------------ @@ -656,475 +656,475 @@ mrp_production_product_line() # then wizard for picking lists & move # class mrp_procurement(osv.osv): - _name = "mrp.procurement" - _description = "Procurement" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'origin': fields.char('Origin', size=64), - 'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority', required=True), - 'date_planned': fields.date('Scheduled date', required=True), - 'date_close': fields.date('Date Closed'), - 'product_id': fields.many2one('product.product', 'Product', required=True), - 'product_qty': fields.float('Quantity', required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_uos_qty': fields.float('Quantity'), - 'product_uos': fields.many2one('product.uom', 'Product UOM'), - 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), - - 'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade', select=True), - - 'close_move': fields.boolean('Close Move at end', required=True), - 'location_id': fields.many2one('stock.location', 'Location', required=True), - 'procure_method': fields.selection([('make_to_stock','from stock'),('make_to_order','on order')], 'Procurement Method', states={'draft':[('readonly',False)], 'confirmed':[('readonly',False)]}, readonly=True), - - 'purchase_id': fields.many2one('purchase.order', 'Purchase Order'), - 'purchase_line_id': fields.many2one('purchase.order.line', 'Purchase Order Line'), - - 'property_ids': fields.many2many('mrp.property', 'mrp_procurement_property_rel', 'procurement_id','property_id', 'Properties'), - - 'message': fields.char('Latest error', size=64), - 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('exception','Exception'),('running','Running'),('cancel','Cancel'),('done','Done'),('waiting','Waiting')], 'State') - } - _defaults = { - 'state': lambda *a: 'draft', - 'priority': lambda *a: '1', - 'close_move': lambda *a: 0, - 'procure_method': lambda *a: 'make_to_order', - } - def check_product(self, cr, uid, ids): - for procurement in self.browse(cr, uid, ids): - if procurement.product_id.type in ('product', 'consu'): - return True - return False - - def check_move_cancel(self, cr, uid, ids, context={}): - res = True - for procurement in self.browse(cr, uid, ids, context): - if procurement.move_id: - if not procurement.move_id.state=='cancel': - res = False - return res - - def check_move_done(self, cr, uid, ids, context={}): - res = True - for proc in self.browse(cr, uid, ids, context): - if proc.move_id: - if not proc.move_id.state=='done': - res = False - return res - - # - # This method may be overrided by objects that override mrp.procurment - # for computing their own purpose - # - def _quantity_compute_get(self, cr, uid, proc, context={}): - if proc.product_id.type=='product': - return proc.move_id.product_uos_qty - return False - - def _uom_compute_get(self, cr, uid, proc, context={}): - if proc.product_id.type=='product': - if proc.move_id.product_uos: - return proc.move_id.product_uos.id - return False - - # - # Return the quantity of product shipped/produced/served, wich may be - # different from the planned quantity - # - def quantity_get(self, cr, uid, id, context={}): - proc = self.browse(cr, uid, id, context) - result = self._quantity_compute_get(cr, uid, proc, context) - if not result: - result = proc.product_qty - return result - - def uom_get(self, cr, uid, id, context=None): - proc = self.browse(cr, uid, id, context) - result = self._uom_compute_get(cr, uid, proc, context) - if not result: - result = proc.product_uom.id - return result - - def check_waiting(self, cr, uid, ids, context=[]): - for procurement in self.browse(cr, uid, ids, context=context): - if procurement.move_id and procurement.move_id.state=='auto': - return True - return False - - def check_produce_service(self, cr, uid, procurement, context=[]): - return True - - def check_produce_product(self, cr, uid, procurement, context=[]): - properties = [x.id for x in procurement.property_ids] - bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties) - if not bom_id: - cr.execute('update mrp_procurement set message=%s where id=%d', ('No BoM defined for this product !', procurement.id)) - return False - return True - - def check_make_to_stock(self, cr, uid, ids, context={}): - ok = True - for procurement in self.browse(cr, uid, ids, context=context): - if procurement.product_id.type=='service': - ok = ok and self._check_make_to_stock_service(cr, uid, procurement, context) - else: - ok = ok and self._check_make_to_stock_product(cr, uid, procurement, context) - return ok - - def check_produce(self, cr, uid, ids, context={}): - res = True - user = self.pool.get('res.users').browse(cr, uid, uid) - for procurement in self.browse(cr, uid, ids): - if procurement.product_id.product_tmpl_id.supply_method=='buy': - if procurement.product_id.seller_ids: - partner = procurement.product_id.seller_ids[0].name - if user.company_id and user.company_id.partner_id: - if partner.id == user.company_id.partner_id.id: - return True - return False - if procurement.product_id.product_tmpl_id.type=='service': - res = res and self.check_produce_service(cr, uid, procurement, context) - else: - res = res and self.check_produce_product(cr, uid, procurement, context) - if not res: - return False - return res - - def check_buy(self, cr, uid, ids): - user = self.pool.get('res.users').browse(cr, uid, uid) - for procurement in self.browse(cr, uid, ids): - if procurement.product_id.product_tmpl_id.supply_method=='produce': - return False - if not procurement.product_id.seller_ids: - cr.execute('update mrp_procurement set message=%s where id=%d', ('No supplier defined for this product !', procurement.id)) - return False - partner = procurement.product_id.seller_ids[0].name - if user.company_id and user.company_id.partner_id: - if partner.id == user.company_id.partner_id.id: - return False - address_id = self.pool.get('res.partner').address_get(cr, uid, [partner.id], ['delivery'])['delivery'] - if not address_id: - cr.execute('update mrp_procurement set message=%s where id=%d', ('No address defined for the supplier', procurement.id)) - return False - return True - - def test_cancel(self, cr, uid, ids): - for record in self.browse(cr, uid, ids): - if record.move_id and record.move_id.state=='cancel': - return True - return False - - def action_confirm(self, cr, uid, ids): - for procurement in self.browse(cr, uid, ids): - if procurement.product_id.type in ('product', 'consu'): - if not procurement.move_id: - source = procurement.location_id.id - if procurement.procure_method=='make_to_order': - source = procurement.product_id.product_tmpl_id.property_stock_procurement.id - id = self.pool.get('stock.move').create(cr, uid, { - 'name': 'PROC:'+procurement.name, - 'location_id': source, - 'location_dest_id': procurement.location_id.id, - 'product_id': procurement.product_id.id, - 'product_qty':procurement.product_qty, - 'product_uom': procurement.product_uom.id, - 'date_planned': procurement.date_planned, - 'state':'confirmed', - }) - self.write(cr, uid, [procurement.id], {'move_id': id, 'close_move':1}) - else: - # TODO: check this - if procurement.procure_method=='make_to_stock' and procurement.move_id.state in ('waiting',): - id = self.pool.get('stock.move').write(cr, uid, [procurement.move_id.id], {'state':'confirmed'}) - self.write(cr, uid, ids, {'state':'confirmed','message':''}) - return True - - def action_move_assigned(self, cr, uid, ids): - self.write(cr, uid, ids, {'state':'running','message':'from stock: products assigned.'}) - return True - - def _check_make_to_stock_service(self, cr, uid, procurement, context={}): - return True - - def _check_make_to_stock_product(self, cr, uid, procurement, context={}): - ok = True - if procurement.move_id: - id = procurement.move_id.id - if not (procurement.move_id.state in ('done','assigned','cancel')): - ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) - cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%d', (procurement.product_id.id,)) - if not cr.fetchone()[0]: - cr.execute('update mrp_procurement set message=%s where id=%d', ('from stock and no minimum orderpoint rule defined', procurement.id)) - return ok - - def action_produce_assign_service(self, cr, uid, ids, context={}): - for procurement in self.browse(cr, uid, ids): - self.write(cr, uid, [procurement.id], {'state':'running'}) - return True - - def action_produce_assign_product(self, cr, uid, ids, context={}): - produce_id = False - for procurement in self.browse(cr, uid, ids): - res_id = procurement.move_id.id - loc_id = procurement.location_id.id - newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d') - DateTime.RelativeDateTime(days=procurement.product_id.product_tmpl_id.produce_delay or 0.0) - produce_id = self.pool.get('mrp.production').create(cr, uid, { - 'origin': procurement.origin, - 'product_id': procurement.product_id.id, - 'product_qty': procurement.product_qty, - 'product_uom': procurement.product_uom.id, - 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False, - 'product_uos': procurement.product_uos and procurement.product_uos.id or False, - 'location_src_id': procurement.location_id.id, - 'location_dest_id': procurement.location_id.id, - 'bom_id': procurement.bom_id and procurement.bom_id.id or False, - 'date_planned': newdate, - 'move_prod_id': res_id, - }) - self.write(cr, uid, [procurement.id], {'state':'running'}) - bom_result = self.pool.get('mrp.production').action_compute(cr, uid, - [produce_id], properties=[x.id for x in procurement.property_ids]) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr) - return produce_id - - def action_po_assign(self, cr, uid, ids): - purchase_id = False - for procurement in self.browse(cr, uid, ids): - res_id = procurement.move_id.id - partner = procurement.product_id.seller_ids[0].name - partner_id = partner.id - address_id = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['delivery'])['delivery'] - pricelist_id = partner.property_product_pricelist_purchase.id - - uom_id = procurement.product_id.uom_po_id.id - - qty = self.pool.get('product.uom')._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, uom_id) - if procurement.product_id.seller_ids[0].qty: - qty=max(qty,procurement.product_id.seller_ids[0].qty) - - price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, False, {'uom': uom_id})[pricelist_id] - - newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d') - DateTime.RelativeDateTime(days=procurement.product_id.product_tmpl_id.seller_delay or 0.0) - line = { - 'name': procurement.product_id.name, - 'product_qty': qty, - 'product_id': procurement.product_id.id, - 'product_uom': uom_id, - 'price_unit': price, - 'date_planned': newdate.strftime('%Y-%m-%d'), - 'taxes_id': [(6, 0, [x.id for x in procurement.product_id.product_tmpl_id.supplier_taxes_id])], - 'move_dest_id': res_id, - } - - purchase_id = self.pool.get('purchase.order').create(cr, uid, { - 'origin': procurement.origin, - 'partner_id': partner_id, - 'partner_address_id': address_id, - 'location_id': procurement.location_id.id, - 'pricelist_id': pricelist_id, - 'order_line': [(0,0,line)] - }) - self.write(cr, uid, [procurement.id], {'state':'running', 'purchase_id':purchase_id}) - return purchase_id - - def action_cancel(self, cr, uid, ids): - todo = [] - for proc in self.browse(cr, uid, ids): - if proc.move_id: - todo.append(proc.move_id.id) - if len(todo): - self.pool.get('stock.move').action_cancel(cr, uid, [proc.move_id.id]) - self.write(cr, uid, ids, {'state':'cancel'}) - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_trigger(uid, 'mrp.procurement', id, cr) - - return True - - def action_check_finnished(self, cr, uid, ids): - return True - - def action_check(self, cr, uid, ids): - ok = False - for procurement in self.browse(cr, uid, ids): - if procurement.move_id.state=='assigned' or procurement.move_id.state=='done': - self.action_done(cr, uid, [procurement.id]) - ok = True - return ok - - def action_done(self, cr, uid, ids): - for procurement in self.browse(cr, uid, ids): - if procurement.move_id: - if procurement.close_move and (procurement.move_id.state <> 'done'): - self.pool.get('stock.move').action_done(cr, uid, [procurement.move_id.id]) - res = self.write(cr, uid, ids, {'state':'done', 'date_close':time.strftime('%Y-%m-%d')}) - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_trigger(uid, 'mrp.procurement', id, cr) - return res - def run_scheduler(self, cr, uid, user_id=False, schedule_cycle=1.0,\ - po_cycle=1.0, po_lead=1.0, security_lead=50.0, picking_lead=1.0,\ - automatic=False, use_new_cursor=False, context=None): - ''' - use_new_cursor: False or the dbname - ''' - if not context: - context={} - self.run_procure_confirm(cr, uid, schedule_cycle=schedule_cycle,\ - po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ - picking_lead=picking_lead, user_id=user_id,\ - use_new_cursor=use_new_cursor, context=context) - self.run_orderpoint_confirm(cr, uid, automatic=automatic,\ - use_new_cursor=use_new_cursor, context=context, user_id=user_id) - - def run_procure_confirm(self, cr, uid, user_id=False, schedule_cycle=1.0,\ - po_cycle=1.0, po_lead=1.0, security_lead=50.0, picking_lead=1.0, \ - use_new_cursor=False, context=None): - ''' - use_new_cursor: False or the dbname - ''' - from wizard.schedulers import _procure_confirm - if not context: - context={} - _procure_confirm(self, cr, uid, schedule_cycle=schedule_cycle,\ - po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ - picking_lead=picking_lead, user_id=user_id, use_new_cursor=use_new_cursor,\ - context=context) - - def run_orderpoint_confirm(self, cr, uid, automatic=False, use_new_cursor=False,\ - context=None, user_id=False): - ''' - use_new_cursor: False or the dbname - ''' - from wizard.schedulers import _procure_orderpoint_confirm - if not context: - context={} - _procure_orderpoint_confirm(self, cr, uid, automatic=automatic,\ - use_new_cursor=use_new_cursor, context=context, user_id=user_id) + _name = "mrp.procurement" + _description = "Procurement" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'origin': fields.char('Origin', size=64), + 'priority': fields.selection([('0','Not urgent'),('1','Normal'),('2','Urgent'),('3','Very Urgent')], 'Priority', required=True), + 'date_planned': fields.date('Scheduled date', required=True), + 'date_close': fields.date('Date Closed'), + 'product_id': fields.many2one('product.product', 'Product', required=True), + 'product_qty': fields.float('Quantity', required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_uos_qty': fields.float('Quantity'), + 'product_uos': fields.many2one('product.uom', 'Product UOM'), + 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), + + 'bom_id': fields.many2one('mrp.bom', 'BoM', ondelete='cascade', select=True), + + 'close_move': fields.boolean('Close Move at end', required=True), + 'location_id': fields.many2one('stock.location', 'Location', required=True), + 'procure_method': fields.selection([('make_to_stock','from stock'),('make_to_order','on order')], 'Procurement Method', states={'draft':[('readonly',False)], 'confirmed':[('readonly',False)]}, readonly=True), + + 'purchase_id': fields.many2one('purchase.order', 'Purchase Order'), + 'purchase_line_id': fields.many2one('purchase.order.line', 'Purchase Order Line'), + + 'property_ids': fields.many2many('mrp.property', 'mrp_procurement_property_rel', 'procurement_id','property_id', 'Properties'), + + 'message': fields.char('Latest error', size=64), + 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('exception','Exception'),('running','Running'),('cancel','Cancel'),('done','Done'),('waiting','Waiting')], 'State') + } + _defaults = { + 'state': lambda *a: 'draft', + 'priority': lambda *a: '1', + 'close_move': lambda *a: 0, + 'procure_method': lambda *a: 'make_to_order', + } + def check_product(self, cr, uid, ids): + for procurement in self.browse(cr, uid, ids): + if procurement.product_id.type in ('product', 'consu'): + return True + return False + + def check_move_cancel(self, cr, uid, ids, context={}): + res = True + for procurement in self.browse(cr, uid, ids, context): + if procurement.move_id: + if not procurement.move_id.state=='cancel': + res = False + return res + + def check_move_done(self, cr, uid, ids, context={}): + res = True + for proc in self.browse(cr, uid, ids, context): + if proc.move_id: + if not proc.move_id.state=='done': + res = False + return res + + # + # This method may be overrided by objects that override mrp.procurment + # for computing their own purpose + # + def _quantity_compute_get(self, cr, uid, proc, context={}): + if proc.product_id.type=='product': + return proc.move_id.product_uos_qty + return False + + def _uom_compute_get(self, cr, uid, proc, context={}): + if proc.product_id.type=='product': + if proc.move_id.product_uos: + return proc.move_id.product_uos.id + return False + + # + # Return the quantity of product shipped/produced/served, wich may be + # different from the planned quantity + # + def quantity_get(self, cr, uid, id, context={}): + proc = self.browse(cr, uid, id, context) + result = self._quantity_compute_get(cr, uid, proc, context) + if not result: + result = proc.product_qty + return result + + def uom_get(self, cr, uid, id, context=None): + proc = self.browse(cr, uid, id, context) + result = self._uom_compute_get(cr, uid, proc, context) + if not result: + result = proc.product_uom.id + return result + + def check_waiting(self, cr, uid, ids, context=[]): + for procurement in self.browse(cr, uid, ids, context=context): + if procurement.move_id and procurement.move_id.state=='auto': + return True + return False + + def check_produce_service(self, cr, uid, procurement, context=[]): + return True + + def check_produce_product(self, cr, uid, procurement, context=[]): + properties = [x.id for x in procurement.property_ids] + bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties) + if not bom_id: + cr.execute('update mrp_procurement set message=%s where id=%d', ('No BoM defined for this product !', procurement.id)) + return False + return True + + def check_make_to_stock(self, cr, uid, ids, context={}): + ok = True + for procurement in self.browse(cr, uid, ids, context=context): + if procurement.product_id.type=='service': + ok = ok and self._check_make_to_stock_service(cr, uid, procurement, context) + else: + ok = ok and self._check_make_to_stock_product(cr, uid, procurement, context) + return ok + + def check_produce(self, cr, uid, ids, context={}): + res = True + user = self.pool.get('res.users').browse(cr, uid, uid) + for procurement in self.browse(cr, uid, ids): + if procurement.product_id.product_tmpl_id.supply_method=='buy': + if procurement.product_id.seller_ids: + partner = procurement.product_id.seller_ids[0].name + if user.company_id and user.company_id.partner_id: + if partner.id == user.company_id.partner_id.id: + return True + return False + if procurement.product_id.product_tmpl_id.type=='service': + res = res and self.check_produce_service(cr, uid, procurement, context) + else: + res = res and self.check_produce_product(cr, uid, procurement, context) + if not res: + return False + return res + + def check_buy(self, cr, uid, ids): + user = self.pool.get('res.users').browse(cr, uid, uid) + for procurement in self.browse(cr, uid, ids): + if procurement.product_id.product_tmpl_id.supply_method=='produce': + return False + if not procurement.product_id.seller_ids: + cr.execute('update mrp_procurement set message=%s where id=%d', ('No supplier defined for this product !', procurement.id)) + return False + partner = procurement.product_id.seller_ids[0].name + if user.company_id and user.company_id.partner_id: + if partner.id == user.company_id.partner_id.id: + return False + address_id = self.pool.get('res.partner').address_get(cr, uid, [partner.id], ['delivery'])['delivery'] + if not address_id: + cr.execute('update mrp_procurement set message=%s where id=%d', ('No address defined for the supplier', procurement.id)) + return False + return True + + def test_cancel(self, cr, uid, ids): + for record in self.browse(cr, uid, ids): + if record.move_id and record.move_id.state=='cancel': + return True + return False + + def action_confirm(self, cr, uid, ids): + for procurement in self.browse(cr, uid, ids): + if procurement.product_id.type in ('product', 'consu'): + if not procurement.move_id: + source = procurement.location_id.id + if procurement.procure_method=='make_to_order': + source = procurement.product_id.product_tmpl_id.property_stock_procurement.id + id = self.pool.get('stock.move').create(cr, uid, { + 'name': 'PROC:'+procurement.name, + 'location_id': source, + 'location_dest_id': procurement.location_id.id, + 'product_id': procurement.product_id.id, + 'product_qty':procurement.product_qty, + 'product_uom': procurement.product_uom.id, + 'date_planned': procurement.date_planned, + 'state':'confirmed', + }) + self.write(cr, uid, [procurement.id], {'move_id': id, 'close_move':1}) + else: + # TODO: check this + if procurement.procure_method=='make_to_stock' and procurement.move_id.state in ('waiting',): + id = self.pool.get('stock.move').write(cr, uid, [procurement.move_id.id], {'state':'confirmed'}) + self.write(cr, uid, ids, {'state':'confirmed','message':''}) + return True + + def action_move_assigned(self, cr, uid, ids): + self.write(cr, uid, ids, {'state':'running','message':'from stock: products assigned.'}) + return True + + def _check_make_to_stock_service(self, cr, uid, procurement, context={}): + return True + + def _check_make_to_stock_product(self, cr, uid, procurement, context={}): + ok = True + if procurement.move_id: + id = procurement.move_id.id + if not (procurement.move_id.state in ('done','assigned','cancel')): + ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) + cr.execute('select count(id) from stock_warehouse_orderpoint where product_id=%d', (procurement.product_id.id,)) + if not cr.fetchone()[0]: + cr.execute('update mrp_procurement set message=%s where id=%d', ('from stock and no minimum orderpoint rule defined', procurement.id)) + return ok + + def action_produce_assign_service(self, cr, uid, ids, context={}): + for procurement in self.browse(cr, uid, ids): + self.write(cr, uid, [procurement.id], {'state':'running'}) + return True + + def action_produce_assign_product(self, cr, uid, ids, context={}): + produce_id = False + for procurement in self.browse(cr, uid, ids): + res_id = procurement.move_id.id + loc_id = procurement.location_id.id + newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d') - DateTime.RelativeDateTime(days=procurement.product_id.product_tmpl_id.produce_delay or 0.0) + produce_id = self.pool.get('mrp.production').create(cr, uid, { + 'origin': procurement.origin, + 'product_id': procurement.product_id.id, + 'product_qty': procurement.product_qty, + 'product_uom': procurement.product_uom.id, + 'product_uos_qty': procurement.product_uos and procurement.product_uos_qty or False, + 'product_uos': procurement.product_uos and procurement.product_uos.id or False, + 'location_src_id': procurement.location_id.id, + 'location_dest_id': procurement.location_id.id, + 'bom_id': procurement.bom_id and procurement.bom_id.id or False, + 'date_planned': newdate, + 'move_prod_id': res_id, + }) + self.write(cr, uid, [procurement.id], {'state':'running'}) + bom_result = self.pool.get('mrp.production').action_compute(cr, uid, + [produce_id], properties=[x.id for x in procurement.property_ids]) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.production', produce_id, 'button_confirm', cr) + return produce_id + + def action_po_assign(self, cr, uid, ids): + purchase_id = False + for procurement in self.browse(cr, uid, ids): + res_id = procurement.move_id.id + partner = procurement.product_id.seller_ids[0].name + partner_id = partner.id + address_id = self.pool.get('res.partner').address_get(cr, uid, [partner_id], ['delivery'])['delivery'] + pricelist_id = partner.property_product_pricelist_purchase.id + + uom_id = procurement.product_id.uom_po_id.id + + qty = self.pool.get('product.uom')._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, uom_id) + if procurement.product_id.seller_ids[0].qty: + qty=max(qty,procurement.product_id.seller_ids[0].qty) + + price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, False, {'uom': uom_id})[pricelist_id] + + newdate = DateTime.strptime(procurement.date_planned, '%Y-%m-%d') - DateTime.RelativeDateTime(days=procurement.product_id.product_tmpl_id.seller_delay or 0.0) + line = { + 'name': procurement.product_id.name, + 'product_qty': qty, + 'product_id': procurement.product_id.id, + 'product_uom': uom_id, + 'price_unit': price, + 'date_planned': newdate.strftime('%Y-%m-%d'), + 'taxes_id': [(6, 0, [x.id for x in procurement.product_id.product_tmpl_id.supplier_taxes_id])], + 'move_dest_id': res_id, + } + + purchase_id = self.pool.get('purchase.order').create(cr, uid, { + 'origin': procurement.origin, + 'partner_id': partner_id, + 'partner_address_id': address_id, + 'location_id': procurement.location_id.id, + 'pricelist_id': pricelist_id, + 'order_line': [(0,0,line)] + }) + self.write(cr, uid, [procurement.id], {'state':'running', 'purchase_id':purchase_id}) + return purchase_id + + def action_cancel(self, cr, uid, ids): + todo = [] + for proc in self.browse(cr, uid, ids): + if proc.move_id: + todo.append(proc.move_id.id) + if len(todo): + self.pool.get('stock.move').action_cancel(cr, uid, [proc.move_id.id]) + self.write(cr, uid, ids, {'state':'cancel'}) + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_trigger(uid, 'mrp.procurement', id, cr) + + return True + + def action_check_finnished(self, cr, uid, ids): + return True + + def action_check(self, cr, uid, ids): + ok = False + for procurement in self.browse(cr, uid, ids): + if procurement.move_id.state=='assigned' or procurement.move_id.state=='done': + self.action_done(cr, uid, [procurement.id]) + ok = True + return ok + + def action_done(self, cr, uid, ids): + for procurement in self.browse(cr, uid, ids): + if procurement.move_id: + if procurement.close_move and (procurement.move_id.state <> 'done'): + self.pool.get('stock.move').action_done(cr, uid, [procurement.move_id.id]) + res = self.write(cr, uid, ids, {'state':'done', 'date_close':time.strftime('%Y-%m-%d')}) + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_trigger(uid, 'mrp.procurement', id, cr) + return res + def run_scheduler(self, cr, uid, user_id=False, schedule_cycle=1.0,\ + po_cycle=1.0, po_lead=1.0, security_lead=50.0, picking_lead=1.0,\ + automatic=False, use_new_cursor=False, context=None): + ''' + use_new_cursor: False or the dbname + ''' + if not context: + context={} + self.run_procure_confirm(cr, uid, schedule_cycle=schedule_cycle,\ + po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ + picking_lead=picking_lead, user_id=user_id,\ + use_new_cursor=use_new_cursor, context=context) + self.run_orderpoint_confirm(cr, uid, automatic=automatic,\ + use_new_cursor=use_new_cursor, context=context, user_id=user_id) + + def run_procure_confirm(self, cr, uid, user_id=False, schedule_cycle=1.0,\ + po_cycle=1.0, po_lead=1.0, security_lead=50.0, picking_lead=1.0, \ + use_new_cursor=False, context=None): + ''' + use_new_cursor: False or the dbname + ''' + from wizard.schedulers import _procure_confirm + if not context: + context={} + _procure_confirm(self, cr, uid, schedule_cycle=schedule_cycle,\ + po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ + picking_lead=picking_lead, user_id=user_id, use_new_cursor=use_new_cursor,\ + context=context) + + def run_orderpoint_confirm(self, cr, uid, automatic=False, use_new_cursor=False,\ + context=None, user_id=False): + ''' + use_new_cursor: False or the dbname + ''' + from wizard.schedulers import _procure_orderpoint_confirm + if not context: + context={} + _procure_orderpoint_confirm(self, cr, uid, automatic=automatic,\ + use_new_cursor=use_new_cursor, context=context, user_id=user_id) mrp_procurement() class stock_warehouse_orderpoint(osv.osv): - _name = "stock.warehouse.orderpoint" - _description = "Orderpoint minimum rule" - _columns = { - 'name': fields.char('Name', size=32, required=True), - 'active': fields.boolean('Active'), - 'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True), - 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True), - 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','=','product')]), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True ), - 'product_min_qty': fields.float('Min Quantity', required=True), - 'product_max_qty': fields.float('Max Quantity', required=True), - 'qty_multiple': fields.integer('Qty Multiple', required=True), - 'procurement_id': fields.many2one('mrp.procurement', 'Purchase Order') - } - _defaults = { - 'active': lambda *a: 1, - 'logic': lambda *a: 'max', - 'qty_multiple': lambda *a: 1, - 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.warehouse.orderpoint') or '', - 'product_uom': lambda sel, cr, uid, context: context.get('product_uom', False), - } - def onchange_product_id(self, cr, uid, ids, product_id, context={}): - if product_id: - prod=self.pool.get('product.product').browse(cr,uid,[product_id])[0] - v = {'product_uom':prod.uom_id.id} - return {'value': v} - return {} + _name = "stock.warehouse.orderpoint" + _description = "Orderpoint minimum rule" + _columns = { + 'name': fields.char('Name', size=32, required=True), + 'active': fields.boolean('Active'), + 'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True), + 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True), + 'product_id': fields.many2one('product.product', 'Product', required=True, domain=[('type','=','product')]), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True ), + 'product_min_qty': fields.float('Min Quantity', required=True), + 'product_max_qty': fields.float('Max Quantity', required=True), + 'qty_multiple': fields.integer('Qty Multiple', required=True), + 'procurement_id': fields.many2one('mrp.procurement', 'Purchase Order') + } + _defaults = { + 'active': lambda *a: 1, + 'logic': lambda *a: 'max', + 'qty_multiple': lambda *a: 1, + 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'mrp.warehouse.orderpoint') or '', + 'product_uom': lambda sel, cr, uid, context: context.get('product_uom', False), + } + def onchange_product_id(self, cr, uid, ids, product_id, context={}): + if product_id: + prod=self.pool.get('product.product').browse(cr,uid,[product_id])[0] + v = {'product_uom':prod.uom_id.id} + return {'value': v} + return {} stock_warehouse_orderpoint() class StockMove(osv.osv): - _inherit = 'stock.move' - _columns = { - 'procurements': fields.one2many('mrp.procurement', 'move_id', 'Procurements'), - } - def _action_explode(self, cr, uid, move, context={}): - if move.product_id.supply_method=='produce' and move.product_id.procure_method=='make_to_order': - bis = self.pool.get('mrp.bom').search(cr, uid, [ - ('product_id','=',move.product_id.id), - ('bom_id','=',False), - ('type','=','phantom')]) - if bis: - factor = move.product_qty - bom_point = self.pool.get('mrp.bom').browse(cr, uid, bis[0]) - res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor, []) - dest = move.product_id.product_tmpl_id.property_stock_production.id - state = 'confirmed' - if move.state=='assigned': - state='assigned' - for line in res[0]: - valdef = { - 'picking_id': move.picking_id.id, - 'product_id': line['product_id'], - 'product_uom': line['product_uom'], - 'product_qty': line['product_qty'], - 'product_uos': line['product_uos'], - 'product_uos_qty': line['product_uos_qty'], - 'move_dest_id': move.id, - 'state': state, - 'location_dest_id': dest, - 'move_history_ids': [(6,0,[move.id])], - 'move_history_ids2': [(6,0,[])], - 'procurements': [] - } - mid = self.pool.get('stock.move').copy(cr, uid, move.id, default=valdef) - prodobj = self.pool.get('product.product').browse(cr, uid, line['product_id'], context=context) - proc_id = self.pool.get('mrp.procurement').create(cr, uid, { - 'name': (move.picking_id.origin or ''), - 'origin': (move.picking_id.origin or ''), - 'date_planned': move.date_planned, - 'product_id': line['product_id'], - 'product_qty': line['product_qty'], - 'product_uom': line['product_uom'], - 'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False, - 'product_uos': line['product_uos'], - 'location_id': move.location_id.id, - 'procure_method': prodobj.procure_method, - 'move_id': mid, - }) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) - self.pool.get('stock.move').write(cr, uid, [move.id], { - 'location_id': move.location_dest_id.id, - 'auto_validate': True, - 'picking_id': False, - 'location_id': dest, - 'state': 'waiting' - }) - for m in self.pool.get('mrp.procurement').search(cr, uid, [('move_id','=',move.id)], context): - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.procurement', m, 'button_wait_done', cr) - return True + _inherit = 'stock.move' + _columns = { + 'procurements': fields.one2many('mrp.procurement', 'move_id', 'Procurements'), + } + def _action_explode(self, cr, uid, move, context={}): + if move.product_id.supply_method=='produce' and move.product_id.procure_method=='make_to_order': + bis = self.pool.get('mrp.bom').search(cr, uid, [ + ('product_id','=',move.product_id.id), + ('bom_id','=',False), + ('type','=','phantom')]) + if bis: + factor = move.product_qty + bom_point = self.pool.get('mrp.bom').browse(cr, uid, bis[0]) + res = self.pool.get('mrp.bom')._bom_explode(cr, uid, bom_point, factor, []) + dest = move.product_id.product_tmpl_id.property_stock_production.id + state = 'confirmed' + if move.state=='assigned': + state='assigned' + for line in res[0]: + valdef = { + 'picking_id': move.picking_id.id, + 'product_id': line['product_id'], + 'product_uom': line['product_uom'], + 'product_qty': line['product_qty'], + 'product_uos': line['product_uos'], + 'product_uos_qty': line['product_uos_qty'], + 'move_dest_id': move.id, + 'state': state, + 'location_dest_id': dest, + 'move_history_ids': [(6,0,[move.id])], + 'move_history_ids2': [(6,0,[])], + 'procurements': [] + } + mid = self.pool.get('stock.move').copy(cr, uid, move.id, default=valdef) + prodobj = self.pool.get('product.product').browse(cr, uid, line['product_id'], context=context) + proc_id = self.pool.get('mrp.procurement').create(cr, uid, { + 'name': (move.picking_id.origin or ''), + 'origin': (move.picking_id.origin or ''), + 'date_planned': move.date_planned, + 'product_id': line['product_id'], + 'product_qty': line['product_qty'], + 'product_uom': line['product_uom'], + 'product_uos_qty': line['product_uos'] and line['product_uos_qty'] or False, + 'product_uos': line['product_uos'], + 'location_id': move.location_id.id, + 'procure_method': prodobj.procure_method, + 'move_id': mid, + }) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) + self.pool.get('stock.move').write(cr, uid, [move.id], { + 'location_id': move.location_dest_id.id, + 'auto_validate': True, + 'picking_id': False, + 'location_id': dest, + 'state': 'waiting' + }) + for m in self.pool.get('mrp.procurement').search(cr, uid, [('move_id','=',move.id)], context): + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.procurement', m, 'button_wait_done', cr) + return True StockMove() class StockPicking(osv.osv): - _inherit = 'stock.picking' - - def test_finnished(self, cursor, user, ids): - wf_service = netsvc.LocalService("workflow") - res = super(StockPicking, self).test_finnished(cursor, user, ids) - for picking in self.browse(cursor, user, ids): - for move in picking.move_lines: - if move.state == 'done' and move.procurements: - for procurement in move.procurements: - wf_service.trg_validate(user, 'mrp.procurement', - procurement.id, 'button_check', cursor) - return res - - # - # Explode picking by replacing phantom BoMs - # - def action_explode(self, cr, uid, ids, *args): - for pick in self.browse(cr, uid, ids): - for move in pick.move_lines: - self.pool.get('stock.move')._action_explode(cr, uid, move) - return True + _inherit = 'stock.picking' + + def test_finnished(self, cursor, user, ids): + wf_service = netsvc.LocalService("workflow") + res = super(StockPicking, self).test_finnished(cursor, user, ids) + for picking in self.browse(cursor, user, ids): + for move in picking.move_lines: + if move.state == 'done' and move.procurements: + for procurement in move.procurements: + wf_service.trg_validate(user, 'mrp.procurement', + procurement.id, 'button_check', cursor) + return res + + # + # Explode picking by replacing phantom BoMs + # + def action_explode(self, cr, uid, ids, *args): + for pick in self.browse(cr, uid, ids): + for move in pick.move_lines: + self.pool.get('stock.move')._action_explode(cr, uid, move) + return True StockPicking() diff --git a/addons/mrp/report/price.py b/addons/mrp/report/price.py index f9604a3ff70..3f7653dec74 100644 --- a/addons/mrp/report/price.py +++ b/addons/mrp/report/price.py @@ -36,166 +36,166 @@ from report.interface import toxml #FIXME: we should use toxml class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context={}): - number = (datas.get('form', False) and datas['form']['number']) or 1 - - def process_bom(bom, currency_id): - xml = '<row>' - sum = 0 - sum_strd = 0 - prod = pooler.get_pool(cr.dbname).get('product.product').browse(cr, uid, bom['product_id']) - - prod_name = bom['name'] - prod_qtty = bom['product_qty'] - prod_uom = prod.uom_id.name - level = 1 - main_sp_price = '' - main_sp_name = '' - main_strd_price = '' - main_strd_price = '' - if prod.seller_ids and prod.seller_ids[0] : - main_sp_name = '<b>' + prod.seller_ids[0].name.name + '</b>\r\n' - pricelist_id = prod.seller_ids[0].name.property_product_pricelist_purchase.id - if pricelist_id: - pricelist_obj = pooler.get_pool(cr.dbname).get('product.pricelist') - price = pricelist_obj.price_get(cr,uid,[pricelist_id], prod.id, number*prod_qtty or 1.0).setdefault(pricelist_id, 0) - price = pooler.get_pool(cr.dbname).get('res.currency').compute(cr, uid, pricelist_obj.browse(cr, uid, pricelist_id).currency_id.id, currency_id, price) - else: - price = 0 - main_sp_price = '%.2f' % price + '\r\n' - sum += prod_qtty*price - - main_strd_price = '%.2f' % prod.standard_price + '\r\n' - sum_strd = prod_qtty*prod.standard_price - - sellers = '' - sellers_price = '' - for seller_id in prod.seller_ids: - sellers += '- <i>'+ seller_id.name.name +'</i>\r\n' - pricelist_id = seller_id.name.property_product_pricelist_purchase.id - if pricelist_id: - pricelist_obj = pooler.get_pool(cr.dbname).get('product.pricelist') - price = pricelist_obj.price_get(cr,uid,[pricelist_id], prod.id, number*prod_qtty or 1.0).setdefault(pricelist_id, 0) - price = pooler.get_pool(cr.dbname).get('res.currency').compute(cr, uid, pricelist_obj.browse(cr, uid, pricelist_id).currency_id.id, currency_id, price) - else: - price = 0 - sellers_price += '%.2f' % price + '\r\n' - - xml += "<col para='yes'>" + prod_name + '</col>' - xml += "<col para='no'>" + main_sp_name + sellers + '</col>' - xml += "<col para='yes'>" + str(prod_qtty) + '</col>' - xml += "<col para='yes'>" + prod_uom + '</col>' - xml += "<col para='yes'>" + main_strd_price + '</col>' - xml += "<col para='no'>" + main_sp_price + sellers_price + '</col>' - - - xml += '</row>' - return xml, sum, sum_strd - - def process_workcenter(wrk): - xml = '<row>' - - workcenter = pooler.get_pool(cr.dbname).get('mrp.workcenter').browse(cr, uid, wrk['workcenter_id']) - - xml += "<col para='yes'>" + wrk['name'] + '</col>' - xml += "<col para='yes'>" + '</col>' - xml += "<col para='no'>" + '</col>' - xml += "<col/>" - xml += "<col para='no'>" + str(wrk['cycle']*workcenter.costs_cycle) + '</col>' - xml += "<col para='yes'>" + str(wrk['hour']*workcenter.costs_hour) + '</col>' - - - xml += '</row>' - return xml, wrk['cycle']*workcenter.costs_cycle+wrk['hour']*workcenter.costs_hour - - - xml = '' - config_start = """ - <config> - <date>09/09/2005</date> - <PageSize>210.00mm,297.00mm</PageSize> - <PageWidth>595.27</PageWidth> - <PageHeight>841.88</PageHeight> - <tableSize>60.00mm,60.00mm, 20.00mm, 20.00mm, 20.00mm, 20.00mm</tableSize> - """ - config_stop = """ - <report-footer>Generated by Tiny ERP</report-footer> - </config> - """ - header = """ - <header> - <field>Product name</field> - <field>Product supplier</field> - <field>Product quantity</field> - <field>Product uom</field> - <field>Product Standard Price</field> - <field>Unit Product Price</field> - </header> - """ - workcenter_header = """ - <lines style='header'> - <row> - <col>Workcenter name</col> - <col/> - <col/> - <col/> - <col>Cycles Cost</col> - <col>Hours Cost</col> - </row> - </lines> - """ - prod_header = """ - <lines style='header'> - <row> - <col para='yes'>Product name</col> - <col para='yes'>Product supplier</col> - <col para='yes'>Product Quantity</col> - <col para='yes'>Product uom</col> - <col para='yes'>Product Standard Price</col> - <col para='yes'>Unit Product Price</col> - </row> - </lines> - """ - - company_currency = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid).company_id.currency_id.id - first = True - for prod_id in ids: - bom_ids = pooler.get_pool(cr.dbname).get('mrp.bom').search(cr, uid, [('product_id','=',prod_id)]) - prod = pooler.get_pool(cr.dbname).get('product.product').browse(cr, uid, prod_id) - - for bom_id in bom_ids: - bom = pooler.get_pool(cr.dbname).get('mrp.bom').browse(cr, uid, bom_id) - - sub_boms = pooler.get_pool(cr.dbname).get('mrp.bom')._bom_explode(cr, uid, bom, number, []) - total = 0 - total_strd = 0 - parent_bom = {'product_qty': bom.product_qty, 'name': bom.product_id.name, 'product_uom': bom.product_id.uom_id.factor, 'product_id': bom.product_id.id} - xml_tmp = '' - for sub_bom in (sub_boms and sub_boms[0]) or [parent_bom]: - txt, sum, sum_strd = process_bom(sub_bom, company_currency) - xml_tmp += txt - total += sum - total_strd += sum_strd - if not first: - xml += prod_header - xml += "<lines style='lines'>" + xml_tmp + '</lines>' - xml += "<lines style='sub_total'><row><col>SUBTOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col>" + '%.2f' % total_strd + '</col><col>' + '%.2f' % total + '</col></row></lines>' - - total2 = 0 - xml_tmp = '' - for wrk in (sub_boms and sub_boms[1]): - txt, sum = process_workcenter(wrk) - xml_tmp += txt - total2 += sum - if xml_tmp: - xml += workcenter_header - xml += "<lines style='lines'>" + xml_tmp + '</lines>' - xml += "<lines style='sub_total'><row><col>SUBTOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col/><col>" + '%.2f' % total2 + '</col></row></lines>' - xml += "<lines style='total'><row><col>TOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col>" + '%.2f' % (total_strd+total2) + "</col><col>" + '%.2f' % (total+total2) + '</col></row></lines>' - first = False - - xml = '<?xml version="1.0" ?><report>' + config_start + '<report-header>Product Cost Structure\n\r' + prod.name + '</report-header>'+ config_stop + header + xml + '</report>' - - return xml + def create_xml(self, cr, uid, ids, datas, context={}): + number = (datas.get('form', False) and datas['form']['number']) or 1 + + def process_bom(bom, currency_id): + xml = '<row>' + sum = 0 + sum_strd = 0 + prod = pooler.get_pool(cr.dbname).get('product.product').browse(cr, uid, bom['product_id']) + + prod_name = bom['name'] + prod_qtty = bom['product_qty'] + prod_uom = prod.uom_id.name + level = 1 + main_sp_price = '' + main_sp_name = '' + main_strd_price = '' + main_strd_price = '' + if prod.seller_ids and prod.seller_ids[0] : + main_sp_name = '<b>' + prod.seller_ids[0].name.name + '</b>\r\n' + pricelist_id = prod.seller_ids[0].name.property_product_pricelist_purchase.id + if pricelist_id: + pricelist_obj = pooler.get_pool(cr.dbname).get('product.pricelist') + price = pricelist_obj.price_get(cr,uid,[pricelist_id], prod.id, number*prod_qtty or 1.0).setdefault(pricelist_id, 0) + price = pooler.get_pool(cr.dbname).get('res.currency').compute(cr, uid, pricelist_obj.browse(cr, uid, pricelist_id).currency_id.id, currency_id, price) + else: + price = 0 + main_sp_price = '%.2f' % price + '\r\n' + sum += prod_qtty*price + + main_strd_price = '%.2f' % prod.standard_price + '\r\n' + sum_strd = prod_qtty*prod.standard_price + + sellers = '' + sellers_price = '' + for seller_id in prod.seller_ids: + sellers += '- <i>'+ seller_id.name.name +'</i>\r\n' + pricelist_id = seller_id.name.property_product_pricelist_purchase.id + if pricelist_id: + pricelist_obj = pooler.get_pool(cr.dbname).get('product.pricelist') + price = pricelist_obj.price_get(cr,uid,[pricelist_id], prod.id, number*prod_qtty or 1.0).setdefault(pricelist_id, 0) + price = pooler.get_pool(cr.dbname).get('res.currency').compute(cr, uid, pricelist_obj.browse(cr, uid, pricelist_id).currency_id.id, currency_id, price) + else: + price = 0 + sellers_price += '%.2f' % price + '\r\n' + + xml += "<col para='yes'>" + prod_name + '</col>' + xml += "<col para='no'>" + main_sp_name + sellers + '</col>' + xml += "<col para='yes'>" + str(prod_qtty) + '</col>' + xml += "<col para='yes'>" + prod_uom + '</col>' + xml += "<col para='yes'>" + main_strd_price + '</col>' + xml += "<col para='no'>" + main_sp_price + sellers_price + '</col>' + + + xml += '</row>' + return xml, sum, sum_strd + + def process_workcenter(wrk): + xml = '<row>' + + workcenter = pooler.get_pool(cr.dbname).get('mrp.workcenter').browse(cr, uid, wrk['workcenter_id']) + + xml += "<col para='yes'>" + wrk['name'] + '</col>' + xml += "<col para='yes'>" + '</col>' + xml += "<col para='no'>" + '</col>' + xml += "<col/>" + xml += "<col para='no'>" + str(wrk['cycle']*workcenter.costs_cycle) + '</col>' + xml += "<col para='yes'>" + str(wrk['hour']*workcenter.costs_hour) + '</col>' + + + xml += '</row>' + return xml, wrk['cycle']*workcenter.costs_cycle+wrk['hour']*workcenter.costs_hour + + + xml = '' + config_start = """ + <config> + <date>09/09/2005</date> + <PageSize>210.00mm,297.00mm</PageSize> + <PageWidth>595.27</PageWidth> + <PageHeight>841.88</PageHeight> + <tableSize>60.00mm,60.00mm, 20.00mm, 20.00mm, 20.00mm, 20.00mm</tableSize> + """ + config_stop = """ + <report-footer>Generated by Tiny ERP</report-footer> + </config> + """ + header = """ + <header> + <field>Product name</field> + <field>Product supplier</field> + <field>Product quantity</field> + <field>Product uom</field> + <field>Product Standard Price</field> + <field>Unit Product Price</field> + </header> + """ + workcenter_header = """ + <lines style='header'> + <row> + <col>Workcenter name</col> + <col/> + <col/> + <col/> + <col>Cycles Cost</col> + <col>Hours Cost</col> + </row> + </lines> + """ + prod_header = """ + <lines style='header'> + <row> + <col para='yes'>Product name</col> + <col para='yes'>Product supplier</col> + <col para='yes'>Product Quantity</col> + <col para='yes'>Product uom</col> + <col para='yes'>Product Standard Price</col> + <col para='yes'>Unit Product Price</col> + </row> + </lines> + """ + + company_currency = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid).company_id.currency_id.id + first = True + for prod_id in ids: + bom_ids = pooler.get_pool(cr.dbname).get('mrp.bom').search(cr, uid, [('product_id','=',prod_id)]) + prod = pooler.get_pool(cr.dbname).get('product.product').browse(cr, uid, prod_id) + + for bom_id in bom_ids: + bom = pooler.get_pool(cr.dbname).get('mrp.bom').browse(cr, uid, bom_id) + + sub_boms = pooler.get_pool(cr.dbname).get('mrp.bom')._bom_explode(cr, uid, bom, number, []) + total = 0 + total_strd = 0 + parent_bom = {'product_qty': bom.product_qty, 'name': bom.product_id.name, 'product_uom': bom.product_id.uom_id.factor, 'product_id': bom.product_id.id} + xml_tmp = '' + for sub_bom in (sub_boms and sub_boms[0]) or [parent_bom]: + txt, sum, sum_strd = process_bom(sub_bom, company_currency) + xml_tmp += txt + total += sum + total_strd += sum_strd + if not first: + xml += prod_header + xml += "<lines style='lines'>" + xml_tmp + '</lines>' + xml += "<lines style='sub_total'><row><col>SUBTOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col>" + '%.2f' % total_strd + '</col><col>' + '%.2f' % total + '</col></row></lines>' + + total2 = 0 + xml_tmp = '' + for wrk in (sub_boms and sub_boms[1]): + txt, sum = process_workcenter(wrk) + xml_tmp += txt + total2 += sum + if xml_tmp: + xml += workcenter_header + xml += "<lines style='lines'>" + xml_tmp + '</lines>' + xml += "<lines style='sub_total'><row><col>SUBTOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col/><col>" + '%.2f' % total2 + '</col></row></lines>' + xml += "<lines style='total'><row><col>TOTAL : </col><col>(for " + str(number) + " products)</col><col/><col/><col>" + '%.2f' % (total_strd+total2) + "</col><col>" + '%.2f' % (total+total2) + '</col></row></lines>' + first = False + + xml = '<?xml version="1.0" ?><report>' + config_start + '<report-header>Product Cost Structure\n\r' + prod.name + '</report-header>'+ config_stop + header + xml + '</report>' + + return xml report_custom('report.product.price', 'product.product', '', 'addons/mrp/report/price.xsl') diff --git a/addons/mrp/report/workcenter_load.py b/addons/mrp/report/workcenter_load.py index 8814ebdb421..0cd1b1d066d 100644 --- a/addons/mrp/report/workcenter_load.py +++ b/addons/mrp/report/workcenter_load.py @@ -46,158 +46,158 @@ random.seed(0) # class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + + def _render(self): + return self.pdf class report_custom(report_int): - def _compute_dates(self, time_unit, start, stop): - if not stop: - stop = start - if time_unit == 'month': - dates = {} - a = Date(*map(int, start.split("-"))).year*12+Date(*map(int, start.split("-"))).month - z = Date(*map(int, stop.split("-"))).year*12+Date(*map(int, stop.split("-"))).month+1 - for i in range(a,z): - year = i/12 - month = i%12 - if month == 0: - year -= 1 - month = 12 - months = {1:"January",2:"February",3:"March",4:"April",5:"May",6:"June",7:"July",8:"August",9:"September",10:"October",11:"November",12:"December"} - dates[i] = { - 'name' :months[month], - 'start':(Date(year, month, 2) + RelativeDateTime(day=1)).strftime('%Y-%m-%d'), - 'stop' :(Date(year, month, 2) + RelativeDateTime(day=-1)).strftime('%Y-%m-%d'), - } - return dates - elif time_unit == 'week': - dates = {} - a = Date(*map(int, start.split("-"))).iso_week[0]*52+Date(*map(int, start.split("-"))).iso_week[1] - z = Date(*map(int, stop.split("-"))).iso_week[0]*52+Date(*map(int, stop.split("-"))).iso_week[1] - for i in range(a,z+1): - year = i/52 - week = i%52 - dates[i] = { - 'name' :"Week #%d" % week, - 'start':ISO.WeekTime(year, week, 1).strftime('%Y-%m-%d'), - 'stop' :ISO.WeekTime(year, week, 7).strftime('%Y-%m-%d'), - } - return dates - else: # time_unit = day - dates = {} - a = Date(*map(int, start.split("-"))) - z = Date(*map(int, stop.split("-"))) - i = a - while i <= z: - dates[map(int,i.strftime('%Y%m%d').split())[0]] = { - 'name' :i.strftime('%Y-%m-%d'), - 'start':i.strftime('%Y-%m-%d'), - 'stop' :i.strftime('%Y-%m-%d'), - } - i = i + RelativeDateTime(days=+1) - return dates - return {} + def _compute_dates(self, time_unit, start, stop): + if not stop: + stop = start + if time_unit == 'month': + dates = {} + a = Date(*map(int, start.split("-"))).year*12+Date(*map(int, start.split("-"))).month + z = Date(*map(int, stop.split("-"))).year*12+Date(*map(int, stop.split("-"))).month+1 + for i in range(a,z): + year = i/12 + month = i%12 + if month == 0: + year -= 1 + month = 12 + months = {1:"January",2:"February",3:"March",4:"April",5:"May",6:"June",7:"July",8:"August",9:"September",10:"October",11:"November",12:"December"} + dates[i] = { + 'name' :months[month], + 'start':(Date(year, month, 2) + RelativeDateTime(day=1)).strftime('%Y-%m-%d'), + 'stop' :(Date(year, month, 2) + RelativeDateTime(day=-1)).strftime('%Y-%m-%d'), + } + return dates + elif time_unit == 'week': + dates = {} + a = Date(*map(int, start.split("-"))).iso_week[0]*52+Date(*map(int, start.split("-"))).iso_week[1] + z = Date(*map(int, stop.split("-"))).iso_week[0]*52+Date(*map(int, stop.split("-"))).iso_week[1] + for i in range(a,z+1): + year = i/52 + week = i%52 + dates[i] = { + 'name' :"Week #%d" % week, + 'start':ISO.WeekTime(year, week, 1).strftime('%Y-%m-%d'), + 'stop' :ISO.WeekTime(year, week, 7).strftime('%Y-%m-%d'), + } + return dates + else: # time_unit = day + dates = {} + a = Date(*map(int, start.split("-"))) + z = Date(*map(int, stop.split("-"))) + i = a + while i <= z: + dates[map(int,i.strftime('%Y%m%d').split())[0]] = { + 'name' :i.strftime('%Y-%m-%d'), + 'start':i.strftime('%Y-%m-%d'), + 'stop' :i.strftime('%Y-%m-%d'), + } + i = i + RelativeDateTime(days=+1) + return dates + return {} - def create(self, cr, uid, ids, datas, context={}): - assert len(ids), 'You should provide some ids!' - colors = choice_colors(len(ids)) - ids_str = ','.join(map(str, ids)) - cr.execute( - "SELECT MAX(date_planned) AS stop "\ - "FROM mrp_workcenter, mrp_production, mrp_production_workcenter_line "\ - "WHERE mrp_production_workcenter_line.production_id=mrp_production.id "\ - "AND mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id "\ - "AND mrp_production.state NOT IN ('cancel','done') "\ - "AND mrp_workcenter.id IN (%s)" % ids_str) - res = cr.dictfetchone() - if not res['stop']: - res['stop'] = time.strftime('%Y-%m-%d') - dates = self._compute_dates(datas['form']['time_unit'], time.strftime('%Y-%m-%d'), res['stop']) - dates_list = dates.keys() - dates_list.sort() - x_index = [] - for date in dates_list: - x_index.append((dates[date]['name'], date)) - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - chart_object.set_defaults(line_plot.T, line_style=None) - if datas['form']['measure_unit'] == 'cycles': - y_label = "Load (Cycles)" - else: - y_label = "Load (Hours)" - ar = area.T(legend = legend.T(), - x_grid_style = line_style.gray70_dash1, - x_axis = axis.X(label="Periods", format="/a90/hC%s"), - x_coord = category_coord.T(x_index, 0), - y_axis = axis.Y(label=y_label), - y_range = (0, None), - size = (640,480)) - bar_plot.fill_styles.reset(); + def create(self, cr, uid, ids, datas, context={}): + assert len(ids), 'You should provide some ids!' + colors = choice_colors(len(ids)) + ids_str = ','.join(map(str, ids)) + cr.execute( + "SELECT MAX(date_planned) AS stop "\ + "FROM mrp_workcenter, mrp_production, mrp_production_workcenter_line "\ + "WHERE mrp_production_workcenter_line.production_id=mrp_production.id "\ + "AND mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id "\ + "AND mrp_production.state NOT IN ('cancel','done') "\ + "AND mrp_workcenter.id IN (%s)" % ids_str) + res = cr.dictfetchone() + if not res['stop']: + res['stop'] = time.strftime('%Y-%m-%d') + dates = self._compute_dates(datas['form']['time_unit'], time.strftime('%Y-%m-%d'), res['stop']) + dates_list = dates.keys() + dates_list.sort() + x_index = [] + for date in dates_list: + x_index.append((dates[date]['name'], date)) + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + chart_object.set_defaults(line_plot.T, line_style=None) + if datas['form']['measure_unit'] == 'cycles': + y_label = "Load (Cycles)" + else: + y_label = "Load (Hours)" + ar = area.T(legend = legend.T(), + x_grid_style = line_style.gray70_dash1, + x_axis = axis.X(label="Periods", format="/a90/hC%s"), + x_coord = category_coord.T(x_index, 0), + y_axis = axis.Y(label=y_label), + y_range = (0, None), + size = (640,480)) + bar_plot.fill_styles.reset(); - # select workcenters - cr.execute( - "SELECT id, name FROM mrp_workcenter " \ - "WHERE id in (%s) "\ - "ORDER BY mrp_workcenter.id" % ids_str) - workcenters = cr.dictfetchall() + # select workcenters + cr.execute( + "SELECT id, name FROM mrp_workcenter " \ + "WHERE id in (%s) "\ + "ORDER BY mrp_workcenter.id" % ids_str) + workcenters = cr.dictfetchall() - data = [] - for date in dates_list: - vals = [] - for workcenter in workcenters: - cr.execute("SELECT SUM(mrp_production_workcenter_line.hour) AS hours, SUM(mrp_production_workcenter_line.cycle) AS cycles, \ - mrp_workcenter.name AS name, mrp_workcenter.id AS id \ - FROM mrp_production_workcenter_line, mrp_production, mrp_workcenter \ - WHERE (mrp_production_workcenter_line.production_id=mrp_production.id) \ - AND (mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id) \ - AND (mrp_workcenter.id=%d) \ - AND (mrp_production.date_planned BETWEEN %s AND %s) \ - GROUP BY mrp_production_workcenter_line.workcenter_id, mrp_workcenter.name, mrp_workcenter.id \ - ORDER BY mrp_workcenter.id", (workcenter['id'], dates[date]['start'], dates[date]['stop'])) - res = cr.dictfetchall() - if not res: - vals.append(0.0) - else: - if datas['form']['measure_unit'] == 'cycles': - vals.append(res[0]['cycles'] or 0.0) - else: - vals.append(res[0]['hours'] or 0.0) - - toto = [dates[date]['name']] - for val in vals: - toto.append(val) - data.append(toto) + data = [] + for date in dates_list: + vals = [] + for workcenter in workcenters: + cr.execute("SELECT SUM(mrp_production_workcenter_line.hour) AS hours, SUM(mrp_production_workcenter_line.cycle) AS cycles, \ + mrp_workcenter.name AS name, mrp_workcenter.id AS id \ + FROM mrp_production_workcenter_line, mrp_production, mrp_workcenter \ + WHERE (mrp_production_workcenter_line.production_id=mrp_production.id) \ + AND (mrp_production_workcenter_line.workcenter_id=mrp_workcenter.id) \ + AND (mrp_workcenter.id=%d) \ + AND (mrp_production.date_planned BETWEEN %s AND %s) \ + GROUP BY mrp_production_workcenter_line.workcenter_id, mrp_workcenter.name, mrp_workcenter.id \ + ORDER BY mrp_workcenter.id", (workcenter['id'], dates[date]['start'], dates[date]['stop'])) + res = cr.dictfetchall() + if not res: + vals.append(0.0) + else: + if datas['form']['measure_unit'] == 'cycles': + vals.append(res[0]['cycles'] or 0.0) + else: + vals.append(res[0]['hours'] or 0.0) + + toto = [dates[date]['name']] + for val in vals: + toto.append(val) + data.append(toto) - workcenter_num = 0 - for workcenter in workcenters: - f = fill_style.Plain() - f.bgcolor = colors[workcenter_num] - ar.add_plot(bar_plot.T(label=workcenter['name'], data=data, fill_style=f, hcol=workcenter_num+1, cluster=(workcenter_num, len(res)))) - workcenter_num += 1 + workcenter_num = 0 + for workcenter in workcenters: + f = fill_style.Plain() + f.bgcolor = colors[workcenter_num] + ar.add_plot(bar_plot.T(label=workcenter['name'], data=data, fill_style=f, hcol=workcenter_num+1, cluster=(workcenter_num, len(res)))) + workcenter_num += 1 - #plot = bar_plot.T(label=workcenter['name'], data=data, hcol=1, fill_style=fill_style.white, cluster=(color_index,len(ids))) - if (len(data[0]) <= 1): - ar = self._empty_graph(data[0][0]) - ar.draw(can) - # close canvas so that the file is written to "disk" - can.close() - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - pdf_string.close() - return (self.obj.pdf, 'pdf') - - def _empty_graph(self, date): - data = [[date, 0]] - ar = area.T(x_coord = category_coord.T(data, 0), y_range = (0, None), - x_axis = axis.X(label="Periods"), - y_axis = axis.Y(label="Load")) - ar.add_plot(bar_plot.T(data = data, label="No production order")) - return ar + #plot = bar_plot.T(label=workcenter['name'], data=data, hcol=1, fill_style=fill_style.white, cluster=(color_index,len(ids))) + if (len(data[0]) <= 1): + ar = self._empty_graph(data[0][0]) + ar.draw(can) + # close canvas so that the file is written to "disk" + can.close() + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + pdf_string.close() + return (self.obj.pdf, 'pdf') + + def _empty_graph(self, date): + data = [[date, 0]] + ar = area.T(x_coord = category_coord.T(data, 0), y_range = (0, None), + x_axis = axis.X(label="Periods"), + y_axis = axis.Y(label="Load")) + ar.add_plot(bar_plot.T(data = data, label="No production order")) + return ar report_custom('report.mrp.workcenter.load') diff --git a/addons/mrp/wizard/schedulers.py b/addons/mrp/wizard/schedulers.py index d0ede4db738..b826f19033b 100644 --- a/addons/mrp/wizard/schedulers.py +++ b/addons/mrp/wizard/schedulers.py @@ -34,82 +34,82 @@ import netsvc from osv import osv def _procure_confirm(self, cr, uid, schedule_cycle=1.0, po_cycle=1.0,\ - po_lead=1.0, security_lead=50.0, picking_lead=1.0, user_id=False,\ - use_new_cursor=False, context=None): - ''' - use_new_cursor: False or the dbname - ''' - if not context: - context={} + po_lead=1.0, security_lead=50.0, picking_lead=1.0, user_id=False,\ + use_new_cursor=False, context=None): + ''' + use_new_cursor: False or the dbname + ''' + if not context: + context={} - if use_new_cursor: - cr = pooler.get_db(use_new_cursor).cursor() - wf_service = netsvc.LocalService("workflow") + if use_new_cursor: + cr = pooler.get_db(use_new_cursor).cursor() + wf_service = netsvc.LocalService("workflow") - cr.execute('select id from mrp_procurement where state=%s order by date_planned', ('exception',)) - ids = map(lambda x:x[0], cr.fetchall()) - for id in ids: - wf_service.trg_validate(uid, 'mrp.procurement', id, 'button_restart', cr) - if use_new_cursor: - cr.commit() + cr.execute('select id from mrp_procurement where state=%s order by date_planned', ('exception',)) + ids = map(lambda x:x[0], cr.fetchall()) + for id in ids: + wf_service.trg_validate(uid, 'mrp.procurement', id, 'button_restart', cr) + if use_new_cursor: + cr.commit() - po_time = po_cycle + po_lead - maxdate = DateTime.now() + DateTime.RelativeDateTime(days=schedule_cycle + security_lead) - start_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss') - offset = 0 - report = [] - report_total = 0 - report_except = 0 - report_later = 0 - ids = [1] - procurement_obj = pooler.get_pool(cr.dbname).get('mrp.procurement') - while len(ids): - cr.execute('select id from mrp_procurement where state=%s and procure_method=%s order by date_planned limit 500 offset %d', ('confirmed','make_to_order',offset)) - ids = map(lambda x:x[0], cr.fetchall()) - for proc in procurement_obj.browse(cr, uid, ids): - if proc.product_id.supply_method=='produce': - wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) - else: - if (maxdate + DateTime.RelativeDateTime(days=(proc.product_id.seller_delay or 0 + po_time))).strftime('%Y-%m-%d')>=proc.date_planned: - wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) - else: - offset+=1 - report_later += 1 - for proc in procurement_obj.browse(cr, uid, ids): - if proc.state == 'exception': - report.append('PROC %d: on order - %3.2f %-5s - %s' % \ - (proc.id, proc.product_qty, proc.product_uom.name, - proc.product_id.name)) - report_except += 1 - report_total += 1 - if use_new_cursor: - cr.commit() + po_time = po_cycle + po_lead + maxdate = DateTime.now() + DateTime.RelativeDateTime(days=schedule_cycle + security_lead) + start_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss') + offset = 0 + report = [] + report_total = 0 + report_except = 0 + report_later = 0 + ids = [1] + procurement_obj = pooler.get_pool(cr.dbname).get('mrp.procurement') + while len(ids): + cr.execute('select id from mrp_procurement where state=%s and procure_method=%s order by date_planned limit 500 offset %d', ('confirmed','make_to_order',offset)) + ids = map(lambda x:x[0], cr.fetchall()) + for proc in procurement_obj.browse(cr, uid, ids): + if proc.product_id.supply_method=='produce': + wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) + else: + if (maxdate + DateTime.RelativeDateTime(days=(proc.product_id.seller_delay or 0 + po_time))).strftime('%Y-%m-%d')>=proc.date_planned: + wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) + else: + offset+=1 + report_later += 1 + for proc in procurement_obj.browse(cr, uid, ids): + if proc.state == 'exception': + report.append('PROC %d: on order - %3.2f %-5s - %s' % \ + (proc.id, proc.product_qty, proc.product_uom.name, + proc.product_id.name)) + report_except += 1 + report_total += 1 + if use_new_cursor: + cr.commit() - offset = 0 - ids = [1] - while len(ids): - report_ids = [] - ids = pooler.get_pool(cr.dbname).get('mrp.procurement').search(cr, uid, [('state','=','confirmed'),('procure_method','=','make_to_stock')], offset=offset) - for proc in procurement_obj.browse(cr, uid, ids): - if (maxdate + DateTime.RelativeDateTime(days=picking_lead)).strftime('%Y-%m-%d') >= proc.date_planned: - wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) - report_ids.append(proc.id) - else: - report_later +=1 - report_total +=1 - for proc in procurement_obj.browse(cr, uid, report_ids): - if proc.state == 'exception': - report.append('PROC %d: from stock - %3.2f %-5s - %s' % \ - (proc.id, proc.product_qty, proc.product_uom.name, - proc.product_id.name,)) - report_except +=1 - if use_new_cursor: - cr.commit() - offset += len(ids) - end_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss') - if user_id: - request = pooler.get_pool(cr.dbname).get('res.request') - summary = '''Here is the procurement scheduling report. + offset = 0 + ids = [1] + while len(ids): + report_ids = [] + ids = pooler.get_pool(cr.dbname).get('mrp.procurement').search(cr, uid, [('state','=','confirmed'),('procure_method','=','make_to_stock')], offset=offset) + for proc in procurement_obj.browse(cr, uid, ids): + if (maxdate + DateTime.RelativeDateTime(days=picking_lead)).strftime('%Y-%m-%d') >= proc.date_planned: + wf_service.trg_validate(uid, 'mrp.procurement', proc.id, 'button_check', cr) + report_ids.append(proc.id) + else: + report_later +=1 + report_total +=1 + for proc in procurement_obj.browse(cr, uid, report_ids): + if proc.state == 'exception': + report.append('PROC %d: from stock - %3.2f %-5s - %s' % \ + (proc.id, proc.product_qty, proc.product_uom.name, + proc.product_id.name,)) + report_except +=1 + if use_new_cursor: + cr.commit() + offset += len(ids) + end_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss') + if user_id: + request = pooler.get_pool(cr.dbname).get('res.request') + summary = '''Here is the procurement scheduling report. Computation Started; %s Computation Finnished; %s @@ -120,145 +120,145 @@ Not run now procurement: %d Exceptions; '''% (start_date,end_date,report_total, report_except,report_later) - summary += '\n'.join(report) - request.create(cr, uid, - {'name' : "Procurement calculation report.", - 'act_from' : user_id, - 'act_to' : user_id, - 'body': summary, - }) - if use_new_cursor: - cr.commit() - cr.close() - return {} + summary += '\n'.join(report) + request.create(cr, uid, + {'name' : "Procurement calculation report.", + 'act_from' : user_id, + 'act_to' : user_id, + 'body': summary, + }) + if use_new_cursor: + cr.commit() + cr.close() + return {} def create_automatic_op(cr, uid, context=None): - if not context: - context={} - product_obj = pooler.get_pool(cr.dbname).get('product.product') - proc_obj = pooler.get_pool(cr.dbname).get('mrp.procurement') - warehouse_obj = pooler.get_pool(cr.dbname).get('stock.warehouse') - wf_service = netsvc.LocalService("workflow") + if not context: + context={} + product_obj = pooler.get_pool(cr.dbname).get('product.product') + proc_obj = pooler.get_pool(cr.dbname).get('mrp.procurement') + warehouse_obj = pooler.get_pool(cr.dbname).get('stock.warehouse') + wf_service = netsvc.LocalService("workflow") - cr.execute('select id from stock_warehouse') - warehouses = [x for x, in cr.fetchall()] - cr.execute('select id from product_product') - products_id = [x for x, in cr.fetchall()] - products = dict(zip(products_id, product_obj.browse(cr, uid, products_id, context))) - for warehouse in warehouses: - v_stock = product_obj._product_virtual_available(cr, uid, products_id, None, {'warehouse': warehouse}) - cr.execute("select lot_stock_id from stock_warehouse") - location_ids_str = ','.join([str(id) for id, in cr.fetchall()]) - stock_locations = warehouse_obj.read(cr, uid, [warehouse], ['lot_input_id', 'lot_stock_id'])[0] - for prod_id, available in v_stock.items(): - if available >= 0: - continue - newdate = DateTime.now() + DateTime.RelativeDateTime(days=products[prod_id].seller_delay) - if products[prod_id].supply_method == 'buy': - location_id = stock_locations['lot_input_id'][0] - elif products[prod_id].supply_method == 'produce': - location_id = stock_locations['lot_stock_id'][0] - else: - continue - proc_id = proc_obj.create(cr, uid, { - 'name': 'PROC:Automatic OP for product:%s' % products[prod_id].name, - 'origin': 'SCHEDULER', - 'date_planned': newdate.strftime('%Y-%m-%d'), - 'product_id': prod_id, - 'product_qty': -available, - 'product_uom': products[prod_id].uom_id.id, - 'location_id': location_id, - 'procure_method': 'make_to_order', - }) - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_check', cr) + cr.execute('select id from stock_warehouse') + warehouses = [x for x, in cr.fetchall()] + cr.execute('select id from product_product') + products_id = [x for x, in cr.fetchall()] + products = dict(zip(products_id, product_obj.browse(cr, uid, products_id, context))) + for warehouse in warehouses: + v_stock = product_obj._product_virtual_available(cr, uid, products_id, None, {'warehouse': warehouse}) + cr.execute("select lot_stock_id from stock_warehouse") + location_ids_str = ','.join([str(id) for id, in cr.fetchall()]) + stock_locations = warehouse_obj.read(cr, uid, [warehouse], ['lot_input_id', 'lot_stock_id'])[0] + for prod_id, available in v_stock.items(): + if available >= 0: + continue + newdate = DateTime.now() + DateTime.RelativeDateTime(days=products[prod_id].seller_delay) + if products[prod_id].supply_method == 'buy': + location_id = stock_locations['lot_input_id'][0] + elif products[prod_id].supply_method == 'produce': + location_id = stock_locations['lot_stock_id'][0] + else: + continue + proc_id = proc_obj.create(cr, uid, { + 'name': 'PROC:Automatic OP for product:%s' % products[prod_id].name, + 'origin': 'SCHEDULER', + 'date_planned': newdate.strftime('%Y-%m-%d'), + 'product_id': prod_id, + 'product_qty': -available, + 'product_uom': products[prod_id].uom_id.id, + 'location_id': location_id, + 'procure_method': 'make_to_order', + }) + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_check', cr) def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\ - use_new_cursor=False, context=None, user_id=False): - ''' - use_new_cursor: False or the dbname - ''' - if not context: - context={} - if use_new_cursor: - cr = pooler.get_db(use_new_cursor).cursor() - pool = pooler.get_pool(cr.dbname) - orderpoint_obj = pool.get('stock.warehouse.orderpoint') - location_obj = pool.get('stock.location') - procurement_obj = pool.get('mrp.procurement') - request_obj = pool.get('res.request') - wf_service = netsvc.LocalService("workflow") - report = [] - offset = 0 - ids = [1] - if automatic: - create_automatic_op(cr, uid, context=context) - while ids: - cr.execute('SELECT id ' \ - 'FROM stock_warehouse_orderpoint ' \ - 'WHERE active OFFSET %d LIMIT 100', (offset,)) - ids = map(lambda x: x[0], cr.fetchall()) - for op in orderpoint_obj.browse(cr, uid, ids): - #if op.procurement_id and op.procurement_id.purchase_id: - # if (op.procurement_id.purchase_id.state=='confirmed'): - # # - # # TODO: Should write a request with this warning - # # - # continue - # elif op.procurement_id.purchase_id.state=='draft': - # wf_service = netsvc.LocalService("workflow") - # wf_service.trg_validate(uid, 'purchase.order', - # op.procurement_id.purchase_id.id, 'purchase_cancel', cr) - try: - prods = location_obj._product_virtual_get(cr, uid, - op.warehouse_id.lot_stock_id.id, [op.product_id.id], - {'uom': op.product_uom.id})[op.product_id.id] - if prods < op.product_min_qty: - # order the quantity necessary to get to max(min_qty, max_qty) - qty = max(op.product_min_qty, op.product_max_qty)-prods - reste = qty % op.qty_multiple - if reste>0: - qty += op.qty_multiple-reste - newdate = DateTime.now() + DateTime.RelativeDateTime( - days=op.product_id.seller_delay) - if op.product_id.supply_method == 'buy': - location_id = op.warehouse_id.lot_input_id - elif op.product_id.supply_method == 'produce': - location_id = op.warehouse_id.lot_stock_id - else: - continue - proc_id = procurement_obj.create(cr, uid, { - 'name': 'OP:'+str(op.id), - 'date_planned': newdate.strftime('%Y-%m-%d'), - 'product_id': op.product_id.id, - 'product_qty': qty, - 'product_uom': op.product_uom.id, - 'location_id': op.warehouse_id.lot_input_id.id, - 'procure_method': 'make_to_order', - 'origin': op.name - }) - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, - 'button_confirm', cr) - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, - 'button_check', cr) - orderpoint_obj.write(cr, uid, [op.id], - {'procurement_id': proc_id}) - except Exception, e: - report.append('OP %d:\n%s\n' % (op.id, str(e))) - offset += len(ids) - if use_new_cursor: - cr.commit() - if user_id and report: - request_obj.create(cr, uid, { - 'name': 'Orderpoint report.', - 'act_from': user_id, - 'act_to': user_id, - 'body': '\n'.join(report) - }) - if use_new_cursor: - cr.commit() - cr.close() - return {} + use_new_cursor=False, context=None, user_id=False): + ''' + use_new_cursor: False or the dbname + ''' + if not context: + context={} + if use_new_cursor: + cr = pooler.get_db(use_new_cursor).cursor() + pool = pooler.get_pool(cr.dbname) + orderpoint_obj = pool.get('stock.warehouse.orderpoint') + location_obj = pool.get('stock.location') + procurement_obj = pool.get('mrp.procurement') + request_obj = pool.get('res.request') + wf_service = netsvc.LocalService("workflow") + report = [] + offset = 0 + ids = [1] + if automatic: + create_automatic_op(cr, uid, context=context) + while ids: + cr.execute('SELECT id ' \ + 'FROM stock_warehouse_orderpoint ' \ + 'WHERE active OFFSET %d LIMIT 100', (offset,)) + ids = map(lambda x: x[0], cr.fetchall()) + for op in orderpoint_obj.browse(cr, uid, ids): + #if op.procurement_id and op.procurement_id.purchase_id: + # if (op.procurement_id.purchase_id.state=='confirmed'): + # # + # # TODO: Should write a request with this warning + # # + # continue + # elif op.procurement_id.purchase_id.state=='draft': + # wf_service = netsvc.LocalService("workflow") + # wf_service.trg_validate(uid, 'purchase.order', + # op.procurement_id.purchase_id.id, 'purchase_cancel', cr) + try: + prods = location_obj._product_virtual_get(cr, uid, + op.warehouse_id.lot_stock_id.id, [op.product_id.id], + {'uom': op.product_uom.id})[op.product_id.id] + if prods < op.product_min_qty: + # order the quantity necessary to get to max(min_qty, max_qty) + qty = max(op.product_min_qty, op.product_max_qty)-prods + reste = qty % op.qty_multiple + if reste>0: + qty += op.qty_multiple-reste + newdate = DateTime.now() + DateTime.RelativeDateTime( + days=op.product_id.seller_delay) + if op.product_id.supply_method == 'buy': + location_id = op.warehouse_id.lot_input_id + elif op.product_id.supply_method == 'produce': + location_id = op.warehouse_id.lot_stock_id + else: + continue + proc_id = procurement_obj.create(cr, uid, { + 'name': 'OP:'+str(op.id), + 'date_planned': newdate.strftime('%Y-%m-%d'), + 'product_id': op.product_id.id, + 'product_qty': qty, + 'product_uom': op.product_uom.id, + 'location_id': op.warehouse_id.lot_input_id.id, + 'procure_method': 'make_to_order', + 'origin': op.name + }) + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, + 'button_confirm', cr) + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, + 'button_check', cr) + orderpoint_obj.write(cr, uid, [op.id], + {'procurement_id': proc_id}) + except Exception, e: + report.append('OP %d:\n%s\n' % (op.id, str(e))) + offset += len(ids) + if use_new_cursor: + cr.commit() + if user_id and report: + request_obj.create(cr, uid, { + 'name': 'Orderpoint report.', + 'act_from': user_id, + 'act_to': user_id, + 'body': '\n'.join(report) + }) + if use_new_cursor: + cr.commit() + cr.close() + return {} diff --git a/addons/mrp/wizard/wizard_orderpoint_procurement.py b/addons/mrp/wizard/wizard_orderpoint_procurement.py index 4879ecae557..8c031cf1b34 100644 --- a/addons/mrp/wizard/wizard_orderpoint_procurement.py +++ b/addons/mrp/wizard/wizard_orderpoint_procurement.py @@ -38,38 +38,38 @@ import pooler parameter_form = '''<?xml version="1.0"?> <form string="Parameters" colspan="4"> - <field name="automatic" /> + <field name="automatic" /> </form>''' parameter_fields = { - 'automatic': {'string': 'Automatic orderpoint', 'type': 'boolean', 'help': 'If the stock of a product is under 0, it will act like an orderpoint', 'default': lambda *a: False}, + 'automatic': {'string': 'Automatic orderpoint', 'type': 'boolean', 'help': 'If the stock of a product is under 0, it will act like an orderpoint', 'default': lambda *a: False}, } def _procure_calculation_orderpoint(self, db_name, uid, data, context): - db, pool = pooler.get_db_and_pool(db_name) - cr = db.cursor() - proc_obj = pool.get('mrp.procurement') - automatic = data['form']['automatic'] - proc_obj.run_orderpoint_confirm(cr, uid, automatic=automatic,\ - use_new_cursor=cr.dbname, context=context, user_id=uid) - return {} + db, pool = pooler.get_db_and_pool(db_name) + cr = db.cursor() + proc_obj = pool.get('mrp.procurement') + automatic = data['form']['automatic'] + proc_obj.run_orderpoint_confirm(cr, uid, automatic=automatic,\ + use_new_cursor=cr.dbname, context=context, user_id=uid) + return {} def _procure_calculation(self, cr, uid, data, context): - threaded_calculation = threading.Thread(target=_procure_calculation_orderpoint, args=(self, cr.dbname, uid, data, context)) - threaded_calculation.start() - return {} + threaded_calculation = threading.Thread(target=_procure_calculation_orderpoint, args=(self, cr.dbname, uid, data, context)) + threaded_calculation.start() + return {} class procurement_compute(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements')]} - }, - 'compute': { - 'actions': [_procure_calculation], - 'result': {'type': 'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements')]} + }, + 'compute': { + 'actions': [_procure_calculation], + 'result': {'type': 'state', 'state':'end'} + }, + } procurement_compute('mrp.procurement.orderpoint.compute') diff --git a/addons/mrp/wizard/wizard_price.py b/addons/mrp/wizard/wizard_price.py index 470f37da9da..ccbb84bfdef 100644 --- a/addons/mrp/wizard/wizard_price.py +++ b/addons/mrp/wizard/wizard_price.py @@ -32,24 +32,24 @@ import wizard price_form = '''<?xml version="1.0"?> <form string="Paid ?"> - <field name="number"/> + <field name="number"/> </form>''' price_fields = { - 'number': {'string':'Number of products to produce', 'type':'integer', 'required':True}, + 'number': {'string':'Number of products to produce', 'type':'integer', 'required':True}, } class wizard_price(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':price_form, 'fields':price_fields, 'state':[('end','Cancel'),('price','Print product price') ]} - }, - 'price': { - 'actions': [], - 'result': {'type':'print', 'report':'product.price', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':price_form, 'fields':price_fields, 'state':[('end','Cancel'),('price','Print product price') ]} + }, + 'price': { + 'actions': [], + 'result': {'type':'print', 'report':'product.price', 'state':'end'} + } + } wizard_price('product_price') diff --git a/addons/mrp/wizard/wizard_procurement.py b/addons/mrp/wizard/wizard_procurement.py index 778288fea2b..9002f0bd729 100644 --- a/addons/mrp/wizard/wizard_procurement.py +++ b/addons/mrp/wizard/wizard_procurement.py @@ -33,55 +33,55 @@ import pooler parameter_form = '''<?xml version="1.0"?> <form string="Parameters" colspan="4"> - <separator string="Time (days)" colspan="4"/> - <field name="po_lead"/> - <field name="picking_lead"/> - <field name="schedule_cycle"/> - <field name="po_cycle"/> - <field name="security_lead"/> - <separator string="Control" colspan="4"/> - <field name="user_id"/> + <separator string="Time (days)" colspan="4"/> + <field name="po_lead"/> + <field name="picking_lead"/> + <field name="schedule_cycle"/> + <field name="po_cycle"/> + <field name="security_lead"/> + <separator string="Control" colspan="4"/> + <field name="user_id"/> </form>''' parameter_fields = { - 'schedule_cycle': {'string':'Scheduler Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'po_cycle': {'string':'PO Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'po_lead': {'string':'PO Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'security_lead': {'string':'Security Days', 'type':'float', 'required':True, 'default': lambda *a: 5.0}, - 'picking_lead': {'string':'Packing Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'user_id': {'string':'Send Result To', 'type':'many2one', 'relation':'res.users', 'default': lambda uid,data,state: uid}, + 'schedule_cycle': {'string':'Scheduler Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'po_cycle': {'string':'PO Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'po_lead': {'string':'PO Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'security_lead': {'string':'Security Days', 'type':'float', 'required':True, 'default': lambda *a: 5.0}, + 'picking_lead': {'string':'Packing Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'user_id': {'string':'Send Result To', 'type':'many2one', 'relation':'res.users', 'default': lambda uid,data,state: uid}, } def _procure_calculation_procure(self, db_name, uid, data, context): - db, pool = pooler.get_db_and_pool(db_name) - cr = db.cursor() - proc_obj = pool.get('mrp.procurement') - schedule_cycle = data['form']['schedule_cycle'] - po_cycle = data['form']['po_cycle'] - po_lead = data['form']['po_lead'] - security_lead = data['form']['security_lead'] - picking_lead = data['form']['picking_lead'] - user_id = data['form']['user_id'] - proc_obj.run_procure_confirm(cr, uid, user_id=user_id, schedule_cycle=schedule_cycle,\ - po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ - picking_lead=picking_lead, use_new_cursor=cr.dbname, context=context) - return {} + db, pool = pooler.get_db_and_pool(db_name) + cr = db.cursor() + proc_obj = pool.get('mrp.procurement') + schedule_cycle = data['form']['schedule_cycle'] + po_cycle = data['form']['po_cycle'] + po_lead = data['form']['po_lead'] + security_lead = data['form']['security_lead'] + picking_lead = data['form']['picking_lead'] + user_id = data['form']['user_id'] + proc_obj.run_procure_confirm(cr, uid, user_id=user_id, schedule_cycle=schedule_cycle,\ + po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ + picking_lead=picking_lead, use_new_cursor=cr.dbname, context=context) + return {} def _procure_calculation(self, cr, uid, data, context): - threaded_calculation = threading.Thread(target=_procure_calculation_procure, args=(self, cr.dbname, uid, data, context)) - threaded_calculation.start() - return {} + threaded_calculation = threading.Thread(target=_procure_calculation_procure, args=(self, cr.dbname, uid, data, context)) + threaded_calculation.start() + return {} class procurement_compute(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements') ]} - }, - 'compute': { - 'actions': [_procure_calculation], - 'result': {'type': 'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements') ]} + }, + 'compute': { + 'actions': [_procure_calculation], + 'result': {'type': 'state', 'state':'end'} + }, + } procurement_compute('mrp.procurement.compute') diff --git a/addons/mrp/wizard/wizard_schedulers_all.py b/addons/mrp/wizard/wizard_schedulers_all.py index 8ed294c77a1..799a66a3a44 100644 --- a/addons/mrp/wizard/wizard_schedulers_all.py +++ b/addons/mrp/wizard/wizard_schedulers_all.py @@ -33,58 +33,58 @@ import pooler parameter_form = '''<?xml version="1.0"?> <form string="Scheduler Parameters" colspan="4"> - <separator string="Time (days)" colspan="4"/> - <field name="po_lead"/> - <field name="picking_lead"/> - <field name="schedule_cycle"/> - <field name="po_cycle"/> - <field name="security_lead"/> - <field name="automatic" /> - <separator string="Control" colspan="4"/> - <field name="user_id"/> + <separator string="Time (days)" colspan="4"/> + <field name="po_lead"/> + <field name="picking_lead"/> + <field name="schedule_cycle"/> + <field name="po_cycle"/> + <field name="security_lead"/> + <field name="automatic" /> + <separator string="Control" colspan="4"/> + <field name="user_id"/> </form>''' parameter_fields = { - 'schedule_cycle': {'string':'Scheduler Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'po_cycle': {'string':'PO Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'po_lead': {'string':'PO Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'security_lead': {'string':'Security Days', 'type':'float', 'required':True, 'default': lambda *a: 50.0}, - 'picking_lead': {'string':'Packing Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, - 'automatic': {'string': 'Automatic orderpoint', 'type': 'boolean', 'help': 'Triggers an automatic procurement for all products that have a virtual stock under 0.', 'default': lambda *a: False}, - 'user_id': {'string':'Send Result To', 'type':'many2one', 'relation':'res.users', 'default': lambda uid,data,state: uid}, + 'schedule_cycle': {'string':'Scheduler Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'po_cycle': {'string':'PO Cycle', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'po_lead': {'string':'PO Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'security_lead': {'string':'Security Days', 'type':'float', 'required':True, 'default': lambda *a: 50.0}, + 'picking_lead': {'string':'Packing Lead Time', 'type':'float', 'required':True, 'default': lambda *a: 1.0}, + 'automatic': {'string': 'Automatic orderpoint', 'type': 'boolean', 'help': 'Triggers an automatic procurement for all products that have a virtual stock under 0.', 'default': lambda *a: False}, + 'user_id': {'string':'Send Result To', 'type':'many2one', 'relation':'res.users', 'default': lambda uid,data,state: uid}, } def _procure_calculation_all(self, db_name, uid, data, context): - db, pool = pooler.get_db_and_pool(db_name) - cr = db.cursor() - proc_obj = pool.get('mrp.procurement') - schedule_cycle = data['form']['schedule_cycle'] - po_cycle = data['form']['po_cycle'] - po_lead = data['form']['po_lead'] - security_lead = data['form']['security_lead'] - picking_lead = data['form']['picking_lead'] - user_id = data['form']['user_id'] - automatic = data['form']['automatic'] - proc_obj.run_scheduler(cr, uid, user_id=user_id, schedule_cycle=schedule_cycle,\ - po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ - picking_lead=picking_lead, automatic=automatic, use_new_cursor=cr.dbname,\ - context=context) - return {} + db, pool = pooler.get_db_and_pool(db_name) + cr = db.cursor() + proc_obj = pool.get('mrp.procurement') + schedule_cycle = data['form']['schedule_cycle'] + po_cycle = data['form']['po_cycle'] + po_lead = data['form']['po_lead'] + security_lead = data['form']['security_lead'] + picking_lead = data['form']['picking_lead'] + user_id = data['form']['user_id'] + automatic = data['form']['automatic'] + proc_obj.run_scheduler(cr, uid, user_id=user_id, schedule_cycle=schedule_cycle,\ + po_cycle=po_cycle, po_lead=po_lead, security_lead=security_lead,\ + picking_lead=picking_lead, automatic=automatic, use_new_cursor=cr.dbname,\ + context=context) + return {} def _procure_calculation(self, cr, uid, data, context): - threaded_calculation = threading.Thread(target=_procure_calculation_all, args=(self, cr.dbname, uid, data, context)) - threaded_calculation.start() - return {} + threaded_calculation = threading.Thread(target=_procure_calculation_all, args=(self, cr.dbname, uid, data, context)) + threaded_calculation.start() + return {} class procurement_compute(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements') ]} - }, - 'compute': { - 'actions': [_procure_calculation], - 'result': {'type': 'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':parameter_form, 'fields': parameter_fields, 'state':[('end','Cancel'),('compute','Compute Procurements') ]} + }, + 'compute': { + 'actions': [_procure_calculation], + 'result': {'type': 'state', 'state':'end'} + }, + } procurement_compute('mrp.procurement.compute.all') diff --git a/addons/mrp/wizard/wizard_track_prod.py b/addons/mrp/wizard/wizard_track_prod.py index 8e10a347c91..ec28ffc96bc 100644 --- a/addons/mrp/wizard/wizard_track_prod.py +++ b/addons/mrp/wizard/wizard_track_prod.py @@ -39,71 +39,71 @@ arch = UpdateableStr() fields = {} def _get_moves(self, cr, uid, data, context): - prod_obj = pooler.get_pool(cr.dbname).get('mrp.production') - prod = prod_obj.browse(cr, uid, [data['id']])[0] - res = {} - fields.clear() - if prod.state != 'done': - arch.string = '''<?xml version="1.0"?><form string="Track lines"><label colspan="4" string="You can not split an unfinished production output" /></form>''' - res['moves'] = [] - else: - arch_lst = ['<?xml version="1.0"?>', '<form string="Track lines">', '<label colspan="4" string="The field on each line says if this lot should be tracked or not." />'] - for m in [line for line in prod.move_created_ids]: - quantity = m.product_qty - arch_lst.append('<field name="track%s" />\n<newline />' % m.id) - fields['track%s' % m.id] = {'string' : m.product_id.name, 'type' : 'boolean', 'default' : lambda x,y,z: False} - res.setdefault('moves', []).append(m.id) - arch_lst.append('</form>') - arch.string = '\n'.join(arch_lst) - return res + prod_obj = pooler.get_pool(cr.dbname).get('mrp.production') + prod = prod_obj.browse(cr, uid, [data['id']])[0] + res = {} + fields.clear() + if prod.state != 'done': + arch.string = '''<?xml version="1.0"?><form string="Track lines"><label colspan="4" string="You can not split an unfinished production output" /></form>''' + res['moves'] = [] + else: + arch_lst = ['<?xml version="1.0"?>', '<form string="Track lines">', '<label colspan="4" string="The field on each line says if this lot should be tracked or not." />'] + for m in [line for line in prod.move_created_ids]: + quantity = m.product_qty + arch_lst.append('<field name="track%s" />\n<newline />' % m.id) + fields['track%s' % m.id] = {'string' : m.product_id.name, 'type' : 'boolean', 'default' : lambda x,y,z: False} + res.setdefault('moves', []).append(m.id) + arch_lst.append('</form>') + arch.string = '\n'.join(arch_lst) + return res def _track_lines(self, cr, uid, data, context): - if not data['form']['moves']: - return {} - prodlot_obj = pooler.get_pool(cr.dbname).get('stock.production.lot') - lot_obj = pooler.get_pool(cr.dbname).get('stock.lot') - move_obj = pooler.get_pool(cr.dbname).get('stock.move') + if not data['form']['moves']: + return {} + prodlot_obj = pooler.get_pool(cr.dbname).get('stock.production.lot') + lot_obj = pooler.get_pool(cr.dbname).get('stock.lot') + move_obj = pooler.get_pool(cr.dbname).get('stock.move') - new_lot = lot_obj.create(cr, uid, {'name': 'PRODUCTION:%d' % data['id']}) - for idx, move in enumerate(move_obj.browse(cr, uid, data['form']['moves'])): - if data['form']['track%s' % move.id]: - for idx in range(move.product_qty): - update_val = {'product_qty': 1, 'lot_id': new_lot} - if idx: - current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) - else: - current_move = move.id - new_prodlot = prodlot_obj.create(cr, uid, {'name': 'PRODUCTION:%d:LOT:%d' % (data['id'], idx+1)}) - update_val['prodlot_id'] = new_prodlot - move_obj.write(cr, uid, [current_move], update_val) - return {} + new_lot = lot_obj.create(cr, uid, {'name': 'PRODUCTION:%d' % data['id']}) + for idx, move in enumerate(move_obj.browse(cr, uid, data['form']['moves'])): + if data['form']['track%s' % move.id]: + for idx in range(move.product_qty): + update_val = {'product_qty': 1, 'lot_id': new_lot} + if idx: + current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) + else: + current_move = move.id + new_prodlot = prodlot_obj.create(cr, uid, {'name': 'PRODUCTION:%d:LOT:%d' % (data['id'], idx+1)}) + update_val['prodlot_id'] = new_prodlot + move_obj.write(cr, uid, [current_move], update_val) + return {} class wizard_track_move(wizard.interface): - def _next(self, cr, uid, data, context): - prod_obj = pooler.get_pool(cr.dbname).get('mrp.production') - prod = prod_obj.browse(cr, uid, [data['id']])[0] - if prod.state != 'done': - return 'notrack' - else: - return 'track' - states = { - 'init': { - 'actions': [], - 'result': {'type':'choice', 'next_state': _next} - }, - 'notrack':{ - 'actions':[_get_moves], - 'result':{'type':'form', 'arch':arch, 'fields':fields, 'state':[('end', 'Cancel')]} - }, - 'track':{ - 'actions':[_get_moves], - 'result': {'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('split','Track')]} - }, - 'split': { - 'actions': [_track_lines], - 'result': {'type':'state', 'state':'end'} - } - } + def _next(self, cr, uid, data, context): + prod_obj = pooler.get_pool(cr.dbname).get('mrp.production') + prod = prod_obj.browse(cr, uid, [data['id']])[0] + if prod.state != 'done': + return 'notrack' + else: + return 'track' + states = { + 'init': { + 'actions': [], + 'result': {'type':'choice', 'next_state': _next} + }, + 'notrack':{ + 'actions':[_get_moves], + 'result':{'type':'form', 'arch':arch, 'fields':fields, 'state':[('end', 'Cancel')]} + }, + 'track':{ + 'actions':[_get_moves], + 'result': {'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('split','Track')]} + }, + 'split': { + 'actions': [_track_lines], + 'result': {'type':'state', 'state':'end'} + } + } wizard_track_move('mrp.production.track') diff --git a/addons/mrp/wizard/wizard_workcenter_load.py b/addons/mrp/wizard/wizard_workcenter_load.py index 89f33393f81..fe98d6ea0a2 100644 --- a/addons/mrp/wizard/wizard_workcenter_load.py +++ b/addons/mrp/wizard/wizard_workcenter_load.py @@ -31,27 +31,27 @@ import wizard _time_unit_form = '''<?xml version="1.0"?> <form string="Select time unit"> - <field name="time_unit"/> - <newline/> - <field name="measure_unit"/> + <field name="time_unit"/> + <newline/> + <field name="measure_unit"/> </form>''' _time_unit_fields = { - 'time_unit': {'string':'Type of period', 'type':'selection', 'selection':[('day', 'Day by day'),('week', 'Per week'),('month', 'Per month')], 'required':True}, - 'measure_unit': {'string':'Amount measuring unit', 'type':'selection', 'selection':[('hours', 'Amount in hours'),('cycles', 'Amount in cycles')], 'required':True}, + 'time_unit': {'string':'Type of period', 'type':'selection', 'selection':[('day', 'Day by day'),('week', 'Per week'),('month', 'Per month')], 'required':True}, + 'measure_unit': {'string':'Amount measuring unit', 'type':'selection', 'selection':[('hours', 'Amount in hours'),('cycles', 'Amount in cycles')], 'required':True}, } class wizard_mrp_workcenter(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch':_time_unit_form, 'fields':_time_unit_fields, 'state':[('end','Cancel'),('report','Print') ]}, - }, - 'report': { - 'actions': [], - 'result': {'type':'print', 'report':'mrp.workcenter.load', 'state':'end'}, - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch':_time_unit_form, 'fields':_time_unit_fields, 'state':[('end','Cancel'),('report','Print') ]}, + }, + 'report': { + 'actions': [], + 'result': {'type':'print', 'report':'mrp.workcenter.load', 'state':'end'}, + }, + } wizard_mrp_workcenter('mrp.workcenter.load') diff --git a/addons/multi_company/__terp__.py b/addons/multi_company/__terp__.py index 0f4f652f69b..3e8557efa54 100644 --- a/addons/multi_company/__terp__.py +++ b/addons/multi_company/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Multi company base module", - "version" : "0.1", - "author" : "Tiny", - "depends" : ["base"], - "category" : "Generic Modules/Base", - "init_xml" : [], - "demo_xml" : ["multi_company_demo.xml",], - "update_xml" : [], - "active": False, - "installable": True + "name" : "Multi company base module", + "version" : "0.1", + "author" : "Tiny", + "depends" : ["base"], + "category" : "Generic Modules/Base", + "init_xml" : [], + "demo_xml" : ["multi_company_demo.xml",], + "update_xml" : [], + "active": False, + "installable": True } diff --git a/addons/multi_company_account/__terp__.py b/addons/multi_company_account/__terp__.py index 03924579c31..04fde51734a 100644 --- a/addons/multi_company_account/__terp__.py +++ b/addons/multi_company_account/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Multi company for accounting module", - "version" : "0.1", - "author" : "Tiny", - "depends" : ["base", "account", "multi_company"], - "category" : "Generic Modules/Accounting", - "init_xml" : [], - "demo_xml" : ["multi_company_account_demo.xml",], - "update_xml" : ["multi_company_account_data.xml", "multi_company_account_view.xml",], - "active": False, - "installable": True + "name" : "Multi company for accounting module", + "version" : "0.1", + "author" : "Tiny", + "depends" : ["base", "account", "multi_company"], + "category" : "Generic Modules/Accounting", + "init_xml" : [], + "demo_xml" : ["multi_company_account_demo.xml",], + "update_xml" : ["multi_company_account_data.xml", "multi_company_account_view.xml",], + "active": False, + "installable": True } diff --git a/addons/multi_company_account/multi_company_account.py b/addons/multi_company_account/multi_company_account.py index 0e22029b82c..a0324a151b6 100644 --- a/addons/multi_company_account/multi_company_account.py +++ b/addons/multi_company_account/multi_company_account.py @@ -30,57 +30,57 @@ from osv import osv, fields class account_journal(osv.osv): - _name = "account.journal" - _inherit = "account.journal" - _columns = { - 'company_id': fields.many2one('res.company', 'Company', required=True), - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } + _name = "account.journal" + _inherit = "account.journal" + _columns = { + 'company_id': fields.many2one('res.company', 'Company', required=True), + } + _defaults = { + 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + } account_journal() class account_analytic_journal(osv.osv): - _name = "account.analytic.journal" - _inherit = "account.analytic.journal" - _columns = { - 'company_id': fields.many2one('res.company', 'Company'), - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } + _name = "account.analytic.journal" + _inherit = "account.analytic.journal" + _columns = { + 'company_id': fields.many2one('res.company', 'Company'), + } + _defaults = { + 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + } account_analytic_journal() class account_budget_post(osv.osv): - _name = 'account.budget.post' - _inherit = 'account.budget.post' - _columns ={ - 'company_id': fields.many2one('res.company', 'Company'), - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } + _name = 'account.budget.post' + _inherit = 'account.budget.post' + _columns ={ + 'company_id': fields.many2one('res.company', 'Company'), + } + _defaults = { + 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + } account_budget_post() class account_period(osv.osv): - _name = 'account.period' - _inherit = 'account.period' - _columns = { - 'company_id': fields.many2one('res.company', 'Company', required=True), - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } + _name = 'account.period' + _inherit = 'account.period' + _columns = { + 'company_id': fields.many2one('res.company', 'Company', required=True), + } + _defaults = { + 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + } account_period() class account_fiscalyear(osv.osv): - _name = 'account.fiscalyear' - _inherit = 'account.fiscalyear' - _columns = { - 'company_id': fields.many2one('res.company', 'Company', required=True), - } - _defaults = { - 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, - } + _name = 'account.fiscalyear' + _inherit = 'account.fiscalyear' + _columns = { + 'company_id': fields.many2one('res.company', 'Company', required=True), + } + _defaults = { + 'company_id': lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, + } account_fiscalyear() diff --git a/addons/processus/report/report_processus.py b/addons/processus/report/report_processus.py index 8c399a1d5f7..cc517c497ee 100644 --- a/addons/processus/report/report_processus.py +++ b/addons/processus/report/report_processus.py @@ -38,64 +38,64 @@ import Image class report_graph_instance(object): - def __init__(self, cr, uid, ids, data): - current_object = 'sale.order' - pool = pooler.get_pool(cr.dbname) - for processus in pool.get('processus.processus').browse(cr, uid, ids): - nodes = {} - start = [] - transitions = {} - for node in processus.node_ids: - nodes[node.id] = node - if node.flow_start: - start.append(node.id) - for tr in node.transition_out: - transitions[tr.id] = tr - g = tools.graph(nodes.keys(), map(lambda x: (x.node_from_id.id,x.node_to_id.id), transitions.values())) - g.process(start) - g.scale(250,250, 100, 10) + def __init__(self, cr, uid, ids, data): + current_object = 'sale.order' + pool = pooler.get_pool(cr.dbname) + for processus in pool.get('processus.processus').browse(cr, uid, ids): + nodes = {} + start = [] + transitions = {} + for node in processus.node_ids: + nodes[node.id] = node + if node.flow_start: + start.append(node.id) + for tr in node.transition_out: + transitions[tr.id] = tr + g = tools.graph(nodes.keys(), map(lambda x: (x.node_from_id.id,x.node_to_id.id), transitions.values())) + g.process(start) + g.scale(250,250, 100, 10) - img = Image.new('RGB',(1024,768),'#ffffff') - g2 = processus_print.graph(img) - positions = g.result - for name,node in positions.items(): - start_color = (nodes[name].model_id.model==current_object) - g2.node(node['y'],node['x'], { - 'title': nodes[name].name, - 'menu': nodes[name].menu_id.complete_name - }, start_color=start_color) - for name,transition in transitions.items(): - if transition.transition_ids: - g2.arrow_role((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']), - (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x'])) - g2.arrow((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']), - (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x'])) - img.save('/tmp/a.pdf') - self.result = file('/tmp/a.pdf').read() - self.done = True + img = Image.new('RGB',(1024,768),'#ffffff') + g2 = processus_print.graph(img) + positions = g.result + for name,node in positions.items(): + start_color = (nodes[name].model_id.model==current_object) + g2.node(node['y'],node['x'], { + 'title': nodes[name].name, + 'menu': nodes[name].menu_id.complete_name + }, start_color=start_color) + for name,transition in transitions.items(): + if transition.transition_ids: + g2.arrow_role((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']), + (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x'])) + g2.arrow((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']), + (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x'])) + img.save('/tmp/a.pdf') + self.result = file('/tmp/a.pdf').read() + self.done = True - def is_done(self): - return self.done + def is_done(self): + return self.done - def get(self): - if self.done: - return self.result - else: - return None + def get(self): + if self.done: + return self.result + else: + return None class report_graph(report.interface.report_int): - def __init__(self, name, table): - report.interface.report_int.__init__(self, name) - self.table = table + def __init__(self, name, table): + report.interface.report_int.__init__(self, name) + self.table = table - def result(self): - if self.obj.is_done(): - return (True, self.obj.get(), 'pdf') - else: - return (False, False, False) + def result(self): + if self.obj.is_done(): + return (True, self.obj.get(), 'pdf') + else: + return (False, False, False) - def create(self, cr, uid, ids, data, context={}): - self.obj = report_graph_instance(cr, uid, ids, data) - return (self.obj.get(), 'pdf') + def create(self, cr, uid, ids, data, context={}): + self.obj = report_graph_instance(cr, uid, ids, data) + return (self.obj.get(), 'pdf') report_graph('report.processus.processus.print', 'processus.processus') diff --git a/addons/product/__terp__.py b/addons/product/__terp__.py index b7566b741a8..90a45f2ef1c 100644 --- a/addons/product/__terp__.py +++ b/addons/product/__terp__.py @@ -26,33 +26,33 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Products & Pricelists", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Inventory Control", - "depends" : ["base"], - "init_xml" : [], - "demo_xml" : ["product_demo.xml"], - "description": """ - This is the base module to manage products and pricelists in Tiny ERP. + "name" : "Products & Pricelists", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Inventory Control", + "depends" : ["base"], + "init_xml" : [], + "demo_xml" : ["product_demo.xml"], + "description": """ + This is the base module to manage products and pricelists in Tiny ERP. - Products support variants, different pricing methods, suppliers - information, make to stock/order, different unit of measures, - packagins and properties. + Products support variants, different pricing methods, suppliers + information, make to stock/order, different unit of measures, + packagins and properties. - Pricelists supports: - * Multiple-level of discount (by product, category, quantities) - * Compute price based on different criterions: - * Other pricelist, - * Cost price, - * List price, - * Supplier price, ... - Pricelists preferences by product and/or partners. + Pricelists supports: + * Multiple-level of discount (by product, category, quantities) + * Compute price based on different criterions: + * Other pricelist, + * Cost price, + * List price, + * Supplier price, ... + Pricelists preferences by product and/or partners. - Print product labels with barcodes. - """, - "update_xml" : ["product_data.xml","product_report.xml", - "product_view.xml", "pricelist_view.xml","product_security.xml"], - "active": False, - "installable": True + Print product labels with barcodes. + """, + "update_xml" : ["product_data.xml","product_report.xml", + "product_view.xml", "pricelist_view.xml","product_security.xml"], + "active": False, + "installable": True } diff --git a/addons/product/_common.py b/addons/product/_common.py index d76cdd5c184..5500b61fdc0 100644 --- a/addons/product/_common.py +++ b/addons/product/_common.py @@ -26,8 +26,8 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### def rounding(f, r): - if not r: - return f - return round(f / r) * r + if not r: + return f + return round(f / r) * r diff --git a/addons/product/partner.py b/addons/product/partner.py index 86ece9df41d..dc70abac22e 100644 --- a/addons/product/partner.py +++ b/addons/product/partner.py @@ -33,19 +33,19 @@ from osv import fields, osv class res_partner(osv.osv): - _name = 'res.partner' - _inherit = 'res.partner' - _columns = { - 'property_product_pricelist': fields.property( - 'product.pricelist', - type='many2one', - relation='product.pricelist', - string="Sale Pricelist", - method=True, - view_load=True, - group_name="Pricelists Properties", - help="This pricelist will be used, instead of the default one, \ - for sales to the current partner"), - } + _name = 'res.partner' + _inherit = 'res.partner' + _columns = { + 'property_product_pricelist': fields.property( + 'product.pricelist', + type='many2one', + relation='product.pricelist', + string="Sale Pricelist", + method=True, + view_load=True, + group_name="Pricelists Properties", + help="This pricelist will be used, instead of the default one, \ + for sales to the current partner"), + } res_partner() diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 4cf2fdc1807..132e8125afc 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -37,34 +37,34 @@ from tools import config from tools.translate import _ class price_type(osv.osv): - """ - The price type is used to points which field in the product form - is a price and in which currency is this price expressed. - When a field is a price, you can use it in pricelists to base - sale and purchase prices based on some fields of the product. - """ - def _price_field_get(self, cr, uid, context={}): - cr.execute('select name, field_description from ir_model_fields where model in (%s,%s) and ttype=%s order by name', ('product.product', 'product.template', 'float')) - return cr.fetchall() - def _get_currency(self, cr, uid, ctx): - comp = self.pool.get('res.users').browse(cr,uid,uid).company_id - if not comp: - comp_id = self.pool.get('res.company').search(cr, uid, [])[0] - comp = self.pool.get('res.company').browse(cr, uid, comp_id) - return comp.currency_id.id + """ + The price type is used to points which field in the product form + is a price and in which currency is this price expressed. + When a field is a price, you can use it in pricelists to base + sale and purchase prices based on some fields of the product. + """ + def _price_field_get(self, cr, uid, context={}): + cr.execute('select name, field_description from ir_model_fields where model in (%s,%s) and ttype=%s order by name', ('product.product', 'product.template', 'float')) + return cr.fetchall() + def _get_currency(self, cr, uid, ctx): + comp = self.pool.get('res.users').browse(cr,uid,uid).company_id + if not comp: + comp_id = self.pool.get('res.company').search(cr, uid, [])[0] + comp = self.pool.get('res.company').browse(cr, uid, comp_id) + return comp.currency_id.id - _name = "product.price.type" - _description = "Price type" - _columns = { - "name" : fields.char("Price Name", size=32, required=True, translate=True) , - "active" : fields.boolean("Active"), - "field" : fields.selection(_price_field_get, "Product Field", required=True), - "currency_id" : fields.many2one('res.currency', "Currency", required=True), - } - _defaults = { - "active": lambda *args: True, - "currency_id": _get_currency - } + _name = "product.price.type" + _description = "Price type" + _columns = { + "name" : fields.char("Price Name", size=32, required=True, translate=True) , + "active" : fields.boolean("Active"), + "field" : fields.selection(_price_field_get, "Product Field", required=True), + "currency_id" : fields.many2one('res.currency', "Currency", required=True), + } + _defaults = { + "active": lambda *args: True, + "currency_id": _get_currency + } price_type() #---------------------------------------------------------- @@ -72,282 +72,282 @@ price_type() #---------------------------------------------------------- class product_pricelist_type(osv.osv): - _name = "product.pricelist.type" - _description = "Pricelist Type" - _columns = { - 'name': fields.char('Name',size=64, required=True), - 'key': fields.char('Key', size=64, required=True), - } + _name = "product.pricelist.type" + _description = "Pricelist Type" + _columns = { + 'name': fields.char('Name',size=64, required=True), + 'key': fields.char('Key', size=64, required=True), + } product_pricelist_type() class product_pricelist(osv.osv): - def _pricelist_type_get(self, cr, uid, context={}): - cr.execute('select key,name from product_pricelist_type order by name') - return cr.fetchall() - _name = "product.pricelist" - _description = "Pricelist" - _columns = { - 'name': fields.char('Name',size=64, required=True, translate=True), - 'active': fields.boolean('Active'), - 'type': fields.selection(_pricelist_type_get, 'Pricelist Type', required=True), - 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions'), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True), - } - def _get_currency(self, cr, uid, ctx): - comp = self.pool.get('res.users').browse(cr,uid,uid).company_id - if not comp: - comp_id = self.pool.get('res.company').search(cr, uid, [])[0] - comp = self.pool.get('res.company').browse(cr, uid, comp_id) - return comp.currency_id.id + def _pricelist_type_get(self, cr, uid, context={}): + cr.execute('select key,name from product_pricelist_type order by name') + return cr.fetchall() + _name = "product.pricelist" + _description = "Pricelist" + _columns = { + 'name': fields.char('Name',size=64, required=True, translate=True), + 'active': fields.boolean('Active'), + 'type': fields.selection(_pricelist_type_get, 'Pricelist Type', required=True), + 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions'), + 'currency_id': fields.many2one('res.currency', 'Currency', required=True), + } + def _get_currency(self, cr, uid, ctx): + comp = self.pool.get('res.users').browse(cr,uid,uid).company_id + if not comp: + comp_id = self.pool.get('res.company').search(cr, uid, [])[0] + comp = self.pool.get('res.company').browse(cr, uid, comp_id) + return comp.currency_id.id - _defaults = { - 'active': lambda *a: 1, - "currency_id": _get_currency - } + _defaults = { + 'active': lambda *a: 1, + "currency_id": _get_currency + } - def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None): - ''' - context = { - 'uom': Unit of Measure (int), - 'partner': Partner ID (int), - 'date': Date of the pricelist (%Y-%m-%d), - } - ''' - context = context or {} - currency_obj = self.pool.get('res.currency') - product_obj = self.pool.get('product.product') - supplierinfo_obj = self.pool.get('product.supplierinfo') - price_type_obj = self.pool.get('product.price.type') + def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None): + ''' + context = { + 'uom': Unit of Measure (int), + 'partner': Partner ID (int), + 'date': Date of the pricelist (%Y-%m-%d), + } + ''' + context = context or {} + currency_obj = self.pool.get('res.currency') + product_obj = self.pool.get('product.product') + supplierinfo_obj = self.pool.get('product.supplierinfo') + price_type_obj = self.pool.get('product.price.type') - if context and ('partner_id' in context): - partner = context['partner_id'] - context['partner_id'] = partner - date = time.strftime('%Y-%m-%d') - if context and ('date' in context): - date = context['date'] - result = {} - for id in ids: - cr.execute('SELECT * ' \ - 'FROM product_pricelist_version ' \ - 'WHERE pricelist_id = %d AND active=True ' \ - 'AND (date_start IS NULL OR date_start <= %s) ' \ - 'AND (date_end IS NULL OR date_end >= %s) ' \ - 'ORDER BY id LIMIT 1', (id, date, date)) - plversion = cr.dictfetchone() + if context and ('partner_id' in context): + partner = context['partner_id'] + context['partner_id'] = partner + date = time.strftime('%Y-%m-%d') + if context and ('date' in context): + date = context['date'] + result = {} + for id in ids: + cr.execute('SELECT * ' \ + 'FROM product_pricelist_version ' \ + 'WHERE pricelist_id = %d AND active=True ' \ + 'AND (date_start IS NULL OR date_start <= %s) ' \ + 'AND (date_end IS NULL OR date_end >= %s) ' \ + 'ORDER BY id LIMIT 1', (id, date, date)) + plversion = cr.dictfetchone() - if not plversion: - raise osv.except_osv(_('Warning !'), - _('No active version for the selected pricelist !\n' \ - 'Please create or activate one.')) + if not plversion: + raise osv.except_osv(_('Warning !'), + _('No active version for the selected pricelist !\n' \ + 'Please create or activate one.')) - cr.execute('SELECT id, categ_id ' \ - 'FROM product_template ' \ - 'WHERE id = (SELECT product_tmpl_id ' \ - 'FROM product_product ' \ - 'WHERE id = %d)', (prod_id,)) - tmpl_id, categ = cr.fetchone() - categ_ids = [] - while categ: - categ_ids.append(str(categ)) - cr.execute('SELECT parent_id ' \ - 'FROM product_category ' \ - 'WHERE id = %d', (categ,)) - categ = cr.fetchone()[0] - if str(categ) in categ_ids: - raise osv.except_osv(_('Warning !'), - _('Could not resolve product category, ' \ - 'you have defined cyclic categories ' \ - 'of products!')) - if categ_ids: - categ_where = '(categ_id IN (' + ','.join(categ_ids) + '))' - else: - categ_where = '(categ_id IS NULL)' + cr.execute('SELECT id, categ_id ' \ + 'FROM product_template ' \ + 'WHERE id = (SELECT product_tmpl_id ' \ + 'FROM product_product ' \ + 'WHERE id = %d)', (prod_id,)) + tmpl_id, categ = cr.fetchone() + categ_ids = [] + while categ: + categ_ids.append(str(categ)) + cr.execute('SELECT parent_id ' \ + 'FROM product_category ' \ + 'WHERE id = %d', (categ,)) + categ = cr.fetchone()[0] + if str(categ) in categ_ids: + raise osv.except_osv(_('Warning !'), + _('Could not resolve product category, ' \ + 'you have defined cyclic categories ' \ + 'of products!')) + if categ_ids: + categ_where = '(categ_id IN (' + ','.join(categ_ids) + '))' + else: + categ_where = '(categ_id IS NULL)' - cr.execute( - 'SELECT i.*, pl.currency_id ' - 'FROM product_pricelist_item AS i, ' - 'product_pricelist_version AS v, product_pricelist AS pl ' - 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %d) ' - 'AND (product_id IS NULL OR product_id = %d) ' - 'AND (' + categ_where + ' OR (categ_id IS NULL)) ' - 'AND price_version_id = %d ' - 'AND (min_quantity IS NULL OR min_quantity <= %f) ' - 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id ' - 'ORDER BY sequence LIMIT 1', - (tmpl_id, prod_id, plversion['id'], qty)) - res = cr.dictfetchone() - if res: - if res['base'] == -1: - if not res['base_pricelist_id']: - price = 0.0 - else: - price_tmp = self.price_get(cr, uid, - [res['base_pricelist_id']], prod_id, - qty)[res['base_pricelist_id']] - ptype_src = self.browse(cr, uid, - res['base_pricelist_id']).currency_id.id - price = currency_obj.compute(cr, uid, ptype_src, - res['currency_id'], price_tmp, round=False) - elif res['base'] == -2: - where = [] - if partner: - where = [('name', '=', partner) ] - sinfo = supplierinfo_obj.search(cr, uid, - [('product_id', '=', tmpl_id)] + where) - price = 0.0 - if sinfo: - cr.execute('SELECT * ' \ - 'FROM pricelist_partnerinfo ' \ - 'WHERE suppinfo_id IN (' + \ - ','.join(map(str, sinfo)) + ') ' \ - 'AND min_quantity <= %f ' \ - 'ORDER BY min_quantity DESC LIMIT 1', (qty,)) - res2 = cr.dictfetchone() - if res2: - price = res2['price'] - else: - price_type = price_type_obj.browse(cr, uid, int(res['base'])) - price = currency_obj.compute(cr, uid, - price_type.currency_id.id, res['currency_id'], - product_obj.price_get(cr, uid, [prod_id], - price_type.field, context)[prod_id], round=False) + cr.execute( + 'SELECT i.*, pl.currency_id ' + 'FROM product_pricelist_item AS i, ' + 'product_pricelist_version AS v, product_pricelist AS pl ' + 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = %d) ' + 'AND (product_id IS NULL OR product_id = %d) ' + 'AND (' + categ_where + ' OR (categ_id IS NULL)) ' + 'AND price_version_id = %d ' + 'AND (min_quantity IS NULL OR min_quantity <= %f) ' + 'AND i.price_version_id = v.id AND v.pricelist_id = pl.id ' + 'ORDER BY sequence LIMIT 1', + (tmpl_id, prod_id, plversion['id'], qty)) + res = cr.dictfetchone() + if res: + if res['base'] == -1: + if not res['base_pricelist_id']: + price = 0.0 + else: + price_tmp = self.price_get(cr, uid, + [res['base_pricelist_id']], prod_id, + qty)[res['base_pricelist_id']] + ptype_src = self.browse(cr, uid, + res['base_pricelist_id']).currency_id.id + price = currency_obj.compute(cr, uid, ptype_src, + res['currency_id'], price_tmp, round=False) + elif res['base'] == -2: + where = [] + if partner: + where = [('name', '=', partner) ] + sinfo = supplierinfo_obj.search(cr, uid, + [('product_id', '=', tmpl_id)] + where) + price = 0.0 + if sinfo: + cr.execute('SELECT * ' \ + 'FROM pricelist_partnerinfo ' \ + 'WHERE suppinfo_id IN (' + \ + ','.join(map(str, sinfo)) + ') ' \ + 'AND min_quantity <= %f ' \ + 'ORDER BY min_quantity DESC LIMIT 1', (qty,)) + res2 = cr.dictfetchone() + if res2: + price = res2['price'] + else: + price_type = price_type_obj.browse(cr, uid, int(res['base'])) + price = currency_obj.compute(cr, uid, + price_type.currency_id.id, res['currency_id'], + product_obj.price_get(cr, uid, [prod_id], + price_type.field, context)[prod_id], round=False) - price_limit = price + price_limit = price - price = price * (1.0-(res['price_discount'] or 0.0)) - price = rounding(price, res['price_round']) - price += (res['price_surcharge'] or 0.0) - if res['price_min_margin']: - price = max(price, price_limit+res['price_min_margin']) - if res['price_max_margin']: - price = min(price, price_limit+res['price_max_margin']) - else: - # False means no valid line found ! But we may not raise an - # exception here because it breaks the search - price = False - result[id] = price - if context and ('uom' in context): - product = product_obj.browse(cr, uid, prod_id) - uom = product.uos_id or product.uom_id - result[id] = self.pool.get('product.uom')._compute_price(cr, - uid, uom.id, result[id], context['uom']) - return result + price = price * (1.0-(res['price_discount'] or 0.0)) + price = rounding(price, res['price_round']) + price += (res['price_surcharge'] or 0.0) + if res['price_min_margin']: + price = max(price, price_limit+res['price_min_margin']) + if res['price_max_margin']: + price = min(price, price_limit+res['price_max_margin']) + else: + # False means no valid line found ! But we may not raise an + # exception here because it breaks the search + price = False + result[id] = price + if context and ('uom' in context): + product = product_obj.browse(cr, uid, prod_id) + uom = product.uos_id or product.uom_id + result[id] = self.pool.get('product.uom')._compute_price(cr, + uid, uom.id, result[id], context['uom']) + return result product_pricelist() class product_pricelist_version(osv.osv): - _name = "product.pricelist.version" - _description = "Pricelist Version" - _columns = { - 'pricelist_id': fields.many2one('product.pricelist', 'Price List', - required=True, select=True), - 'name': fields.char('Name', size=64, required=True), - 'active': fields.boolean('Active'), - 'items_id': fields.one2many('product.pricelist.item', - 'price_version_id', 'Price List Items', required=True), - 'date_start': fields.date('Start Date'), - 'date_end': fields.date('End Date'), - } - _defaults = { - 'active': lambda *a: 1, - } + _name = "product.pricelist.version" + _description = "Pricelist Version" + _columns = { + 'pricelist_id': fields.many2one('product.pricelist', 'Price List', + required=True, select=True), + 'name': fields.char('Name', size=64, required=True), + 'active': fields.boolean('Active'), + 'items_id': fields.one2many('product.pricelist.item', + 'price_version_id', 'Price List Items', required=True), + 'date_start': fields.date('Start Date'), + 'date_end': fields.date('End Date'), + } + _defaults = { + 'active': lambda *a: 1, + } - # - # TODO: improve this function ? - # - def _check_date(self, cursor, user, ids): - for pricelist_version in self.browse(cursor, user, ids): - if not pricelist_version.active: - continue - cursor.execute('SELECT id ' \ - 'FROM product_pricelist_version ' \ - 'WHERE ((date_start <= %s AND %s <= date_end ' \ - 'AND date_end IS NOT NULL) ' \ - 'OR (date_end IS NULL AND date_start IS NOT NULL ' \ - 'AND date_start <= %s) ' \ - 'OR (date_start IS NULL AND date_end IS NOT NULL ' \ - 'AND %s <= date_end) ' \ - 'OR (date_start IS NULL AND date_end IS NULL) ' \ - 'OR (%s = \'0000-01-01\' AND date_start IS NULL) ' \ - 'OR (%s = \'0000-01-01\' AND date_end IS NULL) ' \ - 'OR (%s = \'0000-01-01\' AND %s = \'0000-01-01\') ' \ - 'OR (%s = \'0000-01-01\' AND date_start <= %s) ' \ - 'OR (%s = \'0000-01-01\' AND %s <= date_end)) ' \ - 'AND pricelist_id = %d ' \ - 'AND active ' \ - 'AND id <> %d', (pricelist_version.date_end or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.date_end or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.date_end or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.date_end or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.date_end or '0000-01-01', - pricelist_version.date_end or '0000-01-01', - pricelist_version.date_start or '0000-01-01', - pricelist_version.pricelist_id.id, - pricelist_version.id)) - if cursor.fetchall(): - return False - return True + # + # TODO: improve this function ? + # + def _check_date(self, cursor, user, ids): + for pricelist_version in self.browse(cursor, user, ids): + if not pricelist_version.active: + continue + cursor.execute('SELECT id ' \ + 'FROM product_pricelist_version ' \ + 'WHERE ((date_start <= %s AND %s <= date_end ' \ + 'AND date_end IS NOT NULL) ' \ + 'OR (date_end IS NULL AND date_start IS NOT NULL ' \ + 'AND date_start <= %s) ' \ + 'OR (date_start IS NULL AND date_end IS NOT NULL ' \ + 'AND %s <= date_end) ' \ + 'OR (date_start IS NULL AND date_end IS NULL) ' \ + 'OR (%s = \'0000-01-01\' AND date_start IS NULL) ' \ + 'OR (%s = \'0000-01-01\' AND date_end IS NULL) ' \ + 'OR (%s = \'0000-01-01\' AND %s = \'0000-01-01\') ' \ + 'OR (%s = \'0000-01-01\' AND date_start <= %s) ' \ + 'OR (%s = \'0000-01-01\' AND %s <= date_end)) ' \ + 'AND pricelist_id = %d ' \ + 'AND active ' \ + 'AND id <> %d', (pricelist_version.date_end or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.date_end or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.date_end or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.date_end or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.date_end or '0000-01-01', + pricelist_version.date_end or '0000-01-01', + pricelist_version.date_start or '0000-01-01', + pricelist_version.pricelist_id.id, + pricelist_version.id)) + if cursor.fetchall(): + return False + return True - _constraints = [ - (_check_date, 'You can not have 2 pricelist version that overlaps!', - ['date_start', 'date_end']) - ] + _constraints = [ + (_check_date, 'You can not have 2 pricelist version that overlaps!', + ['date_start', 'date_end']) + ] product_pricelist_version() class product_pricelist_item(osv.osv): - def _price_field_get(self, cr, uid, context={}): - cr.execute('select id,name from product_price_type where active') - result = cr.fetchall() - result.append((-1,'Other Pricelist')) - result.append((-2,'Partner section of the product form')) - return result + def _price_field_get(self, cr, uid, context={}): + cr.execute('select id,name from product_price_type where active') + result = cr.fetchall() + result.append((-1,'Other Pricelist')) + result.append((-2,'Partner section of the product form')) + return result - _name = "product.pricelist.item" - _description = "Pricelist item" - _order = "sequence, min_quantity desc" - _defaults = { - 'base': lambda *a: -1, - 'min_quantity': lambda *a: 0, - 'sequence': lambda *a: 5, - 'price_discount': lambda *a: 0, - } - _columns = { - 'name': fields.char('Name', size=64), - 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True), - 'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade'), - 'product_id': fields.many2one('product.product', 'Product', ondelete='cascade'), - 'categ_id': fields.many2one('product.category', 'Product Category', ondelete='cascade'), + _name = "product.pricelist.item" + _description = "Pricelist item" + _order = "sequence, min_quantity desc" + _defaults = { + 'base': lambda *a: -1, + 'min_quantity': lambda *a: 0, + 'sequence': lambda *a: 5, + 'price_discount': lambda *a: 0, + } + _columns = { + 'name': fields.char('Name', size=64), + 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True), + 'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade'), + 'product_id': fields.many2one('product.product', 'Product', ondelete='cascade'), + 'categ_id': fields.many2one('product.category', 'Product Category', ondelete='cascade'), - 'min_quantity': fields.integer('Min. Quantity', required=True), - 'sequence': fields.integer('Sequence', required=True), - 'base': fields.selection(_price_field_get, 'Based on', required=True, size=-1), - 'base_pricelist_id': fields.many2one('product.pricelist', 'If Other Pricelist'), + 'min_quantity': fields.integer('Min. Quantity', required=True), + 'sequence': fields.integer('Sequence', required=True), + 'base': fields.selection(_price_field_get, 'Based on', required=True, size=-1), + 'base_pricelist_id': fields.many2one('product.pricelist', 'If Other Pricelist'), - 'price_surcharge': fields.float('Price Surcharge', - digits=(16, int(config['price_accuracy']))), - 'price_discount': fields.float('Price Discount', digits=(16,4)), - 'price_round': fields.float('Price Rounding', - digits=(16, int(config['price_accuracy']))), - 'price_min_margin': fields.float('Price Min. Margin', - digits=(16, int(config['price_accuracy']))), - 'price_max_margin': fields.float('Price Max. Margin', - digits=(16, int(config['price_accuracy']))), - } - def product_id_change(self, cr, uid, ids, product_id, context={}): - if not product_id: - return {} - prod = self.pool.get('product.product').read(cr, uid, [product_id], ['code','name']) - if prod[0]['code']: - return {'value': {'name': prod[0]['code']}} - return {} + 'price_surcharge': fields.float('Price Surcharge', + digits=(16, int(config['price_accuracy']))), + 'price_discount': fields.float('Price Discount', digits=(16,4)), + 'price_round': fields.float('Price Rounding', + digits=(16, int(config['price_accuracy']))), + 'price_min_margin': fields.float('Price Min. Margin', + digits=(16, int(config['price_accuracy']))), + 'price_max_margin': fields.float('Price Max. Margin', + digits=(16, int(config['price_accuracy']))), + } + def product_id_change(self, cr, uid, ids, product_id, context={}): + if not product_id: + return {} + prod = self.pool.get('product.product').read(cr, uid, [product_id], ['code','name']) + if prod[0]['code']: + return {'value': {'name': prod[0]['code']}} + return {} product_pricelist_item() diff --git a/addons/product/product.py b/addons/product/product.py index 76e26cf52bd..ac7d0fb46d9 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -36,138 +36,138 @@ from _common import rounding from tools import config def is_pair(x): - return not x%2 + return not x%2 #---------------------------------------------------------- # UOM #---------------------------------------------------------- class product_uom_categ(osv.osv): - _name = 'product.uom.categ' - _description = 'Product uom categ' - _columns = { - 'name': fields.char('Name', size=64, required=True), - } + _name = 'product.uom.categ' + _description = 'Product uom categ' + _columns = { + 'name': fields.char('Name', size=64, required=True), + } product_uom_categ() class product_uom(osv.osv): - _name = 'product.uom' - _description = 'Product Unit of Measure' - - def _factor(self, cursor, user, ids, name, arg, context): - res = {} - for uom in self.browse(cursor, user, ids, context=context): - if uom.factor: - if uom.factor_inv_data: - res[uom.id] = uom.factor_inv_data - else: - res[uom.id] = round(1 / uom.factor, 6) - else: - res[uom.id] = 0.0 - return res - - def _factor_inv(self, cursor, user, id, name, value, arg, context): - ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] - if value: - data = 0.0 - if round(1 / round(1/value, 6), 6) != value: - data = value - self.write(cursor, user, id, { - 'factor': round(1/value, 6), - 'factor_inv_data': data, - }, context=ctx) - else: - self.write(cursor, user, id, { - 'factor': 0.0, - 'factor_inv_data': 0.0, - }, context=ctx) - - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'category_id': fields.many2one('product.uom.categ', 'UOM Category', required=True, ondelete='cascade'), - 'factor': fields.float('Rate', digits=(12, 6), required=True, - help='The coefficient for the formula:\n' \ - '1 (base unit) = coef (this unit)'), - 'factor_inv': fields.function(_factor, fnct_inv=_factor_inv, digits=(12, 6), - method=True, string='Factor', - help='The coefficient for the formula:\n' \ - 'coef (base unit) = 1 (this unit)'), - 'factor_inv_data': fields.float('Factor', digits=(12, 6)), - 'rounding': fields.float('Rounding Precision', digits=(16, 3), required=True), - 'active': fields.boolean('Active'), - } - - _defaults = { - 'factor': lambda *a: 1.0, - 'factor_inv': lambda *a: 1.0, - 'active': lambda *a: 1, - 'rounding': lambda *a: 0.01, - } - - def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False): - if not from_uom_id or not qty or not to_uom_id: - return qty - uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) - if uoms[0].id == from_uom_id: - from_unit, to_unit = uoms[0], uoms[-1] - else: - from_unit, to_unit = uoms[-1], uoms[0] - if from_unit.category_id.id <> to_unit.category_id.id: - return qty - if from_unit['factor_inv_data']: - amount = qty * from_unit['factor_inv_data'] - else: - amount = qty / from_unit['factor'] - if to_uom_id: - if to_unit['factor_inv_data']: - amount = rounding(amount / to_unit['factor_inv_data'], to_unit['rounding']) - else: - amount = rounding(amount * to_unit['factor'], to_unit['rounding']) - return amount - - def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False): - if not from_uom_id or not price or not to_uom_id: - return price - uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) - if uoms[0].id == from_uom_id: - from_unit, to_unit = uoms[0], uoms[-1] - else: - from_unit, to_unit = uoms[-1], uoms[0] - if from_unit.category_id.id <> to_unit.category_id.id: - return price - if from_unit.factor_inv_data: - amount = price / from_unit.factor_inv_data - else: - amount = price * from_unit.factor - if to_uom_id: - if to_unit.factor_inv_data: - amount = amount * to_unit.factor_inv_data - else: - amount = amount / to_unit.factor - return amount - - def onchange_factor_inv(self, cursor, user, ids, value): - if value == 0.0: - return {'value': {'factor': 0}} - return {'value': {'factor': round(1/value, 6)}} - - def onchange_factor(self, cursor, user, ids, value): - if value == 0.0: - return {'value': {'factor_inv': 0}} - return {'value': {'factor_inv': round(1/value, 6)}} + _name = 'product.uom' + _description = 'Product Unit of Measure' + + def _factor(self, cursor, user, ids, name, arg, context): + res = {} + for uom in self.browse(cursor, user, ids, context=context): + if uom.factor: + if uom.factor_inv_data: + res[uom.id] = uom.factor_inv_data + else: + res[uom.id] = round(1 / uom.factor, 6) + else: + res[uom.id] = 0.0 + return res + + def _factor_inv(self, cursor, user, id, name, value, arg, context): + ctx = context.copy() + if 'read_delta' in ctx: + del ctx['read_delta'] + if value: + data = 0.0 + if round(1 / round(1/value, 6), 6) != value: + data = value + self.write(cursor, user, id, { + 'factor': round(1/value, 6), + 'factor_inv_data': data, + }, context=ctx) + else: + self.write(cursor, user, id, { + 'factor': 0.0, + 'factor_inv_data': 0.0, + }, context=ctx) + + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'category_id': fields.many2one('product.uom.categ', 'UOM Category', required=True, ondelete='cascade'), + 'factor': fields.float('Rate', digits=(12, 6), required=True, + help='The coefficient for the formula:\n' \ + '1 (base unit) = coef (this unit)'), + 'factor_inv': fields.function(_factor, fnct_inv=_factor_inv, digits=(12, 6), + method=True, string='Factor', + help='The coefficient for the formula:\n' \ + 'coef (base unit) = 1 (this unit)'), + 'factor_inv_data': fields.float('Factor', digits=(12, 6)), + 'rounding': fields.float('Rounding Precision', digits=(16, 3), required=True), + 'active': fields.boolean('Active'), + } + + _defaults = { + 'factor': lambda *a: 1.0, + 'factor_inv': lambda *a: 1.0, + 'active': lambda *a: 1, + 'rounding': lambda *a: 0.01, + } + + def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False): + if not from_uom_id or not qty or not to_uom_id: + return qty + uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) + if uoms[0].id == from_uom_id: + from_unit, to_unit = uoms[0], uoms[-1] + else: + from_unit, to_unit = uoms[-1], uoms[0] + if from_unit.category_id.id <> to_unit.category_id.id: + return qty + if from_unit['factor_inv_data']: + amount = qty * from_unit['factor_inv_data'] + else: + amount = qty / from_unit['factor'] + if to_uom_id: + if to_unit['factor_inv_data']: + amount = rounding(amount / to_unit['factor_inv_data'], to_unit['rounding']) + else: + amount = rounding(amount * to_unit['factor'], to_unit['rounding']) + return amount + + def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False): + if not from_uom_id or not price or not to_uom_id: + return price + uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) + if uoms[0].id == from_uom_id: + from_unit, to_unit = uoms[0], uoms[-1] + else: + from_unit, to_unit = uoms[-1], uoms[0] + if from_unit.category_id.id <> to_unit.category_id.id: + return price + if from_unit.factor_inv_data: + amount = price / from_unit.factor_inv_data + else: + amount = price * from_unit.factor + if to_uom_id: + if to_unit.factor_inv_data: + amount = amount * to_unit.factor_inv_data + else: + amount = amount / to_unit.factor + return amount + + def onchange_factor_inv(self, cursor, user, ids, value): + if value == 0.0: + return {'value': {'factor': 0}} + return {'value': {'factor': round(1/value, 6)}} + + def onchange_factor(self, cursor, user, ids, value): + if value == 0.0: + return {'value': {'factor_inv': 0}} + return {'value': {'factor_inv': round(1/value, 6)}} product_uom() class product_ul(osv.osv): - _name = "product.ul" - _description = "Shipping Unit" - _columns = { - 'name' : fields.char('Name', size=64,select=True), - 'type' : fields.selection([('unit','Unit'),('pack','Pack'),('box', 'Box'), ('palet', 'Palet')], 'Type', required=True), - } + _name = "product.ul" + _description = "Shipping Unit" + _columns = { + 'name' : fields.char('Name', size=64,select=True), + 'type' : fields.selection([('unit','Unit'),('pack','Pack'),('box', 'Box'), ('palet', 'Palet')], 'Type', required=True), + } product_ul() @@ -176,47 +176,47 @@ product_ul() #---------------------------------------------------------- class product_category(osv.osv): - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','parent_id'], context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res - - def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context): - res = self.name_get(cr, uid, ids) - return dict(res) - - _name = "product.category" - _description = "Product Category" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Name'), - 'parent_id': fields.many2one('product.category','Parent Category', select=True), - 'child_id': fields.one2many('product.category', 'parent_id', string='Childs Categories'), - 'sequence': fields.integer('Sequence'), - } - _order = "sequence" - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from product_category where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id']) - ] - def child_get(self, cr, uid, ids): - return [ids] + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','parent_id'], context) + res = [] + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1]+' / '+name + res.append((record['id'], name)) + return res + + def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context): + res = self.name_get(cr, uid, ids) + return dict(res) + + _name = "product.category" + _description = "Product Category" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Name'), + 'parent_id': fields.many2one('product.category','Parent Category', select=True), + 'child_id': fields.one2many('product.category', 'parent_id', string='Childs Categories'), + 'sequence': fields.integer('Sequence'), + } + _order = "sequence" + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from product_category where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id']) + ] + def child_get(self, cr, uid, ids): + return [ids] product_category() @@ -225,369 +225,369 @@ product_category() # Products #---------------------------------------------------------- class product_template(osv.osv): - _name = "product.template" - _description = "Product Template" - - def _calc_seller_delay(self, cr, uid, ids, name, arg, context={}): - result = {} - for product in self.browse(cr, uid, ids, context): - if product.seller_ids: - result[product.id] = product.seller_ids[0].delay - else: - result[product.id] = 1 - return result - - _columns = { - 'name': fields.char('Name', size=64, required=True, translate=True, select=True), - 'product_manager': fields.many2one('res.users','Product Manager'), - 'description': fields.text('Description'), - 'description_purchase': fields.text('Purchase Description'), - 'description_sale': fields.text('Sale Description'), - 'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True), - 'supply_method': fields.selection([('produce','Produce'),('buy','Buy')], 'Supply method', required=True), - 'sale_delay': fields.float('Customer lead time', help="This is the average time between the confirmation of the customer order and the delivery of the finnished products. It's the time you promise to your customers."), - 'produce_delay': fields.float('Manufacturing lead time', help="Average time to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different delays will be summed for all levels and purchase orders."), - 'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procure Method', required=True, help="'Make to Stock': When needed, take from the stock or wait until refurnishing. 'Make to Order': When needed, purchase or produce for the procurement request."), - 'rental': fields.boolean('Rentable product'), - 'categ_id': fields.many2one('product.category','Category', required=True, change_default=True), - 'list_price': fields.float('Public Price', digits=(16, int(config['price_accuracy']))), - 'standard_price': fields.float('Cost Price', required=True, digits=(16, int(config['price_accuracy']))), - 'volume': fields.float('Volume'), - 'weight': fields.float('Gross weight'), - 'weight_net': fields.float('Net weight'), - 'cost_method': fields.selection([('standard','Standard Price'), ('average','Average Price')], 'Costing Method', required=True), - 'warranty': fields.float('Warranty (months)'), - 'sale_ok': fields.boolean('Can be sold', help="Determine if the product can be visible in the list of product within a selection from a sale order line."), - 'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."), - 'uom_id': fields.many2one('product.uom', 'Default UOM', required=True), - 'uom_po_id': fields.many2one('product.uom', 'Purchase UOM', required=True), - 'state': fields.selection([('draft', 'In Development'),('sellable','In Production'),('end','End of Lifecycle'),('obsolete','Obsolete')], 'Status', help="Tells the user if he can use the product or not."), - 'uos_id' : fields.many2one('product.uom', 'Unit of Sale', - help='Keep empty to use the default UOM'), - 'uos_coeff': fields.float('UOM -> UOS Coeff', digits=(16,4), - help='Coefficient to convert UOM to UOS\n' - ' uom = uos * coeff'), - 'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure type', required=True), - 'tracking': fields.boolean('Track lots'), - 'seller_delay': fields.function(_calc_seller_delay, method=True, type='integer', string='Supplier lead time', help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."), - 'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Partners'), - } - - def _get_uom_id(self, cr, uid, *args): - cr.execute('select id from product_uom order by id limit 1') - res = cr.fetchone() - return res and res[0] or False - - _defaults = { - 'type': lambda *a: 'product', - 'list_price': lambda *a: 1, - 'cost_method': lambda *a: 'standard', - 'supply_method': lambda *a: 'buy', - 'standard_price': lambda *a: 1, - 'sale_ok': lambda *a: 1, - 'sale_delay': lambda *a: 7, - 'produce_delay': lambda *a: 1, - 'purchase_ok': lambda *a: 1, - 'procure_method': lambda *a: 'make_to_stock', - 'uom_id': _get_uom_id, - 'uom_po_id': _get_uom_id, - #'uom_price_id' : _get_uom_id, - #'uos_id' : _get_uom_id, - 'uos_coeff' : lambda *a: 1.0, - 'mes_type' : lambda *a: 'fixed', - } - - def _check_uom(self, cursor, user, ids): - for product in self.browse(cursor, user, ids): - if product.uom_id.id <> product.uom_po_id.id: - return False - return True - - def _check_uos(self, cursor, user, ids): - for product in self.browse(cursor, user, ids): - if product.uos_id \ - and product.uos_id.category_id.id \ - == product.uom_id.category_id.id: - return False - return True - - _constraints = [ - (_check_uos, 'Error: UOS must be in a different category than the UOM', ['uos_id']), - (_check_uom, 'Error: The default UOM and the purchase UOM must be in the same category.', ['uom_id']), - ] - - def name_get(self, cr, user, ids, context={}): - if 'partner_id' in context: - pass - return super(product_template, self).name_get(cr, user, ids, context) + _name = "product.template" + _description = "Product Template" + + def _calc_seller_delay(self, cr, uid, ids, name, arg, context={}): + result = {} + for product in self.browse(cr, uid, ids, context): + if product.seller_ids: + result[product.id] = product.seller_ids[0].delay + else: + result[product.id] = 1 + return result + + _columns = { + 'name': fields.char('Name', size=64, required=True, translate=True, select=True), + 'product_manager': fields.many2one('res.users','Product Manager'), + 'description': fields.text('Description'), + 'description_purchase': fields.text('Purchase Description'), + 'description_sale': fields.text('Sale Description'), + 'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True), + 'supply_method': fields.selection([('produce','Produce'),('buy','Buy')], 'Supply method', required=True), + 'sale_delay': fields.float('Customer lead time', help="This is the average time between the confirmation of the customer order and the delivery of the finnished products. It's the time you promise to your customers."), + 'produce_delay': fields.float('Manufacturing lead time', help="Average time to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different delays will be summed for all levels and purchase orders."), + 'procure_method': fields.selection([('make_to_stock','Make to Stock'),('make_to_order','Make to Order')], 'Procure Method', required=True, help="'Make to Stock': When needed, take from the stock or wait until refurnishing. 'Make to Order': When needed, purchase or produce for the procurement request."), + 'rental': fields.boolean('Rentable product'), + 'categ_id': fields.many2one('product.category','Category', required=True, change_default=True), + 'list_price': fields.float('Public Price', digits=(16, int(config['price_accuracy']))), + 'standard_price': fields.float('Cost Price', required=True, digits=(16, int(config['price_accuracy']))), + 'volume': fields.float('Volume'), + 'weight': fields.float('Gross weight'), + 'weight_net': fields.float('Net weight'), + 'cost_method': fields.selection([('standard','Standard Price'), ('average','Average Price')], 'Costing Method', required=True), + 'warranty': fields.float('Warranty (months)'), + 'sale_ok': fields.boolean('Can be sold', help="Determine if the product can be visible in the list of product within a selection from a sale order line."), + 'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."), + 'uom_id': fields.many2one('product.uom', 'Default UOM', required=True), + 'uom_po_id': fields.many2one('product.uom', 'Purchase UOM', required=True), + 'state': fields.selection([('draft', 'In Development'),('sellable','In Production'),('end','End of Lifecycle'),('obsolete','Obsolete')], 'Status', help="Tells the user if he can use the product or not."), + 'uos_id' : fields.many2one('product.uom', 'Unit of Sale', + help='Keep empty to use the default UOM'), + 'uos_coeff': fields.float('UOM -> UOS Coeff', digits=(16,4), + help='Coefficient to convert UOM to UOS\n' + ' uom = uos * coeff'), + 'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure type', required=True), + 'tracking': fields.boolean('Track lots'), + 'seller_delay': fields.function(_calc_seller_delay, method=True, type='integer', string='Supplier lead time', help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."), + 'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Partners'), + } + + def _get_uom_id(self, cr, uid, *args): + cr.execute('select id from product_uom order by id limit 1') + res = cr.fetchone() + return res and res[0] or False + + _defaults = { + 'type': lambda *a: 'product', + 'list_price': lambda *a: 1, + 'cost_method': lambda *a: 'standard', + 'supply_method': lambda *a: 'buy', + 'standard_price': lambda *a: 1, + 'sale_ok': lambda *a: 1, + 'sale_delay': lambda *a: 7, + 'produce_delay': lambda *a: 1, + 'purchase_ok': lambda *a: 1, + 'procure_method': lambda *a: 'make_to_stock', + 'uom_id': _get_uom_id, + 'uom_po_id': _get_uom_id, + #'uom_price_id' : _get_uom_id, + #'uos_id' : _get_uom_id, + 'uos_coeff' : lambda *a: 1.0, + 'mes_type' : lambda *a: 'fixed', + } + + def _check_uom(self, cursor, user, ids): + for product in self.browse(cursor, user, ids): + if product.uom_id.id <> product.uom_po_id.id: + return False + return True + + def _check_uos(self, cursor, user, ids): + for product in self.browse(cursor, user, ids): + if product.uos_id \ + and product.uos_id.category_id.id \ + == product.uom_id.category_id.id: + return False + return True + + _constraints = [ + (_check_uos, 'Error: UOS must be in a different category than the UOM', ['uos_id']), + (_check_uom, 'Error: The default UOM and the purchase UOM must be in the same category.', ['uom_id']), + ] + + def name_get(self, cr, user, ids, context={}): + if 'partner_id' in context: + pass + return super(product_template, self).name_get(cr, user, ids, context) product_template() class product_product(osv.osv): - def _product_price(self, cr, uid, ids, name, arg, context={}): - res = {} - quantity = context.get('quantity', 1) - pricelist = context.get('pricelist', False) - if pricelist: - for id in ids: - price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], id, quantity, context=context)[pricelist] - res[id] = price - for id in ids: - res.setdefault(id, 0.0) - return res - - def _get_product_available_func(states, what): - def _product_available(self, cr, uid, ids, name, arg, context={}): - res={} - for id in ids: - res.setdefault(id, 0.0) - return res - return _product_available - - _product_qty_available = _get_product_available_func(('done',), ('in', 'out')) - _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out')) - _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) - _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) - - def _product_lst_price(self, cr, uid, ids, name, arg, context=None): - res = {} - product_uom_obj = self.pool.get('product.uom') - for id in ids: - res.setdefault(id, 0.0) - for product in self.browse(cr, uid, ids, context=context): - if 'uom' in context: - uom = product.uos_id or product.uom_id - res[product.id] = product_uom_obj._compute_price(cr, uid, - uom.id, product.list_price, context['uom']) - else: - res[product.id] = product.list_price - return res - - def _get_partner_code_name(self, cr, uid, ids, product_id, partner_id, context={}): - product = self.browse(cr, uid, [product_id], context)[0] - for supinfo in product.seller_ids: - if supinfo.name.id == partner_id: - return {'code': supinfo.product_code, 'name': supinfo.product_name} - return {'code' : product.default_code, 'name' : product.name} - - def _product_code(self, cr, uid, ids, name, arg, context={}): - res = {} - for p in self.browse(cr, uid, ids, context): - res[p.id] = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context)['code'] - return res - - def _product_partner_ref(self, cr, uid, ids, name, arg, context={}): - res = {} - for p in self.browse(cr, uid, ids, context): - data = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context) - res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + \ - (data['name'] or '') - return res - - _defaults = { - 'active': lambda *a: 1, - 'price_extra': lambda *a: 0.0, - 'price_margin': lambda *a: 1.0, - } - - _name = "product.product" - _description = "Product" - _table = "product_product" - _inherits = {'product.template': 'product_tmpl_id'} - _columns = { - 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'), - 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'), - 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'), - 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'), - 'price': fields.function(_product_price, method=True, type='float', string='Customer Price', digits=(16, int(config['price_accuracy']))), - 'lst_price' : fields.function(_product_lst_price, method=True, type='float', string='List Price', digits=(16, int(config['price_accuracy']))), - 'code': fields.function(_product_code, method=True, type='char', string='Code'), - 'partner_ref' : fields.function(_product_partner_ref, method=True, type='char', string='Customer ref'), - 'default_code' : fields.char('Code', size=64), - 'active': fields.boolean('Active'), - 'variants': fields.char('Variants', size=64), - 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True), - 'ean13': fields.char('EAN13', size=13), - 'packaging' : fields.one2many('product.packaging', 'product_id', 'Palettization', help="Gives the different ways to package the same product. This has no impact on the packing order and is mainly used if you use the EDI module."), - 'price_extra': fields.float('Price Extra', digits=(16, int(config['price_accuracy']))), - 'price_margin': fields.float('Price Margin', digits=(16, int(config['price_accuracy']))), - } - - def _check_ean_key(self, cr, uid, ids): - for partner in self.browse(cr, uid, ids): - if not partner.ean13: - continue - if len(partner.ean13) < 12: - return False - try: - int(partner.ean13) - except: - return False - sum=0 - for i in range(12): - if is_pair(i): - sum += int(partner.ean13[i]) - else: - sum += 3 * int(partner.ean13[i]) - check = int(math.ceil(sum / 10.0) * 10 - sum) - if len(partner.ean13) == 12: - self.write(cr, uid, partner.id, { - 'ean13': partner.ean13 + str(check) - }) - elif check != int(partner.ean13[12]): - return False - return True - - _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] - - def on_order(self, cr, uid, ids, orderline, quantity): - pass - - def name_get(self, cr, user, ids, context={}): - if not len(ids): - return [] - def _name_get(d): - #name = self._product_partner_ref(cr, user, [d['id']], '', '', context)[d['id']] - #code = self._product_code(cr, user, [d['id']], '', '', context)[d['id']] - name = d.get('name','') - code = d.get('default_code',False) - if code: - name = '[%s] %s' % (code,name) - if d['variants']: - name = name + ' - %s' % (d['variants'],) - return (d['id'], name) - result = map(_name_get, self.read(cr, user, ids, ['variants','name','default_code'], context)) - return result - - def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context) - if not len(ids): - ids = self.search(cr, user, [('ean13','=',name)]+ args, limit=limit, context=context) - if not len(ids): - ids = self.search(cr, user, [('default_code',operator,name)]+ args, limit=limit, context=context) - ids += self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) - result = self.name_get(cr, user, ids, context) - return result - - # - # Could be overrided for variants matrices prices - # - def price_get(self, cr, uid, ids, ptype='list_price', context={}): - res = {} - product_uom_obj = self.pool.get('product.uom') - - for product in self.browse(cr, uid, ids, context=context): - res[product.id] = product[ptype] or 0.0 - if ptype == 'list_price': - res[product.id] = (res[product.id] * product.price_margin) + \ - product.price_extra - if 'uom' in context: - uom = product.uos_id or product.uom_id - res[product.id] = product_uom_obj._compute_price(cr, uid, - uom.id, res[product.id], context['uom']) - return res - - def copy(self, cr, uid, id, default=None, context=None): - if not context: - context={} - - if ('variant' in context) and context['variant']: - fields = ['product_tmpl_id', 'active', 'variants', 'default_code', - 'price_margin', 'price_extra'] - data = self.read(cr, uid, id, fields=fields, context=context) - for f in fields: - if f in default: - data[f] = default[f] - data['product_tmpl_id'] = data.get('product_tmpl_id', False) \ - and data['product_tmpl_id'][0] - del data['id'] - return self.create(cr, uid, data) - else: - return super(product_product, self).copy(cr, uid, id, default=default, - context=context) + def _product_price(self, cr, uid, ids, name, arg, context={}): + res = {} + quantity = context.get('quantity', 1) + pricelist = context.get('pricelist', False) + if pricelist: + for id in ids: + price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], id, quantity, context=context)[pricelist] + res[id] = price + for id in ids: + res.setdefault(id, 0.0) + return res + + def _get_product_available_func(states, what): + def _product_available(self, cr, uid, ids, name, arg, context={}): + res={} + for id in ids: + res.setdefault(id, 0.0) + return res + return _product_available + + _product_qty_available = _get_product_available_func(('done',), ('in', 'out')) + _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out')) + _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) + _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) + + def _product_lst_price(self, cr, uid, ids, name, arg, context=None): + res = {} + product_uom_obj = self.pool.get('product.uom') + for id in ids: + res.setdefault(id, 0.0) + for product in self.browse(cr, uid, ids, context=context): + if 'uom' in context: + uom = product.uos_id or product.uom_id + res[product.id] = product_uom_obj._compute_price(cr, uid, + uom.id, product.list_price, context['uom']) + else: + res[product.id] = product.list_price + return res + + def _get_partner_code_name(self, cr, uid, ids, product_id, partner_id, context={}): + product = self.browse(cr, uid, [product_id], context)[0] + for supinfo in product.seller_ids: + if supinfo.name.id == partner_id: + return {'code': supinfo.product_code, 'name': supinfo.product_name} + return {'code' : product.default_code, 'name' : product.name} + + def _product_code(self, cr, uid, ids, name, arg, context={}): + res = {} + for p in self.browse(cr, uid, ids, context): + res[p.id] = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context)['code'] + return res + + def _product_partner_ref(self, cr, uid, ids, name, arg, context={}): + res = {} + for p in self.browse(cr, uid, ids, context): + data = self._get_partner_code_name(cr, uid, [], p.id, context.get('partner_id', None), context) + res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + \ + (data['name'] or '') + return res + + _defaults = { + 'active': lambda *a: 1, + 'price_extra': lambda *a: 0.0, + 'price_margin': lambda *a: 1.0, + } + + _name = "product.product" + _description = "Product" + _table = "product_product" + _inherits = {'product.template': 'product_tmpl_id'} + _columns = { + 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'), + 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'), + 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'), + 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'), + 'price': fields.function(_product_price, method=True, type='float', string='Customer Price', digits=(16, int(config['price_accuracy']))), + 'lst_price' : fields.function(_product_lst_price, method=True, type='float', string='List Price', digits=(16, int(config['price_accuracy']))), + 'code': fields.function(_product_code, method=True, type='char', string='Code'), + 'partner_ref' : fields.function(_product_partner_ref, method=True, type='char', string='Customer ref'), + 'default_code' : fields.char('Code', size=64), + 'active': fields.boolean('Active'), + 'variants': fields.char('Variants', size=64), + 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True), + 'ean13': fields.char('EAN13', size=13), + 'packaging' : fields.one2many('product.packaging', 'product_id', 'Palettization', help="Gives the different ways to package the same product. This has no impact on the packing order and is mainly used if you use the EDI module."), + 'price_extra': fields.float('Price Extra', digits=(16, int(config['price_accuracy']))), + 'price_margin': fields.float('Price Margin', digits=(16, int(config['price_accuracy']))), + } + + def _check_ean_key(self, cr, uid, ids): + for partner in self.browse(cr, uid, ids): + if not partner.ean13: + continue + if len(partner.ean13) < 12: + return False + try: + int(partner.ean13) + except: + return False + sum=0 + for i in range(12): + if is_pair(i): + sum += int(partner.ean13[i]) + else: + sum += 3 * int(partner.ean13[i]) + check = int(math.ceil(sum / 10.0) * 10 - sum) + if len(partner.ean13) == 12: + self.write(cr, uid, partner.id, { + 'ean13': partner.ean13 + str(check) + }) + elif check != int(partner.ean13[12]): + return False + return True + + _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] + + def on_order(self, cr, uid, ids, orderline, quantity): + pass + + def name_get(self, cr, user, ids, context={}): + if not len(ids): + return [] + def _name_get(d): + #name = self._product_partner_ref(cr, user, [d['id']], '', '', context)[d['id']] + #code = self._product_code(cr, user, [d['id']], '', '', context)[d['id']] + name = d.get('name','') + code = d.get('default_code',False) + if code: + name = '[%s] %s' % (code,name) + if d['variants']: + name = name + ' - %s' % (d['variants'],) + return (d['id'], name) + result = map(_name_get, self.read(cr, user, ids, ['variants','name','default_code'], context)) + return result + + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context) + if not len(ids): + ids = self.search(cr, user, [('ean13','=',name)]+ args, limit=limit, context=context) + if not len(ids): + ids = self.search(cr, user, [('default_code',operator,name)]+ args, limit=limit, context=context) + ids += self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) + result = self.name_get(cr, user, ids, context) + return result + + # + # Could be overrided for variants matrices prices + # + def price_get(self, cr, uid, ids, ptype='list_price', context={}): + res = {} + product_uom_obj = self.pool.get('product.uom') + + for product in self.browse(cr, uid, ids, context=context): + res[product.id] = product[ptype] or 0.0 + if ptype == 'list_price': + res[product.id] = (res[product.id] * product.price_margin) + \ + product.price_extra + if 'uom' in context: + uom = product.uos_id or product.uom_id + res[product.id] = product_uom_obj._compute_price(cr, uid, + uom.id, res[product.id], context['uom']) + return res + + def copy(self, cr, uid, id, default=None, context=None): + if not context: + context={} + + if ('variant' in context) and context['variant']: + fields = ['product_tmpl_id', 'active', 'variants', 'default_code', + 'price_margin', 'price_extra'] + data = self.read(cr, uid, id, fields=fields, context=context) + for f in fields: + if f in default: + data[f] = default[f] + data['product_tmpl_id'] = data.get('product_tmpl_id', False) \ + and data['product_tmpl_id'][0] + del data['id'] + return self.create(cr, uid, data) + else: + return super(product_product, self).copy(cr, uid, id, default=default, + context=context) product_product() class product_packaging(osv.osv): - _name = "product.packaging" - _description = "Conditionnement" - _rec_name = 'ean' - _columns = { - 'name' : fields.char('Description', size=64), - 'qty' : fields.float('Quantity by UL', - help="The total number of products you can put by UL."), - 'ul' : fields.many2one('product.ul', 'Type of UL', required=True), - 'ul_qty' : fields.integer('UL by layer'), - 'rows' : fields.integer('Number of layer', required=True, - help='The number of layer on palette'), - 'product_id' : fields.many2one('product.product', 'Product', select=1, ondelete='cascade', required=True), - 'ean' : fields.char('EAN', size=14, - help="The EAN code of the transport unit."), - 'code' : fields.char('Code', size=14, - help="The code of the transport unit."), - 'weight': fields.float('Palette Weight', - help='The weight of the empty palette'), - 'weight_ul': fields.float('UL Weight', - help='The weight of the empty UL'), - 'height': fields.float('Height', help='The height of the palette'), - 'width': fields.float('Width', help='The width of the palette'), - 'length': fields.float('Length', help='The length of the palette'), - } - - def _get_1st_ul(self, cr, uid, context={}): - cr.execute('select id from product_ul order by id asc limit 1') - res = cr.fetchone() - return (res and res[0]) or False - - _defaults = { - 'rows' : lambda *a : 3, - 'ul' : _get_1st_ul, - } - - def checksum(ean): - salt = '31' * 6 + '3' - sum = 0 - for ean_part, salt_part in zip(ean, salt): - sum += int(ean_part) * int(salt_part) - return (10 - (sum % 10)) % 10 - checksum = staticmethod(checksum) + _name = "product.packaging" + _description = "Conditionnement" + _rec_name = 'ean' + _columns = { + 'name' : fields.char('Description', size=64), + 'qty' : fields.float('Quantity by UL', + help="The total number of products you can put by UL."), + 'ul' : fields.many2one('product.ul', 'Type of UL', required=True), + 'ul_qty' : fields.integer('UL by layer'), + 'rows' : fields.integer('Number of layer', required=True, + help='The number of layer on palette'), + 'product_id' : fields.many2one('product.product', 'Product', select=1, ondelete='cascade', required=True), + 'ean' : fields.char('EAN', size=14, + help="The EAN code of the transport unit."), + 'code' : fields.char('Code', size=14, + help="The code of the transport unit."), + 'weight': fields.float('Palette Weight', + help='The weight of the empty palette'), + 'weight_ul': fields.float('UL Weight', + help='The weight of the empty UL'), + 'height': fields.float('Height', help='The height of the palette'), + 'width': fields.float('Width', help='The width of the palette'), + 'length': fields.float('Length', help='The length of the palette'), + } + + def _get_1st_ul(self, cr, uid, context={}): + cr.execute('select id from product_ul order by id asc limit 1') + res = cr.fetchone() + return (res and res[0]) or False + + _defaults = { + 'rows' : lambda *a : 3, + 'ul' : _get_1st_ul, + } + + def checksum(ean): + salt = '31' * 6 + '3' + sum = 0 + for ean_part, salt_part in zip(ean, salt): + sum += int(ean_part) * int(salt_part) + return (10 - (sum % 10)) % 10 + checksum = staticmethod(checksum) product_packaging() class product_supplierinfo(osv.osv): - _name = "product.supplierinfo" - _description = "Information about a product supplier" - _columns = { - 'name' : fields.many2one('res.partner', 'Partner', required=True, ondelete='cascade'), - 'product_name': fields.char('Partner product name', size=128), - 'product_code': fields.char('Partner product reference', size=64), - 'sequence' : fields.integer('Priority'), - 'qty' : fields.float('Minimal quantity', required=True), - 'product_id' : fields.many2one('product.template', 'Product', required=True, ondelete='cascade', select=True), - 'delay' : fields.integer('Delivery delay', required=True), - 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Pricelist'), - } - _defaults = { - 'qty': lambda *a: 0.0, - 'delay': lambda *a: 1, - } - _order = 'sequence' + _name = "product.supplierinfo" + _description = "Information about a product supplier" + _columns = { + 'name' : fields.many2one('res.partner', 'Partner', required=True, ondelete='cascade'), + 'product_name': fields.char('Partner product name', size=128), + 'product_code': fields.char('Partner product reference', size=64), + 'sequence' : fields.integer('Priority'), + 'qty' : fields.float('Minimal quantity', required=True), + 'product_id' : fields.many2one('product.template', 'Product', required=True, ondelete='cascade', select=True), + 'delay' : fields.integer('Delivery delay', required=True), + 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Pricelist'), + } + _defaults = { + 'qty': lambda *a: 0.0, + 'delay': lambda *a: 1, + } + _order = 'sequence' product_supplierinfo() class pricelist_partnerinfo(osv.osv): - _name = 'pricelist.partnerinfo' - _columns = { - 'name': fields.char('Description', size=64), - 'suppinfo_id': fields.many2one('product.supplierinfo', 'Partner Information', required=True, ondelete='cascade'), - 'min_quantity': fields.float('Quantity', required=True), - 'price': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), - } - _order = 'min_quantity asc' + _name = 'pricelist.partnerinfo' + _columns = { + 'name': fields.char('Description', size=64), + 'suppinfo_id': fields.many2one('product.supplierinfo', 'Partner Information', required=True, ondelete='cascade'), + 'min_quantity': fields.float('Quantity', required=True), + 'price': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), + } + _order = 'min_quantity asc' pricelist_partnerinfo() diff --git a/addons/profile_accounting/__terp__.py b/addons/profile_accounting/__terp__.py index 1c4350ba1c9..a0d4cd4df96 100644 --- a/addons/profile_accounting/__terp__.py +++ b/addons/profile_accounting/__terp__.py @@ -26,13 +26,13 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Accounting only profile", - "version":"1.0", - "author":"Tiny", - "category":"Profile", - "depends":["account","report_analytic","board_account","account_followup"], - "demo_xml":[], - "update_xml":[], - "active":False, - "installable":True, + "name":"Accounting only profile", + "version":"1.0", + "author":"Tiny", + "category":"Profile", + "depends":["account","report_analytic","board_account","account_followup"], + "demo_xml":[], + "update_xml":[], + "active":False, + "installable":True, } diff --git a/addons/profile_manufacturing/__terp__.py b/addons/profile_manufacturing/__terp__.py index bfdd2f5de50..c15bb89b5b6 100644 --- a/addons/profile_manufacturing/__terp__.py +++ b/addons/profile_manufacturing/__terp__.py @@ -26,13 +26,13 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Manufacturing industry profile", - "version":"1.0", - "author":"Tiny", - "category":"Profile", - "depends":["mrp", "crm", "sale", "delivery","board_manufacturing"], - "demo_xml":[], - "update_xml":[], - "active":False, - "installable":True, + "name":"Manufacturing industry profile", + "version":"1.0", + "author":"Tiny", + "category":"Profile", + "depends":["mrp", "crm", "sale", "delivery","board_manufacturing"], + "demo_xml":[], + "update_xml":[], + "active":False, + "installable":True, } diff --git a/addons/profile_service/__terp__.py b/addons/profile_service/__terp__.py index 240c0fb899e..e3a128ed65d 100644 --- a/addons/profile_service/__terp__.py +++ b/addons/profile_service/__terp__.py @@ -26,23 +26,23 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name":"Service company profile", - "version":"1.0", - "author":"Tiny", - "category":"Profile", - "depends":[ - "hr", - "hr_timesheet", - "hr_timesheet_invoice", - "crm", - "project", - "board_project", - "account_analytic_analysis" - ], - #"board_project", - #"board_account"], - "demo_xml":[], - "update_xml":["profile_service.xml"], - "active":False, - "installable":True, + "name":"Service company profile", + "version":"1.0", + "author":"Tiny", + "category":"Profile", + "depends":[ + "hr", + "hr_timesheet", + "hr_timesheet_invoice", + "crm", + "project", + "board_project", + "account_analytic_analysis" + ], + #"board_project", + #"board_account"], + "demo_xml":[], + "update_xml":["profile_service.xml"], + "active":False, + "installable":True, } diff --git a/addons/project/__terp__.py b/addons/project/__terp__.py index c3dd4dac3d8..e3066342336 100644 --- a/addons/project/__terp__.py +++ b/addons/project/__terp__.py @@ -26,22 +26,22 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Project Management", - "version": "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_project.html", - "category" : "Generic Modules/Projects & Services", - "depends" : ["product", "account", "hr"], - "description": "Project management module that track multi-level projects, tasks, works done on tasks, eso. It is able to render planning, order tasks, eso.", - "init_xml" : [], - "demo_xml" : ["project_demo.xml"], - "update_xml": [ - "project_data.xml", - "project_wizard.xml", - "project_view.xml", - "project_report.xml", - "project_security.xml" - ], - "active": False, - "installable": True + "name" : "Project Management", + "version": "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_project.html", + "category" : "Generic Modules/Projects & Services", + "depends" : ["product", "account", "hr"], + "description": "Project management module that track multi-level projects, tasks, works done on tasks, eso. It is able to render planning, order tasks, eso.", + "init_xml" : [], + "demo_xml" : ["project_demo.xml"], + "update_xml": [ + "project_data.xml", + "project_wizard.xml", + "project_view.xml", + "project_report.xml", + "project_security.xml" + ], + "active": False, + "installable": True } diff --git a/addons/project/project.py b/addons/project/project.py index 09d0b7200b4..5c29bf8a136 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -36,300 +36,300 @@ from osv import fields, osv import ir class project(osv.osv): - _name = "project.project" - _description = "Project" + _name = "project.project" + _description = "Project" - def _calc_effective(self, cr, uid, ids, name, args, context): - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - res_sum = {} - if ids2: - cr.execute('SELECT t.project_id, COALESCE(SUM(w.hours),0) \ - FROM project_task t \ - LEFT JOIN project_task_work w \ - ON (w.task_id = t.id) \ - WHERE t.project_id in (' + ','.join([str(x) for x in ids2]) + ') \ - AND active \ - GROUP BY project_id') - for project_id, sum in cr.fetchall(): - res_sum[project_id] = sum - res={} - for id in ids: - ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) - res.setdefault(id, 0.0) - for idx in ids3: - res[id] += res_sum.get(idx, 0.0) - return res + def _calc_effective(self, cr, uid, ids, name, args, context): + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + res_sum = {} + if ids2: + cr.execute('SELECT t.project_id, COALESCE(SUM(w.hours),0) \ + FROM project_task t \ + LEFT JOIN project_task_work w \ + ON (w.task_id = t.id) \ + WHERE t.project_id in (' + ','.join([str(x) for x in ids2]) + ') \ + AND active \ + GROUP BY project_id') + for project_id, sum in cr.fetchall(): + res_sum[project_id] = sum + res={} + for id in ids: + ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) + res.setdefault(id, 0.0) + for idx in ids3: + res[id] += res_sum.get(idx, 0.0) + return res - def _calc_planned(self, cr, uid, ids, name, args, context): - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - res_sum = {} - if ids2: - cr.execute('SELECT project_id, COALESCE(SUM(planned_hours),0) \ - FROM project_task \ - WHERE project_id IN (' + ','.join([str(x) for x in ids2]) + ') \ - AND active \ - GROUP BY project_id') - for project_id, sum in cr.fetchall(): - res_sum[project_id] = sum - res = {} - for id in ids: - ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) - res.setdefault(id, 0.0) - for idx in ids3: - res[id] += res_sum.get(idx, 0.0) - return res + def _calc_planned(self, cr, uid, ids, name, args, context): + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + res_sum = {} + if ids2: + cr.execute('SELECT project_id, COALESCE(SUM(planned_hours),0) \ + FROM project_task \ + WHERE project_id IN (' + ','.join([str(x) for x in ids2]) + ') \ + AND active \ + GROUP BY project_id') + for project_id, sum in cr.fetchall(): + res_sum[project_id] = sum + res = {} + for id in ids: + ids3 = self.search(cr, uid, [('parent_id', 'child_of', [id])]) + res.setdefault(id, 0.0) + for idx in ids3: + res[id] += res_sum.get(idx, 0.0) + return res - def check_recursion(self, cursor, user, ids, parent=None): - return super(project, self).check_recursion(cursor, user, ids, - parent=parent) + def check_recursion(self, cursor, user, ids, parent=None): + return super(project, self).check_recursion(cursor, user, ids, + parent=parent) - def onchange_partner_id(self, cr, uid, ids, part): - if not part: - return {'value':{'contact_id': False, 'pricelist_id': False}} - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) + def onchange_partner_id(self, cr, uid, ids, part): + if not part: + return {'value':{'contact_id': False, 'pricelist_id': False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact']) - pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist.id - return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist}} + pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist.id + return {'value':{'contact_id': addr['contact'], 'pricelist_id': pricelist}} - _columns = { - 'name': fields.char("Project name", size=128, required=True), - 'active': fields.boolean('Active'), - 'category_id': fields.many2one('account.analytic.account','Analytic Account'), - 'priority': fields.integer('Sequence'), - 'manager': fields.many2one('res.users', 'Project manager'), - 'warn_manager': fields.boolean('Warn manager'), - 'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project members'), - 'tasks': fields.one2many('project.task', 'project_id', "Project tasks"), - 'parent_id': fields.many2one('project.project', 'Parent project'), - 'child_id': fields.one2many('project.project', 'parent_id', 'Subproject'), - 'planned_hours': fields.function(_calc_planned, method=True, string='Planned hours'), - 'effective_hours': fields.function(_calc_effective, method=True, string='Hours spent'), - 'date_start': fields.date('Project started on'), - 'date_end': fields.date('Project should end on'), - 'tariff': fields.float('Sales price'), - 'mode': fields.selection([ - ('', ''), - ('project', 'By Task'), - ('hour', 'By Hour'), - ('effective', 'By Effective Hour') - ], 'Price setting mode'), - 'partner_id': fields.many2one('res.partner', 'Customer'), - 'contact_id': fields.many2one('res.partner.address', 'Contact'), - 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'), - 'tax_ids': fields.many2many('account.tax', 'project_account_tax_rel', 'project_id','tax_id', 'Applicable taxes'), - 'warn_customer': fields.boolean('Warn customer'), - 'warn_header': fields.text('Mail header'), - 'warn_footer': fields.text('Mail footer'), - 'notes': fields.text('Notes'), - 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working hours'), - 'state': fields.selection([('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', required=True), - } + _columns = { + 'name': fields.char("Project name", size=128, required=True), + 'active': fields.boolean('Active'), + 'category_id': fields.many2one('account.analytic.account','Analytic Account'), + 'priority': fields.integer('Sequence'), + 'manager': fields.many2one('res.users', 'Project manager'), + 'warn_manager': fields.boolean('Warn manager'), + 'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project members'), + 'tasks': fields.one2many('project.task', 'project_id', "Project tasks"), + 'parent_id': fields.many2one('project.project', 'Parent project'), + 'child_id': fields.one2many('project.project', 'parent_id', 'Subproject'), + 'planned_hours': fields.function(_calc_planned, method=True, string='Planned hours'), + 'effective_hours': fields.function(_calc_effective, method=True, string='Hours spent'), + 'date_start': fields.date('Project started on'), + 'date_end': fields.date('Project should end on'), + 'tariff': fields.float('Sales price'), + 'mode': fields.selection([ + ('', ''), + ('project', 'By Task'), + ('hour', 'By Hour'), + ('effective', 'By Effective Hour') + ], 'Price setting mode'), + 'partner_id': fields.many2one('res.partner', 'Customer'), + 'contact_id': fields.many2one('res.partner.address', 'Contact'), + 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'), + 'tax_ids': fields.many2many('account.tax', 'project_account_tax_rel', 'project_id','tax_id', 'Applicable taxes'), + 'warn_customer': fields.boolean('Warn customer'), + 'warn_header': fields.text('Mail header'), + 'warn_footer': fields.text('Mail footer'), + 'notes': fields.text('Notes'), + 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working hours'), + 'state': fields.selection([('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', required=True), + } - _defaults = { - 'active': lambda *a: True, - 'manager': lambda object,cr,uid,context: uid, - 'priority': lambda *a: 1, - 'date_start': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'open' - } + _defaults = { + 'active': lambda *a: True, + 'manager': lambda object,cr,uid,context: uid, + 'priority': lambda *a: 1, + 'date_start': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'open' + } - _order = "priority" - _constraints = [ - (check_recursion, 'Error ! You can not create recursive projects.', ['parent_id']) - ] + _order = "priority" + _constraints = [ + (check_recursion, 'Error ! You can not create recursive projects.', ['parent_id']) + ] - # toggle activity of projects, their sub projects and their tasks - def toggleActive(self, cr, uid, ids, context={}): - for proj in self.browse(cr, uid, ids, context): - self.setActive(cr, uid, proj.id, not proj.active, context) - return True + # toggle activity of projects, their sub projects and their tasks + def toggleActive(self, cr, uid, ids, context={}): + for proj in self.browse(cr, uid, ids, context): + self.setActive(cr, uid, proj.id, not proj.active, context) + return True - # set active value for a project, its sub projects and its tasks - def setActive(self, cr, uid, id, value, context={}): - proj = self.browse(cr, uid, id, context) - self.write(cr, uid, [id], {'active': value}, context) - cr.execute('select id from project_task where project_id=%d', (proj.id,)) - tasks_id = [x[0] for x in cr.fetchall()] - self.pool.get('project.task').write(cr, uid, tasks_id, {'active': value}, context) - project_ids = [x[0] for x in cr.fetchall()] - for child in project_ids: - self.setActive(cr, uid, child, value, context) - return True + # set active value for a project, its sub projects and its tasks + def setActive(self, cr, uid, id, value, context={}): + proj = self.browse(cr, uid, id, context) + self.write(cr, uid, [id], {'active': value}, context) + cr.execute('select id from project_task where project_id=%d', (proj.id,)) + tasks_id = [x[0] for x in cr.fetchall()] + self.pool.get('project.task').write(cr, uid, tasks_id, {'active': value}, context) + project_ids = [x[0] for x in cr.fetchall()] + for child in project_ids: + self.setActive(cr, uid, child, value, context) + return True project() class project_task_type(osv.osv): - _name = 'project.task.type' - _description = 'Project task type' - _columns = { - 'name': fields.char('Type', required=True, size=64), - 'description': fields.text('Description'), - } + _name = 'project.task.type' + _description = 'Project task type' + _columns = { + 'name': fields.char('Type', required=True, size=64), + 'description': fields.text('Description'), + } project_task_type() class task(osv.osv): - _name = "project.task" - _description = "Task" - _date_name = "date_start" - def _str_get(self, task, level=0, border='***', context={}): - return border+' '+(task.user_id and task.user_id.name.upper() or '')+(level and (': L'+str(level)) or '')+(' - %.1fh / %.1fh'%(task.effective_hours or 0.0,task.planned_hours))+' '+border+'\n'+ \ - border[0]+' '+(task.name or '')+'\n'+ \ - (task.description or '')+'\n\n' + _name = "project.task" + _description = "Task" + _date_name = "date_start" + def _str_get(self, task, level=0, border='***', context={}): + return border+' '+(task.user_id and task.user_id.name.upper() or '')+(level and (': L'+str(level)) or '')+(' - %.1fh / %.1fh'%(task.effective_hours or 0.0,task.planned_hours))+' '+border+'\n'+ \ + border[0]+' '+(task.name or '')+'\n'+ \ + (task.description or '')+'\n\n' - def _history_get(self, cr, uid, ids, name, args, context={}): - result = {} - for task in self.browse(cr, uid, ids, context=context): - result[task.id] = self._str_get(task, border='===') - t2 = task.parent_id - level = 0 - while t2: - level -= 1 - result[task.id] = self._str_get(t2, level) + result[task.id] - t2 = t2.parent_id - t3 = map(lambda x: (x,1), task.child_ids) - while t3: - t2 = t3.pop(0) - result[task.id] = result[task.id] + self._str_get(t2[0], t2[1]) - t3 += map(lambda x: (x,t2[1]+1), t2[0].child_ids) - return result + def _history_get(self, cr, uid, ids, name, args, context={}): + result = {} + for task in self.browse(cr, uid, ids, context=context): + result[task.id] = self._str_get(task, border='===') + t2 = task.parent_id + level = 0 + while t2: + level -= 1 + result[task.id] = self._str_get(t2, level) + result[task.id] + t2 = t2.parent_id + t3 = map(lambda x: (x,1), task.child_ids) + while t3: + t2 = t3.pop(0) + result[task.id] = result[task.id] + self._str_get(t2[0], t2[1]) + t3 += map(lambda x: (x,t2[1]+1), t2[0].child_ids) + return result - def _hours_effect(self, cr, uid, ids, name, args, context): - task_set = ','.join(map(str, ids)) - cr.execute(("SELECT task_id, COALESCE(SUM(hours),0) FROM project_task_work WHERE task_id in (%s) GROUP BY task_id") % (task_set,)) - res = {} - for id in ids: - res[id] = 0.0 - for task_id, sum in cr.fetchall(): - res[task_id] = sum - return res + def _hours_effect(self, cr, uid, ids, name, args, context): + task_set = ','.join(map(str, ids)) + cr.execute(("SELECT task_id, COALESCE(SUM(hours),0) FROM project_task_work WHERE task_id in (%s) GROUP BY task_id") % (task_set,)) + res = {} + for id in ids: + res[id] = 0.0 + for task_id, sum in cr.fetchall(): + res[task_id] = sum + return res - _columns = { - 'name': fields.char('Task summary', size=128, required=True), - 'active': fields.boolean('Active'), - 'description': fields.text('Description'), - 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Importance'), - 'sequence': fields.integer('Sequence'), - 'type': fields.many2one('project.task.type', 'Type'), - 'state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State'), - 'date_start': fields.datetime('Date'), - 'date_deadline': fields.datetime('Deadline'), - 'date_close': fields.datetime('Date Closed', readonly=True), - 'project_id': fields.many2one('project.project', 'Project', ondelete='cascade'), - 'parent_id': fields.many2one('project.task', 'Parent Task'), - 'child_ids': fields.one2many('project.task', 'parent_id', 'Delegated Tasks'), - 'history': fields.function(_history_get, method=True, string="Task Details", type="text"), - 'notes': fields.text('Notes'), - 'start_sequence': fields.boolean('Wait for previous sequences'), - 'planned_hours': fields.float('Plan. hours'), - 'effective_hours': fields.function(_hours_effect, method=True, string='Eff. Hours'), - 'progress': fields.integer('Progress (0-100)'), - 'billable': fields.boolean('To be invoiced'), - 'invoice_id': fields.many2one('account.invoice','Generated Invoice'), - 'user_id': fields.many2one('res.users', 'Assigned to'), - 'partner_id': fields.many2one('res.partner', 'Customer'), - 'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done'), - } - _defaults = { - 'user_id': lambda obj,cr,uid,context: uid, - 'state': lambda *a: 'open', - 'priority': lambda *a: '2', - 'progress': lambda *a: 0, - 'sequence': lambda *a: 10, - 'active': lambda *a: True, - 'date_start': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } - _order = "state, sequence, priority, date_deadline, id" + _columns = { + 'name': fields.char('Task summary', size=128, required=True), + 'active': fields.boolean('Active'), + 'description': fields.text('Description'), + 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Importance'), + 'sequence': fields.integer('Sequence'), + 'type': fields.many2one('project.task.type', 'Type'), + 'state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State'), + 'date_start': fields.datetime('Date'), + 'date_deadline': fields.datetime('Deadline'), + 'date_close': fields.datetime('Date Closed', readonly=True), + 'project_id': fields.many2one('project.project', 'Project', ondelete='cascade'), + 'parent_id': fields.many2one('project.task', 'Parent Task'), + 'child_ids': fields.one2many('project.task', 'parent_id', 'Delegated Tasks'), + 'history': fields.function(_history_get, method=True, string="Task Details", type="text"), + 'notes': fields.text('Notes'), + 'start_sequence': fields.boolean('Wait for previous sequences'), + 'planned_hours': fields.float('Plan. hours'), + 'effective_hours': fields.function(_hours_effect, method=True, string='Eff. Hours'), + 'progress': fields.integer('Progress (0-100)'), + 'billable': fields.boolean('To be invoiced'), + 'invoice_id': fields.many2one('account.invoice','Generated Invoice'), + 'user_id': fields.many2one('res.users', 'Assigned to'), + 'partner_id': fields.many2one('res.partner', 'Customer'), + 'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done'), + } + _defaults = { + 'user_id': lambda obj,cr,uid,context: uid, + 'state': lambda *a: 'open', + 'priority': lambda *a: '2', + 'progress': lambda *a: 0, + 'sequence': lambda *a: 10, + 'active': lambda *a: True, + 'date_start': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + _order = "state, sequence, priority, date_deadline, id" - def do_close(self, cr, uid, ids, *args): - request = self.pool.get('res.request') - tasks = self.browse(cr, uid, ids) - for task in tasks: - project = task.project_id - if project: - if project.warn_manager and project.manager and (project.manager.id != uid): - request.create(cr, uid, { - 'name': "Task '%s' closed" % task.name, - 'state': 'waiting', - 'act_from': uid, - 'act_to': project.manager.id, - 'ref_partner_id': task.partner_id.id, - 'ref_doc1': 'project.task,%d'% (task.id,), - 'ref_doc2': 'project.project,%d'% (project.id,), - }) - self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'progress': 100}) - if task.parent_id and task.parent_id.state in ('pending','draft'): - self.do_reopen(cr, uid, [task.parent_id.id]) - return True + def do_close(self, cr, uid, ids, *args): + request = self.pool.get('res.request') + tasks = self.browse(cr, uid, ids) + for task in tasks: + project = task.project_id + if project: + if project.warn_manager and project.manager and (project.manager.id != uid): + request.create(cr, uid, { + 'name': "Task '%s' closed" % task.name, + 'state': 'waiting', + 'act_from': uid, + 'act_to': project.manager.id, + 'ref_partner_id': task.partner_id.id, + 'ref_doc1': 'project.task,%d'% (task.id,), + 'ref_doc2': 'project.project,%d'% (project.id,), + }) + self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'progress': 100}) + if task.parent_id and task.parent_id.state in ('pending','draft'): + self.do_reopen(cr, uid, [task.parent_id.id]) + return True - def do_reopen(self, cr, uid, ids, *args): - request = self.pool.get('res.request') - tasks = self.browse(cr, uid, ids) - for task in tasks: - project = task.project_id - if project and project.warn_manager and project.manager.id and (project.manager.id != uid): - request.create(cr, uid, { - 'name': "Task '%s' reopened" % task.name, - 'state': 'waiting', - 'act_from': uid, - 'act_to': project.manager.id, - 'ref_partner_id': task.partner_id.id, - 'ref_doc1': 'project.task,%d' % task.id, - 'ref_doc2': 'project.project,%d' % project.id, - }) + def do_reopen(self, cr, uid, ids, *args): + request = self.pool.get('res.request') + tasks = self.browse(cr, uid, ids) + for task in tasks: + project = task.project_id + if project and project.warn_manager and project.manager.id and (project.manager.id != uid): + request.create(cr, uid, { + 'name': "Task '%s' reopened" % task.name, + 'state': 'waiting', + 'act_from': uid, + 'act_to': project.manager.id, + 'ref_partner_id': task.partner_id.id, + 'ref_doc1': 'project.task,%d' % task.id, + 'ref_doc2': 'project.project,%d' % project.id, + }) - self.write(cr, uid, [task.id], {'state': 'open'}) - return True + self.write(cr, uid, [task.id], {'state': 'open'}) + return True - def do_cancel(self, cr, uid, ids, *args): - request = self.pool.get('res.request') - tasks = self.browse(cr, uid, ids) - for task in tasks: - project = task.project_id - if project.warn_manager and project.manager and (project.manager.id != uid): - request.create(cr, uid, { - 'name': "Task '%s' cancelled" % task.name, - 'state': 'waiting', - 'act_from': uid, - 'act_to': project.manager.id, - 'ref_partner_id': task.partner_id.id, - 'ref_doc1': 'project.task,%d' % task.id, - 'ref_doc2': 'project.project,%d' % project.id, - }) - self.write(cr, uid, [task.id], {'state': 'cancelled'}) - return True + def do_cancel(self, cr, uid, ids, *args): + request = self.pool.get('res.request') + tasks = self.browse(cr, uid, ids) + for task in tasks: + project = task.project_id + if project.warn_manager and project.manager and (project.manager.id != uid): + request.create(cr, uid, { + 'name': "Task '%s' cancelled" % task.name, + 'state': 'waiting', + 'act_from': uid, + 'act_to': project.manager.id, + 'ref_partner_id': task.partner_id.id, + 'ref_doc1': 'project.task,%d' % task.id, + 'ref_doc2': 'project.project,%d' % project.id, + }) + self.write(cr, uid, [task.id], {'state': 'cancelled'}) + return True - def do_open(self, cr, uid, ids, *args): - tasks= self.browse(cr,uid,ids) - for t in tasks: - self.write(cr, uid, [t.id], {'state': 'open'}) - return True + def do_open(self, cr, uid, ids, *args): + tasks= self.browse(cr,uid,ids) + for t in tasks: + self.write(cr, uid, [t.id], {'state': 'open'}) + return True - def do_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state': 'draft'}) - return True + def do_draft(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state': 'draft'}) + return True - def do_pending(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state': 'pending'}) - return True + def do_pending(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state': 'pending'}) + return True task() class project_work(osv.osv): - _name = "project.task.work" - _description = "Task Work" - _columns = { - 'name': fields.char('Work summary', size=128), - 'date': fields.datetime('Date'), - 'task_id': fields.many2one('project.task', 'Task', ondelete='cascade'), - 'hours': fields.float('Hours spent'), - 'user_id': fields.many2one('res.users', 'Done by', required=True), - } - _defaults = { - 'user_id': lambda obj,cr,uid,context: uid, - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S') - } - _order = "date desc" + _name = "project.task.work" + _description = "Task Work" + _columns = { + 'name': fields.char('Work summary', size=128), + 'date': fields.datetime('Date'), + 'task_id': fields.many2one('project.task', 'Task', ondelete='cascade'), + 'hours': fields.float('Hours spent'), + 'user_id': fields.many2one('res.users', 'Done by', required=True), + } + _defaults = { + 'user_id': lambda obj,cr,uid,context: uid, + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S') + } + _order = "date desc" project_work() diff --git a/addons/project/report/_date_compute.py b/addons/project/report/_date_compute.py index 91ad6f1a15c..b35da5b2ffe 100644 --- a/addons/project/report/_date_compute.py +++ b/addons/project/report/_date_compute.py @@ -36,66 +36,66 @@ import pooler # TODO: improve sequences code # def _compute_tasks(cr, uid, task_list, date_begin): - sequences = [] - users = {} - tasks = {} - last_date = date_begin - for task in task_list: - # TODO: reorder ! with dependencies - if not task.planned_hours: - continue - if task.state in ('open','progress') and task.user_id: + sequences = [] + users = {} + tasks = {} + last_date = date_begin + for task in task_list: + # TODO: reorder ! with dependencies + if not task.planned_hours: + continue + if task.state in ('open','progress') and task.user_id: - # Find the starting date of the task - if task.user_id.id in users: - date_start = users[task.user_id.id] - else: - date_start = date_begin - if task.start_sequence: - sequences.sort() - for (seq,dt) in sequences: - if seq<task.sequence: - date_start = max(dt,date_start) - else: - break + # Find the starting date of the task + if task.user_id.id in users: + date_start = users[task.user_id.id] + else: + date_start = date_begin + if task.start_sequence: + sequences.sort() + for (seq,dt) in sequences: + if seq<task.sequence: + date_start = max(dt,date_start) + else: + break - if task.date_start: - task_date_start = DateTime.strptime(task.date_start, '%Y-%m-%d %H:%M:%S') - if DateTime.cmp(date_start, task_date_start) < 0: - date_start = task_date_start + if task.date_start: + task_date_start = DateTime.strptime(task.date_start, '%Y-%m-%d %H:%M:%S') + if DateTime.cmp(date_start, task_date_start) < 0: + date_start = task_date_start - # Compute the closing date of the task - tasks[task.id] = [] - res = pooler.get_pool(cr.dbname).get('hr.timesheet.group').interval_get(cr, uid, task.project_id.timesheet_id.id, date_start, task.planned_hours) - for (d1,d2) in res: - tasks[task.id].append((d1, d2, task.name, task.user_id.login)) - date_close = tasks[task.id][-1][1] + # Compute the closing date of the task + tasks[task.id] = [] + res = pooler.get_pool(cr.dbname).get('hr.timesheet.group').interval_get(cr, uid, task.project_id.timesheet_id.id, date_start, task.planned_hours) + for (d1,d2) in res: + tasks[task.id].append((d1, d2, task.name, task.user_id.login)) + date_close = tasks[task.id][-1][1] - # Store result - users[task.user_id.id] = date_close - sequences.append((task.sequence, date_close)) - if date_close>last_date: - last_date=date_close - return tasks, last_date + # Store result + users[task.user_id.id] = date_close + sequences.append((task.sequence, date_close)) + if date_close>last_date: + last_date=date_close + return tasks, last_date def _compute_project(cr, uid, project, date_begin): - tasks, last_date = _compute_tasks(cr, uid, project.tasks, date_begin) - for proj in project.child_id: - d0 = DateTime.strptime(proj.date_start,'%Y-%m-%d') - if d0 > last_date: - last_date = d0 - t2, l2 = _compute_project(cr, uid, proj, last_date) - tasks.update(t2) - last_date = l2 - return tasks, last_date + tasks, last_date = _compute_tasks(cr, uid, project.tasks, date_begin) + for proj in project.child_id: + d0 = DateTime.strptime(proj.date_start,'%Y-%m-%d') + if d0 > last_date: + last_date = d0 + t2, l2 = _compute_project(cr, uid, proj, last_date) + tasks.update(t2) + last_date = l2 + return tasks, last_date def _project_compute(cr, uid, project_id): - project = pooler.get_pool(cr.dbname).get('project.project').browse(cr, uid, project_id) - if project.date_start: - date_begin = DateTime.strptime(project.date_start, '%Y-%m-%d') - else: - date_begin = DateTime.now() - tasks, last_date = _compute_project(cr, uid, project, date_begin) - return tasks, last_date + project = pooler.get_pool(cr.dbname).get('project.project').browse(cr, uid, project_id) + if project.date_start: + date_begin = DateTime.strptime(project.date_start, '%Y-%m-%d') + else: + date_begin = DateTime.now() + tasks, last_date = _compute_project(cr, uid, project, date_begin) + return tasks, last_date diff --git a/addons/project/report/gantt.py b/addons/project/report/gantt.py index 1f4e279b718..30d43baf847 100644 --- a/addons/project/report/gantt.py +++ b/addons/project/report/gantt.py @@ -34,78 +34,78 @@ from report.misc import choice_colors # Draw a graph # class GanttCanvas(object): - def __init__(self, io, convertors=(lambda x:x,lambda x:x)): - self._datas = {} - self._canvas = canvas.init(fname=io, format='pdf') - self._canvas.set_author("Tiny ERP") - self._names = {} - self._conv = convertors - self._min = 0 - self._max = 0 + def __init__(self, io, convertors=(lambda x:x,lambda x:x)): + self._datas = {} + self._canvas = canvas.init(fname=io, format='pdf') + self._canvas.set_author("Tiny ERP") + self._names = {} + self._conv = convertors + self._min = 0 + self._max = 0 - def add(self, user, name, datas): - if hasattr(user, 'replace'): - user=user.replace('/', '//') - if hasattr(name, 'replace'): - name=name.replace('/', '//') - if user not in self._datas: - self._datas[user] = [] - for f in datas: - x = map(self._conv[0], f) - if x[0]<self._min or not self._min: - self._min = x[0] - if x[1]>self._max or not self._max: - self._max = x[1] - self._datas[user].append( (name, x)) - self._names.setdefault(name, x[0]) + def add(self, user, name, datas): + if hasattr(user, 'replace'): + user=user.replace('/', '//') + if hasattr(name, 'replace'): + name=name.replace('/', '//') + if user not in self._datas: + self._datas[user] = [] + for f in datas: + x = map(self._conv[0], f) + if x[0]<self._min or not self._min: + self._min = x[0] + if x[1]>self._max or not self._max: + self._max = x[1] + self._datas[user].append( (name, x)) + self._names.setdefault(name, x[0]) - def draw(self): - colors = choice_colors(len(self._datas.keys())) - user_color = {} - for user in self._datas.keys(): - user_color[user] = colors.pop() + def draw(self): + colors = choice_colors(len(self._datas.keys())) + user_color = {} + for user in self._datas.keys(): + user_color[user] = colors.pop() - names = [] - for n in self._names: - names.append((self._names[n], n)) - names.sort() - names.reverse() - def _interval_get(*args): - result = [] - for i in range(20): - d = localtime(self._min + (((self._max-self._min)/20)*(i+1))) - res = DateTime(d.year, d.month, d.day).ticks() - if (not result) or result[-1]<>res: - result.append(res) - return result + names = [] + for n in self._names: + names.append((self._names[n], n)) + names.sort() + names.reverse() + def _interval_get(*args): + result = [] + for i in range(20): + d = localtime(self._min + (((self._max-self._min)/20)*(i+1))) + res = DateTime(d.year, d.month, d.day).ticks() + if (not result) or result[-1]<>res: + result.append(res) + return result - ar = area.T(y_coord = category_coord.T(names, 1), - x_grid_style=line_style.gray50_dash1, - x_grid_interval=_interval_get, - x_range = (self._min,self._max), - x_axis=axis.X(label="Date", format=self._conv[1]), - y_axis=axis.Y(label="Tasks"), - legend = legend.T(), size = (680,450)) + ar = area.T(y_coord = category_coord.T(names, 1), + x_grid_style=line_style.gray50_dash1, + x_grid_interval=_interval_get, + x_range = (self._min,self._max), + x_axis=axis.X(label="Date", format=self._conv[1]), + y_axis=axis.Y(label="Tasks"), + legend = legend.T(), size = (680,450)) - for user in self._datas: - chart_object.set_defaults(interval_bar_plot.T, direction="horizontal", data=self._datas[user]) - f = fill_style.Plain() - f.bgcolor = user_color[user] - ar.add_plot(interval_bar_plot.T(fill_styles = [f, None], label=user, cluster=(0,1))) + for user in self._datas: + chart_object.set_defaults(interval_bar_plot.T, direction="horizontal", data=self._datas[user]) + f = fill_style.Plain() + f.bgcolor = user_color[user] + ar.add_plot(interval_bar_plot.T(fill_styles = [f, None], label=user, cluster=(0,1))) - ar.draw(self._canvas) + ar.draw(self._canvas) - def close(self): - self._canvas.close() + def close(self): + self._canvas.close() if __name__ == '__main__': - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') - gt = GanttCanvas('test.pdf', convertors=(date_to_int, int_to_date)) - gt.add('nicoe', 'Graphe de gantt', [(DateTime(2005,6,12), DateTime(2005,6,13))]) - gt.add('nicoe', 'Tarifs', [(DateTime(2005,6,19), DateTime(2005,6,21))]) - gt.add('gaetan', 'Calcul des prix', [(DateTime(2005,6,12), DateTime(2005,6,13))]) - gt.add('nico', 'Mise a jour du site', [(DateTime(2005,6,13), DateTime(2005,6,16))]) - gt.add('tom', 'Coucou', [(DateTime(2005,6,11), DateTime(2005,6,12))]) - gt.draw() - gt.close() + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') + gt = GanttCanvas('test.pdf', convertors=(date_to_int, int_to_date)) + gt.add('nicoe', 'Graphe de gantt', [(DateTime(2005,6,12), DateTime(2005,6,13))]) + gt.add('nicoe', 'Tarifs', [(DateTime(2005,6,19), DateTime(2005,6,21))]) + gt.add('gaetan', 'Calcul des prix', [(DateTime(2005,6,12), DateTime(2005,6,13))]) + gt.add('nico', 'Mise a jour du site', [(DateTime(2005,6,13), DateTime(2005,6,16))]) + gt.add('tom', 'Coucou', [(DateTime(2005,6,11), DateTime(2005,6,12))]) + gt.draw() + gt.close() diff --git a/addons/project/report/gantt_report.py b/addons/project/report/gantt_report.py index 43616a374d4..ab6532c8487 100644 --- a/addons/project/report/gantt_report.py +++ b/addons/project/report/gantt_report.py @@ -40,54 +40,54 @@ from _date_compute import _project_compute, _compute_tasks import pooler class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + + def _render(self): + return self.pdf class report_tasks(report_int): - def create(self, cr, uid, ids, datas, context={}): - io = StringIO.StringIO() + def create(self, cr, uid, ids, datas, context={}): + io = StringIO.StringIO() - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') - gt = GanttCanvas(io, convertors=(date_to_int, int_to_date)) + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') + gt = GanttCanvas(io, convertors=(date_to_int, int_to_date)) - tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, ids) - tasks, last_date = _compute_tasks(cr, uid, tasks, now()) - for user_id in tasks.keys(): - for t in tasks[user_id]: - gt.add(t[3], t[2], [(t[0],t[1])]) - try: - gt.draw() - except: - pass - gt.close() - self.obj = external_pdf(io.getvalue()) - self.obj.render() - return (self.obj.pdf, 'pdf') + tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, ids) + tasks, last_date = _compute_tasks(cr, uid, tasks, now()) + for user_id in tasks.keys(): + for t in tasks[user_id]: + gt.add(t[3], t[2], [(t[0],t[1])]) + try: + gt.draw() + except: + pass + gt.close() + self.obj = external_pdf(io.getvalue()) + self.obj.render() + return (self.obj.pdf, 'pdf') report_tasks('report.project.tasks.gantt') class report_projects(report_int): - def create(self, cr, uid, ids, datas, context={}): - io = StringIO.StringIO() - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') - gt = GanttCanvas(io, convertors=(date_to_int, int_to_date)) - tasks, last_date = _project_compute(cr, uid, ids[0]) - for user_id in tasks.keys(): - for t in tasks[user_id]: - gt.add(t[3], t[2], [(t[0],t[1])]) - try: - gt.draw() - except: - pass - gt.close() - self.obj = external_pdf(io.getvalue()) - self.obj.render() - return (self.obj.pdf, 'pdf') + def create(self, cr, uid, ids, datas, context={}): + io = StringIO.StringIO() + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+localtime(x).strftime('%d %m %Y') + gt = GanttCanvas(io, convertors=(date_to_int, int_to_date)) + tasks, last_date = _project_compute(cr, uid, ids[0]) + for user_id in tasks.keys(): + for t in tasks[user_id]: + gt.add(t[3], t[2], [(t[0],t[1])]) + try: + gt.draw() + except: + pass + gt.close() + self.obj = external_pdf(io.getvalue()) + self.obj.render() + return (self.obj.pdf, 'pdf') report_projects('report.project.project.gantt') diff --git a/addons/project/wizard/billing.py b/addons/project/wizard/billing.py index 348de0f3563..f8c81ad533b 100644 --- a/addons/project/wizard/billing.py +++ b/addons/project/wizard/billing.py @@ -35,128 +35,128 @@ import netsvc bill_form = """<?xml version="1.0" ?> <form string="Billing wizard"> - <field name="journal_id"/> - <field name="sale_account_id"/> - <newline/> - <field name="task_total"/> - <field name="task_invoice"/> - <field name="invoice_num"/> - <field name="total"/> + <field name="journal_id"/> + <field name="sale_account_id"/> + <newline/> + <field name="task_total"/> + <field name="task_invoice"/> + <field name="invoice_num"/> + <field name="total"/> </form>""" bill_fields = { - 'journal_id':{'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required': 'True'}, - 'sale_account_id':{'string':'Income account', 'type':'many2one', 'relation':'account.account', 'required': 'True'}, - 'invoice_num' : {'string':'Generated invoices', 'type': 'integer', 'readonly': 'True'}, - 'task_total' : {'string':'Number of tasks', 'type': 'integer', 'readonly': 'True'}, - 'task_invoice' : {'string':'Tasks to invoice', 'type': 'integer', 'readonly': 'True'}, - 'total' : {'string':'Total Price', 'type': 'float', 'readonly': 'True'}, + 'journal_id':{'string':'Journal', 'type':'many2one', 'relation':'account.journal', 'required': 'True'}, + 'sale_account_id':{'string':'Income account', 'type':'many2one', 'relation':'account.account', 'required': 'True'}, + 'invoice_num' : {'string':'Generated invoices', 'type': 'integer', 'readonly': 'True'}, + 'task_total' : {'string':'Number of tasks', 'type': 'integer', 'readonly': 'True'}, + 'task_invoice' : {'string':'Tasks to invoice', 'type': 'integer', 'readonly': 'True'}, + 'total' : {'string':'Total Price', 'type': 'float', 'readonly': 'True'}, } ack_form = """<?xml version="1.0" ?> <form string="Billing wizard"> - <separator string="The orders were successfully generated" /> + <separator string="The orders were successfully generated" /> </form>""" ack_fields = {} def _compute_orders(self, cr, uid, data, context): - tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids']) - projects = {} - tot = 0.0 - task_tot = 0 - task_inv = 0 - for task in tasks: - project = task.project_id - if not task.invoice_id and task.billable: - if project.mode == 'project': - tot += project.tariff - elif project.mode == 'effective': - tot += project.tariff * task.effective_hours - elif project.mode == 'hour': - tot += project.tariff * task.planned_hours - task_inv += 1 - projects[project.id] = projects.get(project.id, []) + [task.id] - task_tot += 1 - res = {'invoice_num': len(projects), 'total': tot, 'task_total': task_tot, 'task_invoice':task_inv} - return res + tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids']) + projects = {} + tot = 0.0 + task_tot = 0 + task_inv = 0 + for task in tasks: + project = task.project_id + if not task.invoice_id and task.billable: + if project.mode == 'project': + tot += project.tariff + elif project.mode == 'effective': + tot += project.tariff * task.effective_hours + elif project.mode == 'hour': + tot += project.tariff * task.planned_hours + task_inv += 1 + projects[project.id] = projects.get(project.id, []) + [task.id] + task_tot += 1 + res = {'invoice_num': len(projects), 'total': tot, 'task_total': task_tot, 'task_invoice':task_inv} + return res def _do_orders(self, cr, uid, data, context): - tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids']) - invoiceline = pooler.get_pool(cr.dbname).get('account.invoice.line') - customers = {} - for task in filter(lambda t: t.billable and not t.invoice_id, tasks): - if not task.project_id.id in customers: - partner = task.partner_id or task.project_id.partner_id - if not partner.id: - raise wizard.except_wizard(_('Error !'), _('No partner defined for the task or project.')) - if not task.project_id.pricelist_id.id: - raise wizard.except_wizard(_('Error !'), _('No pricelist defined in the project definition.')) - adr = pooler.get_pool(cr.dbname).get('res.partner').address_get(cr, uid, [partner.id], ['default','invoice','shipping']) + tasks = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids']) + invoiceline = pooler.get_pool(cr.dbname).get('account.invoice.line') + customers = {} + for task in filter(lambda t: t.billable and not t.invoice_id, tasks): + if not task.project_id.id in customers: + partner = task.partner_id or task.project_id.partner_id + if not partner.id: + raise wizard.except_wizard(_('Error !'), _('No partner defined for the task or project.')) + if not task.project_id.pricelist_id.id: + raise wizard.except_wizard(_('Error !'), _('No pricelist defined in the project definition.')) + adr = pooler.get_pool(cr.dbname).get('res.partner').address_get(cr, uid, [partner.id], ['default','invoice','shipping']) - a = partner.property_account_receivable.id - if partner.property_payment_term: - pay_term = partner.property_payment_term.id - else: - pay_term = False + a = partner.property_account_receivable.id + if partner.property_payment_term: + pay_term = partner.property_payment_term.id + else: + pay_term = False - oid = pooler.get_pool(cr.dbname).get('account.invoice').create(cr, uid, { - 'name': task.project_id.name, - 'origin': 'Task:'+str(task.id), - 'state': 'draft', - 'partner_id': partner.id, - 'address_contact_id': adr['default'], - 'address_invoice_id': adr['invoice'], - 'account_id': a, - 'type': 'out_invoice', - 'currency_id': task.project_id.pricelist_id.currency_id.id, - 'payment_term': pay_term, - 'journal_id': data['form']['journal_id'], - }) - customers[task.project_id.id] = oid - else: - oid = customers[task.project_id.id] + oid = pooler.get_pool(cr.dbname).get('account.invoice').create(cr, uid, { + 'name': task.project_id.name, + 'origin': 'Task:'+str(task.id), + 'state': 'draft', + 'partner_id': partner.id, + 'address_contact_id': adr['default'], + 'address_invoice_id': adr['invoice'], + 'account_id': a, + 'type': 'out_invoice', + 'currency_id': task.project_id.pricelist_id.currency_id.id, + 'payment_term': pay_term, + 'journal_id': data['form']['journal_id'], + }) + customers[task.project_id.id] = oid + else: + oid = customers[task.project_id.id] - if task.project_id.mode == 'project': - qty = 1 - elif task.project_id.mode == 'effective': - qty = task.effective_hours - else: - qty = task.planned_hours + if task.project_id.mode == 'project': + qty = 1 + elif task.project_id.mode == 'effective': + qty = task.effective_hours + else: + qty = task.planned_hours - invoiceline.create(cr, uid, { - 'invoice_id': oid, - 'name': task.name, - 'account_id': data['form']['sale_account_id'], - 'price_unit': task.project_id.tariff, - 'quantity': qty, - 'invoice_line_tax_id': [(6,0, [t.id for t in task.project_id.tax_ids])], - 'account_analytic_id': task.project_id.category_id.id, - }) - #pooler.get_pool(cr.dbname).get('project.task').write(cr, uid, [task.id], {'invoice_id':oid}) - return { - 'domain': "[('id','in', ["+','.join(map(str,customers.values()))+"])]", - 'name': 'Invoices', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.invoice', - 'view_id': False, - 'context': "{'type':'out_invoice'}", - 'type': 'ir.actions.act_window' - } + invoiceline.create(cr, uid, { + 'invoice_id': oid, + 'name': task.name, + 'account_id': data['form']['sale_account_id'], + 'price_unit': task.project_id.tariff, + 'quantity': qty, + 'invoice_line_tax_id': [(6,0, [t.id for t in task.project_id.tax_ids])], + 'account_analytic_id': task.project_id.category_id.id, + }) + #pooler.get_pool(cr.dbname).get('project.task').write(cr, uid, [task.id], {'invoice_id':oid}) + return { + 'domain': "[('id','in', ["+','.join(map(str,customers.values()))+"])]", + 'name': 'Invoices', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.invoice', + 'view_id': False, + 'context': "{'type':'out_invoice'}", + 'type': 'ir.actions.act_window' + } class wiz_bill(wizard.interface): - states = { - 'init':{ - 'actions': [_compute_orders], - 'result': {'type':'form', 'arch':bill_form, 'fields':bill_fields, 'state':[('end', 'Cancel'), ('bill', 'Ok')] }, - }, - 'bill':{ - 'actions': [], - 'result': {'type':'action', 'action':_do_orders, 'state':'end' }, - }, - } + states = { + 'init':{ + 'actions': [_compute_orders], + 'result': {'type':'form', 'arch':bill_form, 'fields':bill_fields, 'state':[('end', 'Cancel'), ('bill', 'Ok')] }, + }, + 'bill':{ + 'actions': [], + 'result': {'type':'action', 'action':_do_orders, 'state':'end' }, + }, + } wiz_bill('project.wiz_bill') # vim:noexpandtab:tw=0 diff --git a/addons/project/wizard/close_task.py b/addons/project/wizard/close_task.py index 264048ee489..c975bd7de88 100644 --- a/addons/project/wizard/close_task.py +++ b/addons/project/wizard/close_task.py @@ -33,85 +33,85 @@ import pooler mail_form = """<?xml version="1.0" ?> <form string="Send mail to customer"> - <field name="email" colspan="4"/> - <field name="description" colspan="4"/> + <field name="email" colspan="4"/> + <field name="description" colspan="4"/> </form>""" mail_fields = { - 'email': {'string': 'E-Mails', 'type': 'char', 'required': 'True', 'size':64}, - 'description': {'string':'Description', 'type':'text', 'required':'True'}, + 'email': {'string': 'E-Mails', 'type': 'char', 'required': 'True', 'size':64}, + 'description': {'string':'Description', 'type':'text', 'required':'True'}, } def email_send(cr, uid, ids, to_adr, description, context={}): - for task in pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, ids, context): - project = task.project_id - subject = "Task '%s' closed" % task.name - if task.user_id and task.user_id.address_id and task.user_id.address_id.email: - from_adr = task.user_id.address_id.email - signature = task.user_id.signature - else: - raise osv.except_osv(_('Error'), _("Couldn't send mail because your email address is not configured!")) + for task in pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, ids, context): + project = task.project_id + subject = "Task '%s' closed" % task.name + if task.user_id and task.user_id.address_id and task.user_id.address_id.email: + from_adr = task.user_id.address_id.email + signature = task.user_id.signature + else: + raise osv.except_osv(_('Error'), _("Couldn't send mail because your email address is not configured!")) - if to_adr: - val = { - 'name': task.name, - 'user_id': task.user_id.name, - 'task_id': "%d/%d" % (project.id, task.id), - 'date_start': task.date_start, - 'date_close': task.date_close, - 'state': task.state - } - header = (project.warn_header or '') % val - footer = (project.warn_footer or '') % val - body = u'%s\n%s\n%s\n\n-- \n%s' % (header, description, footer, signature) - email(from_adr, [to_adr], subject, body.encode('utf-8'), email_bcc=[from_adr]) - else: - raise osv.except_osv(_('Error'), _("Couldn't send mail because the contact for this task (%s) has no email address!") % contact.name) + if to_adr: + val = { + 'name': task.name, + 'user_id': task.user_id.name, + 'task_id': "%d/%d" % (project.id, task.id), + 'date_start': task.date_start, + 'date_close': task.date_close, + 'state': task.state + } + header = (project.warn_header or '') % val + footer = (project.warn_footer or '') % val + body = u'%s\n%s\n%s\n\n-- \n%s' % (header, description, footer, signature) + email(from_adr, [to_adr], subject, body.encode('utf-8'), email_bcc=[from_adr]) + else: + raise osv.except_osv(_('Error'), _("Couldn't send mail because the contact for this task (%s) has no email address!") % contact.name) class wizard_close(wizard.interface): - def _check_complete(self, cr, uid, data, context): - task = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids'])[0] - if not (task.project_id and task.project_id.warn_customer): - return 'close' - return 'mail_ask' + def _check_complete(self, cr, uid, data, context): + task = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids'])[0] + if not (task.project_id and task.project_id.warn_customer): + return 'close' + return 'mail_ask' - def _get_data(self, cr, uid, data, context): - email = '' - task = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids'][0]) - partner_id = task.partner_id or task.project_id.partner_id - if partner_id and partner_id.address[0].email: - email = partner_id.address[0].email - return {'description': task.description, 'email':email} - - def _data_send(self, cr, uid, data, context): - task_obj = pooler.get_pool(cr.dbname).get('project.task') - if data['form']['email']: - description = data['form'].get('description', False) - email_send(cr, uid, data['ids'], data['form']['email'], data['form']['description']) - return {} + def _get_data(self, cr, uid, data, context): + email = '' + task = pooler.get_pool(cr.dbname).get('project.task').browse(cr, uid, data['ids'][0]) + partner_id = task.partner_id or task.project_id.partner_id + if partner_id and partner_id.address[0].email: + email = partner_id.address[0].email + return {'description': task.description, 'email':email} + + def _data_send(self, cr, uid, data, context): + task_obj = pooler.get_pool(cr.dbname).get('project.task') + if data['form']['email']: + description = data['form'].get('description', False) + email_send(cr, uid, data['ids'], data['form']['email'], data['form']['description']) + return {} - def _do_close(self, cr, uid, data, context): - task_obj = pooler.get_pool(cr.dbname).get('project.task') - task_obj.do_close(cr, uid, data['ids'], context) - return {} + def _do_close(self, cr, uid, data, context): + task_obj = pooler.get_pool(cr.dbname).get('project.task') + task_obj.do_close(cr, uid, data['ids'], context) + return {} - states = { - 'init': { - 'actions': [], - 'result': {'type':'choice', 'next_state':_check_complete} - }, - 'mail_ask': { - 'actions': [_get_data], - 'result': {'type':'form', 'arch':mail_form, 'fields':mail_fields, 'state':[('end', 'Cancel'), ('close', 'Quiet close'), ('mail_send', 'Send Message')]}, - }, - 'mail_send': { - 'actions': [_data_send], - 'result': {'type':'state', 'state':'close'}, - }, - 'close': { - 'actions': [_do_close], - 'result': {'type':'state', 'state':'end'}, - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'choice', 'next_state':_check_complete} + }, + 'mail_ask': { + 'actions': [_get_data], + 'result': {'type':'form', 'arch':mail_form, 'fields':mail_fields, 'state':[('end', 'Cancel'), ('close', 'Quiet close'), ('mail_send', 'Send Message')]}, + }, + 'mail_send': { + 'actions': [_data_send], + 'result': {'type':'state', 'state':'close'}, + }, + 'close': { + 'actions': [_do_close], + 'result': {'type':'state', 'state':'end'}, + }, + } wizard_close('project.task.close') diff --git a/addons/project/wizard/task_delegate.py b/addons/project/wizard/task_delegate.py index a5c695d6b47..b8bf231e946 100644 --- a/addons/project/wizard/task_delegate.py +++ b/addons/project/wizard/task_delegate.py @@ -33,77 +33,77 @@ import pooler ask_form = """<?xml version="1.0" ?> <form string="Delegate this task to a user"> - <separator string="Delegated Task" colspan="4"/> - <field name="user_id" colspan="4"/> - <field name="planned_hours" colspan="4"/> - <field name="name" colspan="4"/> - <field name="include_info"/> - <separator string="Validation Task" colspan="4"/> - <field name="planned_hours_me" colspan="4"/> - <field name="prefix" colspan="4"/> - <field name="state" colspan="4"/> + <separator string="Delegated Task" colspan="4"/> + <field name="user_id" colspan="4"/> + <field name="planned_hours" colspan="4"/> + <field name="name" colspan="4"/> + <field name="include_info"/> + <separator string="Validation Task" colspan="4"/> + <field name="planned_hours_me" colspan="4"/> + <field name="prefix" colspan="4"/> + <field name="state" colspan="4"/> </form>""" ask_fields = { - 'name': {'string': 'Title', 'type': 'char', 'required': 'True', 'size':64}, - 'prefix': {'string': 'Prefix of Task', 'type': 'char', 'required': 'True', 'size':64}, - 'user_id': {'string':'Assign To', 'type':'many2one', 'relation': 'res.users', 'required':'True'}, - 'include_info': {'string':'Copy Description', 'type':'boolean'}, - 'planned_hours': {'string':'Planned Hours', 'type':'float', 'widget':'float_time'}, - 'planned_hours_me': {'string':'Hours to Validate', 'type':'float', 'widget':'float_time'}, - 'state': {'string':'Validation State', 'type':'selection', 'selection': [('pending','Pending'),('done','Done')]}, + 'name': {'string': 'Title', 'type': 'char', 'required': 'True', 'size':64}, + 'prefix': {'string': 'Prefix of Task', 'type': 'char', 'required': 'True', 'size':64}, + 'user_id': {'string':'Assign To', 'type':'many2one', 'relation': 'res.users', 'required':'True'}, + 'include_info': {'string':'Copy Description', 'type':'boolean'}, + 'planned_hours': {'string':'Planned Hours', 'type':'float', 'widget':'float_time'}, + 'planned_hours_me': {'string':'Hours to Validate', 'type':'float', 'widget':'float_time'}, + 'state': {'string':'Validation State', 'type':'selection', 'selection': [('pending','Pending'),('done','Done')]}, } class wizard_delegate(wizard.interface): - def _do_assign(self, cr, uid, data, context): - task_obj = pooler.get_pool(cr.dbname).get('project.task') - task = task_obj.browse(cr, uid, data['id'], context) - newname = task.name - if not task.name.startswith(data['form']['prefix'] or '++'): - newname = (data['form']['prefix'] or '')+task.name - task_obj.copy(cr, uid, data['id'], { - 'name': data['form']['name'], - 'user_id': data['form']['user_id'], - 'planned_hours': data['form']['planned_hours'], - 'parent_id': data['id'], - 'state': 'open', - 'description': data['form']['include_info'] and task.description or '', - 'child_ids': [] - }) - task_obj.write(cr, uid, data['id'], { - 'planned_hours': data['form']['planned_hours_me'], - 'name': newname - }) - if data['form']['state']=='pending': - task_obj.do_pending(cr, uid, [data['id']]) - else: - task_obj.do_close(cr, uid, [data['id']]) - return {} + def _do_assign(self, cr, uid, data, context): + task_obj = pooler.get_pool(cr.dbname).get('project.task') + task = task_obj.browse(cr, uid, data['id'], context) + newname = task.name + if not task.name.startswith(data['form']['prefix'] or '++'): + newname = (data['form']['prefix'] or '')+task.name + task_obj.copy(cr, uid, data['id'], { + 'name': data['form']['name'], + 'user_id': data['form']['user_id'], + 'planned_hours': data['form']['planned_hours'], + 'parent_id': data['id'], + 'state': 'open', + 'description': data['form']['include_info'] and task.description or '', + 'child_ids': [] + }) + task_obj.write(cr, uid, data['id'], { + 'planned_hours': data['form']['planned_hours_me'], + 'name': newname + }) + if data['form']['state']=='pending': + task_obj.do_pending(cr, uid, [data['id']]) + else: + task_obj.do_close(cr, uid, [data['id']]) + return {} - def _ask_auto_complete(self, cr, uid, data, context): - task_obj = pooler.get_pool(cr.dbname).get('project.task') - task = task_obj.browse(cr, uid, data['id'], context) - return { - 'name':task.name, - 'user_id': False, - 'planned_hours': task.planned_hours, - 'planned_hours_me': task.planned_hours / 5.0, - 'prefix': 'CHECK: ', - 'state': 'pending' - } + def _ask_auto_complete(self, cr, uid, data, context): + task_obj = pooler.get_pool(cr.dbname).get('project.task') + task = task_obj.browse(cr, uid, data['id'], context) + return { + 'name':task.name, + 'user_id': False, + 'planned_hours': task.planned_hours, + 'planned_hours_me': task.planned_hours / 5.0, + 'prefix': 'CHECK: ', + 'state': 'pending' + } - states = { - 'init': { - 'actions': [_ask_auto_complete], - 'result': {'type':'form', 'arch':ask_form, 'fields':ask_fields, 'state':[ - ('end', 'Cancel'), - ('valid', 'Validate') - ]}, - }, - 'valid': { - 'actions': [_do_assign], - 'result': {'type':'state', 'state':'end'}, - } - } + states = { + 'init': { + 'actions': [_ask_auto_complete], + 'result': {'type':'form', 'arch':ask_form, 'fields':ask_fields, 'state':[ + ('end', 'Cancel'), + ('valid', 'Validate') + ]}, + }, + 'valid': { + 'actions': [_do_assign], + 'result': {'type':'state', 'state':'end'}, + } + } wizard_delegate('project.task.delegate') diff --git a/addons/purchase/__terp__.py b/addons/purchase/__terp__.py index 9ee9c3b0149..a7dd41deb87 100644 --- a/addons/purchase/__terp__.py +++ b/addons/purchase/__terp__.py @@ -26,26 +26,26 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Purchase Management", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_purchase.html", - "depends" : ["base", "account", "stock"], - "category" : "Generic Modules/Sales & Purchases", - "init_xml" : [], - "demo_xml" : ["purchase_demo.xml", - #"purchase_unit_test.xml" - ], - "update_xml" : [ - "purchase_workflow.xml", - "purchase_sequence.xml", - "purchase_data.xml", - "purchase_view.xml", - "purchase_report.xml", - "purchase_wizard.xml", - "stock_view.xml", - "purchase_security.xml" - ], - "active": False, - "installable": True + "name" : "Purchase Management", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_purchase.html", + "depends" : ["base", "account", "stock"], + "category" : "Generic Modules/Sales & Purchases", + "init_xml" : [], + "demo_xml" : ["purchase_demo.xml", + #"purchase_unit_test.xml" + ], + "update_xml" : [ + "purchase_workflow.xml", + "purchase_sequence.xml", + "purchase_data.xml", + "purchase_view.xml", + "purchase_report.xml", + "purchase_wizard.xml", + "stock_view.xml", + "purchase_security.xml" + ], + "active": False, + "installable": True } diff --git a/addons/purchase/partner.py b/addons/purchase/partner.py index 56160321dd0..4f359388cd7 100644 --- a/addons/purchase/partner.py +++ b/addons/purchase/partner.py @@ -31,19 +31,19 @@ from osv import fields, osv class res_partner(osv.osv): - _name = 'res.partner' - _inherit = 'res.partner' - _columns = { - 'property_product_pricelist_purchase': fields.property( - 'product.pricelist', - type='many2one', - relation='product.pricelist', - string="Purchase Pricelist", - method=True, - view_load=True, - group_name="Pricelists Properties", - help="This pricelist will be used, instead of the default one, for purchases from the current partner"), - } + _name = 'res.partner' + _inherit = 'res.partner' + _columns = { + 'property_product_pricelist_purchase': fields.property( + 'product.pricelist', + type='many2one', + relation='product.pricelist', + string="Purchase Pricelist", + method=True, + view_load=True, + group_name="Pricelists Properties", + help="This pricelist will be used, instead of the default one, for purchases from the current partner"), + } res_partner() diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 7c7297b6516..07ad5af8712 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -42,345 +42,345 @@ from tools.translate import _ # Model definition # class purchase_order(osv.osv): - def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res = {} - for order in self.browse(cr, uid, ids): - res[order.id] = 0 - for oline in order.order_line: - res[order.id] += oline.price_unit * oline.product_qty - return res - - def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): - res = {} - cur_obj=self.pool.get('res.currency') - for purchase in self.browse(cr, uid, ids): - res[purchase.id] = 0.0 - for line in purchase.order_line: - res[purchase.id] += line.price_subtotal - cur = purchase.pricelist_id.currency_id - res[purchase.id] = cur_obj.round(cr, uid, cur, res[purchase.id]) - - return res - - def _amount_tax(self, cr, uid, ids, field_name, arg, context): - res = {} - cur_obj=self.pool.get('res.currency') - for order in self.browse(cr, uid, ids): - val = 0.0 - cur=order.pricelist_id.currency_id - for line in order.order_line: - for c in self.pool.get('account.tax').compute(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id): - val+= cur_obj.round(cr, uid, cur, c['amount']) - res[order.id]=cur_obj.round(cr, uid, cur, val) - return res - - def _amount_total(self, cr, uid, ids, field_name, arg, context): - res = {} - untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context) - tax = self._amount_tax(cr, uid, ids, field_name, arg, context) - cur_obj=self.pool.get('res.currency') - for id in ids: - order=self.browse(cr, uid, [id])[0] - cur=order.pricelist_id.currency_id - res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0)) - return res - - _columns = { - 'name': fields.char('Order Reference', size=64, required=True, select=True), - 'origin': fields.char('Origin', size=64), - 'partner_ref': fields.char('Partner Ref.', size=64), - 'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), - 'date_approve':fields.date('Date Approved'), - 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True), - 'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True, states={'posted':[('readonly',True)]}), - - 'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]}), - 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}), - 'location_id': fields.many2one('stock.location', 'Delivery destination', required=True), - - 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), - - 'state': fields.selection([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Confirmed'), ('approved', 'Approved'),('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Order State', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True), - 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order State', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), - 'validator' : fields.many2one('res.users', 'Validated by', readonly=True), - 'notes': fields.text('Notes'), - 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), - 'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of picking list that have been generated for this purchase"), - 'shipped':fields.boolean('Received', readonly=True, select=True), - 'invoiced':fields.boolean('Invoiced & Paid', readonly=True, select=True), - 'invoice_method': fields.selection([('manual','Manual'),('order','From order'),('picking','From picking')], 'Invoicing Control', required=True), - - 'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'), - 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'), - 'amount_total': fields.function(_amount_total, method=True, string='Total'), - } - _defaults = { - 'date_order': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'draft', - 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), - 'shipped': lambda *a: 0, - 'invoice_method': lambda *a: 'order', - 'invoiced': lambda *a: 0, - 'partner_address_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['default'])['default'], - 'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id, - } - _name = "purchase.order" - _description = "Purchase order" - _order = "name desc" - - def button_dummy(self, cr, uid, ids, context={}): - return True - - def onchange_dest_address_id(self, cr, uid, ids, adr_id): - if not adr_id: - return {} - part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0] - loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer.id - return {'value':{'location_id': loc_id, 'warehouse_id': False}} - - def onchange_warehouse_id(self, cr, uid, ids, warehouse_id): - if not warehouse_id: - return {} - res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0] - return {'value':{'location_id': res, 'dest_address_id': False}} - - def onchange_partner_id(self, cr, uid, ids, part): - if not part: - return {'value':{'partner_address_id': False}} - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['default']) - pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist_purchase.id - return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist}} - - def wkf_approve_order(self, cr, uid, ids): - self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')}) - return True - - def wkf_confirm_order(self, cr, uid, ids, context={}): - for po in self.browse(cr, uid, ids): - if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'): - self.pool.get('res.partner.event').create(cr, uid, {'name':'Purchase Order: '+po.name, 'partner_id':po.partner_id.id, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id':uid, 'partner_type':'retailer', 'probability': 1.0, 'planned_cost':po.amount_untaxed}) - current_name = self.name_get(cr, uid, ids)[0][1] - for id in ids: - self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid}) - return True - - def wkf_warn_buyer(self, cr, uid, ids): - self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid}) - request = pooler.get_pool(cr.dbname).get('res.request') - for po in self.browse(cr, uid, ids): - managers = [] - for oline in po.order_line: - manager = oline.product_id.product_manager - if manager and not (manager.id in managers): - managers.append(manager.id) - for manager_id in managers: - request.create(cr, uid, - {'name' : "Purchase amount over the limit", - 'act_from' : uid, - 'act_to' : manager_id, - 'body': 'Somebody has just confirmed a purchase with an amount over the defined limit', - 'ref_partner_id': po.partner_id.id, - 'ref_doc1': 'purchase.order,%d' % (po.id,), - }) - def inv_line_create(self,a,ol): - return (0, False, { - 'name': ol.name, - 'account_id': a, - 'price_unit': ol.price_unit or 0.0, - 'quantity': ol.product_qty, - 'product_id': ol.product_id.id or False, - 'uos_id': ol.product_uom.id or False, - 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])], - 'account_analytic_id': ol.account_analytic_id.id, - }) - - def action_invoice_create(self, cr, uid, ids, *args): - res = False - for o in self.browse(cr, uid, ids): - il = [] - for ol in o.order_line: - - if ol.product_id: - a = ol.product_id.product_tmpl_id.property_account_expense.id - if not a: - a = ol.product_id.categ_id.property_account_expense_categ.id - if not a: - raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (ol.product_id.name, ol.product_id.id,)) - else: - a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category') - il.append(self.inv_line_create(a,ol)) -# il.append((0, False, { -# 'name': ol.name, -# 'account_id': a, -# 'price_unit': ol.price_unit or 0.0, -# 'quantity': ol.product_qty, -# 'product_id': ol.product_id.id or False, -# 'uos_id': ol.product_uom.id or False, -# 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])], -# 'account_analytic_id': ol.account_analytic_id.id, -# })) - - a = o.partner_id.property_account_payable.id - inv = { - 'name': o.partner_ref or o.name, - 'reference': "P%dPO%d" % (o.partner_id.id, o.id), - 'account_id': a, - 'type': 'in_invoice', - 'partner_id': o.partner_id.id, - 'currency_id': o.pricelist_id.currency_id.id, - 'address_invoice_id': o.partner_address_id.id, - 'address_contact_id': o.partner_address_id.id, - 'origin': o.name, - 'invoice_line': il, - } - inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'}) - self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True) - - self.write(cr, uid, [o.id], {'invoice_id': inv_id}) - res = inv_id - return res - - def has_stockable_product(self,cr, uid, ids, *args): - for order in self.browse(cr, uid, ids): - for order_line in order.order_line: - if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'): - return True - return False - - def action_picking_create(self,cr, uid, ids, *args): - picking_id = False - for order in self.browse(cr, uid, ids): - loc_id = order.partner_id.property_stock_supplier.id - istate = 'none' - if order.invoice_method=='picking': - istate = '2binvoiced' - picking_id = self.pool.get('stock.picking').create(cr, uid, { - 'origin': order.name+((order.origin and (':'+order.origin)) or ''), - 'type': 'in', - 'address_id': order.dest_address_id.id or order.partner_address_id.id, - 'invoice_state': istate, - 'purchase_id': order.id, - }) - for order_line in order.order_line: - if not order_line.product_id: - continue - if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): - dest = order.location_id.id - self.pool.get('stock.move').create(cr, uid, { - 'name': 'PO:'+order_line.name, - 'product_id': order_line.product_id.id, - 'product_qty': order_line.product_qty, - 'product_uos_qty': order_line.product_qty, - 'product_uom': order_line.product_uom.id, - 'product_uos': order_line.product_uom.id, - 'date_planned': order_line.date_planned, - 'location_id': loc_id, - 'location_dest_id': dest, - 'picking_id': picking_id, - 'move_dest_id': order_line.move_dest_id.id, - 'state': 'assigned', - 'purchase_line_id': order_line.id, - }) - if order_line.move_dest_id: - self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id}) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) - return picking_id - def copy(self, cr, uid, id, default=None,context={}): - if not default: - default = {} - default.update({ - 'state':'draft', - 'shipped':False, - 'invoiced':False, - 'invoice_id':False, - 'picking_ids':[], - 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), - }) - return super(purchase_order, self).copy(cr, uid, id, default, context) + def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res = {} + for order in self.browse(cr, uid, ids): + res[order.id] = 0 + for oline in order.order_line: + res[order.id] += oline.price_unit * oline.product_qty + return res + + def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): + res = {} + cur_obj=self.pool.get('res.currency') + for purchase in self.browse(cr, uid, ids): + res[purchase.id] = 0.0 + for line in purchase.order_line: + res[purchase.id] += line.price_subtotal + cur = purchase.pricelist_id.currency_id + res[purchase.id] = cur_obj.round(cr, uid, cur, res[purchase.id]) + + return res + + def _amount_tax(self, cr, uid, ids, field_name, arg, context): + res = {} + cur_obj=self.pool.get('res.currency') + for order in self.browse(cr, uid, ids): + val = 0.0 + cur=order.pricelist_id.currency_id + for line in order.order_line: + for c in self.pool.get('account.tax').compute(cr, uid, line.taxes_id, line.price_unit, line.product_qty, order.partner_address_id.id, line.product_id, order.partner_id): + val+= cur_obj.round(cr, uid, cur, c['amount']) + res[order.id]=cur_obj.round(cr, uid, cur, val) + return res + + def _amount_total(self, cr, uid, ids, field_name, arg, context): + res = {} + untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context) + tax = self._amount_tax(cr, uid, ids, field_name, arg, context) + cur_obj=self.pool.get('res.currency') + for id in ids: + order=self.browse(cr, uid, [id])[0] + cur=order.pricelist_id.currency_id + res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0)) + return res + + _columns = { + 'name': fields.char('Order Reference', size=64, required=True, select=True), + 'origin': fields.char('Origin', size=64), + 'partner_ref': fields.char('Partner Ref.', size=64), + 'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + 'date_approve':fields.date('Date Approved'), + 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True), + 'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True, states={'posted':[('readonly',True)]}), + + 'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]}), + 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}), + 'location_id': fields.many2one('stock.location', 'Delivery destination', required=True), + + 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + + 'state': fields.selection([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Confirmed'), ('approved', 'Approved'),('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Order State', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True), + 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order State', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + 'validator' : fields.many2one('res.users', 'Validated by', readonly=True), + 'notes': fields.text('Notes'), + 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), + 'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of picking list that have been generated for this purchase"), + 'shipped':fields.boolean('Received', readonly=True, select=True), + 'invoiced':fields.boolean('Invoiced & Paid', readonly=True, select=True), + 'invoice_method': fields.selection([('manual','Manual'),('order','From order'),('picking','From picking')], 'Invoicing Control', required=True), + + 'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'), + 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'), + 'amount_total': fields.function(_amount_total, method=True, string='Total'), + } + _defaults = { + 'date_order': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), + 'shipped': lambda *a: 0, + 'invoice_method': lambda *a: 'order', + 'invoiced': lambda *a: 0, + 'partner_address_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['default'])['default'], + 'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id, + } + _name = "purchase.order" + _description = "Purchase order" + _order = "name desc" + + def button_dummy(self, cr, uid, ids, context={}): + return True + + def onchange_dest_address_id(self, cr, uid, ids, adr_id): + if not adr_id: + return {} + part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0] + loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer.id + return {'value':{'location_id': loc_id, 'warehouse_id': False}} + + def onchange_warehouse_id(self, cr, uid, ids, warehouse_id): + if not warehouse_id: + return {} + res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0] + return {'value':{'location_id': res, 'dest_address_id': False}} + + def onchange_partner_id(self, cr, uid, ids, part): + if not part: + return {'value':{'partner_address_id': False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['default']) + pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist_purchase.id + return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist}} + + def wkf_approve_order(self, cr, uid, ids): + self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')}) + return True + + def wkf_confirm_order(self, cr, uid, ids, context={}): + for po in self.browse(cr, uid, ids): + if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'): + self.pool.get('res.partner.event').create(cr, uid, {'name':'Purchase Order: '+po.name, 'partner_id':po.partner_id.id, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id':uid, 'partner_type':'retailer', 'probability': 1.0, 'planned_cost':po.amount_untaxed}) + current_name = self.name_get(cr, uid, ids)[0][1] + for id in ids: + self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid}) + return True + + def wkf_warn_buyer(self, cr, uid, ids): + self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid}) + request = pooler.get_pool(cr.dbname).get('res.request') + for po in self.browse(cr, uid, ids): + managers = [] + for oline in po.order_line: + manager = oline.product_id.product_manager + if manager and not (manager.id in managers): + managers.append(manager.id) + for manager_id in managers: + request.create(cr, uid, + {'name' : "Purchase amount over the limit", + 'act_from' : uid, + 'act_to' : manager_id, + 'body': 'Somebody has just confirmed a purchase with an amount over the defined limit', + 'ref_partner_id': po.partner_id.id, + 'ref_doc1': 'purchase.order,%d' % (po.id,), + }) + def inv_line_create(self,a,ol): + return (0, False, { + 'name': ol.name, + 'account_id': a, + 'price_unit': ol.price_unit or 0.0, + 'quantity': ol.product_qty, + 'product_id': ol.product_id.id or False, + 'uos_id': ol.product_uom.id or False, + 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])], + 'account_analytic_id': ol.account_analytic_id.id, + }) + + def action_invoice_create(self, cr, uid, ids, *args): + res = False + for o in self.browse(cr, uid, ids): + il = [] + for ol in o.order_line: + + if ol.product_id: + a = ol.product_id.product_tmpl_id.property_account_expense.id + if not a: + a = ol.product_id.categ_id.property_account_expense_categ.id + if not a: + raise osv.except_osv(_('Error !'), _('There is no expense account defined for this product: "%s" (id:%d)') % (ol.product_id.name, ol.product_id.id,)) + else: + a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category') + il.append(self.inv_line_create(a,ol)) +# il.append((0, False, { +# 'name': ol.name, +# 'account_id': a, +# 'price_unit': ol.price_unit or 0.0, +# 'quantity': ol.product_qty, +# 'product_id': ol.product_id.id or False, +# 'uos_id': ol.product_uom.id or False, +# 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])], +# 'account_analytic_id': ol.account_analytic_id.id, +# })) + + a = o.partner_id.property_account_payable.id + inv = { + 'name': o.partner_ref or o.name, + 'reference': "P%dPO%d" % (o.partner_id.id, o.id), + 'account_id': a, + 'type': 'in_invoice', + 'partner_id': o.partner_id.id, + 'currency_id': o.pricelist_id.currency_id.id, + 'address_invoice_id': o.partner_address_id.id, + 'address_contact_id': o.partner_address_id.id, + 'origin': o.name, + 'invoice_line': il, + } + inv_id = self.pool.get('account.invoice').create(cr, uid, inv, {'type':'in_invoice'}) + self.pool.get('account.invoice').button_compute(cr, uid, [inv_id], {'type':'in_invoice'}, set_total=True) + + self.write(cr, uid, [o.id], {'invoice_id': inv_id}) + res = inv_id + return res + + def has_stockable_product(self,cr, uid, ids, *args): + for order in self.browse(cr, uid, ids): + for order_line in order.order_line: + if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + return True + return False + + def action_picking_create(self,cr, uid, ids, *args): + picking_id = False + for order in self.browse(cr, uid, ids): + loc_id = order.partner_id.property_stock_supplier.id + istate = 'none' + if order.invoice_method=='picking': + istate = '2binvoiced' + picking_id = self.pool.get('stock.picking').create(cr, uid, { + 'origin': order.name+((order.origin and (':'+order.origin)) or ''), + 'type': 'in', + 'address_id': order.dest_address_id.id or order.partner_address_id.id, + 'invoice_state': istate, + 'purchase_id': order.id, + }) + for order_line in order.order_line: + if not order_line.product_id: + continue + if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + dest = order.location_id.id + self.pool.get('stock.move').create(cr, uid, { + 'name': 'PO:'+order_line.name, + 'product_id': order_line.product_id.id, + 'product_qty': order_line.product_qty, + 'product_uos_qty': order_line.product_qty, + 'product_uom': order_line.product_uom.id, + 'product_uos': order_line.product_uom.id, + 'date_planned': order_line.date_planned, + 'location_id': loc_id, + 'location_dest_id': dest, + 'picking_id': picking_id, + 'move_dest_id': order_line.move_dest_id.id, + 'state': 'assigned', + 'purchase_line_id': order_line.id, + }) + if order_line.move_dest_id: + self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id}) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + return picking_id + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({ + 'state':'draft', + 'shipped':False, + 'invoiced':False, + 'invoice_id':False, + 'picking_ids':[], + 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), + }) + return super(purchase_order, self).copy(cr, uid, id, default, context) purchase_order() class purchase_order_line(osv.osv): - def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): - res = {} - cur_obj=self.pool.get('res.currency') - for line in self.browse(cr, uid, ids): - cur = line.order_id.pricelist_id.currency_id - res[line.id] = cur_obj.round(cr, uid, cur, line.price_unit * line.product_qty) - return res - - _columns = { - 'name': fields.char('Description', size=64, required=True), - 'product_qty': fields.float('Quantity', required=True, digits=(16,2)), - 'date_planned': fields.date('Scheduled date', required=True), - 'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True), - 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), - 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'), - 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), - 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), - 'notes': fields.text('Notes'), - 'order_id': fields.many2one('purchase.order', 'Order Ref', select=True, required=True, ondelete='cascade'), - 'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',), - } - _defaults = { - 'product_qty': lambda *a: 1.0 - } - _table = 'purchase_order_line' - _name = 'purchase.order.line' - _description = 'Purchase Order line' - def copy(self, cr, uid, id, default=None,context={}): - if not default: - default = {} - default.update({'state':'draft', 'move_id':False}) - return super(purchase_order_line, self).copy(cr, uid, id, default, context) - - def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom, - partner_id, date_order=False): - if not pricelist: - raise osv.except_osv(_('No Pricelist !'), _('You have to select a pricelist in the purchase form !\nPlease set one before choosing a product.')) - if not product: - return {'value': {'price_unit': 0.0, 'name':'','notes':''}, 'domain':{'product_uom':[]}} - lang=False - if partner_id: - lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang'] - context={'lang':lang} - - prod = self.pool.get('product.product').read(cr, uid, [product], ['supplier_taxes_id','name','seller_delay','uom_po_id','description_purchase'])[0] - prod_uom_po = prod['uom_po_id'][0] - if not uom: - uom = prod_uom_po - if not date_order: - date_order = time.strftime('%Y-%m-%d') - price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], - product, qty or 1.0, partner_id, { - 'uom': uom, - 'date': date_order, - })[pricelist] - dt = (DateTime.now() + DateTime.RelativeDateTime(days=prod['seller_delay'] or 0.0)).strftime('%Y-%m-%d') - prod_name = self.pool.get('product.product').name_get(cr, uid, [product], context=context)[0][1] - - res = {'value': {'price_unit': price, 'name':prod_name, 'taxes_id':prod['supplier_taxes_id'], 'date_planned': dt,'notes':prod['description_purchase'], 'product_uom': uom}} - domain = {} - - res2 = self.pool.get('product.uom').read(cr, uid, [uom], ['category_id']) - res3 = self.pool.get('product.uom').read(cr, uid, [prod_uom_po], ['category_id']) - domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]} - if res2[0]['category_id'] != res3[0]['category_id']: - raise osv.except_osv(_('Wrong Product UOM !'), _('You have to select a product UOM in the same category than the purchase UOM of the product')) - - res['domain'] = domain - return res - - def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom, - partner_id, date_order=False): - res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom, - partner_id, date_order=date_order) - if 'product_uom' in res['value']: - del res['value']['product_uom'] - if not uom: - res['value']['price_unit'] = 0.0 - return res + def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): + res = {} + cur_obj=self.pool.get('res.currency') + for line in self.browse(cr, uid, ids): + cur = line.order_id.pricelist_id.currency_id + res[line.id] = cur_obj.round(cr, uid, cur, line.price_unit * line.product_qty) + return res + + _columns = { + 'name': fields.char('Description', size=64, required=True), + 'product_qty': fields.float('Quantity', required=True, digits=(16,2)), + 'date_planned': fields.date('Scheduled date', required=True), + 'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True), + 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), + 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'), + 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), + 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), + 'notes': fields.text('Notes'), + 'order_id': fields.many2one('purchase.order', 'Order Ref', select=True, required=True, ondelete='cascade'), + 'account_analytic_id':fields.many2one('account.analytic.account', 'Analytic Account',), + } + _defaults = { + 'product_qty': lambda *a: 1.0 + } + _table = 'purchase_order_line' + _name = 'purchase.order.line' + _description = 'Purchase Order line' + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({'state':'draft', 'move_id':False}) + return super(purchase_order_line, self).copy(cr, uid, id, default, context) + + def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom, + partner_id, date_order=False): + if not pricelist: + raise osv.except_osv(_('No Pricelist !'), _('You have to select a pricelist in the purchase form !\nPlease set one before choosing a product.')) + if not product: + return {'value': {'price_unit': 0.0, 'name':'','notes':''}, 'domain':{'product_uom':[]}} + lang=False + if partner_id: + lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang'] + context={'lang':lang} + + prod = self.pool.get('product.product').read(cr, uid, [product], ['supplier_taxes_id','name','seller_delay','uom_po_id','description_purchase'])[0] + prod_uom_po = prod['uom_po_id'][0] + if not uom: + uom = prod_uom_po + if not date_order: + date_order = time.strftime('%Y-%m-%d') + price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], + product, qty or 1.0, partner_id, { + 'uom': uom, + 'date': date_order, + })[pricelist] + dt = (DateTime.now() + DateTime.RelativeDateTime(days=prod['seller_delay'] or 0.0)).strftime('%Y-%m-%d') + prod_name = self.pool.get('product.product').name_get(cr, uid, [product], context=context)[0][1] + + res = {'value': {'price_unit': price, 'name':prod_name, 'taxes_id':prod['supplier_taxes_id'], 'date_planned': dt,'notes':prod['description_purchase'], 'product_uom': uom}} + domain = {} + + res2 = self.pool.get('product.uom').read(cr, uid, [uom], ['category_id']) + res3 = self.pool.get('product.uom').read(cr, uid, [prod_uom_po], ['category_id']) + domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]} + if res2[0]['category_id'] != res3[0]['category_id']: + raise osv.except_osv(_('Wrong Product UOM !'), _('You have to select a product UOM in the same category than the purchase UOM of the product')) + + res['domain'] = domain + return res + + def product_uom_change(self, cr, uid, ids, pricelist, product, qty, uom, + partner_id, date_order=False): + res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom, + partner_id, date_order=date_order) + if 'product_uom' in res['value']: + del res['value']['product_uom'] + if not uom: + res['value']['price_unit'] = 0.0 + return res purchase_order_line() diff --git a/addons/purchase/report/order.py b/addons/purchase/report/order.py index bf0bd2f6d41..a8af3e212d7 100644 --- a/addons/purchase/report/order.py +++ b/addons/purchase/report/order.py @@ -34,56 +34,56 @@ from osv import osv import pooler class order(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(order, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'get_line_tax': self._get_line_tax, - 'get_tax': self._get_tax, - 'get_product_code': self._get_product_code, - }) - def _get_line_tax(self, line_obj): - self.cr.execute("SELECT tax_id FROM purchase_order_taxe WHERE order_line_id=%d" % (line_obj.id)) - res = self.cr.fetchall() or None - if not res: - return "" - if isinstance(res, list): - tax_ids = [t[0] for t in res] - else: - tax_ids = res[0] - res = [tax.name for tax in pooler.get_pool(cr.dbname).get('account.tax').browse(self.cr, self.uid, tax_ids)] - return ",\n ".join(res) - - def _get_tax(self, order_obj): - self.cr.execute("SELECT DISTINCT tax_id FROM purchase_order_taxe, purchase_order_line, purchase_order \ - WHERE (purchase_order_line.order_id=purchase_order.id) AND (purchase_order.id=%d)" % (order_obj.id)) - res = self.cr.fetchall() or None - if not res: - return [] - if isinstance(res, list): - tax_ids = [t[0] for t in res] - else: - tax_ids = res[0] - tax_obj = pooler.get_pool(cr.dbname).get('account.tax') - res = [] - for tax in tax_obj.browse(self.cr, self.uid, tax_ids): - self.cr.execute("SELECT DISTINCT order_line_id FROM purchase_order_line, purchase_order_taxe \ - WHERE (purchase_order_taxe.tax_id=%d) AND (purchase_order_line.order_id=%d)" % (tax.id, order_obj.id)) - lines = self.cr.fetchall() or None - if lines: - if isinstance(lines, list): - line_ids = [l[0] for l in lines] - else: - line_ids = lines[0] - base = 0 - for line in pooler.get_pool(cr.dbname).get('purchase.order.line').browse(self.cr, self.uid, line_ids): - base += line.price_subtotal - res.append({'code':tax.name, - 'base':base, - 'amount':base*tax.amount}) - return res - def _get_product_code(self, product_id, partner_id): - product_obj=pooler.get_pool(self.cr.dbname).get('product.product') - return product_obj._product_code(self.cr, self.uid, [product_id], name=None, arg=None, context={'partner_id': partner_id})[product_id] + def __init__(self, cr, uid, name, context): + super(order, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'get_line_tax': self._get_line_tax, + 'get_tax': self._get_tax, + 'get_product_code': self._get_product_code, + }) + def _get_line_tax(self, line_obj): + self.cr.execute("SELECT tax_id FROM purchase_order_taxe WHERE order_line_id=%d" % (line_obj.id)) + res = self.cr.fetchall() or None + if not res: + return "" + if isinstance(res, list): + tax_ids = [t[0] for t in res] + else: + tax_ids = res[0] + res = [tax.name for tax in pooler.get_pool(cr.dbname).get('account.tax').browse(self.cr, self.uid, tax_ids)] + return ",\n ".join(res) + + def _get_tax(self, order_obj): + self.cr.execute("SELECT DISTINCT tax_id FROM purchase_order_taxe, purchase_order_line, purchase_order \ + WHERE (purchase_order_line.order_id=purchase_order.id) AND (purchase_order.id=%d)" % (order_obj.id)) + res = self.cr.fetchall() or None + if not res: + return [] + if isinstance(res, list): + tax_ids = [t[0] for t in res] + else: + tax_ids = res[0] + tax_obj = pooler.get_pool(cr.dbname).get('account.tax') + res = [] + for tax in tax_obj.browse(self.cr, self.uid, tax_ids): + self.cr.execute("SELECT DISTINCT order_line_id FROM purchase_order_line, purchase_order_taxe \ + WHERE (purchase_order_taxe.tax_id=%d) AND (purchase_order_line.order_id=%d)" % (tax.id, order_obj.id)) + lines = self.cr.fetchall() or None + if lines: + if isinstance(lines, list): + line_ids = [l[0] for l in lines] + else: + line_ids = lines[0] + base = 0 + for line in pooler.get_pool(cr.dbname).get('purchase.order.line').browse(self.cr, self.uid, line_ids): + base += line.price_subtotal + res.append({'code':tax.name, + 'base':base, + 'amount':base*tax.amount}) + return res + def _get_product_code(self, product_id, partner_id): + product_obj=pooler.get_pool(self.cr.dbname).get('product.product') + return product_obj._product_code(self.cr, self.uid, [product_id], name=None, arg=None, context={'partner_id': partner_id})[product_id] report_sxw.report_sxw('report.purchase.order','purchase.order','addons/purchase/report/order.rml',parser=order) diff --git a/addons/purchase/report/request_quotation.py b/addons/purchase/report/request_quotation.py index 5dbd957d309..9a5e8abfde2 100644 --- a/addons/purchase/report/request_quotation.py +++ b/addons/purchase/report/request_quotation.py @@ -34,10 +34,10 @@ from osv import osv import pooler class request_quotation(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(request_quotation, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'user': self.pool.get('res.users').browse(cr, uid, uid, context) - }) + def __init__(self, cr, uid, name, context): + super(request_quotation, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'user': self.pool.get('res.users').browse(cr, uid, uid, context) + }) report_sxw.report_sxw('report.purchase.quotation','purchase.order','addons/purchase/report/request_quotation.rml',parser=request_quotation) diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index 3de0f53c512..c4c3bb5d1ff 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -30,74 +30,74 @@ from osv import osv, fields class stock_move(osv.osv): - _inherit = 'stock.move' - _columns = { - 'purchase_line_id': fields.many2one('purchase.order.line', - 'Purchase Order Line', ondelete='set null', select=True, - readonly=True), - } - _defaults = { - 'purchase_line_id': lambda *a:False - } + _inherit = 'stock.move' + _columns = { + 'purchase_line_id': fields.many2one('purchase.order.line', + 'Purchase Order Line', ondelete='set null', select=True, + readonly=True), + } + _defaults = { + 'purchase_line_id': lambda *a:False + } stock_move() # # Inherit of picking to add the link to the PO # class stock_picking(osv.osv): - _inherit = 'stock.picking' - _columns = { - 'purchase_id': fields.many2one('purchase.order', 'Purchase Order', - ondelete='set null', select=True, readonly=True), - } - _defaults = { - 'purchase_id': lambda *a: False, - } + _inherit = 'stock.picking' + _columns = { + 'purchase_id': fields.many2one('purchase.order', 'Purchase Order', + ondelete='set null', select=True, readonly=True), + } + _defaults = { + 'purchase_id': lambda *a: False, + } - def _get_comment_invoice(self, cursor, user, picking): - if picking.purchase_id and picking.purchase_id.notes: - if picking.note: - return picking.note + '\n' + picking.purchase_id.notes - else: - return picking.purchase_id.notes - return super(stock_picking, self)._get_comment_invoice(cursor, user, - picking) + def _get_comment_invoice(self, cursor, user, picking): + if picking.purchase_id and picking.purchase_id.notes: + if picking.note: + return picking.note + '\n' + picking.purchase_id.notes + else: + return picking.purchase_id.notes + return super(stock_picking, self)._get_comment_invoice(cursor, user, + picking) - def _get_price_unit_invoice(self, cursor, user, move_line, type): - if move_line.purchase_line_id: - return move_line.purchase_line_id.price_unit - return super(stock_picking, self)._get_price_unit_invoice(cursor, - user, move_line, type) + def _get_price_unit_invoice(self, cursor, user, move_line, type): + if move_line.purchase_line_id: + return move_line.purchase_line_id.price_unit + return super(stock_picking, self)._get_price_unit_invoice(cursor, + user, move_line, type) - def _get_discount_invoice(self, cursor, user, move_line): - if move_line.purchase_line_id: - return 0.0 - return super(stock_picking, self)._get_discount_invoice(cursor, user, - move_line) + def _get_discount_invoice(self, cursor, user, move_line): + if move_line.purchase_line_id: + return 0.0 + return super(stock_picking, self)._get_discount_invoice(cursor, user, + move_line) - def _get_taxes_invoice(self, cursor, user, move_line, type): - if move_line.purchase_line_id: - return [x.id for x in move_line.purchase_line_id.taxes_id] - return super(stock_picking, self)._get_taxes_invoice(cursor, user, - move_line, type) + def _get_taxes_invoice(self, cursor, user, move_line, type): + if move_line.purchase_line_id: + return [x.id for x in move_line.purchase_line_id.taxes_id] + return super(stock_picking, self)._get_taxes_invoice(cursor, user, + move_line, type) - def _get_account_analytic_invoice(self, cursor, user, picking, move_line): - if move_line.purchase_line_id: - return move_line.purchase_line_id.account_analytic_id.id - return super(stock_picking, self)._get_account_analytic_invoice(cursor, - user, picking, move_line) + def _get_account_analytic_invoice(self, cursor, user, picking, move_line): + if move_line.purchase_line_id: + return move_line.purchase_line_id.account_analytic_id.id + return super(stock_picking, self)._get_account_analytic_invoice(cursor, + user, picking, move_line) - def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): - return super(stock_picking, self)._invoice_line_hook(cursor, user, - move_line, invoice_line_id) + def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): + return super(stock_picking, self)._invoice_line_hook(cursor, user, + move_line, invoice_line_id) - def _invoice_hook(self, cursor, user, picking, invoice_id): - purchase_obj = self.pool.get('purchase.order') - if picking.purchase_id: - purchase_obj.write(cursor, user, [picking.purchase_id.id], { - 'invoice_id': invoice_id, - }) - return super(stock_picking, self)._invoice_hook(cursor, user, - picking, invoice_id) + def _invoice_hook(self, cursor, user, picking, invoice_id): + purchase_obj = self.pool.get('purchase.order') + if picking.purchase_id: + purchase_obj.write(cursor, user, [picking.purchase_id.id], { + 'invoice_id': invoice_id, + }) + return super(stock_picking, self)._invoice_hook(cursor, user, + picking, invoice_id) stock_picking() diff --git a/addons/purchase/wizard/wizard_group.py b/addons/purchase/wizard/wizard_group.py index b3e5c401150..393a7183b2e 100644 --- a/addons/purchase/wizard/wizard_group.py +++ b/addons/purchase/wizard/wizard_group.py @@ -36,9 +36,9 @@ from osv.orm import browse_record merge_form = """<?xml version="1.0"?> <form string="Merge orders"> - <separator string="Are you sure you want to merge these orders ?"/> - <newline/> - <label string="Please note that orders will only be merged if they are to the same partner and going to the same location, and lines will only be merged if they are exactly the same except for the quantity and unit."/> + <separator string="Are you sure you want to merge these orders ?"/> + <newline/> + <label string="Please note that orders will only be merged if they are to the same partner and going to the same location, and lines will only be merged if they are exactly the same except for the quantity and unit."/> </form> """ @@ -47,115 +47,115 @@ merge_fields = { ack_form = """<?xml version="1.0"?> <form string="Merge orders"> - <separator string="Orders merged"/> + <separator string="Orders merged"/> </form>""" ack_fields = {} def _mergeOrders(self, cr, uid, data, context): - order_obj = pooler.get_pool(cr.dbname).get('purchase.order') + order_obj = pooler.get_pool(cr.dbname).get('purchase.order') - def make_key(br, fields): - list_key = [] - for field in fields: - field_val = getattr(br, field) - if field in ('product_id','move_dest_id', 'account_analytic_id'): - if not field_val: - field_val = False - if isinstance(field_val, browse_record): - field_val = field_val.id - elif isinstance(field_val, list): - field_val = ((6, 0, tuple([v.id for v in field_val])),) - list_key.append((field, field_val)) - list_key.sort() - return tuple(list_key) - - # compute what the new orders should contain - new_orders = {} - for porder in [order for order in order_obj.browse(cr, uid, data['ids']) if order.state == 'draft']: - order_key = make_key(porder, ('partner_id', 'location_id')) - new_order = new_orders.setdefault(order_key, ({}, [])) - new_order[1].append(porder.id) - order_infos = new_order[0] - if not order_infos: - order_infos.update({ - 'origin': porder.origin, - 'date_order': time.strftime('%Y-%m-%d'), - 'partner_id': porder.partner_id.id, - 'partner_address_id': porder.partner_address_id.id, - 'dest_address_id': porder.dest_address_id.id, - 'warehouse_id': porder.warehouse_id.id, - 'location_id': porder.location_id.id, - 'pricelist_id': porder.pricelist_id.id, - 'state': 'draft', - 'order_line': {}, - 'notes': '%s' % (porder.notes or '',), - }) - else: - #order_infos['name'] += ', %s' % porder.name - if porder.notes: - order_infos['notes'] += ('\n%s' % (porder.notes,)) - if porder.origin: - order_infos['origin'] = order_infos['origin'] +' '+ porder.origin + def make_key(br, fields): + list_key = [] + for field in fields: + field_val = getattr(br, field) + if field in ('product_id','move_dest_id', 'account_analytic_id'): + if not field_val: + field_val = False + if isinstance(field_val, browse_record): + field_val = field_val.id + elif isinstance(field_val, list): + field_val = ((6, 0, tuple([v.id for v in field_val])),) + list_key.append((field, field_val)) + list_key.sort() + return tuple(list_key) + + # compute what the new orders should contain + new_orders = {} + for porder in [order for order in order_obj.browse(cr, uid, data['ids']) if order.state == 'draft']: + order_key = make_key(porder, ('partner_id', 'location_id')) + new_order = new_orders.setdefault(order_key, ({}, [])) + new_order[1].append(porder.id) + order_infos = new_order[0] + if not order_infos: + order_infos.update({ + 'origin': porder.origin, + 'date_order': time.strftime('%Y-%m-%d'), + 'partner_id': porder.partner_id.id, + 'partner_address_id': porder.partner_address_id.id, + 'dest_address_id': porder.dest_address_id.id, + 'warehouse_id': porder.warehouse_id.id, + 'location_id': porder.location_id.id, + 'pricelist_id': porder.pricelist_id.id, + 'state': 'draft', + 'order_line': {}, + 'notes': '%s' % (porder.notes or '',), + }) + else: + #order_infos['name'] += ', %s' % porder.name + if porder.notes: + order_infos['notes'] += ('\n%s' % (porder.notes,)) + if porder.origin: + order_infos['origin'] = order_infos['origin'] +' '+ porder.origin - for order_line in porder.order_line: - line_key = make_key(order_line, ('name', 'date_planned', 'taxes_id', 'price_unit', 'notes', 'product_id', 'move_dest_id', 'account_analytic_id')) - o_line = order_infos['order_line'].setdefault(line_key, {}) - if o_line: - # merge the line with an existing line - o_line['product_qty'] += order_line.product_qty * order_line.product_uom.factor / o_line['uom_factor'] - else: - # append a new "standalone" line - for field in ('product_qty', 'product_uom'): - field_val = getattr(order_line, field) - if isinstance(field_val, browse_record): - field_val = field_val.id - o_line[field] = field_val - o_line['uom_factor'] = order_line.product_uom and order_line.product_uom.factor or 1.0 + for order_line in porder.order_line: + line_key = make_key(order_line, ('name', 'date_planned', 'taxes_id', 'price_unit', 'notes', 'product_id', 'move_dest_id', 'account_analytic_id')) + o_line = order_infos['order_line'].setdefault(line_key, {}) + if o_line: + # merge the line with an existing line + o_line['product_qty'] += order_line.product_qty * order_line.product_uom.factor / o_line['uom_factor'] + else: + # append a new "standalone" line + for field in ('product_qty', 'product_uom'): + field_val = getattr(order_line, field) + if isinstance(field_val, browse_record): + field_val = field_val.id + o_line[field] = field_val + o_line['uom_factor'] = order_line.product_uom and order_line.product_uom.factor or 1.0 - wf_service = netsvc.LocalService("workflow") + wf_service = netsvc.LocalService("workflow") - allorders = [] - for order_key, (order_data, old_ids) in new_orders.iteritems(): - # skip merges with only one order - if len(old_ids) < 2: - allorders += (old_ids or []) - continue + allorders = [] + for order_key, (order_data, old_ids) in new_orders.iteritems(): + # skip merges with only one order + if len(old_ids) < 2: + allorders += (old_ids or []) + continue - # cleanup order line data - for key, value in order_data['order_line'].iteritems(): - del value['uom_factor'] - value.update(dict(key)) - order_data['order_line'] = [(0, 0, value) for value in order_data['order_line'].itervalues()] - - # create the new order - neworder_id = order_obj.create(cr, uid, order_data) - allorders.append(neworder_id) + # cleanup order line data + for key, value in order_data['order_line'].iteritems(): + del value['uom_factor'] + value.update(dict(key)) + order_data['order_line'] = [(0, 0, value) for value in order_data['order_line'].itervalues()] + + # create the new order + neworder_id = order_obj.create(cr, uid, order_data) + allorders.append(neworder_id) - # make triggers pointing to the old orders point to the new order - for old_id in old_ids: - wf_service.trg_redirect(uid, 'purchase.order', old_id, neworder_id, cr) - wf_service.trg_validate(uid, 'purchase.order', old_id, 'purchase_cancel', cr) + # make triggers pointing to the old orders point to the new order + for old_id in old_ids: + wf_service.trg_redirect(uid, 'purchase.order', old_id, neworder_id, cr) + wf_service.trg_validate(uid, 'purchase.order', old_id, 'purchase_cancel', cr) - return { - 'domain': "[('id','in', ["+','.join(map(str,allorders))+"])]", - 'name': 'Purchase orders', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'purchase.order', - 'view_id': False, - 'type': 'ir.actions.act_window' - } + return { + 'domain': "[('id','in', ["+','.join(map(str,allorders))+"])]", + 'name': 'Purchase orders', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'purchase.order', + 'view_id': False, + 'type': 'ir.actions.act_window' + } class merge_orders(wizard.interface): - states = { - 'init' : { - 'actions' : [], - 'result' : {'type' : 'form', 'arch' : merge_form, 'fields' : merge_fields, 'state' : [('end', 'Cancel'),('merge', 'Merge orders') ]} - }, - 'merge' : { - 'actions' : [], - 'result' : {'type':'action', 'action':_mergeOrders, 'state':'end'} - }, - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type' : 'form', 'arch' : merge_form, 'fields' : merge_fields, 'state' : [('end', 'Cancel'),('merge', 'Merge orders') ]} + }, + 'merge' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_mergeOrders, 'state':'end'} + }, + } merge_orders("purchase.order.merge") diff --git a/addons/report_account/__terp__.py b/addons/report_account/__terp__.py index a6354b8e33a..8932619cb03 100644 --- a/addons/report_account/__terp__.py +++ b/addons/report_account/__terp__.py @@ -26,16 +26,16 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Account Reporting - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["account"], - "category" : "Generic Modules/Accounting", - "description": "A module that adds new reports based on the account module.", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_receivable_view.xml"], - "active": False, - "installable": True + "name" : "Account Reporting - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["account"], + "category" : "Generic Modules/Accounting", + "description": "A module that adds new reports based on the account module.", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_receivable_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_account/report_receivable.py b/addons/report_account/report_receivable.py index 5958e99e69a..0313185175c 100644 --- a/addons/report_account/report_receivable.py +++ b/addons/report_account/report_receivable.py @@ -30,44 +30,44 @@ from osv import fields,osv def _code_get(self, cr, uid, context={}): - acc_type_obj = self.pool.get('account.account.type') - ids = acc_type_obj.search(cr, uid, []) - res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) - return [(r['code'], r['name']) for r in res] + acc_type_obj = self.pool.get('account.account.type') + ids = acc_type_obj.search(cr, uid, []) + res = acc_type_obj.read(cr, uid, ids, ['code', 'name'], context) + return [(r['code'], r['name']) for r in res] class report_account_receivable(osv.osv): - _name = "report.account.receivable" - _description = "Receivable accounts" - _auto = False - _columns = { - 'name': fields.char('Week of Year', size=7, readonly=True), - 'type': fields.selection(_code_get, 'Account Type', required=True), - 'balance':fields.float('Balance', readonly=True), - 'debit':fields.float('Debit', readonly=True), - 'credit':fields.float('Credit', readonly=True), - } - _order = 'name desc' - def init(self, cr): - cr.execute(""" - create or replace view report_account_receivable as ( - select - min(l.id) as id, - to_char(date,'YYYY:IW') as name, - sum(l.debit-l.credit) as balance, - sum(l.debit) as debit, - sum(l.credit) as credit, - a.type - from - account_move_line l - left join - account_account a on (l.account_id=a.id) - where - l.state <> 'draft' - group by - to_char(date,'YYYY:IW'), a.type - )""") + _name = "report.account.receivable" + _description = "Receivable accounts" + _auto = False + _columns = { + 'name': fields.char('Week of Year', size=7, readonly=True), + 'type': fields.selection(_code_get, 'Account Type', required=True), + 'balance':fields.float('Balance', readonly=True), + 'debit':fields.float('Debit', readonly=True), + 'credit':fields.float('Credit', readonly=True), + } + _order = 'name desc' + def init(self, cr): + cr.execute(""" + create or replace view report_account_receivable as ( + select + min(l.id) as id, + to_char(date,'YYYY:IW') as name, + sum(l.debit-l.credit) as balance, + sum(l.debit) as debit, + sum(l.credit) as credit, + a.type + from + account_move_line l + left join + account_account a on (l.account_id=a.id) + where + l.state <> 'draft' + group by + to_char(date,'YYYY:IW'), a.type + )""") report_account_receivable() - #a.type in ('receivable','payable') + #a.type in ('receivable','payable') diff --git a/addons/report_analytic/__terp__.py b/addons/report_analytic/__terp__.py index ee018c4125f..ded29819546 100644 --- a/addons/report_analytic/__terp__.py +++ b/addons/report_analytic/__terp__.py @@ -26,16 +26,16 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Analytic Account Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["account","hr_timesheet_invoice"], - "category" : "Generic Modules/Accounting", - "description": "A module that adds new reports based on analytic accounts.", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_analytic_view.xml"], - "active": False, - "installable": True + "name" : "Analytic Account Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["account","hr_timesheet_invoice"], + "category" : "Generic Modules/Accounting", + "description": "A module that adds new reports based on analytic accounts.", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_analytic_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_analytic/report_analytic.py b/addons/report_analytic/report_analytic.py index f40fb504dee..f0bd5ba5f13 100644 --- a/addons/report_analytic/report_analytic.py +++ b/addons/report_analytic/report_analytic.py @@ -30,38 +30,38 @@ from osv import fields,osv class report_analytic_account_close(osv.osv): - _name = "report.analytic.account.close" - _description = "Analytic account to close" - _auto = False - _columns = { - 'name': fields.many2one('account.analytic.account', 'Analytic account', readonly=True), - 'state': fields.char('State', size=32, readonly=True), - 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), - 'quantity': fields.float('Quantity', readonly=True), - 'quantity_max': fields.float('Max. Quantity', readonly=True), - 'balance': fields.float('Balance', readonly=True), - 'date_deadline': fields.date('Deadline', readonly=True), - } - def init(self, cr): - cr.execute(""" - create or replace view report_analytic_account_close as ( - select - a.id as id, - a.id as name, - a.state as state, - sum(l.unit_amount) as quantity, - sum(l.amount) as balance, - a.partner_id as partner_id, - a.quantity_max as quantity_max, - a.date as date_deadline - from - account_analytic_line l - right join - account_analytic_account a on (l.account_id=a.id) - group by - a.id,a.state, a.quantity_max,a.date,a.partner_id - having - (a.quantity_max>0 and (sum(l.unit_amount)>=a.quantity_max)) or - a.date <= current_date - )""") + _name = "report.analytic.account.close" + _description = "Analytic account to close" + _auto = False + _columns = { + 'name': fields.many2one('account.analytic.account', 'Analytic account', readonly=True), + 'state': fields.char('State', size=32, readonly=True), + 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), + 'quantity': fields.float('Quantity', readonly=True), + 'quantity_max': fields.float('Max. Quantity', readonly=True), + 'balance': fields.float('Balance', readonly=True), + 'date_deadline': fields.date('Deadline', readonly=True), + } + def init(self, cr): + cr.execute(""" + create or replace view report_analytic_account_close as ( + select + a.id as id, + a.id as name, + a.state as state, + sum(l.unit_amount) as quantity, + sum(l.amount) as balance, + a.partner_id as partner_id, + a.quantity_max as quantity_max, + a.date as date_deadline + from + account_analytic_line l + right join + account_analytic_account a on (l.account_id=a.id) + group by + a.id,a.state, a.quantity_max,a.date,a.partner_id + having + (a.quantity_max>0 and (sum(l.unit_amount)>=a.quantity_max)) or + a.date <= current_date + )""") report_analytic_account_close() diff --git a/addons/report_analytic_line/__terp__.py b/addons/report_analytic_line/__terp__.py index 9686e65eef3..2a0ecea7241 100644 --- a/addons/report_analytic_line/__terp__.py +++ b/addons/report_analytic_line/__terp__.py @@ -26,19 +26,19 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Analytic lines - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["account", "hr_timesheet_invoice"], - "category" : "Generic Modules/Accounting", - "description": "A report on analytic lines, costs by products, months and accounts.", - "init_xml" : [], - "demo_xml" : [ - ], - "update_xml" : [ - "report_analytic_line_view.xml", - ], - "active": False, - "installable": True + "name" : "Analytic lines - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["account", "hr_timesheet_invoice"], + "category" : "Generic Modules/Accounting", + "description": "A report on analytic lines, costs by products, months and accounts.", + "init_xml" : [], + "demo_xml" : [ + ], + "update_xml" : [ + "report_analytic_line_view.xml", + ], + "active": False, + "installable": True } diff --git a/addons/report_analytic_planning/__terp__.py b/addons/report_analytic_planning/__terp__.py index 116f89f4c0f..a48d2490f5f 100644 --- a/addons/report_analytic_planning/__terp__.py +++ b/addons/report_analytic_planning/__terp__.py @@ -26,21 +26,21 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Analytic planning - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["account", "hr_timesheet_invoice","project","report_analytic_line"], - "category" : "Generic Modules/Accounting", - "description": "Planning on analytic accounts.", - "init_xml" : [], - "demo_xml" : [ - "report_account_analytic.planning.csv" - ], - "update_xml" : [ - "report_analytic_planning_view.xml", - "report_analytic_planning_report.xml" - ], - "active": False, - "installable": True + "name" : "Analytic planning - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["account", "hr_timesheet_invoice","project","report_analytic_line"], + "category" : "Generic Modules/Accounting", + "description": "Planning on analytic accounts.", + "init_xml" : [], + "demo_xml" : [ + "report_account_analytic.planning.csv" + ], + "update_xml" : [ + "report_analytic_planning_view.xml", + "report_analytic_planning_report.xml" + ], + "active": False, + "installable": True } diff --git a/addons/report_analytic_planning/report_analytic_planning.py b/addons/report_analytic_planning/report_analytic_planning.py index 32c83ee0bd0..8de8cc02af6 100644 --- a/addons/report_analytic_planning/report_analytic_planning.py +++ b/addons/report_analytic_planning/report_analytic_planning.py @@ -33,182 +33,182 @@ import time import mx.DateTime class report_account_analytic_planning(osv.osv): - _name = "report_account_analytic.planning" - _description = "Planning" - _columns = { - 'name': fields.char('Planning Name', size=32, required=True), - 'user_id': fields.many2one('res.users', 'Responsible', required=True), - 'date_from':fields.date('Start Date', required=True), - 'date_to':fields.date('End Date', required=True), - 'line_ids': fields.one2many('report_account_analytic.planning.line', 'planning_id', 'Planning lines'), - 'stat_ids': fields.one2many('report_account_analytic.planning.stat', 'planning_id', 'Planning analysis', readonly=True), - 'stat_user_ids': fields.one2many('report_account_analytic.planning.stat.user', 'planning_id', 'Planning by user', readonly=True), - 'stat_account_ids': fields.one2many('report_account_analytic.planning.stat.account', 'planning_id', 'Planning by account', readonly=True), - 'state': fields.selection([('open','Open'),('done','Done')], 'State', required=True) - } - _defaults = { - 'name': lambda *a: time.strftime('%Y-%m-%d'), - 'date_from': lambda *a: time.strftime('%Y-%m-01'), - 'date_to': lambda *a: (mx.DateTime.now()+mx.DateTime.RelativeDateTime(months=1,day=1,days=-1)).strftime('%Y-%m-%d'), - 'user_id': lambda self,cr,uid,c: uid, - 'state': lambda *args: 'open' - } - _order = 'date_from desc' + _name = "report_account_analytic.planning" + _description = "Planning" + _columns = { + 'name': fields.char('Planning Name', size=32, required=True), + 'user_id': fields.many2one('res.users', 'Responsible', required=True), + 'date_from':fields.date('Start Date', required=True), + 'date_to':fields.date('End Date', required=True), + 'line_ids': fields.one2many('report_account_analytic.planning.line', 'planning_id', 'Planning lines'), + 'stat_ids': fields.one2many('report_account_analytic.planning.stat', 'planning_id', 'Planning analysis', readonly=True), + 'stat_user_ids': fields.one2many('report_account_analytic.planning.stat.user', 'planning_id', 'Planning by user', readonly=True), + 'stat_account_ids': fields.one2many('report_account_analytic.planning.stat.account', 'planning_id', 'Planning by account', readonly=True), + 'state': fields.selection([('open','Open'),('done','Done')], 'State', required=True) + } + _defaults = { + 'name': lambda *a: time.strftime('%Y-%m-%d'), + 'date_from': lambda *a: time.strftime('%Y-%m-01'), + 'date_to': lambda *a: (mx.DateTime.now()+mx.DateTime.RelativeDateTime(months=1,day=1,days=-1)).strftime('%Y-%m-%d'), + 'user_id': lambda self,cr,uid,c: uid, + 'state': lambda *args: 'open' + } + _order = 'date_from desc' report_account_analytic_planning() class report_account_analytic_planning_line(osv.osv): - _name = "report_account_analytic.planning.line" - _description = "Planning Line" - _rec_name = 'user_id' - _columns = { - 'account_id':fields.many2one('account.analytic.account', 'Analytic account', required=True), - 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning', required=True, ondelete='cascade'), - 'user_id': fields.many2one('res.users', 'User'), - 'amount': fields.float('Quantity', required=True), - 'amount_unit':fields.many2one('product.uom', 'Qty UoM', required=True), - 'note':fields.text('Note', size=64), - } - _order = 'user_id,account_id' + _name = "report_account_analytic.planning.line" + _description = "Planning Line" + _rec_name = 'user_id' + _columns = { + 'account_id':fields.many2one('account.analytic.account', 'Analytic account', required=True), + 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning', required=True, ondelete='cascade'), + 'user_id': fields.many2one('res.users', 'User'), + 'amount': fields.float('Quantity', required=True), + 'amount_unit':fields.many2one('product.uom', 'Qty UoM', required=True), + 'note':fields.text('Note', size=64), + } + _order = 'user_id,account_id' report_account_analytic_planning_line() class report_account_analytic_planning_stat_account(osv.osv): - _name = "report_account_analytic.planning.stat.account" - _description = "Planning account stat" - _rec_name = 'account_id' - _auto = False - _log_access = False - def _sum_amount_real(self, cr, uid, ids, name, args, context): - result = {} - for line in self.browse(cr, uid, ids, context): - cr.execute('select sum(unit_amount) from account_analytic_line where account_id=%d and date>=%s and date<=%s', (line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) - result[line.id] = cr.fetchone()[0] - return result - _columns = { - 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning'), - 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True), - 'quantity': fields.float('Planned', required=True), - 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), - } - def init(self, cr): - cr.execute(""" - create or replace view report_account_analytic_planning_stat_account as ( - select - min(l.id) as id, - l.account_id as account_id, - sum(l.amount*u.factor) as quantity, - l.planning_id - from - report_account_analytic_planning_line l - left join - product_uom u on (l.amount_unit = u.id) - group by - planning_id, account_id - ) - """) + _name = "report_account_analytic.planning.stat.account" + _description = "Planning account stat" + _rec_name = 'account_id' + _auto = False + _log_access = False + def _sum_amount_real(self, cr, uid, ids, name, args, context): + result = {} + for line in self.browse(cr, uid, ids, context): + cr.execute('select sum(unit_amount) from account_analytic_line where account_id=%d and date>=%s and date<=%s', (line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) + result[line.id] = cr.fetchone()[0] + return result + _columns = { + 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning'), + 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True), + 'quantity': fields.float('Planned', required=True), + 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), + } + def init(self, cr): + cr.execute(""" + create or replace view report_account_analytic_planning_stat_account as ( + select + min(l.id) as id, + l.account_id as account_id, + sum(l.amount*u.factor) as quantity, + l.planning_id + from + report_account_analytic_planning_line l + left join + product_uom u on (l.amount_unit = u.id) + group by + planning_id, account_id + ) + """) report_account_analytic_planning_stat_account() class report_account_analytic_planning_stat(osv.osv): - _name = "report_account_analytic.planning.stat" - _description = "Planning stat" - _rec_name = 'user_id' - _auto = False - _log_access = False - def _sum_amount_real(self, cr, uid, ids, name, args, context): - result = {} - for line in self.browse(cr, uid, ids, context): - if line.user_id: - cr.execute('select sum(unit_amount) from account_analytic_line where user_id=%d and account_id=%d and date>=%s and date<=%s', (line.user_id.id,line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) - else: - cr.execute('select sum(unit_amount) from account_analytic_line where account_id=%d and date>=%s and date<=%s', (line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) - result[line.id] = cr.fetchone()[0] - return result - def _sum_amount_tasks(self, cr, uid, ids, name, args, context): - result = {} - for line in self.browse(cr, uid, ids, context): - where = '' - if line.user_id: - where='user_id='+str(line.user_id.id)+' and ' - cr.execute('''select - sum(planned_hours) - from - project_task - where - '''+where+''' - project_id in (select id from project_project where category_id=%d) and - date_close>=%s and - date_close<=%s''', ( - line.account_id.id, - line.planning_id.date_from, - line.planning_id.date_to) - ) - result[line.id] = cr.fetchone()[0] - return result - _columns = { - 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning'), - 'user_id': fields.many2one('res.users', 'User'), - 'manager_id': fields.many2one('res.users', 'Manager'), - 'account_id': fields.many2one('account.analytic.account', 'Account', required=True), - 'sum_amount': fields.float('Planned hours', required=True), - 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), - 'sum_amount_tasks': fields.function(_sum_amount_tasks, method=True, string='Tasks'), - } - _order = 'planning_id,user_id' - def init(self, cr): - cr.execute(""" - create or replace view report_account_analytic_planning_stat as ( - select - min(l.id) as id, - l.user_id as user_id, - a.user_id as manager_id, - l.account_id as account_id, - sum(l.amount*u.factor) as sum_amount, - l.planning_id - from - report_account_analytic_planning_line l - left join - report_account_analytic_planning a on (a.id = l.planning_id) - left join - product_uom u on (l.amount_unit = u.id) - group by - l.planning_id, l.user_id, l.account_id, a.user_id - ) - """) + _name = "report_account_analytic.planning.stat" + _description = "Planning stat" + _rec_name = 'user_id' + _auto = False + _log_access = False + def _sum_amount_real(self, cr, uid, ids, name, args, context): + result = {} + for line in self.browse(cr, uid, ids, context): + if line.user_id: + cr.execute('select sum(unit_amount) from account_analytic_line where user_id=%d and account_id=%d and date>=%s and date<=%s', (line.user_id.id,line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) + else: + cr.execute('select sum(unit_amount) from account_analytic_line where account_id=%d and date>=%s and date<=%s', (line.account_id.id,line.planning_id.date_from,line.planning_id.date_to)) + result[line.id] = cr.fetchone()[0] + return result + def _sum_amount_tasks(self, cr, uid, ids, name, args, context): + result = {} + for line in self.browse(cr, uid, ids, context): + where = '' + if line.user_id: + where='user_id='+str(line.user_id.id)+' and ' + cr.execute('''select + sum(planned_hours) + from + project_task + where + '''+where+''' + project_id in (select id from project_project where category_id=%d) and + date_close>=%s and + date_close<=%s''', ( + line.account_id.id, + line.planning_id.date_from, + line.planning_id.date_to) + ) + result[line.id] = cr.fetchone()[0] + return result + _columns = { + 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning'), + 'user_id': fields.many2one('res.users', 'User'), + 'manager_id': fields.many2one('res.users', 'Manager'), + 'account_id': fields.many2one('account.analytic.account', 'Account', required=True), + 'sum_amount': fields.float('Planned hours', required=True), + 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), + 'sum_amount_tasks': fields.function(_sum_amount_tasks, method=True, string='Tasks'), + } + _order = 'planning_id,user_id' + def init(self, cr): + cr.execute(""" + create or replace view report_account_analytic_planning_stat as ( + select + min(l.id) as id, + l.user_id as user_id, + a.user_id as manager_id, + l.account_id as account_id, + sum(l.amount*u.factor) as sum_amount, + l.planning_id + from + report_account_analytic_planning_line l + left join + report_account_analytic_planning a on (a.id = l.planning_id) + left join + product_uom u on (l.amount_unit = u.id) + group by + l.planning_id, l.user_id, l.account_id, a.user_id + ) + """) report_account_analytic_planning_stat() class report_account_analytic_planning_stat_user(osv.osv): - _name = "report_account_analytic.planning.stat.user" - _description = "Planning user stat" - _rec_name = 'user_id' - _auto = False - _log_access = False - def _sum_amount_real(self, cr, uid, ids, name, args, context): - result = {} - for line in self.browse(cr, uid, ids, context): - result[line.id] = 0.0 - if line.user_id: - cr.execute('select sum(unit_amount) from account_analytic_line where user_id=%d and date>=%s and date<=%s', (line.user_id.id,line.planning_id.date_from,line.planning_id.date_to)) - result[line.id] = cr.fetchone()[0] - return result - _columns = { - 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning', required=True), - 'user_id': fields.many2one('res.users', 'User'), - 'quantity': fields.float('Planned', required=True), - 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), - } - def init(self, cr): - cr.execute(""" - create or replace view report_account_analytic_planning_stat_user as ( - select - min(l.id) as id, - l.user_id as user_id, - sum(l.amount*u.factor) as quantity, - l.planning_id - from - report_account_analytic_planning_line l - left join - product_uom u on (l.amount_unit = u.id) - group by - planning_id, user_id - ) - """) + _name = "report_account_analytic.planning.stat.user" + _description = "Planning user stat" + _rec_name = 'user_id' + _auto = False + _log_access = False + def _sum_amount_real(self, cr, uid, ids, name, args, context): + result = {} + for line in self.browse(cr, uid, ids, context): + result[line.id] = 0.0 + if line.user_id: + cr.execute('select sum(unit_amount) from account_analytic_line where user_id=%d and date>=%s and date<=%s', (line.user_id.id,line.planning_id.date_from,line.planning_id.date_to)) + result[line.id] = cr.fetchone()[0] + return result + _columns = { + 'planning_id': fields.many2one('report_account_analytic.planning', 'Planning', required=True), + 'user_id': fields.many2one('res.users', 'User'), + 'quantity': fields.float('Planned', required=True), + 'sum_amount_real': fields.function(_sum_amount_real, method=True, string='Timesheet'), + } + def init(self, cr): + cr.execute(""" + create or replace view report_account_analytic_planning_stat_user as ( + select + min(l.id) as id, + l.user_id as user_id, + sum(l.amount*u.factor) as quantity, + l.planning_id + from + report_account_analytic_planning_line l + left join + product_uom u on (l.amount_unit = u.id) + group by + planning_id, user_id + ) + """) report_account_analytic_planning_stat_user() diff --git a/addons/report_crm/__terp__.py b/addons/report_crm/__terp__.py index c43cf82cd21..c9b0b830344 100644 --- a/addons/report_crm/__terp__.py +++ b/addons/report_crm/__terp__.py @@ -26,16 +26,16 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "CRM Management - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_crm.html", - "depends" : ["crm"], - "category" : "Generic Modules/CRM & SRM", - "description": "A module that adds new reports based on CRM cases.", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_crm_view.xml"], - "active": False, - "installable": True + "name" : "CRM Management - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_crm.html", + "depends" : ["crm"], + "category" : "Generic Modules/CRM & SRM", + "description": "A module that adds new reports based on CRM cases.", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_crm_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_crm/report_crm.py b/addons/report_crm/report_crm.py index 826adf35adb..5cb9314ba14 100644 --- a/addons/report_crm/report_crm.py +++ b/addons/report_crm/report_crm.py @@ -30,85 +30,85 @@ from osv import fields,osv AVAILABLE_STATES = [ - ('draft','Draft'), - ('open','Open'), - ('cancel', 'Canceled'), - ('done', 'Closed'), - ('pending','Pending') + ('draft','Draft'), + ('open','Open'), + ('cancel', 'Canceled'), + ('done', 'Closed'), + ('pending','Pending') ] class report_crm_case_user(osv.osv): - _name = "report.crm.case.user" - _description = "Cases by user and section" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'section_id':fields.many2one('crm.case.section', 'Section', readonly=True), - 'amount_revenue': fields.float('Est.Revenue', readonly=True), - 'amount_costs': fields.float('Est.Cost', readonly=True), - 'amount_revenue_prob': fields.float('Est. Rev*Prob.', readonly=True), - 'nbr': fields.integer('# of Cases', readonly=True), - 'probability': fields.float('Avg. Probability', readonly=True), - 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), - 'delay_close': fields.char('Delay to close', size=20, readonly=True), - } - _order = 'name desc, user_id, section_id' - def init(self, cr): - cr.execute(""" - create or replace view report_crm_case_user as ( - select - min(c.id) as id, - to_char(c.create_date, 'YYYY-MM-01') as name, - c.state, - c.user_id, - c.section_id, - count(*) as nbr, - sum(planned_revenue) as amount_revenue, - sum(planned_cost) as amount_costs, - sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, - avg(probability)::decimal(16,2) as probability, - to_char(avg(date_closed-c.create_date), 'DD"d" HH24:MI:SS') as delay_close - from - crm_case c - group by to_char(c.create_date, 'YYYY-MM-01'), c.state, c.user_id, c.section_id - )""") + _name = "report.crm.case.user" + _description = "Cases by user and section" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'section_id':fields.many2one('crm.case.section', 'Section', readonly=True), + 'amount_revenue': fields.float('Est.Revenue', readonly=True), + 'amount_costs': fields.float('Est.Cost', readonly=True), + 'amount_revenue_prob': fields.float('Est. Rev*Prob.', readonly=True), + 'nbr': fields.integer('# of Cases', readonly=True), + 'probability': fields.float('Avg. Probability', readonly=True), + 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), + 'delay_close': fields.char('Delay to close', size=20, readonly=True), + } + _order = 'name desc, user_id, section_id' + def init(self, cr): + cr.execute(""" + create or replace view report_crm_case_user as ( + select + min(c.id) as id, + to_char(c.create_date, 'YYYY-MM-01') as name, + c.state, + c.user_id, + c.section_id, + count(*) as nbr, + sum(planned_revenue) as amount_revenue, + sum(planned_cost) as amount_costs, + sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, + avg(probability)::decimal(16,2) as probability, + to_char(avg(date_closed-c.create_date), 'DD"d" HH24:MI:SS') as delay_close + from + crm_case c + group by to_char(c.create_date, 'YYYY-MM-01'), c.state, c.user_id, c.section_id + )""") report_crm_case_user() class report_crm_case_categ(osv.osv): - _name = "report.crm.case.categ" - _description = "Cases by section and category" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'categ_id':fields.many2one('crm.case.categ', 'Category', readonly=True), - 'section_id':fields.many2one('crm.case.section', 'Section', readonly=True), - 'amount_revenue': fields.float('Est.Revenue', readonly=True), - 'amount_costs': fields.float('Est.Cost', readonly=True), - 'amount_revenue_prob': fields.float('Est. Rev*Prob.', readonly=True), - 'nbr': fields.integer('# of Cases', readonly=True), - 'probability': fields.float('Avg. Probability', readonly=True), - 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), - 'delay_close': fields.char('Delay Close', size=20, readonly=True), - } - _order = 'name desc, categ_id, section_id' - def init(self, cr): - cr.execute(""" - create or replace view report_crm_case_categ as ( - select - min(c.id) as id, - to_char(c.create_date, 'YYYY-MM-01') as name, - c.state, - c.categ_id, - c.section_id, - count(*) as nbr, - sum(planned_revenue) as amount_revenue, - sum(planned_cost) as amount_costs, - sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, - avg(probability)::decimal(16,2) as probability, - to_char(avg(date_closed-c.create_date), 'DD"d" HH24:MI:SS') as delay_close - from - crm_case c - group by to_char(c.create_date, 'YYYY-MM-01'), c.state, c.categ_id, c.section_id - )""") + _name = "report.crm.case.categ" + _description = "Cases by section and category" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'categ_id':fields.many2one('crm.case.categ', 'Category', readonly=True), + 'section_id':fields.many2one('crm.case.section', 'Section', readonly=True), + 'amount_revenue': fields.float('Est.Revenue', readonly=True), + 'amount_costs': fields.float('Est.Cost', readonly=True), + 'amount_revenue_prob': fields.float('Est. Rev*Prob.', readonly=True), + 'nbr': fields.integer('# of Cases', readonly=True), + 'probability': fields.float('Avg. Probability', readonly=True), + 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True), + 'delay_close': fields.char('Delay Close', size=20, readonly=True), + } + _order = 'name desc, categ_id, section_id' + def init(self, cr): + cr.execute(""" + create or replace view report_crm_case_categ as ( + select + min(c.id) as id, + to_char(c.create_date, 'YYYY-MM-01') as name, + c.state, + c.categ_id, + c.section_id, + count(*) as nbr, + sum(planned_revenue) as amount_revenue, + sum(planned_cost) as amount_costs, + sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, + avg(probability)::decimal(16,2) as probability, + to_char(avg(date_closed-c.create_date), 'DD"d" HH24:MI:SS') as delay_close + from + crm_case c + group by to_char(c.create_date, 'YYYY-MM-01'), c.state, c.categ_id, c.section_id + )""") report_crm_case_categ() diff --git a/addons/report_intrastat/__terp__.py b/addons/report_intrastat/__terp__.py index dd73d3721d6..739215f7ea1 100644 --- a/addons/report_intrastat/__terp__.py +++ b/addons/report_intrastat/__terp__.py @@ -26,16 +26,16 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Intrastat Reporting - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["base", "product", "stock", "sale", "purchase"], - "category" : "Generic Modules/Inventory Control", - "description": "A module that adds intrastat reports.", - "init_xml" : ["report_intrastat_data.xml",], - "demo_xml" : [], - "update_xml" : ["report_intrastat_view.xml",], - "active": False, - "installable": True + "name" : "Intrastat Reporting - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["base", "product", "stock", "sale", "purchase"], + "category" : "Generic Modules/Inventory Control", + "description": "A module that adds intrastat reports.", + "init_xml" : ["report_intrastat_data.xml",], + "demo_xml" : [], + "update_xml" : ["report_intrastat_view.xml",], + "active": False, + "installable": True } diff --git a/addons/report_intrastat/report_intrastat.py b/addons/report_intrastat/report_intrastat.py index 0327083817a..22384f614ed 100644 --- a/addons/report_intrastat/report_intrastat.py +++ b/addons/report_intrastat/report_intrastat.py @@ -30,99 +30,99 @@ from osv import osv, fields class res_country(osv.osv): - _name = 'res.country' - _inherit = 'res.country' - _columns = { - 'intrastat': fields.boolean('Intrastat member'), - } - _defaults = { - 'intrastat': lambda *a: False, - } + _name = 'res.country' + _inherit = 'res.country' + _columns = { + 'intrastat': fields.boolean('Intrastat member'), + } + _defaults = { + 'intrastat': lambda *a: False, + } res_country() class report_intrastat_code(osv.osv): - _name = "report.intrastat.code" - _description = "Intrastat code" - _columns = { - 'name': fields.char('Intrastat Code', size=16), - 'description': fields.char('Description', size=64), - } + _name = "report.intrastat.code" + _description = "Intrastat code" + _columns = { + 'name': fields.char('Intrastat Code', size=16), + 'description': fields.char('Description', size=64), + } report_intrastat_code() class product_template(osv.osv): - _name = "product.template" - _inherit = "product.template" - _columns = { - 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code'), - } + _name = "product.template" + _inherit = "product.template" + _columns = { + 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code'), + } product_template() class report_intrastat(osv.osv): - _name = "report.intrastat" - _description = "Intrastat report" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'code': fields.char('Country code', size="2", readonly=True), - 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code', readonly=True), - 'weight': fields.float('Weight', readonly=True), - 'value': fields.float('Value', readonly=True), - 'type': fields.selection([('import', 'Import'), ('export', 'Export')], 'Type'), - 'currency_id': fields.many2one('res.currency', "Currency", readonly=True), - } - def init(self, cr): - cr.execute(""" - create or replace view report_intrastat as ( - select - to_char(m.create_date, 'YYYY-MM-01') as name, - min(m.id) as id, - pt.intrastat_id as intrastat_id, - case when l.usage in ('supplier', 'customer') then upper(pc.code) else upper(c.code) end as code, - sum(case when pol.price_unit is not null - then pol.price_unit * m.product_qty - else - case when sol.price_unit is not null - then sol.price_unit * m.product_qty - else 0 - end - end) as value, - sum(pt.weight_net * m.product_qty) as weight, - case when l.usage in ('supplier', 'customer') then 'import' else 'export' end as type, - case when ppl.currency_id is not null - then ppl.currency_id - else spl.currency_id - end as currency_id - from - stock_move m - left join (product_template pt - left join product_product pp on (pp.product_tmpl_id = pt.id)) - on (m.product_id = pt.id) - left join (res_partner_address a - left join res_country c on (c.id = a.country_id)) - on (a.id = m.address_id) - left join (stock_picking sp - left join (res_partner_address pa - left join res_country pc on (pc.id = pa.country_id)) - on (pa.id = sp.address_id)) - on (sp.id = m.picking_id) - left join stock_location l on (l.id = m.location_id) - left join stock_location dl on (dl.id = m.location_dest_id) - left join (purchase_order_line pol - left join (purchase_order po - left join product_pricelist ppl on (ppl.id = po.pricelist_id)) - on (po.id = pol.order_id)) - on (pol.id = m.purchase_line_id) - left join (sale_order_line sol - left join (sale_order so - left join product_pricelist spl on (spl.id = so.pricelist_id)) - on (so.id = sol.order_id)) - on (sol.id = m.sale_line_id) - where - m.state != 'draft' - and ((l.usage in ('supplier', 'customer') and dl.usage not in ('supplier', 'customer')) - or (dl.usage in ('supplier', 'customer') and l.usage not in ('supplier', 'customer'))) - and (c.intrastat is not null or pc.intrastat is not null) - group by to_char(m.create_date, 'YYYY-MM-01'), pt.intrastat_id, c.code, pc.code, l.usage, dl.usage, ppl.currency_id, spl.currency_id - )""") + _name = "report.intrastat" + _description = "Intrastat report" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'code': fields.char('Country code', size="2", readonly=True), + 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code', readonly=True), + 'weight': fields.float('Weight', readonly=True), + 'value': fields.float('Value', readonly=True), + 'type': fields.selection([('import', 'Import'), ('export', 'Export')], 'Type'), + 'currency_id': fields.many2one('res.currency', "Currency", readonly=True), + } + def init(self, cr): + cr.execute(""" + create or replace view report_intrastat as ( + select + to_char(m.create_date, 'YYYY-MM-01') as name, + min(m.id) as id, + pt.intrastat_id as intrastat_id, + case when l.usage in ('supplier', 'customer') then upper(pc.code) else upper(c.code) end as code, + sum(case when pol.price_unit is not null + then pol.price_unit * m.product_qty + else + case when sol.price_unit is not null + then sol.price_unit * m.product_qty + else 0 + end + end) as value, + sum(pt.weight_net * m.product_qty) as weight, + case when l.usage in ('supplier', 'customer') then 'import' else 'export' end as type, + case when ppl.currency_id is not null + then ppl.currency_id + else spl.currency_id + end as currency_id + from + stock_move m + left join (product_template pt + left join product_product pp on (pp.product_tmpl_id = pt.id)) + on (m.product_id = pt.id) + left join (res_partner_address a + left join res_country c on (c.id = a.country_id)) + on (a.id = m.address_id) + left join (stock_picking sp + left join (res_partner_address pa + left join res_country pc on (pc.id = pa.country_id)) + on (pa.id = sp.address_id)) + on (sp.id = m.picking_id) + left join stock_location l on (l.id = m.location_id) + left join stock_location dl on (dl.id = m.location_dest_id) + left join (purchase_order_line pol + left join (purchase_order po + left join product_pricelist ppl on (ppl.id = po.pricelist_id)) + on (po.id = pol.order_id)) + on (pol.id = m.purchase_line_id) + left join (sale_order_line sol + left join (sale_order so + left join product_pricelist spl on (spl.id = so.pricelist_id)) + on (so.id = sol.order_id)) + on (sol.id = m.sale_line_id) + where + m.state != 'draft' + and ((l.usage in ('supplier', 'customer') and dl.usage not in ('supplier', 'customer')) + or (dl.usage in ('supplier', 'customer') and l.usage not in ('supplier', 'customer'))) + and (c.intrastat is not null or pc.intrastat is not null) + group by to_char(m.create_date, 'YYYY-MM-01'), pt.intrastat_id, c.code, pc.code, l.usage, dl.usage, ppl.currency_id, spl.currency_id + )""") report_intrastat() diff --git a/addons/report_mrp/__terp__.py b/addons/report_mrp/__terp__.py index 3c45b15a66f..2c40453fffe 100644 --- a/addons/report_mrp/__terp__.py +++ b/addons/report_mrp/__terp__.py @@ -26,15 +26,15 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name": "MRP Management - Reporting", - "version": "1.0", - "author": "Tiny", - "website": "http://tinyerp.com/module_mrp.html", - "depends": ["mrp"], - "category": "Generic Modules/Production", - "description": "A module that adds new reports based on MRP cases.", - "init_xml": [], - "demo_xml": [], - "update_xml": ["report_mrp_view.xml"], - "installable": True + "name": "MRP Management - Reporting", + "version": "1.0", + "author": "Tiny", + "website": "http://tinyerp.com/module_mrp.html", + "depends": ["mrp"], + "category": "Generic Modules/Production", + "description": "A module that adds new reports based on MRP cases.", + "init_xml": [], + "demo_xml": [], + "update_xml": ["report_mrp_view.xml"], + "installable": True } diff --git a/addons/report_mrp/report_mrp.py b/addons/report_mrp/report_mrp.py index 61ae4105538..27a9591f374 100644 --- a/addons/report_mrp/report_mrp.py +++ b/addons/report_mrp/report_mrp.py @@ -33,77 +33,77 @@ from osv import fields,osv class report_workcenter_load(osv.osv): - _name="report.workcenter.load" - _description="Workcenter Load" - _auto = False - _columns = { - 'name': fields.char('Week', size=64, required=True), - 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), - 'cycle': fields.float('Nbr of cycle'), - 'hour': fields.float('Nbr of hour'), - } + _name="report.workcenter.load" + _description="Workcenter Load" + _auto = False + _columns = { + 'name': fields.char('Week', size=64, required=True), + 'workcenter_id': fields.many2one('mrp.workcenter', 'Workcenter', required=True), + 'cycle': fields.float('Nbr of cycle'), + 'hour': fields.float('Nbr of hour'), + } - def init(self, cr): - cr.execute(""" - create or replace view report_workcenter_load as ( - SELECT - min(wl.id) as id, - to_char(p.date_planned,'YYYY:IW') as name, - SUM(wl.hour) AS hour, - SUM(wl.cycle) AS cycle, - wl.workcenter_id as workcenter_id - FROM - mrp_production_workcenter_line wl - LEFT JOIN mrp_production p - ON p.id = wl.production_id - GROUP BY - wl.workcenter_id, - to_char(p.date_planned,'YYYY:IW') - )""") + def init(self, cr): + cr.execute(""" + create or replace view report_workcenter_load as ( + SELECT + min(wl.id) as id, + to_char(p.date_planned,'YYYY:IW') as name, + SUM(wl.hour) AS hour, + SUM(wl.cycle) AS cycle, + wl.workcenter_id as workcenter_id + FROM + mrp_production_workcenter_line wl + LEFT JOIN mrp_production p + ON p.id = wl.production_id + GROUP BY + wl.workcenter_id, + to_char(p.date_planned,'YYYY:IW') + )""") report_workcenter_load() class report_mrp_inout(osv.osv): - _name="report.mrp.inout" - _description="Stock value variation" - _auto = False - _rec_name = 'date' - _columns = { - 'date': fields.char('Week', size=64, required=True), - 'value': fields.float('Stock value', required=True, digits=(16,2)), - } + _name="report.mrp.inout" + _description="Stock value variation" + _auto = False + _rec_name = 'date' + _columns = { + 'date': fields.char('Week', size=64, required=True), + 'value': fields.float('Stock value', required=True, digits=(16,2)), + } - def init(self, cr): - cr.execute(""" - create or replace view report_mrp_inout as ( - select - min(sm.id) as id, - to_char(sm.date_planned,'YYYY:IW') as date, - sum(case when (sl.usage='internal') then - pt.standard_price * sm.product_qty - else - 0.0 - end - case when (sl2.usage='internal') then - pt.standard_price * sm.product_qty - else - 0.0 - end) as value - from - stock_move sm - left join product_product pp - on (pp.id = sm.product_id) - left join product_template pt - on (pt.id = pp.product_tmpl_id) - left join stock_location sl - on ( sl.id = sm.location_id) - left join stock_location sl2 - on ( sl2.id = sm.location_dest_id) - where - sm.state in ('waiting','confirmed','assigned') - group by - to_char(sm.date_planned,'YYYY:IW') - )""") + def init(self, cr): + cr.execute(""" + create or replace view report_mrp_inout as ( + select + min(sm.id) as id, + to_char(sm.date_planned,'YYYY:IW') as date, + sum(case when (sl.usage='internal') then + pt.standard_price * sm.product_qty + else + 0.0 + end - case when (sl2.usage='internal') then + pt.standard_price * sm.product_qty + else + 0.0 + end) as value + from + stock_move sm + left join product_product pp + on (pp.id = sm.product_id) + left join product_template pt + on (pt.id = pp.product_tmpl_id) + left join stock_location sl + on ( sl.id = sm.location_id) + left join stock_location sl2 + on ( sl2.id = sm.location_dest_id) + where + sm.state in ('waiting','confirmed','assigned') + group by + to_char(sm.date_planned,'YYYY:IW') + )""") report_mrp_inout() diff --git a/addons/report_project/__terp__.py b/addons/report_project/__terp__.py index 83310b8997b..3ff8139e346 100644 --- a/addons/report_project/__terp__.py +++ b/addons/report_project/__terp__.py @@ -26,16 +26,16 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Sales Management - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_project.html", - "depends" : ["project"], - "category" : "Generic Modules/Sales & Purchases", - "description": "A module that adds some reports on the sales", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_project_view.xml"], - "active": False, - "installable": True + "name" : "Sales Management - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_project.html", + "depends" : ["project"], + "category" : "Generic Modules/Sales & Purchases", + "description": "A module that adds some reports on the sales", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_project_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_purchase/__terp__.py b/addons/report_purchase/__terp__.py index 7efbe56ceef..89a547aaaac 100644 --- a/addons/report_purchase/__terp__.py +++ b/addons/report_purchase/__terp__.py @@ -26,15 +26,15 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Sales Management - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_purchase.html", - "depends" : ["purchase"], - "category" : "Generic Modules/Sales & Purchases", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_purchase_view.xml"], - "active": False, - "installable": True + "name" : "Sales Management - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_purchase.html", + "depends" : ["purchase"], + "category" : "Generic Modules/Sales & Purchases", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_purchase_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_purchase/report_purchase.py b/addons/report_purchase/report_purchase.py index 97159afb2f1..36e6a17513b 100644 --- a/addons/report_purchase/report_purchase.py +++ b/addons/report_purchase/report_purchase.py @@ -34,89 +34,89 @@ from osv import fields,osv class report_purchase_order_product(osv.osv): - _name = "report.purchase.order.product" - _description = "Purchases Orders by Products" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'state': fields.selection([ - ('draft','Quotation'), - ('waiting_date','Waiting Schedule'), - ('manual','Manual in progress'), - ('progress','In progress'), - ('shipping_except','Shipping Exception'), - ('invoice_except','Invoice Exception'), - ('done','Done'), - ('cancel','Cancel') - ], 'Order State', readonly=True), - 'product_id':fields.many2one('product.product', 'Product', readonly=True), - 'quantity': fields.float('# of Products', readonly=True), - 'price_total': fields.float('Total Price', readonly=True), - 'price_average': fields.float('Average Price', readonly=True), - 'count': fields.integer('# of Lines', readonly=True), - } - _order = 'name desc,price_total desc' - def init(self, cr): - cr.execute(""" - create or replace view report_purchase_order_product as ( - select - min(l.id) as id, - to_char(s.date_order, 'YYYY-MM-01') as name, - s.state, - l.product_id, - sum(l.product_qty*u.factor) as quantity, - count(*), - sum(l.product_qty*l.price_unit) as price_total, - (sum(l.product_qty*l.price_unit)/sum(l.product_qty*u.factor))::decimal(16,2) as price_average - from purchase_order s - left join purchase_order_line l on (s.id=l.order_id) - left join product_uom u on (u.id=l.product_uom) - group by l.product_id, to_char(s.date_order, 'YYYY-MM-01'),s.state - ) - """) + _name = "report.purchase.order.product" + _description = "Purchases Orders by Products" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'state': fields.selection([ + ('draft','Quotation'), + ('waiting_date','Waiting Schedule'), + ('manual','Manual in progress'), + ('progress','In progress'), + ('shipping_except','Shipping Exception'), + ('invoice_except','Invoice Exception'), + ('done','Done'), + ('cancel','Cancel') + ], 'Order State', readonly=True), + 'product_id':fields.many2one('product.product', 'Product', readonly=True), + 'quantity': fields.float('# of Products', readonly=True), + 'price_total': fields.float('Total Price', readonly=True), + 'price_average': fields.float('Average Price', readonly=True), + 'count': fields.integer('# of Lines', readonly=True), + } + _order = 'name desc,price_total desc' + def init(self, cr): + cr.execute(""" + create or replace view report_purchase_order_product as ( + select + min(l.id) as id, + to_char(s.date_order, 'YYYY-MM-01') as name, + s.state, + l.product_id, + sum(l.product_qty*u.factor) as quantity, + count(*), + sum(l.product_qty*l.price_unit) as price_total, + (sum(l.product_qty*l.price_unit)/sum(l.product_qty*u.factor))::decimal(16,2) as price_average + from purchase_order s + left join purchase_order_line l on (s.id=l.order_id) + left join product_uom u on (u.id=l.product_uom) + group by l.product_id, to_char(s.date_order, 'YYYY-MM-01'),s.state + ) + """) report_purchase_order_product() class report_purchase_order_category(osv.osv): - _name = "report.purchase.order.category" - _description = "Purchases Orders by Categories" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'state': fields.selection([ - ('draft','Quotation'), - ('waiting_date','Waiting Schedule'), - ('manual','Manual in progress'), - ('progress','In progress'), - ('shipping_except','Shipping Exception'), - ('invoice_except','Invoice Exception'), - ('done','Done'), - ('cancel','Cancel') - ], 'Order State', readonly=True), - 'category_id': fields.many2one('product.category', 'Categories', readonly=True), - 'quantity': fields.float('# of Products', readonly=True), - 'price_total': fields.float('Total Price', readonly=True), - 'price_average': fields.float('Average Price', readonly=True), - 'count': fields.integer('# of Lines', readonly=True), - } - _order = 'name desc,price_total desc' - def init(self, cr): - cr.execute(""" - create or replace view report_purchase_order_category as ( - select - min(l.id) as id, - to_char(s.date_order, 'YYYY-MM-01') as name, - s.state, - t.categ_id as category_id, - sum(l.product_qty*u.factor) as quantity, - count(*), - sum(l.product_qty*l.price_unit) as price_total, - (sum(l.product_qty*l.price_unit)/sum(l.product_qty*u.factor))::decimal(16,2) as price_average - from purchase_order s - left join purchase_order_line l on (s.id=l.order_id) - left join product_product p on (p.id=l.product_id) - left join product_template t on (t.id=p.product_tmpl_id) - left join product_uom u on (u.id=l.product_uom) - group by t.categ_id, to_char(s.date_order, 'YYYY-MM-01'),s.state - ) - """) + _name = "report.purchase.order.category" + _description = "Purchases Orders by Categories" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'state': fields.selection([ + ('draft','Quotation'), + ('waiting_date','Waiting Schedule'), + ('manual','Manual in progress'), + ('progress','In progress'), + ('shipping_except','Shipping Exception'), + ('invoice_except','Invoice Exception'), + ('done','Done'), + ('cancel','Cancel') + ], 'Order State', readonly=True), + 'category_id': fields.many2one('product.category', 'Categories', readonly=True), + 'quantity': fields.float('# of Products', readonly=True), + 'price_total': fields.float('Total Price', readonly=True), + 'price_average': fields.float('Average Price', readonly=True), + 'count': fields.integer('# of Lines', readonly=True), + } + _order = 'name desc,price_total desc' + def init(self, cr): + cr.execute(""" + create or replace view report_purchase_order_category as ( + select + min(l.id) as id, + to_char(s.date_order, 'YYYY-MM-01') as name, + s.state, + t.categ_id as category_id, + sum(l.product_qty*u.factor) as quantity, + count(*), + sum(l.product_qty*l.price_unit) as price_total, + (sum(l.product_qty*l.price_unit)/sum(l.product_qty*u.factor))::decimal(16,2) as price_average + from purchase_order s + left join purchase_order_line l on (s.id=l.order_id) + left join product_product p on (p.id=l.product_id) + left join product_template t on (t.id=p.product_tmpl_id) + left join product_uom u on (u.id=l.product_uom) + group by t.categ_id, to_char(s.date_order, 'YYYY-MM-01'),s.state + ) + """) report_purchase_order_category() diff --git a/addons/report_sale/__terp__.py b/addons/report_sale/__terp__.py index a5885d03760..99421db814a 100644 --- a/addons/report_sale/__terp__.py +++ b/addons/report_sale/__terp__.py @@ -26,26 +26,26 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Sales Management - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_sale.html", - "depends" : ["sale"], - "category" : "Generic Modules/Sales & Purchases", - "description": """ - Reporting for the sale module: - * Sales order by product (my/this month/all) - * Sales order by category of product (my/this month/all) + "name" : "Sales Management - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_sale.html", + "depends" : ["sale"], + "category" : "Generic Modules/Sales & Purchases", + "description": """ + Reporting for the sale module: + * Sales order by product (my/this month/all) + * Sales order by category of product (my/this month/all) - Some predefined lists: - * Sales of the month - * Open quotations - * Uninvoiced Sales - * Uninvoiced but shipped Sales - """, - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_sale_view.xml","report_sale_graph.xml"], - "active": False, - "installable": True + Some predefined lists: + * Sales of the month + * Open quotations + * Uninvoiced Sales + * Uninvoiced but shipped Sales + """, + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_sale_view.xml","report_sale_graph.xml"], + "active": False, + "installable": True } diff --git a/addons/report_sale/report_sale.py b/addons/report_sale/report_sale.py index e16b7693150..88884d11636 100644 --- a/addons/report_sale/report_sale.py +++ b/addons/report_sale/report_sale.py @@ -30,98 +30,98 @@ from osv import fields,osv class report_sale_order_product(osv.osv): - _name = "report.sale.order.product" - _description = "Sales Orders by Products" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'state': fields.selection([ - ('draft','Quotation'), - ('waiting_date','Waiting Schedule'), - ('manual','Manual in progress'), - ('progress','In progress'), - ('shipping_except','Shipping Exception'), - ('invoice_except','Invoice Exception'), - ('done','Done'), - ('cancel','Cancel') - ], 'Order State', readonly=True), - 'product_id':fields.many2one('product.product', 'Product', readonly=True), - 'quantity': fields.float('# of Products', readonly=True), - 'price_total': fields.float('Total Price', readonly=True), - 'price_average': fields.float('Average Price', readonly=True), - 'count': fields.integer('# of Lines', readonly=True), - } - _order = 'name desc,price_total desc' - def init(self, cr): - cr.execute(""" - create or replace view report_sale_order_product as ( - select - min(l.id) as id, - to_char(s.date_order, 'YYYY-MM-01') as name, - s.state, - l.product_id, - sum(l.product_uom_qty*u.factor) as quantity, - count(*), - sum(l.product_uom_qty*l.price_unit) as price_total, - (sum(l.product_uom_qty*l.price_unit)/sum(l.product_uom_qty*u.factor))::decimal(16,2) as price_average - from sale_order s - right join sale_order_line l on (s.id=l.order_id) - left join product_uom u on (u.id=l.product_uom) - group by l.product_id, to_char(s.date_order, 'YYYY-MM-01'),s.state - ) - """) + _name = "report.sale.order.product" + _description = "Sales Orders by Products" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'state': fields.selection([ + ('draft','Quotation'), + ('waiting_date','Waiting Schedule'), + ('manual','Manual in progress'), + ('progress','In progress'), + ('shipping_except','Shipping Exception'), + ('invoice_except','Invoice Exception'), + ('done','Done'), + ('cancel','Cancel') + ], 'Order State', readonly=True), + 'product_id':fields.many2one('product.product', 'Product', readonly=True), + 'quantity': fields.float('# of Products', readonly=True), + 'price_total': fields.float('Total Price', readonly=True), + 'price_average': fields.float('Average Price', readonly=True), + 'count': fields.integer('# of Lines', readonly=True), + } + _order = 'name desc,price_total desc' + def init(self, cr): + cr.execute(""" + create or replace view report_sale_order_product as ( + select + min(l.id) as id, + to_char(s.date_order, 'YYYY-MM-01') as name, + s.state, + l.product_id, + sum(l.product_uom_qty*u.factor) as quantity, + count(*), + sum(l.product_uom_qty*l.price_unit) as price_total, + (sum(l.product_uom_qty*l.price_unit)/sum(l.product_uom_qty*u.factor))::decimal(16,2) as price_average + from sale_order s + right join sale_order_line l on (s.id=l.order_id) + left join product_uom u on (u.id=l.product_uom) + group by l.product_id, to_char(s.date_order, 'YYYY-MM-01'),s.state + ) + """) # Done in the _auto_init -# for k in self._columns: -# f = self._columns[k] -# cr.execute("select id from ir_model_fields where model=%s and name=%s", (self._name,k)) -# if not cr.rowcount: -# cr.execute("select id from ir_model where model='%s'" % self._name) -# model_id = cr.fetchone()[0] -# cr.execute("INSERT INTO ir_model_fields (model_id, model, name, field_description, ttype, relate,relation,group_name,view_load) VALUES (%d,%s,%s,%s,%s,%s,%s,%s,%s)", (model_id, self._name, k, f.string.replace("'", " "), f._type, (f.relate and 'True') or 'False', f._obj or 'NULL', f.group_name or '', (f.view_load and 'True') or 'False')) +# for k in self._columns: +# f = self._columns[k] +# cr.execute("select id from ir_model_fields where model=%s and name=%s", (self._name,k)) +# if not cr.rowcount: +# cr.execute("select id from ir_model where model='%s'" % self._name) +# model_id = cr.fetchone()[0] +# cr.execute("INSERT INTO ir_model_fields (model_id, model, name, field_description, ttype, relate,relation,group_name,view_load) VALUES (%d,%s,%s,%s,%s,%s,%s,%s,%s)", (model_id, self._name, k, f.string.replace("'", " "), f._type, (f.relate and 'True') or 'False', f._obj or 'NULL', f.group_name or '', (f.view_load and 'True') or 'False')) report_sale_order_product() class report_sale_order_category(osv.osv): - _name = "report.sale.order.category" - _description = "Sales Orders by Categories" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'state': fields.selection([ - ('draft','Quotation'), - ('waiting_date','Waiting Schedule'), - ('manual','Manual in progress'), - ('progress','In progress'), - ('shipping_except','Shipping Exception'), - ('invoice_except','Invoice Exception'), - ('done','Done'), - ('cancel','Cancel') - ], 'Order State', readonly=True), - 'category_id': fields.many2one('product.category', 'Categories', readonly=True), - 'quantity': fields.float('# of Products', readonly=True), - 'price_total': fields.float('Total Price', readonly=True), - 'price_average': fields.float('Average Price', readonly=True), - 'count': fields.integer('# of Lines', readonly=True), - } - _order = 'name desc,price_total desc' - def init(self, cr): - cr.execute(""" - create or replace view report_sale_order_category as ( - select - min(l.id) as id, - to_char(s.date_order, 'YYYY-MM-01') as name, - s.state, - t.categ_id as category_id, - sum(l.product_uom_qty*u.factor) as quantity, - count(*), - sum(l.product_uom_qty*l.price_unit) as price_total, - (sum(l.product_uom_qty*l.price_unit)/sum(l.product_uom_qty*u.factor))::decimal(16,2) as price_average - from sale_order s - right join sale_order_line l on (s.id=l.order_id) - left join product_product p on (p.id=l.product_id) - left join product_template t on (t.id=p.product_tmpl_id) - left join product_uom u on (u.id=l.product_uom) - group by t.categ_id, to_char(s.date_order, 'YYYY-MM-01'),s.state - ) - """) + _name = "report.sale.order.category" + _description = "Sales Orders by Categories" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'state': fields.selection([ + ('draft','Quotation'), + ('waiting_date','Waiting Schedule'), + ('manual','Manual in progress'), + ('progress','In progress'), + ('shipping_except','Shipping Exception'), + ('invoice_except','Invoice Exception'), + ('done','Done'), + ('cancel','Cancel') + ], 'Order State', readonly=True), + 'category_id': fields.many2one('product.category', 'Categories', readonly=True), + 'quantity': fields.float('# of Products', readonly=True), + 'price_total': fields.float('Total Price', readonly=True), + 'price_average': fields.float('Average Price', readonly=True), + 'count': fields.integer('# of Lines', readonly=True), + } + _order = 'name desc,price_total desc' + def init(self, cr): + cr.execute(""" + create or replace view report_sale_order_category as ( + select + min(l.id) as id, + to_char(s.date_order, 'YYYY-MM-01') as name, + s.state, + t.categ_id as category_id, + sum(l.product_uom_qty*u.factor) as quantity, + count(*), + sum(l.product_uom_qty*l.price_unit) as price_total, + (sum(l.product_uom_qty*l.price_unit)/sum(l.product_uom_qty*u.factor))::decimal(16,2) as price_average + from sale_order s + right join sale_order_line l on (s.id=l.order_id) + left join product_product p on (p.id=l.product_id) + left join product_template t on (t.id=p.product_tmpl_id) + left join product_uom u on (u.id=l.product_uom) + group by t.categ_id, to_char(s.date_order, 'YYYY-MM-01'),s.state + ) + """) report_sale_order_category() diff --git a/addons/report_stock/__terp__.py b/addons/report_stock/__terp__.py index 152ca1cbddf..059ced88d48 100644 --- a/addons/report_stock/__terp__.py +++ b/addons/report_stock/__terp__.py @@ -26,22 +26,22 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Stock report", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/", - "depends" : ["stock", "product",], - "category" : "Generic Modules/Inventory Control", - "description" : """ - This module adds new reports based on the stock module. + "name" : "Stock report", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/", + "depends" : ["stock", "product",], + "category" : "Generic Modules/Inventory Control", + "description" : """ + This module adds new reports based on the stock module. - It create a new menu to get products by production lots within - the different locations: - Inventory Control/Reporting/Traceability/Stock by production lots - """, - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_stock_view.xml",], - "active": False, - "installable": True + It create a new menu to get products by production lots within + the different locations: + Inventory Control/Reporting/Traceability/Stock by production lots + """, + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_stock_view.xml",], + "active": False, + "installable": True } diff --git a/addons/report_stock/report_stock.py b/addons/report_stock/report_stock.py index a536bf6f3e2..fd78506760f 100644 --- a/addons/report_stock/report_stock.py +++ b/addons/report_stock/report_stock.py @@ -30,48 +30,48 @@ from osv import osv, fields class report_stock_prodlots(osv.osv): - _name = "report.stock.prodlots" - _description = "Stock report by production lots" - _auto = False - _columns = { - 'name': fields.float('Quantity', readonly=True), - 'location_id': fields.many2one('stock.location', 'Location', readonly=True, select=True), - 'product_id': fields.many2one('product.product', 'Product', readonly=True, select=True), - 'prodlot_id': fields.many2one('stock.production.lot', 'Production lot', readonly=True, select=True), - } - def init(self, cr): - cr.execute(""" - create or replace view report_stock_prodlots as ( - select max(id) as id, - location_id, - product_id, - prodlot_id, - sum(qty) as name - from ( - select -max(sm.id) as id, - sm.location_id, - sm.product_id, - sm.prodlot_id, - -sum(sm.product_qty) as qty - from stock_move as sm - left join stock_location sl - on (sl.id = sm.location_id) - where state = 'done' - and sl.usage = 'internal' - group by sm.location_id, sm.product_id, sm.product_uom, sm.prodlot_id - union all - select max(sm.id) as id, - sm.location_dest_id as location_id, - sm.product_id, - sm.prodlot_id, - sum(sm.product_qty) as qty - from stock_move as sm - left join stock_location sl - on (sl.id = sm.location_dest_id) - where sm.state = 'done' - and sl.usage = 'internal' - group by sm.location_dest_id, sm.product_id, sm.product_uom, sm.prodlot_id - ) as report - group by location_id, product_id, prodlot_id - )""") + _name = "report.stock.prodlots" + _description = "Stock report by production lots" + _auto = False + _columns = { + 'name': fields.float('Quantity', readonly=True), + 'location_id': fields.many2one('stock.location', 'Location', readonly=True, select=True), + 'product_id': fields.many2one('product.product', 'Product', readonly=True, select=True), + 'prodlot_id': fields.many2one('stock.production.lot', 'Production lot', readonly=True, select=True), + } + def init(self, cr): + cr.execute(""" + create or replace view report_stock_prodlots as ( + select max(id) as id, + location_id, + product_id, + prodlot_id, + sum(qty) as name + from ( + select -max(sm.id) as id, + sm.location_id, + sm.product_id, + sm.prodlot_id, + -sum(sm.product_qty) as qty + from stock_move as sm + left join stock_location sl + on (sl.id = sm.location_id) + where state = 'done' + and sl.usage = 'internal' + group by sm.location_id, sm.product_id, sm.product_uom, sm.prodlot_id + union all + select max(sm.id) as id, + sm.location_dest_id as location_id, + sm.product_id, + sm.prodlot_id, + sum(sm.product_qty) as qty + from stock_move as sm + left join stock_location sl + on (sl.id = sm.location_dest_id) + where sm.state = 'done' + and sl.usage = 'internal' + group by sm.location_dest_id, sm.product_id, sm.product_uom, sm.prodlot_id + ) as report + group by location_id, product_id, prodlot_id + )""") report_stock_prodlots() diff --git a/addons/report_task/__init__.py b/addons/report_task/__init__.py index 525c71da296..1dddadc7e30 100644 --- a/addons/report_task/__init__.py +++ b/addons/report_task/__init__.py @@ -26,8 +26,8 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### ##################################################################### -#Created By : Eiffel Consultancy Pvt. Ltd. -#Created Date : 14/02/2007 +#Created By : Eiffel Consultancy Pvt. Ltd. +#Created Date : 14/02/2007 ##################################################################### import report_task diff --git a/addons/report_task/__terp__.py b/addons/report_task/__terp__.py index f177bc5fe70..1a1774b4792 100644 --- a/addons/report_task/__terp__.py +++ b/addons/report_task/__terp__.py @@ -26,18 +26,18 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - 'name' : 'Report on tasks by user for projects', - 'version' :'1.0', - 'author' : 'Tiny', - "category": "Generic Modules/Projects & Services", - 'depends' : ['base','project'], - 'description': 'Gives statistics on tasks by user on projects to check the pipeline of users.', - 'init_xml' : [], - 'update_xml': [ - 'report_task_view.xml', - ], - 'active': False, - 'installable': True + 'name' : 'Report on tasks by user for projects', + 'version' :'1.0', + 'author' : 'Tiny', + "category": "Generic Modules/Projects & Services", + 'depends' : ['base','project'], + 'description': 'Gives statistics on tasks by user on projects to check the pipeline of users.', + 'init_xml' : [], + 'update_xml': [ + 'report_task_view.xml', + ], + 'active': False, + 'installable': True } diff --git a/addons/report_task/report_task.py b/addons/report_task/report_task.py index c3810492b02..9539bfc9323 100644 --- a/addons/report_task/report_task.py +++ b/addons/report_task/report_task.py @@ -30,39 +30,39 @@ from osv import fields,osv class report_task_user_pipeline_open (osv.osv): - _name = "report.task.user.pipeline.open" - _description = "Tasks by user and project" - _auto = False - _columns = { - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'task_nbr': fields.float('Task Number', readonly=True), - 'task_hrs': fields.float('Task Hours', readonly=True), - 'task_progress': fields.float('Task Progress', readonly=True), - 'company_id' : fields.many2one('res.company', 'Company'), - 'task_state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done'),('no','No Task')], 'State', readonly=True), - } + _name = "report.task.user.pipeline.open" + _description = "Tasks by user and project" + _auto = False + _columns = { + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'task_nbr': fields.float('Task Number', readonly=True), + 'task_hrs': fields.float('Task Hours', readonly=True), + 'task_progress': fields.float('Task Progress', readonly=True), + 'company_id' : fields.many2one('res.company', 'Company'), + 'task_state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done'),('no','No Task')], 'State', readonly=True), + } - def init(self, cr): - cr.execute(''' - create or replace view report_task_user_pipeline_open as ( - select - u.id as id, - u.id as user_id, - u.company_id as company_id, - count(t.*) as task_nbr, - sum(t.planned_hours) as task_hrs, - sum(t.planned_hours * (100 - t.progress) / 100) as task_progress, - case when t.state is null then 'no' else t.state end as task_state - from - res_users u - left join - project_task t on (u.id = t.user_id) - where - u.active - group by - u.id, u.company_id, t.state - ) - ''') + def init(self, cr): + cr.execute(''' + create or replace view report_task_user_pipeline_open as ( + select + u.id as id, + u.id as user_id, + u.company_id as company_id, + count(t.*) as task_nbr, + sum(t.planned_hours) as task_hrs, + sum(t.planned_hours * (100 - t.progress) / 100) as task_progress, + case when t.state is null then 'no' else t.state end as task_state + from + res_users u + left join + project_task t on (u.id = t.user_id) + where + u.active + group by + u.id, u.company_id, t.state + ) + ''') report_task_user_pipeline_open() diff --git a/addons/report_timesheet/__terp__.py b/addons/report_timesheet/__terp__.py index e9ee83871c6..796b4072ac5 100644 --- a/addons/report_timesheet/__terp__.py +++ b/addons/report_timesheet/__terp__.py @@ -26,15 +26,15 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Timesheet - Reporting", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "depends" : ["hr_timesheet",'hr_timesheet_invoice'], - "category" : "Generic Modules/Human Ressources", - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["report_timesheet_view.xml"], - "active": False, - "installable": True + "name" : "Timesheet - Reporting", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "depends" : ["hr_timesheet",'hr_timesheet_invoice'], + "category" : "Generic Modules/Human Ressources", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["report_timesheet_view.xml"], + "active": False, + "installable": True } diff --git a/addons/report_timesheet/report_timesheet.py b/addons/report_timesheet/report_timesheet.py index cb0babb42c1..f493149ebe7 100644 --- a/addons/report_timesheet/report_timesheet.py +++ b/addons/report_timesheet/report_timesheet.py @@ -30,128 +30,128 @@ from osv import fields,osv class report_timesheet_user(osv.osv): - _name = "report_timesheet.user" - _description = "Timesheet per day" - _auto = False - _columns = { - 'name': fields.date('Date', readonly=True), - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'quantity': fields.float('Quantity', readonly=True), - 'cost': fields.float('Cost', readonly=True) - } - _order = 'name desc,user_id desc' - def init(self, cr): - cr.execute(""" - create or replace view report_timesheet_user as ( - select - min(l.id) as id, - l.date as name, - l.user_id, - sum(l.unit_amount) as quantity, - sum(l.amount) as cost - from - account_analytic_line l - where - user_id is not null - group by l.date, l.user_id - ) - """) + _name = "report_timesheet.user" + _description = "Timesheet per day" + _auto = False + _columns = { + 'name': fields.date('Date', readonly=True), + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'quantity': fields.float('Quantity', readonly=True), + 'cost': fields.float('Cost', readonly=True) + } + _order = 'name desc,user_id desc' + def init(self, cr): + cr.execute(""" + create or replace view report_timesheet_user as ( + select + min(l.id) as id, + l.date as name, + l.user_id, + sum(l.unit_amount) as quantity, + sum(l.amount) as cost + from + account_analytic_line l + where + user_id is not null + group by l.date, l.user_id + ) + """) report_timesheet_user() class report_timesheet_account(osv.osv): - _name = "report_timesheet.account" - _description = "Timesheet per account" - _auto = False - _columns = { - 'name': fields.date('Month', readonly=True), - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), - 'quantity': fields.float('Quantity', readonly=True), - } - _order = 'name desc,account_id desc,user_id desc' - def init(self, cr): - cr.execute(""" - create or replace view report_timesheet_account as ( - select - min(id) as id, - to_char(create_date, 'YYYY-MM-01') as name, - user_id, - account_id, - sum(unit_amount) as quantity - from - account_analytic_line - group by - to_char(create_date, 'YYYY-MM-01'), user_id, account_id - ) - """) + _name = "report_timesheet.account" + _description = "Timesheet per account" + _auto = False + _columns = { + 'name': fields.date('Month', readonly=True), + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'quantity': fields.float('Quantity', readonly=True), + } + _order = 'name desc,account_id desc,user_id desc' + def init(self, cr): + cr.execute(""" + create or replace view report_timesheet_account as ( + select + min(id) as id, + to_char(create_date, 'YYYY-MM-01') as name, + user_id, + account_id, + sum(unit_amount) as quantity + from + account_analytic_line + group by + to_char(create_date, 'YYYY-MM-01'), user_id, account_id + ) + """) report_timesheet_account() class report_timesheet_account_date(osv.osv): - _name = "report_timesheet.account.date" - _description = "Daily timesheet per account" - _auto = False - _columns = { - 'name': fields.date('Date', readonly=True), - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), - 'quantity': fields.float('Quantity', readonly=True), - } - _order = 'name desc,account_id desc,user_id desc' - def init(self, cr): - cr.execute(""" - create or replace view report_timesheet_account_date as ( - select - min(id) as id, - date as name, - user_id, - account_id, - sum(unit_amount) as quantity - from - account_analytic_line - group by - date, user_id, account_id - ) - """) + _name = "report_timesheet.account.date" + _description = "Daily timesheet per account" + _auto = False + _columns = { + 'name': fields.date('Date', readonly=True), + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'account_id':fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'quantity': fields.float('Quantity', readonly=True), + } + _order = 'name desc,account_id desc,user_id desc' + def init(self, cr): + cr.execute(""" + create or replace view report_timesheet_account_date as ( + select + min(id) as id, + date as name, + user_id, + account_id, + sum(unit_amount) as quantity + from + account_analytic_line + group by + date, user_id, account_id + ) + """) report_timesheet_account_date() class report_timesheet_invoice(osv.osv): - _name = "report_timesheet.invoice" - _description = "Costs to invoice" - _auto = False - _columns = { - 'user_id':fields.many2one('res.users', 'User', readonly=True), - 'account_id':fields.many2one('account.analytic.account', 'Project', readonly=True), - 'manager_id':fields.many2one('res.users', 'Manager', readonly=True), - 'quantity': fields.float('Quantity', readonly=True), - 'amount_invoice': fields.float('To invoice', readonly=True) - } - _rec_name = 'user_id' - _order = 'user_id desc' - def init(self, cr): - cr.execute(""" - create or replace view report_timesheet_invoice as ( - select - min(l.id) as id, - l.user_id as user_id, - l.account_id as account_id, - a.user_id as manager_id, - sum(l.unit_amount) as quantity, - sum(l.unit_amount * t.list_price) as revenue - from account_analytic_line l - left join hr_timesheet_invoice_factor f on (l.to_invoice=f.id) - left join account_analytic_account a on (l.account_id=a.id) - left join product_product p on (l.to_invoice=f.id) - left join product_template t on (l.to_invoice=f.id) - where - l.to_invoice is not null and - l.invoice_id is null - group by - l.user_id, - l.account_id, - a.user_id - ) - """) + _name = "report_timesheet.invoice" + _description = "Costs to invoice" + _auto = False + _columns = { + 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'account_id':fields.many2one('account.analytic.account', 'Project', readonly=True), + 'manager_id':fields.many2one('res.users', 'Manager', readonly=True), + 'quantity': fields.float('Quantity', readonly=True), + 'amount_invoice': fields.float('To invoice', readonly=True) + } + _rec_name = 'user_id' + _order = 'user_id desc' + def init(self, cr): + cr.execute(""" + create or replace view report_timesheet_invoice as ( + select + min(l.id) as id, + l.user_id as user_id, + l.account_id as account_id, + a.user_id as manager_id, + sum(l.unit_amount) as quantity, + sum(l.unit_amount * t.list_price) as revenue + from account_analytic_line l + left join hr_timesheet_invoice_factor f on (l.to_invoice=f.id) + left join account_analytic_account a on (l.account_id=a.id) + left join product_product p on (l.to_invoice=f.id) + left join product_template t on (l.to_invoice=f.id) + where + l.to_invoice is not null and + l.invoice_id is null + group by + l.user_id, + l.account_id, + a.user_id + ) + """) report_timesheet_invoice() diff --git a/addons/sale/__terp__.py b/addons/sale/__terp__.py index a702930de2a..cb3f512585a 100644 --- a/addons/sale/__terp__.py +++ b/addons/sale/__terp__.py @@ -26,42 +26,42 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Sales Management", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_sale.html", - "depends" : ["product", "stock", "mrp"], - "category" : "Generic Modules/Sales & Purchases", - "init_xml" : [], - "demo_xml" : ["sale_demo.xml"], - "description": """ - The base module to manage quotations and sales orders. + "name" : "Sales Management", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_sale.html", + "depends" : ["product", "stock", "mrp"], + "category" : "Generic Modules/Sales & Purchases", + "init_xml" : [], + "demo_xml" : ["sale_demo.xml"], + "description": """ + The base module to manage quotations and sales orders. - * Workflow with validation steps: - - Quotation -> Sale order -> Invoice - * Invoicing methods: - - Invoice on order (before or after shipping) - - Invoice on delivery - - Invoice on timesheets - - Advance invoice - * Partners preferences (shipping, invoicing, incoterm, ...) - * Products stocks and prices - * Delivery methods: - - all at once, multi-parcel - - delivery costs - """, - "update_xml" : [ + * Workflow with validation steps: + - Quotation -> Sale order -> Invoice + * Invoicing methods: + - Invoice on order (before or after shipping) + - Invoice on delivery + - Invoice on timesheets + - Advance invoice + * Partners preferences (shipping, invoicing, incoterm, ...) + * Products stocks and prices + * Delivery methods: + - all at once, multi-parcel + - delivery costs + """, + "update_xml" : [ - "sale_workflow.xml", - "sale_sequence.xml", - "sale_data.xml", - "sale_wizard.xml", - "sale_view.xml", - "sale_report.xml", - "sale_wizard.xml", - "stock_view.xml", - "sale_security.xml" - ], - "active": False, - "installable": True + "sale_workflow.xml", + "sale_sequence.xml", + "sale_data.xml", + "sale_wizard.xml", + "sale_view.xml", + "sale_report.xml", + "sale_wizard.xml", + "stock_view.xml", + "sale_security.xml" + ], + "active": False, + "installable": True } diff --git a/addons/sale/partner.py b/addons/sale/partner.py index 444b6b6377c..5a95db57313 100644 --- a/addons/sale/partner.py +++ b/addons/sale/partner.py @@ -33,11 +33,11 @@ from osv import fields, osv class res_partner(osv.osv): - _name = 'res.partner' - _inherit = 'res.partner' - _columns = { - 'sales': fields.one2many('sale.order', 'partner_id', 'Sales'), - } + _name = 'res.partner' + _inherit = 'res.partner' + _columns = { + 'sales': fields.one2many('sale.order', 'partner_id', 'Sales'), + } res_partner() diff --git a/addons/sale/report/order.py b/addons/sale/report/order.py index b84c3a490c7..d8eea3fe092 100644 --- a/addons/sale/report/order.py +++ b/addons/sale/report/order.py @@ -33,9 +33,9 @@ from report import report_sxw from osv import osv class order(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(order, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - }) + def __init__(self, cr, uid, name, context): + super(order, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + }) report_sxw.report_sxw('report.sale.order','sale.order','addons/sale/report/order.rml',parser=order) diff --git a/addons/sale/report/sale_prepare_allot.py b/addons/sale/report/sale_prepare_allot.py index cc972f761e9..464852f2c1a 100644 --- a/addons/sale/report/sale_prepare_allot.py +++ b/addons/sale/report/sale_prepare_allot.py @@ -31,21 +31,21 @@ import time from report import report_sxw class sale_prepare(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(sale_prepare, self).__init__(cr, uid, name, context) - self.localcontext.update( { - 'allotment': self._allotment, - }) - - def _allotment(self, object): - allotments = {} - for line in object.order_line: - if line.address_allotment_id: - allotments.setdefault(line.address_allotment_id.id, ([line.address_allotment_id],[]) ) - else: - allotments.setdefault(line.address_allotment_id.id, ([],[]) ) - allotments[line.address_allotment_id.id][1].append(line) - return allotments.values() + def __init__(self, cr, uid, name, context): + super(sale_prepare, self).__init__(cr, uid, name, context) + self.localcontext.update( { + 'allotment': self._allotment, + }) + + def _allotment(self, object): + allotments = {} + for line in object.order_line: + if line.address_allotment_id: + allotments.setdefault(line.address_allotment_id.id, ([line.address_allotment_id],[]) ) + else: + allotments.setdefault(line.address_allotment_id.id, ([],[]) ) + allotments[line.address_allotment_id.id][1].append(line) + return allotments.values() report_sxw.report_sxw('report.sale.order.prepare.allot', 'sale.order', 'addons/sale/report/prepare_allot.rml',parser=sale_prepare) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 433c16e0a38..5f7eaf85203 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -36,802 +36,802 @@ from tools import config from tools.translate import _ class sale_shop(osv.osv): - _name = "sale.shop" - _description = "Sale Shop" - _columns = { - 'name': fields.char('Shop name',size=64, required=True), - 'payment_default_id': fields.many2one('account.payment.term','Default Payment Term',required=True), - 'payment_account_id': fields.many2many('account.account','sale_shop_account','shop_id','account_id','Payment accounts'), - 'warehouse_id': fields.many2one('stock.warehouse','Warehouse'), - 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'), - 'project_id': fields.many2one('account.analytic.account', 'Analytic Account'), - } + _name = "sale.shop" + _description = "Sale Shop" + _columns = { + 'name': fields.char('Shop name',size=64, required=True), + 'payment_default_id': fields.many2one('account.payment.term','Default Payment Term',required=True), + 'payment_account_id': fields.many2many('account.account','sale_shop_account','shop_id','account_id','Payment accounts'), + 'warehouse_id': fields.many2one('stock.warehouse','Warehouse'), + 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'), + 'project_id': fields.many2one('account.analytic.account', 'Analytic Account'), + } sale_shop() def _incoterm_get(self, cr, uid, context={}): - cr.execute('select code, code||\', \'||name from stock_incoterms where active') - return cr.fetchall() + cr.execute('select code, code||\', \'||name from stock_incoterms where active') + return cr.fetchall() class sale_order(osv.osv): - _name = "sale.order" - _description = "Sale Order" - def copy(self, cr, uid, id, default=None,context={}): - if not default: - default = {} - default.update({ - 'state':'draft', - 'shipped':False, - 'invoice_ids':[], - 'picking_ids':[], - 'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order'), - }) - return super(sale_order, self).copy(cr, uid, id, default, context) - - def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): - res = {} - cur_obj=self.pool.get('res.currency') - for sale in self.browse(cr, uid, ids): - res[sale.id] = 0.0 - for line in sale.order_line: - res[sale.id] += line.price_subtotal - cur = sale.pricelist_id.currency_id - res[sale.id] = cur_obj.round(cr, uid, cur, res[sale.id]) - return res - - def _amount_tax(self, cr, uid, ids, field_name, arg, context): - res = {} - cur_obj=self.pool.get('res.currency') - for order in self.browse(cr, uid, ids): - val = 0.0 - cur=order.pricelist_id.currency_id - for line in order.order_line: - for c in self.pool.get('account.tax').compute(cr, uid, line.tax_id, line.price_unit * (1-(line.discount or 0.0)/100.0), line.product_uom_qty, order.partner_invoice_id.id, line.product_id, order.partner_id): - val+= cur_obj.round(cr, uid, cur, c['amount']) - res[order.id]=cur_obj.round(cr, uid, cur, val) - return res - - def _amount_total(self, cr, uid, ids, field_name, arg, context): - res = {} - untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context) - tax = self._amount_tax(cr, uid, ids, field_name, arg, context) - cur_obj=self.pool.get('res.currency') - for id in ids: - order=self.browse(cr, uid, [id])[0] - cur=order.pricelist_id.currency_id - res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0)) - return res - - def _invoiced(self, cursor, user, ids, name, arg, context=None): - res = {} - for sale in self.browse(cursor, user, ids, context=context): - res[sale.id] = True - for invoice in sale.invoice_ids: - if invoice.state <> 'paid': - res[sale.id] = False - break - if not sale.invoice_ids: - res[sale.id] = False - return res - - def _invoiced_search(self, cursor, user, obj, name, args): - if not len(args): - return [] - - clause = '' - no_invoiced = False - for arg in args: - if arg[1] == '=': - if arg[2]: - clause += 'AND inv.state = \'paid\'' - else: - clause += 'AND inv.state <> \'paid\'' - no_invoiced = True - - cursor.execute('SELECT rel.order_id ' \ - 'FROM sale_order_invoice_rel AS rel, account_invoice AS inv ' \ - 'WHERE rel.invoice_id = inv.id ' + clause) - res = cursor.fetchall() - if no_invoiced: - cursor.execute('SELECT sale.id ' \ - 'FROM sale_order AS sale ' \ - 'WHERE sale.id NOT IN ' \ - '(SELECT rel.order_id ' \ - 'FROM sale_order_invoice_rel AS rel)') - res.extend(cursor.fetchall()) - if not res: - return [('id', '=', 0)] - return [('id', 'in', [x[0] for x in res])] - - _columns = { - 'name': fields.char('Order Reference', size=64, required=True, select=True), - 'shop_id':fields.many2one('sale.shop', 'Shop', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'origin': fields.char('Origin', size=64), - 'client_order_ref': fields.char('Customer Ref.',size=64), - - 'state': fields.selection([ - ('draft','Quotation'), - ('waiting_date','Waiting Schedule'), - ('manual','Manual in progress'), - ('progress','In progress'), - ('shipping_except','Shipping Exception'), - ('invoice_except','Invoice Exception'), - ('done','Done'), - ('cancel','Cancel') - ], 'Order State', readonly=True, help="Gives the state of the quotation or sale order. The exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the packing list process (Shipping Exception). The 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to be on the date 'Date Ordered'.", select=True), - 'date_order':fields.date('Date Ordered', required=True, readonly=True, states={'draft':[('readonly',False)]}), - - 'user_id':fields.many2one('res.users', 'Salesman', states={'draft':[('readonly',False)]}, select=True), - 'partner_id':fields.many2one('res.partner', 'Customer', readonly=True, states={'draft':[('readonly',False)]}, change_default=True, select=True), - 'partner_invoice_id':fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), - 'partner_order_id':fields.many2one('res.partner.address', 'Ordering Contact', readonly=True, required=True, states={'draft':[('readonly',False)]}, help="The name and address of the contact that requested the order or quotation."), - 'partner_shipping_id':fields.many2one('res.partner.address', 'Shipping Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), - - 'incoterm': fields.selection(_incoterm_get, 'Incoterm',size=3), - 'picking_policy': fields.selection([('direct','Direct Delivery'),('one','All at once')], 'Packing Policy', required=True ), - 'order_policy': fields.selection([ - ('prepaid','Payment before delivery'), - ('manual','Shipping & Manual Invoice'), - ('postpaid','Automatic Invoice after delivery'), - ('picking','Invoice from the packings'), - ], 'Shipping Policy', required=True, readonly=True, states={'draft':[('readonly',False)]}, - help="""The Shipping Policy is used to synchronise invoice and delivery operations. + _name = "sale.order" + _description = "Sale Order" + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({ + 'state':'draft', + 'shipped':False, + 'invoice_ids':[], + 'picking_ids':[], + 'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order'), + }) + return super(sale_order, self).copy(cr, uid, id, default, context) + + def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): + res = {} + cur_obj=self.pool.get('res.currency') + for sale in self.browse(cr, uid, ids): + res[sale.id] = 0.0 + for line in sale.order_line: + res[sale.id] += line.price_subtotal + cur = sale.pricelist_id.currency_id + res[sale.id] = cur_obj.round(cr, uid, cur, res[sale.id]) + return res + + def _amount_tax(self, cr, uid, ids, field_name, arg, context): + res = {} + cur_obj=self.pool.get('res.currency') + for order in self.browse(cr, uid, ids): + val = 0.0 + cur=order.pricelist_id.currency_id + for line in order.order_line: + for c in self.pool.get('account.tax').compute(cr, uid, line.tax_id, line.price_unit * (1-(line.discount or 0.0)/100.0), line.product_uom_qty, order.partner_invoice_id.id, line.product_id, order.partner_id): + val+= cur_obj.round(cr, uid, cur, c['amount']) + res[order.id]=cur_obj.round(cr, uid, cur, val) + return res + + def _amount_total(self, cr, uid, ids, field_name, arg, context): + res = {} + untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context) + tax = self._amount_tax(cr, uid, ids, field_name, arg, context) + cur_obj=self.pool.get('res.currency') + for id in ids: + order=self.browse(cr, uid, [id])[0] + cur=order.pricelist_id.currency_id + res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0)) + return res + + def _invoiced(self, cursor, user, ids, name, arg, context=None): + res = {} + for sale in self.browse(cursor, user, ids, context=context): + res[sale.id] = True + for invoice in sale.invoice_ids: + if invoice.state <> 'paid': + res[sale.id] = False + break + if not sale.invoice_ids: + res[sale.id] = False + return res + + def _invoiced_search(self, cursor, user, obj, name, args): + if not len(args): + return [] + + clause = '' + no_invoiced = False + for arg in args: + if arg[1] == '=': + if arg[2]: + clause += 'AND inv.state = \'paid\'' + else: + clause += 'AND inv.state <> \'paid\'' + no_invoiced = True + + cursor.execute('SELECT rel.order_id ' \ + 'FROM sale_order_invoice_rel AS rel, account_invoice AS inv ' \ + 'WHERE rel.invoice_id = inv.id ' + clause) + res = cursor.fetchall() + if no_invoiced: + cursor.execute('SELECT sale.id ' \ + 'FROM sale_order AS sale ' \ + 'WHERE sale.id NOT IN ' \ + '(SELECT rel.order_id ' \ + 'FROM sale_order_invoice_rel AS rel)') + res.extend(cursor.fetchall()) + if not res: + return [('id', '=', 0)] + return [('id', 'in', [x[0] for x in res])] + + _columns = { + 'name': fields.char('Order Reference', size=64, required=True, select=True), + 'shop_id':fields.many2one('sale.shop', 'Shop', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'origin': fields.char('Origin', size=64), + 'client_order_ref': fields.char('Customer Ref.',size=64), + + 'state': fields.selection([ + ('draft','Quotation'), + ('waiting_date','Waiting Schedule'), + ('manual','Manual in progress'), + ('progress','In progress'), + ('shipping_except','Shipping Exception'), + ('invoice_except','Invoice Exception'), + ('done','Done'), + ('cancel','Cancel') + ], 'Order State', readonly=True, help="Gives the state of the quotation or sale order. The exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the packing list process (Shipping Exception). The 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to be on the date 'Date Ordered'.", select=True), + 'date_order':fields.date('Date Ordered', required=True, readonly=True, states={'draft':[('readonly',False)]}), + + 'user_id':fields.many2one('res.users', 'Salesman', states={'draft':[('readonly',False)]}, select=True), + 'partner_id':fields.many2one('res.partner', 'Customer', readonly=True, states={'draft':[('readonly',False)]}, change_default=True, select=True), + 'partner_invoice_id':fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), + 'partner_order_id':fields.many2one('res.partner.address', 'Ordering Contact', readonly=True, required=True, states={'draft':[('readonly',False)]}, help="The name and address of the contact that requested the order or quotation."), + 'partner_shipping_id':fields.many2one('res.partner.address', 'Shipping Address', readonly=True, required=True, states={'draft':[('readonly',False)]}), + + 'incoterm': fields.selection(_incoterm_get, 'Incoterm',size=3), + 'picking_policy': fields.selection([('direct','Direct Delivery'),('one','All at once')], 'Packing Policy', required=True ), + 'order_policy': fields.selection([ + ('prepaid','Payment before delivery'), + ('manual','Shipping & Manual Invoice'), + ('postpaid','Automatic Invoice after delivery'), + ('picking','Invoice from the packings'), + ], 'Shipping Policy', required=True, readonly=True, states={'draft':[('readonly',False)]}, + help="""The Shipping Policy is used to synchronise invoice and delivery operations. - The 'Pay before delivery' choice will first generate the invoice and then generate the packing order after the payment of this invoice. - The 'Shipping & Manual Invoice' will create the packing order directly and wait for the user to manually click on the 'Invoice' button to generate the draft invoice. - The 'Invoice after delivery' choice will generate the draft invoice after the packing list have been finished. - The 'Invoice from the packings' choice is used to create an invoice during the packing process."""), - 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'project_id':fields.many2one('account.analytic.account', 'Analytic account', readonly=True, states={'draft':[('readonly', False)]}), - - 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft':[('readonly',False)]}), - 'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoice', help="This is the list of invoices that have been generated for this sale order. The same sale order may have been invoiced in several times (by line for example)."), - 'picking_ids': fields.one2many('stock.picking', 'sale_id', 'Packing List', readonly=True, help="This is the list of picking list that have been generated for this invoice"), - 'shipped':fields.boolean('Picked', readonly=True), - 'invoiced': fields.function(_invoiced, method=True, string='Paid', - fnct_search=_invoiced_search, type='boolean'), - 'note': fields.text('Notes'), - 'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'), - 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'), - 'amount_total': fields.function(_amount_total, method=True, string='Total'), - 'invoice_quantity': fields.selection([('order','Ordered Quantities'),('procurement','Shipped Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks."), - } - _defaults = { - 'picking_policy': lambda *a: 'direct', - 'date_order': lambda *a: time.strftime('%Y-%m-%d'), - 'order_policy': lambda *a: 'manual', - 'state': lambda *a: 'draft', - 'user_id': lambda obj, cr, uid, context: uid, - 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'sale.order'), - 'invoice_quantity': lambda *a: 'order', - 'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'], - 'partner_order_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['contact'])['contact'], - 'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'], - 'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist.id, - } - _order = 'name desc' - - # Form filling - def onchange_shop_id(self, cr, uid, ids, shop_id): - v={} - if shop_id: - shop=self.pool.get('sale.shop').browse(cr,uid,shop_id) - v['project_id']=shop.project_id.id - # Que faire si le client a une pricelist a lui ? - if shop.pricelist_id.id: - v['pricelist_id']=shop.pricelist_id.id - #v['payment_default_id']=shop.payment_default_id.id - return {'value':v} - - def action_cancel_draft(self, cr, uid, ids, *args): - if not len(ids): - return False - cr.execute('select id from sale_order_line where order_id in ('+','.join(map(str, ids))+')', ('draft',)) - line_ids = map(lambda x: x[0], cr.fetchall()) - self.write(cr, uid, ids, {'state':'draft', 'invoice_ids':[], 'shipped':0}) - self.pool.get('sale.order.line').write(cr, uid, line_ids, {'invoiced':False, 'state':'draft', 'invoice_lines':[(6,0,[])]}) - wf_service = netsvc.LocalService("workflow") - for inv_id in ids: - wf_service.trg_create(uid, 'sale.order', inv_id, cr) - return True - - def onchange_partner_id(self, cr, uid, ids, part): - if not part: - return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'partner_order_id':False}} - addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['delivery','invoice','contact']) - pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist.id - return {'value':{'partner_invoice_id': addr['invoice'], 'partner_order_id':addr['contact'], 'partner_shipping_id':addr['delivery'], 'pricelist_id': pricelist}} - - def button_dummy(self, cr, uid, ids, context={}): - return True + 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'project_id':fields.many2one('account.analytic.account', 'Analytic account', readonly=True, states={'draft':[('readonly', False)]}), + + 'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', readonly=True, states={'draft':[('readonly',False)]}), + 'invoice_ids': fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', 'Invoice', help="This is the list of invoices that have been generated for this sale order. The same sale order may have been invoiced in several times (by line for example)."), + 'picking_ids': fields.one2many('stock.picking', 'sale_id', 'Packing List', readonly=True, help="This is the list of picking list that have been generated for this invoice"), + 'shipped':fields.boolean('Picked', readonly=True), + 'invoiced': fields.function(_invoiced, method=True, string='Paid', + fnct_search=_invoiced_search, type='boolean'), + 'note': fields.text('Notes'), + 'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'), + 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'), + 'amount_total': fields.function(_amount_total, method=True, string='Total'), + 'invoice_quantity': fields.selection([('order','Ordered Quantities'),('procurement','Shipped Quantities')], 'Invoice on', help="The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks."), + } + _defaults = { + 'picking_policy': lambda *a: 'direct', + 'date_order': lambda *a: time.strftime('%Y-%m-%d'), + 'order_policy': lambda *a: 'manual', + 'state': lambda *a: 'draft', + 'user_id': lambda obj, cr, uid, context: uid, + 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'sale.order'), + 'invoice_quantity': lambda *a: 'order', + 'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'], + 'partner_order_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['contact'])['contact'], + 'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'], + 'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist.id, + } + _order = 'name desc' + + # Form filling + def onchange_shop_id(self, cr, uid, ids, shop_id): + v={} + if shop_id: + shop=self.pool.get('sale.shop').browse(cr,uid,shop_id) + v['project_id']=shop.project_id.id + # Que faire si le client a une pricelist a lui ? + if shop.pricelist_id.id: + v['pricelist_id']=shop.pricelist_id.id + #v['payment_default_id']=shop.payment_default_id.id + return {'value':v} + + def action_cancel_draft(self, cr, uid, ids, *args): + if not len(ids): + return False + cr.execute('select id from sale_order_line where order_id in ('+','.join(map(str, ids))+')', ('draft',)) + line_ids = map(lambda x: x[0], cr.fetchall()) + self.write(cr, uid, ids, {'state':'draft', 'invoice_ids':[], 'shipped':0}) + self.pool.get('sale.order.line').write(cr, uid, line_ids, {'invoiced':False, 'state':'draft', 'invoice_lines':[(6,0,[])]}) + wf_service = netsvc.LocalService("workflow") + for inv_id in ids: + wf_service.trg_create(uid, 'sale.order', inv_id, cr) + return True + + def onchange_partner_id(self, cr, uid, ids, part): + if not part: + return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'partner_order_id':False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['delivery','invoice','contact']) + pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist.id + return {'value':{'partner_invoice_id': addr['invoice'], 'partner_order_id':addr['contact'], 'partner_shipping_id':addr['delivery'], 'pricelist_id': pricelist}} + + def button_dummy(self, cr, uid, ids, context={}): + return True #FIXME: the method should return the list of invoices created (invoice_ids) # and not the id of the last invoice created (res). The problem is that we # cannot change it directly since the method is called by the sale order # workflow and I suppose it expects a single id... - def _inv_get(self, cr, uid, order, context={}): - return {} - - def _make_invoice(self, cr, uid, order, lines): - a = order.partner_id.property_account_receivable.id - if order.partner_id and order.partner_id.property_payment_term.id: - pay_term = order.partner_id.property_payment_term.id - else: - pay_term = False - for preinv in order.invoice_ids: - if preinv.state in ('open','paid','proforma'): - for preline in preinv.invoice_line: - inv_line_id = self.pool.get('account.invoice.line').copy(cr, uid, preline.id, {'invoice_id':False, 'price_unit':-preline.price_unit}) - lines.append(inv_line_id) - inv = { - 'name': order.client_order_ref or order.name, - 'origin': order.name, - 'type': 'out_invoice', - 'reference': "P%dSO%d"%(order.partner_id.id,order.id), - 'account_id': a, - 'partner_id': order.partner_id.id, - 'address_invoice_id': order.partner_invoice_id.id, - 'address_contact_id': order.partner_invoice_id.id, - 'invoice_line': [(6,0,lines)], - 'currency_id' : order.pricelist_id.currency_id.id, - 'comment': order.note, - 'payment_term': pay_term, - } - inv.update(self._inv_get(cr, uid, order)) - inv_obj = self.pool.get('account.invoice') - inv_id = inv_obj.create(cr, uid, inv) - inv_obj.button_compute(cr, uid, [inv_id]) - return inv_id - - - def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed','done']): - res = False - invoices = {} - invoice_ids = [] - - for o in self.browse(cr,uid,ids): - lines = [] - for line in o.order_line: - if (line.state in states) and not line.invoiced: - lines.append(line.id) - created_lines = self.pool.get('sale.order.line').invoice_line_create(cr, uid, lines) - if created_lines: - invoices.setdefault(o.partner_id.id, []).append((o, created_lines)) - - if not invoices: - for o in self.browse(cr, uid, ids): - for i in o.invoice_ids: - if i.state == 'draft': - return i.id - - for val in invoices.values(): - if grouped: - res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x,y: x + y, [l for o,l in val], [])) - for o,l in val: - self.write(cr, uid, [o.id], {'state' : 'progress'}) - cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%d,%d)', (o.id, res)) - else: - for order, il in val: - res = self._make_invoice(cr, uid, order, il) - invoice_ids.append(res) - self.write(cr, uid, [order.id], {'state' : 'progress'}) - cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%d,%d)', (order.id, res)) - return res - - def action_invoice_cancel(self, cr, uid, ids, context={}): - for sale in self.browse(cr, uid, ids): - for line in sale.order_line: - invoiced=False - for iline in line.invoice_lines: - if iline.invoice_id and iline.invoice_id.state == 'cancel': - continue - else: - invoiced=True - self.pool.get('sale.order.line').write(cr, uid, [line.id], {'invoiced': invoiced}) - self.write(cr, uid, ids, {'state':'invoice_except', 'invoice_ids':False}) - return True - - - def action_cancel(self, cr, uid, ids, context={}): - ok = True - sale_order_line_obj = self.pool.get('sale.order.line') - for sale in self.browse(cr, uid, ids): - for pick in sale.picking_ids: - if pick.state not in ('draft','cancel'): - raise osv.except_osv( - _('Could not cancel sale order !'), - _('You must first cancel all packings attached to this sale order.')) - for r in self.read(cr,uid,ids,['picking_ids']): - for pick in r['picking_ids']: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr) - for inv in sale.invoice_ids: - if inv.state not in ('draft','cancel'): - raise osv.except_osv( - _('Could not cancel this sale order !'), - _('You must first cancel all invoices attached to this sale order.')) - for r in self.read(cr,uid,ids,['invoice_ids']): - for inv in r['invoice_ids']: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'account.invoice', inv, 'invoice_cancel', cr) - sale_order_line_obj.write(cr, uid, [l.id for l in sale.order_line], - {'state': 'cancel'}) - self.write(cr,uid,ids,{'state':'cancel'}) - return True - - def action_wait(self, cr, uid, ids, *args): - event_p = self.pool.get('res.partner.event.type').check(cr, uid, 'sale_open') - event_obj = self.pool.get('res.partner.event') - for o in self.browse(cr, uid, ids): - if event_p: - event_obj.create(cr, uid, {'name': 'Sale Order: '+ o.name,\ - 'partner_id': o.partner_id.id,\ - 'date': time.strftime('%Y-%m-%d %H:%M:%S'),\ - 'user_id': (o.user_id and o.user_id.id) or uid,\ - 'partner_type': 'customer', 'probability': 1.0,\ - 'planned_revenue': o.amount_untaxed}) - if (o.order_policy == 'manual') and (not o.invoice_ids): - self.write(cr, uid, [o.id], {'state': 'manual'}) - else: - self.write(cr, uid, [o.id], {'state': 'progress'}) - self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line]) - - def procurement_lines_get(self, cr, uid, ids, *args): - res = [] - for order in self.browse(cr, uid, ids, context={}): - for line in order.order_line: - if line.procurement_id: - res.append(line.procurement_id.id) - return res - - # if mode == 'finished': - # returns True if all lines are done, False otherwise - # if mode == 'canceled': - # returns True if there is at least one canceled line, False otherwise - def test_state(self, cr, uid, ids, mode, *args): - assert mode in ('finished', 'canceled'), _("invalid mode for test_state") - finished = True - canceled = False - write_done_ids = [] - write_cancel_ids = [] - for order in self.browse(cr, uid, ids, context={}): - for line in order.order_line: - if line.procurement_id and (line.procurement_id.state != 'done') and (line.state!='done'): - finished = False - if line.procurement_id and line.procurement_id.state == 'cancel': - canceled = True - # if a line is finished (ie its procuremnt is done or it has not procuremernt and it - # is not already marked as done, mark it as being so... - if ((not line.procurement_id) or line.procurement_id.state == 'done') and line.state != 'done': - write_done_ids.append(line.id) - # ... same for canceled lines - if line.procurement_id and line.procurement_id.state == 'cancel' and line.state != 'cancel': - write_cancel_ids.append(line.id) - if write_done_ids: - self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) - if write_cancel_ids: - self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'cancel'}) - - if mode=='finished': - return finished - elif mode=='canceled': - return canceled - - def action_ship_create(self, cr, uid, ids, *args): - picking_id=False - for order in self.browse(cr, uid, ids, context={}): - output_id = order.shop_id.warehouse_id.lot_output_id.id - picking_id = False - for line in order.order_line: - proc_id=False - date_planned = (DateTime.now() + DateTime.RelativeDateTime(days=line.delay or 0.0)).strftime('%Y-%m-%d') - if line.state == 'done': - continue - if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'): - location_id = order.shop_id.warehouse_id.lot_stock_id.id - if not picking_id: - loc_dest_id = order.partner_id.property_stock_customer.id - picking_id = self.pool.get('stock.picking').create(cr, uid, { - 'origin': order.name, - 'type': 'out', - 'state': 'auto', - 'move_type': order.picking_policy, - 'sale_id': order.id, - 'address_id': order.partner_shipping_id.id, - 'note': order.note, - 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', - - }) - - move_id = self.pool.get('stock.move').create(cr, uid, { - 'name': line.name[:64], - 'picking_id': picking_id, - 'product_id': line.product_id.id, - 'date_planned': date_planned, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos_qty, - 'product_uos': (line.product_uos and line.product_uos.id)\ - or line.product_uom.id, - 'product_packaging' : line.product_packaging.id, - 'address_id' : line.address_allotment_id.id or order.partner_shipping_id.id, - 'location_id': location_id, - 'location_dest_id': output_id, - 'sale_line_id': line.id, - 'tracking_id': False, - 'state': 'waiting', - 'note': line.notes, - }) - proc_id = self.pool.get('mrp.procurement').create(cr, uid, { - 'name': order.name, - 'origin': order.name, - 'date_planned': date_planned, - 'product_id': line.product_id.id, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos_qty, - 'product_uos': (line.product_uos and line.product_uos.id)\ - or line.product_uom.id, - 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, - 'procure_method': line.type, - 'move_id': move_id, - 'property_ids': [(6, 0, [x.id for x in line.property_ids])], - }) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) - self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id}) - elif line.product_id and line.product_id.product_tmpl_id.type=='service': - proc_id = self.pool.get('mrp.procurement').create(cr, uid, { - 'name': line.name, - 'origin': order.name, - 'date_planned': date_planned, - 'product_id': line.product_id.id, - 'product_qty': line.product_uom_qty, - 'product_uom': line.product_uom.id, - 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, - 'procure_method': line.type, - 'property_ids': [(6, 0, [x.id for x in line.property_ids])], - }) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) - self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id}) - else: - # - # No procurement because no product in the sale.order.line. - # - pass - - val = {} - if picking_id: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) - - if order.state=='shipping_except': - val['state'] = 'progress' - if (order.order_policy == 'manual') and order.invoice_ids: - val['state'] = 'manual' - self.write(cr, uid, [order.id], val) - - return True - - def action_ship_end(self, cr, uid, ids, context={}): - for order in self.browse(cr, uid, ids): - val = {'shipped':True} - if order.state=='shipping_except': - if (order.order_policy=='manual') and not order.invoice_ids: - val['state'] = 'manual' - else: - val['state'] = 'progress' - self.write(cr, uid, [order.id], val) - return True - - def _log_event(self, cr, uid, ids, factor=0.7, name='Open Order'): - invs = self.read(cr, uid, ids, ['date_order','partner_id','amount_untaxed']) - for inv in invs: - part=inv['partner_id'] and inv['partner_id'][0] - pr = inv['amount_untaxed'] or 0.0 - partnertype = 'customer' - eventtype = 'sale' - self.pool.get('res.partner.event').create(cr, uid, {'name':'Order: '+name, 'som':False, 'description':'Order '+str(inv['id']), 'document':'', 'partner_id':part, 'date':time.strftime('%Y-%m-%d'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':0.0, 'type':eventtype}) - - def has_stockable_products(self,cr, uid, ids, *args): - for order in self.browse(cr, uid, ids): - for order_line in order.order_line: - if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'): - return True - return False + def _inv_get(self, cr, uid, order, context={}): + return {} + + def _make_invoice(self, cr, uid, order, lines): + a = order.partner_id.property_account_receivable.id + if order.partner_id and order.partner_id.property_payment_term.id: + pay_term = order.partner_id.property_payment_term.id + else: + pay_term = False + for preinv in order.invoice_ids: + if preinv.state in ('open','paid','proforma'): + for preline in preinv.invoice_line: + inv_line_id = self.pool.get('account.invoice.line').copy(cr, uid, preline.id, {'invoice_id':False, 'price_unit':-preline.price_unit}) + lines.append(inv_line_id) + inv = { + 'name': order.client_order_ref or order.name, + 'origin': order.name, + 'type': 'out_invoice', + 'reference': "P%dSO%d"%(order.partner_id.id,order.id), + 'account_id': a, + 'partner_id': order.partner_id.id, + 'address_invoice_id': order.partner_invoice_id.id, + 'address_contact_id': order.partner_invoice_id.id, + 'invoice_line': [(6,0,lines)], + 'currency_id' : order.pricelist_id.currency_id.id, + 'comment': order.note, + 'payment_term': pay_term, + } + inv.update(self._inv_get(cr, uid, order)) + inv_obj = self.pool.get('account.invoice') + inv_id = inv_obj.create(cr, uid, inv) + inv_obj.button_compute(cr, uid, [inv_id]) + return inv_id + + + def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed','done']): + res = False + invoices = {} + invoice_ids = [] + + for o in self.browse(cr,uid,ids): + lines = [] + for line in o.order_line: + if (line.state in states) and not line.invoiced: + lines.append(line.id) + created_lines = self.pool.get('sale.order.line').invoice_line_create(cr, uid, lines) + if created_lines: + invoices.setdefault(o.partner_id.id, []).append((o, created_lines)) + + if not invoices: + for o in self.browse(cr, uid, ids): + for i in o.invoice_ids: + if i.state == 'draft': + return i.id + + for val in invoices.values(): + if grouped: + res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x,y: x + y, [l for o,l in val], [])) + for o,l in val: + self.write(cr, uid, [o.id], {'state' : 'progress'}) + cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%d,%d)', (o.id, res)) + else: + for order, il in val: + res = self._make_invoice(cr, uid, order, il) + invoice_ids.append(res) + self.write(cr, uid, [order.id], {'state' : 'progress'}) + cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%d,%d)', (order.id, res)) + return res + + def action_invoice_cancel(self, cr, uid, ids, context={}): + for sale in self.browse(cr, uid, ids): + for line in sale.order_line: + invoiced=False + for iline in line.invoice_lines: + if iline.invoice_id and iline.invoice_id.state == 'cancel': + continue + else: + invoiced=True + self.pool.get('sale.order.line').write(cr, uid, [line.id], {'invoiced': invoiced}) + self.write(cr, uid, ids, {'state':'invoice_except', 'invoice_ids':False}) + return True + + + def action_cancel(self, cr, uid, ids, context={}): + ok = True + sale_order_line_obj = self.pool.get('sale.order.line') + for sale in self.browse(cr, uid, ids): + for pick in sale.picking_ids: + if pick.state not in ('draft','cancel'): + raise osv.except_osv( + _('Could not cancel sale order !'), + _('You must first cancel all packings attached to this sale order.')) + for r in self.read(cr,uid,ids,['picking_ids']): + for pick in r['picking_ids']: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', pick, 'button_cancel', cr) + for inv in sale.invoice_ids: + if inv.state not in ('draft','cancel'): + raise osv.except_osv( + _('Could not cancel this sale order !'), + _('You must first cancel all invoices attached to this sale order.')) + for r in self.read(cr,uid,ids,['invoice_ids']): + for inv in r['invoice_ids']: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'account.invoice', inv, 'invoice_cancel', cr) + sale_order_line_obj.write(cr, uid, [l.id for l in sale.order_line], + {'state': 'cancel'}) + self.write(cr,uid,ids,{'state':'cancel'}) + return True + + def action_wait(self, cr, uid, ids, *args): + event_p = self.pool.get('res.partner.event.type').check(cr, uid, 'sale_open') + event_obj = self.pool.get('res.partner.event') + for o in self.browse(cr, uid, ids): + if event_p: + event_obj.create(cr, uid, {'name': 'Sale Order: '+ o.name,\ + 'partner_id': o.partner_id.id,\ + 'date': time.strftime('%Y-%m-%d %H:%M:%S'),\ + 'user_id': (o.user_id and o.user_id.id) or uid,\ + 'partner_type': 'customer', 'probability': 1.0,\ + 'planned_revenue': o.amount_untaxed}) + if (o.order_policy == 'manual') and (not o.invoice_ids): + self.write(cr, uid, [o.id], {'state': 'manual'}) + else: + self.write(cr, uid, [o.id], {'state': 'progress'}) + self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line]) + + def procurement_lines_get(self, cr, uid, ids, *args): + res = [] + for order in self.browse(cr, uid, ids, context={}): + for line in order.order_line: + if line.procurement_id: + res.append(line.procurement_id.id) + return res + + # if mode == 'finished': + # returns True if all lines are done, False otherwise + # if mode == 'canceled': + # returns True if there is at least one canceled line, False otherwise + def test_state(self, cr, uid, ids, mode, *args): + assert mode in ('finished', 'canceled'), _("invalid mode for test_state") + finished = True + canceled = False + write_done_ids = [] + write_cancel_ids = [] + for order in self.browse(cr, uid, ids, context={}): + for line in order.order_line: + if line.procurement_id and (line.procurement_id.state != 'done') and (line.state!='done'): + finished = False + if line.procurement_id and line.procurement_id.state == 'cancel': + canceled = True + # if a line is finished (ie its procuremnt is done or it has not procuremernt and it + # is not already marked as done, mark it as being so... + if ((not line.procurement_id) or line.procurement_id.state == 'done') and line.state != 'done': + write_done_ids.append(line.id) + # ... same for canceled lines + if line.procurement_id and line.procurement_id.state == 'cancel' and line.state != 'cancel': + write_cancel_ids.append(line.id) + if write_done_ids: + self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'}) + if write_cancel_ids: + self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'cancel'}) + + if mode=='finished': + return finished + elif mode=='canceled': + return canceled + + def action_ship_create(self, cr, uid, ids, *args): + picking_id=False + for order in self.browse(cr, uid, ids, context={}): + output_id = order.shop_id.warehouse_id.lot_output_id.id + picking_id = False + for line in order.order_line: + proc_id=False + date_planned = (DateTime.now() + DateTime.RelativeDateTime(days=line.delay or 0.0)).strftime('%Y-%m-%d') + if line.state == 'done': + continue + if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'): + location_id = order.shop_id.warehouse_id.lot_stock_id.id + if not picking_id: + loc_dest_id = order.partner_id.property_stock_customer.id + picking_id = self.pool.get('stock.picking').create(cr, uid, { + 'origin': order.name, + 'type': 'out', + 'state': 'auto', + 'move_type': order.picking_policy, + 'sale_id': order.id, + 'address_id': order.partner_shipping_id.id, + 'note': order.note, + 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none', + + }) + + move_id = self.pool.get('stock.move').create(cr, uid, { + 'name': line.name[:64], + 'picking_id': picking_id, + 'product_id': line.product_id.id, + 'date_planned': date_planned, + 'product_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos_qty, + 'product_uos': (line.product_uos and line.product_uos.id)\ + or line.product_uom.id, + 'product_packaging' : line.product_packaging.id, + 'address_id' : line.address_allotment_id.id or order.partner_shipping_id.id, + 'location_id': location_id, + 'location_dest_id': output_id, + 'sale_line_id': line.id, + 'tracking_id': False, + 'state': 'waiting', + 'note': line.notes, + }) + proc_id = self.pool.get('mrp.procurement').create(cr, uid, { + 'name': order.name, + 'origin': order.name, + 'date_planned': date_planned, + 'product_id': line.product_id.id, + 'product_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'product_uos_qty': line.product_uos_qty, + 'product_uos': (line.product_uos and line.product_uos.id)\ + or line.product_uom.id, + 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, + 'procure_method': line.type, + 'move_id': move_id, + 'property_ids': [(6, 0, [x.id for x in line.property_ids])], + }) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) + self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id}) + elif line.product_id and line.product_id.product_tmpl_id.type=='service': + proc_id = self.pool.get('mrp.procurement').create(cr, uid, { + 'name': line.name, + 'origin': order.name, + 'date_planned': date_planned, + 'product_id': line.product_id.id, + 'product_qty': line.product_uom_qty, + 'product_uom': line.product_uom.id, + 'location_id': order.shop_id.warehouse_id.lot_stock_id.id, + 'procure_method': line.type, + 'property_ids': [(6, 0, [x.id for x in line.property_ids])], + }) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr) + self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id}) + else: + # + # No procurement because no product in the sale.order.line. + # + pass + + val = {} + if picking_id: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + + if order.state=='shipping_except': + val['state'] = 'progress' + if (order.order_policy == 'manual') and order.invoice_ids: + val['state'] = 'manual' + self.write(cr, uid, [order.id], val) + + return True + + def action_ship_end(self, cr, uid, ids, context={}): + for order in self.browse(cr, uid, ids): + val = {'shipped':True} + if order.state=='shipping_except': + if (order.order_policy=='manual') and not order.invoice_ids: + val['state'] = 'manual' + else: + val['state'] = 'progress' + self.write(cr, uid, [order.id], val) + return True + + def _log_event(self, cr, uid, ids, factor=0.7, name='Open Order'): + invs = self.read(cr, uid, ids, ['date_order','partner_id','amount_untaxed']) + for inv in invs: + part=inv['partner_id'] and inv['partner_id'][0] + pr = inv['amount_untaxed'] or 0.0 + partnertype = 'customer' + eventtype = 'sale' + self.pool.get('res.partner.event').create(cr, uid, {'name':'Order: '+name, 'som':False, 'description':'Order '+str(inv['id']), 'document':'', 'partner_id':part, 'date':time.strftime('%Y-%m-%d'), 'canal_id':False, 'user_id':uid, 'partner_type':partnertype, 'probability':1.0, 'planned_revenue':pr, 'planned_cost':0.0, 'type':eventtype}) + + def has_stockable_products(self,cr, uid, ids, *args): + for order in self.browse(cr, uid, ids): + for order_line in order.order_line: + if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + return True + return False sale_order() # TODO add a field price_unit_uos # - update it on change product and unit price # - use it in report if there is a uos class sale_order_line(osv.osv): - def copy(self, cr, uid, id, default=None, context={}): - if not default: default = {} - default.update( {'invoice_lines':[]}) - return super(sale_order_line, self).copy(cr, uid, id, default, context) - - def _amount_line_net(self, cr, uid, ids, field_name, arg, context): - res = {} - for line in self.browse(cr, uid, ids): - res[line.id] = line.price_unit * (1 - (line.discount or 0.0) / 100.0) - return res - - def _amount_line(self, cr, uid, ids, field_name, arg, context): - res = {} - cur_obj=self.pool.get('res.currency') - for line in self.browse(cr, uid, ids): - res[line.id] = line.price_unit * line.product_uom_qty * (1 - (line.discount or 0.0) / 100.0) - cur = line.order_id.pricelist_id.currency_id - res[line.id] = cur_obj.round(cr, uid, cur, res[line.id]) - return res - - def _number_packages(self, cr, uid, ids, field_name, arg, context): - res = {} - for line in self.browse(cr, uid, ids): - try: - res[line.id] = int(line.product_uom_qty / line.product_packaging.qty) - except: - res[line.id] = 1 - return res - - def _get_1st_packaging(self, cr, uid, context={}): - cr.execute('select id from product_packaging order by id asc limit 1') - res = cr.fetchone() - if not res: - return False - return res[0] - - _name = 'sale.order.line' - _description = 'Sale Order line' - _columns = { - 'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True), - 'name': fields.char('Description', size=256, required=True, select=True), - 'sequence': fields.integer('Sequence'), - 'delay': fields.float('Delivery Delay', required=True), - 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], change_default=True), - 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id','invoice_id', 'Invoice Lines', readonly=True), - 'invoiced': fields.boolean('Invoiced', readonly=True), - 'procurement_id': fields.many2one('mrp.procurement', 'Procurement'), - 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), - 'price_net': fields.function(_amount_line_net, method=True, string='Net Price', digits=(16, int(config['price_accuracy']))), - 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), - 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes'), - 'type': fields.selection([('make_to_stock','from stock'),('make_to_order','on order')],'Procure Method', required=True), - 'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties'), - 'address_allotment_id' : fields.many2one('res.partner.address', 'Allotment Partner'), - 'product_uom_qty': fields.float('Quantity (UOM)', digits=(16,2), required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_uos_qty': fields.float('Quantity (UOS)'), - 'product_uos': fields.many2one('product.uom', 'Product UOS'), - 'product_packaging': fields.many2one('product.packaging', 'Packaging used'), - 'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True), - 'discount': fields.float('Discount (%)', digits=(16,2)), - 'number_packages': fields.function(_number_packages, method=True, type='integer', string='Number packages'), - 'notes': fields.text('Notes'), - 'th_weight' : fields.float('Weight'), - 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('done','Done'),('cancel','Canceled')], 'State', required=True, readonly=True), - } - _order = 'sequence, id' - _defaults = { - 'discount': lambda *a: 0.0, - 'delay': lambda *a: 0.0, - 'product_uom_qty': lambda *a: 1, - 'product_uos_qty': lambda *a: 1, - 'sequence': lambda *a: 10, - 'invoiced': lambda *a: 0, - 'state': lambda *a: 'draft', - 'type': lambda *a: 'make_to_stock', - 'product_packaging': _get_1st_packaging, - } - def invoice_line_create(self, cr, uid, ids, context={}): - def _get_line_qty(line): - if (line.order_id.invoice_quantity=='order') or not line.procurement_id: - if line.product_uos: - return line.product_uos_qty or 0.0 - return line.product_uom_qty - else: - return self.pool.get('mrp.procurement').quantity_get(cr, uid, - line.procurement_id.id, context) - - def _get_line_uom(line): - if (line.order_id.invoice_quantity=='order') or not line.procurement_id: - if line.product_uos: - return line.product_uos.id - return line.product_uom.id - else: - return self.pool.get('mrp.procurement').uom_get(cr, uid, - line.procurement_id.id, context) - - create_ids = [] - for line in self.browse(cr, uid, ids, context): - if not line.invoiced: - if line.product_id: - a = line.product_id.product_tmpl_id.property_account_income.id - if not a: - a = line.product_id.categ_id.property_account_income_categ.id - if not a: - raise osv.except_osv(_('Error !'), - _('There is no income account defined ' \ - 'for this product: "%s" (id:%d)') % \ - (line.product_id.name, line.product_id.id,)) - else: - a = self.pool.get('ir.property').get(cr, uid, - 'property_account_income_categ', 'product.category', - context=context) - uosqty = _get_line_qty(line) - uos_id = _get_line_uom(line) - pu = 0.0 - if uosqty: - pu = round(line.price_unit * line.product_uom_qty / uosqty, - int(config['price_accuracy'])) - inv_id = self.pool.get('account.invoice.line').create(cr, uid, { - 'name': line.name, - 'account_id': a, - 'price_unit': pu, - 'quantity': uosqty, - 'discount': line.discount, - 'uos_id': uos_id, - 'product_id': line.product_id.id or False, - 'invoice_line_tax_id': [(6,0,[x.id for x in line.tax_id])], - 'note': line.notes, - 'account_analytic_id': line.order_id.project_id.id, - }) - cr.execute('insert into sale_order_line_invoice_rel (order_line_id,invoice_id) values (%d,%d)', (line.id, inv_id)) - self.write(cr, uid, [line.id], {'invoiced':True}) - create_ids.append(inv_id) - return create_ids - - def button_confirm(self, cr, uid, ids, context={}): - return self.write(cr, uid, ids, {'state':'confirmed'}) - - def button_done(self, cr, uid, ids, context={}): - wf_service = netsvc.LocalService("workflow") - res = self.write(cr, uid, ids, {'state':'done'}) - for line in self.browse(cr,uid,ids,context): - wf_service.trg_write(uid, 'sale.order', line.order_id.id, cr) - - return res - - def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): - product_obj = self.pool.get('product.product') - if not product_id: - return {'value': {'product_uom': product_uos, - 'product_uom_qty': product_uos_qty}, 'domain':{}} - - product = product_obj.browse(cr, uid, product_id) - value = { - 'product_uom' : product.uom_id.id, - } - # FIXME must depend on uos/uom of the product and not only of the coeff. - try: - value.update({ - 'product_uom_qty' : product_uos_qty / product.uos_coeff, - 'th_weight' : product_uos_qty / product.uos_coeff * product.weight - }) - except ZeroDivisionError: - pass - return {'value' : value} - - def copy(self, cr, uid, id, default=None,context={}): - if not default: - default = {} - default.update({'state':'draft', 'move_ids':[], 'invoiced':False, 'invoice_lines':[]}) - return super(sale_order_line, self).copy(cr, uid, id, default, context) - - def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, - uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False): - product_uom_obj = self.pool.get('product.uom') - partner_obj = self.pool.get('res.partner') - product_obj = self.pool.get('product.product') - - if partner_id: - lang = partner_obj.browse(cr, uid, partner_id).lang - context = {'lang': lang, 'partner_id': partner_id} - - if not product: - return {'value': {'th_weight' : 0, - 'product_uos_qty': qty}, 'domain': {'product_uom': [], - 'product_uos': []}} - - if not pricelist: - raise osv.except_osv(_('No Pricelist !'), - _('You have to select a pricelist in the sale form !\n' - 'Please set one before choosing a product.')) - - if not date_order: - date_order = time.strftime('%Y-%m-%d') - price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], - product, qty or 1.0, partner_id, { - 'uom': uom, - 'date': date_order, - })[pricelist] - if price is False: - raise osv.except_osv(_('No valid pricelist line found !'), - _("Couldn't find a pricelist line matching this product and quantity.\n" - "You have to change either the product, the quantity or the pricelist.")) - - product = product_obj.browse(cr, uid, product, context=context) - - if uom: - uom2 = product_uom_obj.browse(cr, uid, uom) - if product.uom_id.category_id.id <> uom2.category_id.id: - uom = False - - if uos: - if product.uos_id: - uos2 = product_uom_obj.browse(cr, uid, uos) - if product.uos_id.category_id.id <> uos2.category_id.id: - uos = False - else: - uos = False - - result = {'price_unit': price, 'type': product.procure_method} - if product.description_sale: - result['notes'] = product.description_sale - - if update_tax: #The quantity only have changed - result['delay'] = (product.sale_delay or 0.0) - taxes = self.pool.get('account.tax').browse(cr, uid, - [x.id for x in product.taxes_id]) - taxep = None - if partner_id: - taxep = self.pool.get('res.partner').browse(cr, uid, - partner_id).property_account_tax - if not taxep or not taxep.id: - result['tax_id'] = [x.id for x in product.taxes_id] - else: - res5 = [taxep.id] - for t in taxes: - if not t.tax_group==taxep.tax_group: - res5.append(t.id) - result['tax_id'] = res5 - - result['name'] = product.partner_ref - - domain = {} - if not uom and not uos: - result['product_uom'] = product.uom_id.id - if product.uos_id: - result['product_uos'] = product.uos_id.id - result['product_uos_qty'] = qty * product.uos_coeff - uos_category_id = product.uos_id.category_id.id - else: - result['product_uos'] = False - result['product_uos_qty'] = qty - uos_category_id = False - result['th_weight'] = qty * product.weight - domain = {'product_uom': - [('category_id', '=', product.uom_id.category_id.id)], - 'product_uos': - [('category_id', '=', uos_category_id)]} - elif uom: # whether uos is set or not - default_uom = product.uom_id and product.uom_id.id - q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom) - if product.uos_id: - result['product_uos'] = product.uos_id.id - result['product_uos_qty'] = q * product.uos_coeff - else: - result['product_uos'] = False - result['product_uos_qty'] = q - result['th_weight'] = q * product.weight - elif uos: # only happens if uom is False - result['product_uom'] = product.uom_id and product.uom_id.id - result['product_uom_qty'] = qty_uos / product.uos_coeff - result['th_weight'] = result['product_uom_qty'] * product.weight - return {'value': result, 'domain': domain} - - def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0, - uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False): - res = self.product_id_change(cursor, user, ids, pricelist, product, - qty=0, uom=uom, qty_uos=qty_uos, uos=uos, name=name, - partner_id=partner_id, lang=lang, update_tax=update_tax, - date_order=date_order) - if 'product_uom' in res['value']: - del res['value']['product_uom'] - if not uom: - res['value']['price_unit'] = 0.0 - return res + def copy(self, cr, uid, id, default=None, context={}): + if not default: default = {} + default.update( {'invoice_lines':[]}) + return super(sale_order_line, self).copy(cr, uid, id, default, context) + + def _amount_line_net(self, cr, uid, ids, field_name, arg, context): + res = {} + for line in self.browse(cr, uid, ids): + res[line.id] = line.price_unit * (1 - (line.discount or 0.0) / 100.0) + return res + + def _amount_line(self, cr, uid, ids, field_name, arg, context): + res = {} + cur_obj=self.pool.get('res.currency') + for line in self.browse(cr, uid, ids): + res[line.id] = line.price_unit * line.product_uom_qty * (1 - (line.discount or 0.0) / 100.0) + cur = line.order_id.pricelist_id.currency_id + res[line.id] = cur_obj.round(cr, uid, cur, res[line.id]) + return res + + def _number_packages(self, cr, uid, ids, field_name, arg, context): + res = {} + for line in self.browse(cr, uid, ids): + try: + res[line.id] = int(line.product_uom_qty / line.product_packaging.qty) + except: + res[line.id] = 1 + return res + + def _get_1st_packaging(self, cr, uid, context={}): + cr.execute('select id from product_packaging order by id asc limit 1') + res = cr.fetchone() + if not res: + return False + return res[0] + + _name = 'sale.order.line' + _description = 'Sale Order line' + _columns = { + 'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True), + 'name': fields.char('Description', size=256, required=True, select=True), + 'sequence': fields.integer('Sequence'), + 'delay': fields.float('Delivery Delay', required=True), + 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], change_default=True), + 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id','invoice_id', 'Invoice Lines', readonly=True), + 'invoiced': fields.boolean('Invoiced', readonly=True), + 'procurement_id': fields.many2one('mrp.procurement', 'Procurement'), + 'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))), + 'price_net': fields.function(_amount_line_net, method=True, string='Net Price', digits=(16, int(config['price_accuracy']))), + 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), + 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes'), + 'type': fields.selection([('make_to_stock','from stock'),('make_to_order','on order')],'Procure Method', required=True), + 'property_ids': fields.many2many('mrp.property', 'sale_order_line_property_rel', 'order_id', 'property_id', 'Properties'), + 'address_allotment_id' : fields.many2one('res.partner.address', 'Allotment Partner'), + 'product_uom_qty': fields.float('Quantity (UOM)', digits=(16,2), required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_uos_qty': fields.float('Quantity (UOS)'), + 'product_uos': fields.many2one('product.uom', 'Product UOS'), + 'product_packaging': fields.many2one('product.packaging', 'Packaging used'), + 'move_ids': fields.one2many('stock.move', 'sale_line_id', 'Inventory Moves', readonly=True), + 'discount': fields.float('Discount (%)', digits=(16,2)), + 'number_packages': fields.function(_number_packages, method=True, type='integer', string='Number packages'), + 'notes': fields.text('Notes'), + 'th_weight' : fields.float('Weight'), + 'state': fields.selection([('draft','Draft'),('confirmed','Confirmed'),('done','Done'),('cancel','Canceled')], 'State', required=True, readonly=True), + } + _order = 'sequence, id' + _defaults = { + 'discount': lambda *a: 0.0, + 'delay': lambda *a: 0.0, + 'product_uom_qty': lambda *a: 1, + 'product_uos_qty': lambda *a: 1, + 'sequence': lambda *a: 10, + 'invoiced': lambda *a: 0, + 'state': lambda *a: 'draft', + 'type': lambda *a: 'make_to_stock', + 'product_packaging': _get_1st_packaging, + } + def invoice_line_create(self, cr, uid, ids, context={}): + def _get_line_qty(line): + if (line.order_id.invoice_quantity=='order') or not line.procurement_id: + if line.product_uos: + return line.product_uos_qty or 0.0 + return line.product_uom_qty + else: + return self.pool.get('mrp.procurement').quantity_get(cr, uid, + line.procurement_id.id, context) + + def _get_line_uom(line): + if (line.order_id.invoice_quantity=='order') or not line.procurement_id: + if line.product_uos: + return line.product_uos.id + return line.product_uom.id + else: + return self.pool.get('mrp.procurement').uom_get(cr, uid, + line.procurement_id.id, context) + + create_ids = [] + for line in self.browse(cr, uid, ids, context): + if not line.invoiced: + if line.product_id: + a = line.product_id.product_tmpl_id.property_account_income.id + if not a: + a = line.product_id.categ_id.property_account_income_categ.id + if not a: + raise osv.except_osv(_('Error !'), + _('There is no income account defined ' \ + 'for this product: "%s" (id:%d)') % \ + (line.product_id.name, line.product_id.id,)) + else: + a = self.pool.get('ir.property').get(cr, uid, + 'property_account_income_categ', 'product.category', + context=context) + uosqty = _get_line_qty(line) + uos_id = _get_line_uom(line) + pu = 0.0 + if uosqty: + pu = round(line.price_unit * line.product_uom_qty / uosqty, + int(config['price_accuracy'])) + inv_id = self.pool.get('account.invoice.line').create(cr, uid, { + 'name': line.name, + 'account_id': a, + 'price_unit': pu, + 'quantity': uosqty, + 'discount': line.discount, + 'uos_id': uos_id, + 'product_id': line.product_id.id or False, + 'invoice_line_tax_id': [(6,0,[x.id for x in line.tax_id])], + 'note': line.notes, + 'account_analytic_id': line.order_id.project_id.id, + }) + cr.execute('insert into sale_order_line_invoice_rel (order_line_id,invoice_id) values (%d,%d)', (line.id, inv_id)) + self.write(cr, uid, [line.id], {'invoiced':True}) + create_ids.append(inv_id) + return create_ids + + def button_confirm(self, cr, uid, ids, context={}): + return self.write(cr, uid, ids, {'state':'confirmed'}) + + def button_done(self, cr, uid, ids, context={}): + wf_service = netsvc.LocalService("workflow") + res = self.write(cr, uid, ids, {'state':'done'}) + for line in self.browse(cr,uid,ids,context): + wf_service.trg_write(uid, 'sale.order', line.order_id.id, cr) + + return res + + def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + product_obj = self.pool.get('product.product') + if not product_id: + return {'value': {'product_uom': product_uos, + 'product_uom_qty': product_uos_qty}, 'domain':{}} + + product = product_obj.browse(cr, uid, product_id) + value = { + 'product_uom' : product.uom_id.id, + } + # FIXME must depend on uos/uom of the product and not only of the coeff. + try: + value.update({ + 'product_uom_qty' : product_uos_qty / product.uos_coeff, + 'th_weight' : product_uos_qty / product.uos_coeff * product.weight + }) + except ZeroDivisionError: + pass + return {'value' : value} + + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({'state':'draft', 'move_ids':[], 'invoiced':False, 'invoice_lines':[]}) + return super(sale_order_line, self).copy(cr, uid, id, default, context) + + def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + product_uom_obj = self.pool.get('product.uom') + partner_obj = self.pool.get('res.partner') + product_obj = self.pool.get('product.product') + + if partner_id: + lang = partner_obj.browse(cr, uid, partner_id).lang + context = {'lang': lang, 'partner_id': partner_id} + + if not product: + return {'value': {'th_weight' : 0, + 'product_uos_qty': qty}, 'domain': {'product_uom': [], + 'product_uos': []}} + + if not pricelist: + raise osv.except_osv(_('No Pricelist !'), + _('You have to select a pricelist in the sale form !\n' + 'Please set one before choosing a product.')) + + if not date_order: + date_order = time.strftime('%Y-%m-%d') + price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], + product, qty or 1.0, partner_id, { + 'uom': uom, + 'date': date_order, + })[pricelist] + if price is False: + raise osv.except_osv(_('No valid pricelist line found !'), + _("Couldn't find a pricelist line matching this product and quantity.\n" + "You have to change either the product, the quantity or the pricelist.")) + + product = product_obj.browse(cr, uid, product, context=context) + + if uom: + uom2 = product_uom_obj.browse(cr, uid, uom) + if product.uom_id.category_id.id <> uom2.category_id.id: + uom = False + + if uos: + if product.uos_id: + uos2 = product_uom_obj.browse(cr, uid, uos) + if product.uos_id.category_id.id <> uos2.category_id.id: + uos = False + else: + uos = False + + result = {'price_unit': price, 'type': product.procure_method} + if product.description_sale: + result['notes'] = product.description_sale + + if update_tax: #The quantity only have changed + result['delay'] = (product.sale_delay or 0.0) + taxes = self.pool.get('account.tax').browse(cr, uid, + [x.id for x in product.taxes_id]) + taxep = None + if partner_id: + taxep = self.pool.get('res.partner').browse(cr, uid, + partner_id).property_account_tax + if not taxep or not taxep.id: + result['tax_id'] = [x.id for x in product.taxes_id] + else: + res5 = [taxep.id] + for t in taxes: + if not t.tax_group==taxep.tax_group: + res5.append(t.id) + result['tax_id'] = res5 + + result['name'] = product.partner_ref + + domain = {} + if not uom and not uos: + result['product_uom'] = product.uom_id.id + if product.uos_id: + result['product_uos'] = product.uos_id.id + result['product_uos_qty'] = qty * product.uos_coeff + uos_category_id = product.uos_id.category_id.id + else: + result['product_uos'] = False + result['product_uos_qty'] = qty + uos_category_id = False + result['th_weight'] = qty * product.weight + domain = {'product_uom': + [('category_id', '=', product.uom_id.category_id.id)], + 'product_uos': + [('category_id', '=', uos_category_id)]} + elif uom: # whether uos is set or not + default_uom = product.uom_id and product.uom_id.id + q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom) + if product.uos_id: + result['product_uos'] = product.uos_id.id + result['product_uos_qty'] = q * product.uos_coeff + else: + result['product_uos'] = False + result['product_uos_qty'] = q + result['th_weight'] = q * product.weight + elif uos: # only happens if uom is False + result['product_uom'] = product.uom_id and product.uom_id.id + result['product_uom_qty'] = qty_uos / product.uos_coeff + result['th_weight'] = result['product_uom_qty'] * product.weight + return {'value': result, 'domain': domain} + + def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False): + res = self.product_id_change(cursor, user, ids, pricelist, product, + qty=0, uom=uom, qty_uos=qty_uos, uos=uos, name=name, + partner_id=partner_id, lang=lang, update_tax=update_tax, + date_order=date_order) + if 'product_uom' in res['value']: + del res['value']['product_uom'] + if not uom: + res['value']['price_unit'] = 0.0 + return res sale_order_line() @@ -846,31 +846,31 @@ _policy_fields = { 'picking_policy': {'string': 'Packing Policy', 'type': 'selection','selection': [('direct','Direct Delivery'),('one','All at once')],'required': True,} } class sale_config_picking_policy(osv.osv_memory): - _name='sale.config.picking_policy' - _columns = { - 'name':fields.char('Name', size=64), - 'picking_policy': fields.selection([('direct','Direct Delivery'),('one','All at once')], 'Packing Policy', required=True ), + _name='sale.config.picking_policy' + _columns = { + 'name':fields.char('Name', size=64), + 'picking_policy': fields.selection([('direct','Direct Delivery'),('one','All at once')], 'Packing Policy', required=True ), + } + _defaults={ + 'picking_policy': lambda *a: 'direct', } - _defaults={ - 'picking_policy': lambda *a: 'direct', - } - def set_default(self, cr, uid, ids, context=None): - if 'name' in context: - ir_values_obj = self.pool.get('ir.values') - ir_values_obj.set(cr,uid,'default',False,'picking_policy',['sale.order'],context['picking_policy']) - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', + def set_default(self, cr, uid, ids, context=None): + if 'name' in context: + ir_values_obj = self.pool.get('ir.values') + ir_values_obj.set(cr,uid,'default',False,'picking_policy',['sale.order'],context['picking_policy']) + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', } - def action_cancel(self,cr,uid,ids,conect=None): - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', + def action_cancel(self,cr,uid,ids,conect=None): + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', } sale_config_picking_policy() diff --git a/addons/sale/stock.py b/addons/sale/stock.py index f90b5613d6d..2b0ebd0edda 100644 --- a/addons/sale/stock.py +++ b/addons/sale/stock.py @@ -30,83 +30,83 @@ from osv import osv, fields class stock_move(osv.osv): - _inherit = 'stock.move' - _columns = { - 'sale_line_id': fields.many2one('sale.order.line', 'Sale Order Line', ondelete='set null', select=True, readonly=True), - } - _defaults = { - 'sale_line_id': lambda *a:False - } + _inherit = 'stock.move' + _columns = { + 'sale_line_id': fields.many2one('sale.order.line', 'Sale Order Line', ondelete='set null', select=True, readonly=True), + } + _defaults = { + 'sale_line_id': lambda *a:False + } stock_move() class stock_picking(osv.osv): - _inherit = 'stock.picking' - _columns = { - 'sale_id': fields.many2one('sale.order', 'Sale Order', ondelete='set null', select=True, readonly=True), - } - _defaults = { - 'sale_id': lambda *a: False - } + _inherit = 'stock.picking' + _columns = { + 'sale_id': fields.many2one('sale.order', 'Sale Order', ondelete='set null', select=True, readonly=True), + } + _defaults = { + 'sale_id': lambda *a: False + } - def _get_address_invoice(self, cursor, user, picking): - res = {} - if picking.sale_id: - res['contact'] = picking.sale_id.partner_order_id.id - res['invoice'] = picking.sale_id.partner_invoice_id.id - return res - return super(stock_picking, self)._get_address_invoice(cursor, - user, picking) + def _get_address_invoice(self, cursor, user, picking): + res = {} + if picking.sale_id: + res['contact'] = picking.sale_id.partner_order_id.id + res['invoice'] = picking.sale_id.partner_invoice_id.id + return res + return super(stock_picking, self)._get_address_invoice(cursor, + user, picking) - def _get_comment_invoice(self, cursor, user, picking): - if picking.sale_id and picking.sale_id.note: - if picking.note: - return picking.note + '\n' + picking.sale_id.note - else: - return picking.sale_id.note - return super(stock_picking, self)._get_comment_invoice(cursor, user, - picking) + def _get_comment_invoice(self, cursor, user, picking): + if picking.sale_id and picking.sale_id.note: + if picking.note: + return picking.note + '\n' + picking.sale_id.note + else: + return picking.sale_id.note + return super(stock_picking, self)._get_comment_invoice(cursor, user, + picking) - def _get_price_unit_invoice(self, cursor, user, move_line, type): - if move_line.sale_line_id: - return move_line.sale_line_id.price_unit - return super(stock_picking, self)._get_price_unit_invoice(cursor, - user, move_line, type) + def _get_price_unit_invoice(self, cursor, user, move_line, type): + if move_line.sale_line_id: + return move_line.sale_line_id.price_unit + return super(stock_picking, self)._get_price_unit_invoice(cursor, + user, move_line, type) - def _get_discount_invoice(self, cursor, user, move_line): - if move_line.sale_line_id: - return move_line.sale_line_id.discount - return super(stock_picking, self)._get_discount_invoice(cursor, user, - move_line) + def _get_discount_invoice(self, cursor, user, move_line): + if move_line.sale_line_id: + return move_line.sale_line_id.discount + return super(stock_picking, self)._get_discount_invoice(cursor, user, + move_line) - def _get_taxes_invoice(self, cursor, user, move_line, type): - if move_line.sale_line_id: - return [x.id for x in move_line.sale_line_id.tax_id] - return super(stock_picking, self)._get_taxes_invoice(cursor, user, - move_line, type) + def _get_taxes_invoice(self, cursor, user, move_line, type): + if move_line.sale_line_id: + return [x.id for x in move_line.sale_line_id.tax_id] + return super(stock_picking, self)._get_taxes_invoice(cursor, user, + move_line, type) - def _get_account_analytic_invoice(self, cursor, user, picking, move_line): - if picking.sale_id: - return picking.sale_id.project_id.id - return super(stock_picking, self)._get_account_analytic_invoice(cursor, - user, picking, move_line) + def _get_account_analytic_invoice(self, cursor, user, picking, move_line): + if picking.sale_id: + return picking.sale_id.project_id.id + return super(stock_picking, self)._get_account_analytic_invoice(cursor, + user, picking, move_line) - def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): - sale_line_obj = self.pool.get('sale.order.line') - if move_line.sale_line_id: - sale_line_obj.write(cursor, user, [move_line.sale_line_id.id], { - 'invoice_lines': [(6, 0, [invoice_line_id])], - }) - return super(stock_picking, self)._invoice_line_hook(cursor, user, - move_line, invoice_line_id) + def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): + sale_line_obj = self.pool.get('sale.order.line') + if move_line.sale_line_id: + sale_line_obj.write(cursor, user, [move_line.sale_line_id.id], { + 'invoice_lines': [(6, 0, [invoice_line_id])], + }) + return super(stock_picking, self)._invoice_line_hook(cursor, user, + move_line, invoice_line_id) - def _invoice_hook(self, cursor, user, picking, invoice_id): - sale_obj = self.pool.get('sale.order') - if picking.sale_id: - sale_obj.write(cursor, user, [picking.sale_id.id], { - 'invoice_ids': [(4, invoice_id)], - }) - return super(stock_picking, self)._invoice_hook(cursor, user, - picking, invoice_id) + def _invoice_hook(self, cursor, user, picking, invoice_id): + sale_obj = self.pool.get('sale.order') + if picking.sale_id: + sale_obj.write(cursor, user, [picking.sale_id.id], { + 'invoice_ids': [(4, invoice_id)], + }) + return super(stock_picking, self)._invoice_hook(cursor, user, + picking, invoice_id) stock_picking() diff --git a/addons/sale/wizard/make_invoice.py b/addons/sale/wizard/make_invoice.py index f24b7533055..11df3550ee9 100644 --- a/addons/sale/wizard/make_invoice.py +++ b/addons/sale/wizard/make_invoice.py @@ -33,60 +33,60 @@ import pooler invoice_form = """<?xml version="1.0"?> <form string="Create invoices"> - <separator colspan="4" string="Do you really want to create the invoices ?" /> - <field name="grouped" /> + <separator colspan="4" string="Do you really want to create the invoices ?" /> + <field name="grouped" /> </form> """ invoice_fields = { - 'grouped' : {'string':'Group the invoices', 'type':'boolean', 'default': lambda x,y,z: False} + 'grouped' : {'string':'Group the invoices', 'type':'boolean', 'default': lambda x,y,z: False} } ack_form = """<?xml version="1.0"?> <form string="Create invoices"> - <separator string="Invoices created" /> + <separator string="Invoices created" /> </form>""" ack_fields = {} def _makeInvoices(self, cr, uid, data, context): - order_obj = pooler.get_pool(cr.dbname).get('sale.order') - newinv = [] + order_obj = pooler.get_pool(cr.dbname).get('sale.order') + newinv = [] - order_obj.action_invoice_create(cr, uid, data['ids'], data['form']['grouped']) - for id in data['ids']: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'sale.order', id, 'manual_invoice', cr) + order_obj.action_invoice_create(cr, uid, data['ids'], data['form']['grouped']) + for id in data['ids']: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'sale.order', id, 'manual_invoice', cr) - for o in order_obj.browse(cr, uid, data['ids'], context): - for i in o.invoice_ids: - newinv.append(i.id) - return { - 'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]", - 'name': 'Invoices', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.invoice', - 'view_id': False, - 'context': "{'type':'out_refund'}", - 'type': 'ir.actions.act_window' - } - return {} + for o in order_obj.browse(cr, uid, data['ids'], context): + for i in o.invoice_ids: + newinv.append(i.id) + return { + 'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]", + 'name': 'Invoices', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'account.invoice', + 'view_id': False, + 'context': "{'type':'out_refund'}", + 'type': 'ir.actions.act_window' + } + return {} class make_invoice(wizard.interface): - states = { - 'init' : { - 'actions' : [], - 'result' : {'type' : 'form', - 'arch' : invoice_form, - 'fields' : invoice_fields, - 'state' : [('end', 'Cancel'),('invoice', 'Create invoices') ]} - }, - 'invoice' : { - 'actions' : [_makeInvoices], - 'result' : {'type' : 'action', - 'action' : _makeInvoices, - 'state' : 'end'} - }, - } + states = { + 'init' : { + 'actions' : [], + 'result' : {'type' : 'form', + 'arch' : invoice_form, + 'fields' : invoice_fields, + 'state' : [('end', 'Cancel'),('invoice', 'Create invoices') ]} + }, + 'invoice' : { + 'actions' : [_makeInvoices], + 'result' : {'type' : 'action', + 'action' : _makeInvoices, + 'state' : 'end'} + }, + } make_invoice("sale.order.make_invoice") diff --git a/addons/sale/wizard/wizard_sale_line_invoice.py b/addons/sale/wizard/wizard_sale_line_invoice.py index 2c36feaa651..d347f7e8c24 100644 --- a/addons/sale/wizard/wizard_sale_line_invoice.py +++ b/addons/sale/wizard/wizard_sale_line_invoice.py @@ -34,92 +34,92 @@ import pooler invoice_form = """<?xml version="1.0"?> <form string="Create invoices"> - <separator colspan="4" string="Do you really want to create the invoices ?" /> - <field name="grouped" /> + <separator colspan="4" string="Do you really want to create the invoices ?" /> + <field name="grouped" /> </form> """ invoice_fields = { - 'grouped' : {'string':'Group the invoices', 'type':'boolean', 'default': lambda *a: False} + 'grouped' : {'string':'Group the invoices', 'type':'boolean', 'default': lambda *a: False} } def _makeInvoices(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - res = False - invoices = {} + pool = pooler.get_pool(cr.dbname) + res = False + invoices = {} - #TODO: merge with sale.py/make_invoice - def make_invoice(order, lines): - a = order.partner_id.property_account_receivable.id - if order.partner_id and order.partner_id.property_payment_term.id: - pay_term = order.partner_id.property_payment_term.id - else: - pay_term = False - inv = { - 'name': order.name, - 'origin': order.name, - 'type': 'out_invoice', - 'reference': "P%dSO%d" % (order.partner_id.id, order.id), - 'account_id': a, - 'partner_id': order.partner_id.id, - 'address_invoice_id': order.partner_invoice_id.id, - 'address_contact_id': order.partner_invoice_id.id, - 'invoice_line': [(6,0,lines)], - 'currency_id' : order.pricelist_id.currency_id.id, - 'comment': order.note, - 'payment_term': pay_term, - } - inv_id = pool.get('account.invoice').create(cr, uid, inv) - return inv_id + #TODO: merge with sale.py/make_invoice + def make_invoice(order, lines): + a = order.partner_id.property_account_receivable.id + if order.partner_id and order.partner_id.property_payment_term.id: + pay_term = order.partner_id.property_payment_term.id + else: + pay_term = False + inv = { + 'name': order.name, + 'origin': order.name, + 'type': 'out_invoice', + 'reference': "P%dSO%d" % (order.partner_id.id, order.id), + 'account_id': a, + 'partner_id': order.partner_id.id, + 'address_invoice_id': order.partner_invoice_id.id, + 'address_contact_id': order.partner_invoice_id.id, + 'invoice_line': [(6,0,lines)], + 'currency_id' : order.pricelist_id.currency_id.id, + 'comment': order.note, + 'payment_term': pay_term, + } + inv_id = pool.get('account.invoice').create(cr, uid, inv) + return inv_id - for line in pool.get('sale.order.line').browse(cr,uid,data['ids']): - if not line.invoiced: - if not line.order_id.id in invoices: - invoices[line.order_id.id] = [] - line_id = pool.get('sale.order.line').invoice_line_create(cr, uid, - [line.id]) - for lid in line_id: - invoices[line.order_id.id].append((line, lid)) - pool.get('sale.order.line').write(cr, uid, [line.id], - {'invoiced': True}) - flag = True - data_sale = pool.get('sale.order').browse(cr,uid,line.order_id.id) - for line in data_sale.order_line: - if not line.invoiced: - flag = False - break - if flag: - wf_service = netsvc.LocalService('workflow') - wf_service.trg_validate(uid, 'sale.order', line.order_id.id, 'all_lines', cr) - pool.get('sale.order').write(cr,uid,[line.order_id.id],{'state' : 'progress'}) + for line in pool.get('sale.order.line').browse(cr,uid,data['ids']): + if not line.invoiced: + if not line.order_id.id in invoices: + invoices[line.order_id.id] = [] + line_id = pool.get('sale.order.line').invoice_line_create(cr, uid, + [line.id]) + for lid in line_id: + invoices[line.order_id.id].append((line, lid)) + pool.get('sale.order.line').write(cr, uid, [line.id], + {'invoiced': True}) + flag = True + data_sale = pool.get('sale.order').browse(cr,uid,line.order_id.id) + for line in data_sale.order_line: + if not line.invoiced: + flag = False + break + if flag: + wf_service = netsvc.LocalService('workflow') + wf_service.trg_validate(uid, 'sale.order', line.order_id.id, 'all_lines', cr) + pool.get('sale.order').write(cr,uid,[line.order_id.id],{'state' : 'progress'}) - for result in invoices.values(): - order = result[0][0].order_id - il = map(lambda x: x[1], result) - res = make_invoice(order, il) - cr.execute('INSERT INTO sale_order_invoice_rel \ - (order_id,invoice_id) values (%d,%d)', (order.id, res)) - return {} + for result in invoices.values(): + order = result[0][0].order_id + il = map(lambda x: x[1], result) + res = make_invoice(order, il) + cr.execute('INSERT INTO sale_order_invoice_rel \ + (order_id,invoice_id) values (%d,%d)', (order.id, res)) + return {} class line_make_invoice(wizard.interface): - states = { - 'init' : { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': invoice_form, - 'fields': invoice_fields, - 'state': [ - ('end', 'Cancel'), - ('invoice', 'Create invoices') - ] - } - }, - 'invoice' : { - 'actions' : [_makeInvoices], - 'result' : {'type': 'state', 'state': 'end'} - }, - } + states = { + 'init' : { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': invoice_form, + 'fields': invoice_fields, + 'state': [ + ('end', 'Cancel'), + ('invoice', 'Create invoices') + ] + } + }, + 'invoice' : { + 'actions' : [_makeInvoices], + 'result' : {'type': 'state', 'state': 'end'} + }, + } line_make_invoice("sale.order.line.make_invoice") diff --git a/addons/scrum/__terp__.py b/addons/scrum/__terp__.py index 8981b30af7c..785767f0740 100644 --- a/addons/scrum/__terp__.py +++ b/addons/scrum/__terp__.py @@ -26,36 +26,36 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Scrum, Agile Development Method", - "version": "1.0", - "author" : "Tiny", - "depends" : ["project"], - "category" : "Enterprise Specific Modules/Information Technology", - "init_xml" : [], - "description": """ - This modules implements all concepts defined by the scrum project - management methodology for IT companies: - * Project with sprints, product owner, scrum master - * Sprints with reviews, daily meetings, feedbacks - * Product backlog - * Sprint backlog + "name" : "Scrum, Agile Development Method", + "version": "1.0", + "author" : "Tiny", + "depends" : ["project"], + "category" : "Enterprise Specific Modules/Information Technology", + "init_xml" : [], + "description": """ + This modules implements all concepts defined by the scrum project + management methodology for IT companies: + * Project with sprints, product owner, scrum master + * Sprints with reviews, daily meetings, feedbacks + * Product backlog + * Sprint backlog - It adds some concepts to the project management module: - * Mid-term, long-term roadmaps - * Customers/functionnal requests, vs technical ones + It adds some concepts to the project management module: + * Mid-term, long-term roadmaps + * Customers/functionnal requests, vs technical ones - It also create a new reporting: - * Burndown chart + It also create a new reporting: + * Burndown chart - The scrum projects and tasks inherits from the real projects and - tasks, so you can continue working on normal tasks that will also - include tasks from scrum projects. + The scrum projects and tasks inherits from the real projects and + tasks, so you can continue working on normal tasks that will also + include tasks from scrum projects. - More information on the methodology: - * http://controlchaos.com - """, - "demo_xml" : ["scrum_demo.xml"], - "update_xml": ["scrum_view.xml","scrum_report.xml", "scrum_wizard.xml"], - "active": False, - "installable": True + More information on the methodology: + * http://controlchaos.com + """, + "demo_xml" : ["scrum_demo.xml"], + "update_xml": ["scrum_view.xml","scrum_report.xml", "scrum_wizard.xml"], + "active": False, + "installable": True } diff --git a/addons/scrum/report/_burndown.py b/addons/scrum/report/_burndown.py index 064b5fa9dd0..81395def3d2 100644 --- a/addons/scrum/report/_burndown.py +++ b/addons/scrum/report/_burndown.py @@ -32,42 +32,42 @@ from mx import DateTime import time def compute_burndown(cr, uid, tasks_id, date_start, date_stop): - latest = False - if len(tasks_id): - cr.execute('select id,create_date,state,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') order by create_date') - tasks = cr.fetchall() + latest = False + if len(tasks_id): + cr.execute('select id,create_date,state,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') order by create_date') + tasks = cr.fetchall() - cr.execute('select w.date,w.hours from project_task_work w left join project_task t on (t.id=w.task_id) where t.id in ('+','.join(map(str,tasks_id))+') and t.state in (\'open\',\'progress\') order by date') + cr.execute('select w.date,w.hours from project_task_work w left join project_task t on (t.id=w.task_id) where t.id in ('+','.join(map(str,tasks_id))+') and t.state in (\'open\',\'progress\') order by date') - tasks2 = cr.fetchall() + tasks2 = cr.fetchall() - cr.execute('select date_close,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') and state in (\'cancelled\',\'done\') order by date_close') - tasks2 += cr.fetchall() - tasks2.sort() - else: - tasks = [] - tasks2 = [] + cr.execute('select date_close,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') and state in (\'cancelled\',\'done\') order by date_close') + tasks2 += cr.fetchall() + tasks2.sort() + else: + tasks = [] + tasks2 = [] - current_date = date_start - total = 0 - done = 0 - result = [] - while current_date<=date_stop: - while len(tasks) and tasks[0][1] and tasks[0][1][:10]<=current_date: - latest = tasks.pop(0) - total += latest[3] - i = 0 - while i<len(tasks2): - if tasks2[i][0][:10]<=current_date: - t = tasks2.pop(i) - done += t[1] - else: - i+=1 - result.append( (int(time.mktime(time.strptime(current_date,'%Y-%m-%d'))), total-done) ) - current_date = (DateTime.strptime(current_date, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') - if not len(tasks) and not len(tasks2): - break - result.append( (int(time.mktime(time.strptime(date_stop,'%Y-%m-%d'))), 0) ) - return result + current_date = date_start + total = 0 + done = 0 + result = [] + while current_date<=date_stop: + while len(tasks) and tasks[0][1] and tasks[0][1][:10]<=current_date: + latest = tasks.pop(0) + total += latest[3] + i = 0 + while i<len(tasks2): + if tasks2[i][0][:10]<=current_date: + t = tasks2.pop(i) + done += t[1] + else: + i+=1 + result.append( (int(time.mktime(time.strptime(current_date,'%Y-%m-%d'))), total-done) ) + current_date = (DateTime.strptime(current_date, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') + if not len(tasks) and not len(tasks2): + break + result.append( (int(time.mktime(time.strptime(date_stop,'%Y-%m-%d'))), 0) ) + return result diff --git a/addons/scrum/report/sprint_burndown.py b/addons/scrum/report/sprint_burndown.py index bbbb33e46e4..c95c25b3080 100644 --- a/addons/scrum/report/sprint_burndown.py +++ b/addons/scrum/report/sprint_burndown.py @@ -42,57 +42,57 @@ import pychart.legend import _burndown class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + def _render(self): + return self.pdf class report_tasks(report_int): - def create(self, cr, uid, ids, datas, context={}): - io = StringIO.StringIO() - - canv = canvas.init(fname=io, format='pdf') - canv.set_author("Tiny ERP") - - cr.execute('select id,date_start,date_stop from scrum_sprint where id=%d', (datas['id'],)) - for (id,date_start,date_stop) in cr.fetchall(): - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d/%m/%Y') - - cr.execute('select id from project_task where product_backlog_id in(select id from scrum_product_backlog where sprint_id=%d)', (id,)) - - ids = map(lambda x: x[0], cr.fetchall()) - datas = _burndown.compute_burndown(cr, uid, ids, date_start, date_stop) - - max_hour = reduce(lambda x,y: max(y[1],x), datas, 0) - - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d %m %Y') - - def _interval_get(*args): - result = [] - for i in range(20): - d = DateTime.localtime(datas[0][0] + (((datas[-1][0]-datas[0][0])/20)*(i+1))) - res = DateTime.DateTime(d.year, d.month, d.day).ticks() - if (not result) or result[-1]<>res: - result.append(res) - return result - - ar = area.T(x_grid_style=line_style.gray50_dash1, - x_axis=axis.X(label="Date", format=int_to_date), - y_axis=axis.Y(label="Burndown Chart - Planned Hours"), - x_grid_interval=_interval_get, - x_range = (datas[0][0],datas[-1][0]), - y_range = (0,max_hour), - legend = None, - size = (680,450)) - ar.add_plot(line_plot.T(data=datas)) - ar.draw(canv) - canv.close() - - self.obj = external_pdf(io.getvalue()) - self.obj.render() - return (self.obj.pdf, 'pdf') + def create(self, cr, uid, ids, datas, context={}): + io = StringIO.StringIO() + + canv = canvas.init(fname=io, format='pdf') + canv.set_author("Tiny ERP") + + cr.execute('select id,date_start,date_stop from scrum_sprint where id=%d', (datas['id'],)) + for (id,date_start,date_stop) in cr.fetchall(): + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d/%m/%Y') + + cr.execute('select id from project_task where product_backlog_id in(select id from scrum_product_backlog where sprint_id=%d)', (id,)) + + ids = map(lambda x: x[0], cr.fetchall()) + datas = _burndown.compute_burndown(cr, uid, ids, date_start, date_stop) + + max_hour = reduce(lambda x,y: max(y[1],x), datas, 0) + + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d %m %Y') + + def _interval_get(*args): + result = [] + for i in range(20): + d = DateTime.localtime(datas[0][0] + (((datas[-1][0]-datas[0][0])/20)*(i+1))) + res = DateTime.DateTime(d.year, d.month, d.day).ticks() + if (not result) or result[-1]<>res: + result.append(res) + return result + + ar = area.T(x_grid_style=line_style.gray50_dash1, + x_axis=axis.X(label="Date", format=int_to_date), + y_axis=axis.Y(label="Burndown Chart - Planned Hours"), + x_grid_interval=_interval_get, + x_range = (datas[0][0],datas[-1][0]), + y_range = (0,max_hour), + legend = None, + size = (680,450)) + ar.add_plot(line_plot.T(data=datas)) + ar.draw(canv) + canv.close() + + self.obj = external_pdf(io.getvalue()) + self.obj.render() + return (self.obj.pdf, 'pdf') report_tasks('report.scrum.sprint.burndown') diff --git a/addons/scrum/report/task_burndown.py b/addons/scrum/report/task_burndown.py index eaecf51bd17..6ff897fb9f0 100644 --- a/addons/scrum/report/task_burndown.py +++ b/addons/scrum/report/task_burndown.py @@ -41,92 +41,92 @@ import pychart.legend import _burndown class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + def _render(self): + return self.pdf def burndown_chart(cr, uid, tasks_id, date_start, date_stop): - latest = False - cr.execute('select id,date_start,state,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') order by date_start') - tasks = cr.fetchall() - cr.execute('select id,date_close,state,planned_hours*progress/100 from project_task where id in ('+','.join(map(str,tasks_id))+') and state in (%s,%s) order by date_close', ('progress','done')) - tasks2 = cr.fetchall() - current_date = date_start - total = 0 - done = 0 - result = [] - while current_date<=date_stop: - while len(tasks) and tasks[0][1] and tasks[0][1][:10]<=current_date: - total += tasks.pop(0)[3] - while len(tasks2) and tasks2[0][1] and tasks2[0][1][:10]<=current_date: - t2 = tasks2.pop(0) - if t2[2]<>'cancel': - done += t2[3] - else: - total -= t2[3] - result.append( (int(time.mktime(time.strptime(current_date,'%Y-%m-%d'))), total-done) ) - current_date = (DateTime.strptime(current_date, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') - if not len(tasks) and not len(tasks2): - break - result.append( (int(time.mktime(time.strptime(date_stop,'%Y-%m-%d'))), 0) ) - return result + latest = False + cr.execute('select id,date_start,state,planned_hours from project_task where id in ('+','.join(map(str,tasks_id))+') order by date_start') + tasks = cr.fetchall() + cr.execute('select id,date_close,state,planned_hours*progress/100 from project_task where id in ('+','.join(map(str,tasks_id))+') and state in (%s,%s) order by date_close', ('progress','done')) + tasks2 = cr.fetchall() + current_date = date_start + total = 0 + done = 0 + result = [] + while current_date<=date_stop: + while len(tasks) and tasks[0][1] and tasks[0][1][:10]<=current_date: + total += tasks.pop(0)[3] + while len(tasks2) and tasks2[0][1] and tasks2[0][1][:10]<=current_date: + t2 = tasks2.pop(0) + if t2[2]<>'cancel': + done += t2[3] + else: + total -= t2[3] + result.append( (int(time.mktime(time.strptime(current_date,'%Y-%m-%d'))), total-done) ) + current_date = (DateTime.strptime(current_date, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') + if not len(tasks) and not len(tasks2): + break + result.append( (int(time.mktime(time.strptime(date_stop,'%Y-%m-%d'))), 0) ) + return result class report_tasks(report_int): - def create(self, cr, uid, ids, datas, context={}): - io = StringIO.StringIO() - - if 'date_start' not in datas: - cr.execute('select min(date_start) from project_task where id in ('+','.join(map(str,ids))+')') - dt = cr.fetchone()[0] - if dt: - datas['date_start'] = dt[:10] - else: - datas['date_start'] = time.strftime('%Y-%m-%d') - if 'date_stop' not in datas: - cr.execute('select max(date_start),max(date_close) from project_task where id in ('+','.join(map(str,ids))+')') - res = cr.fetchone() - datas['date_stop'] = (res[0] and res[0][:10]) or time.strftime('%Y-%m-%d') - if res[1] and datas['date_stop']<res[1]: - datas['date_stop'] = res[1][:10] - - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d/%m/%Y') - - datas = _burndown.compute_burndown(cr, uid, ids, datas['date_start'], datas['date_stop']) - - canv = canvas.init(fname=io, format='pdf') - canv.set_author("Tiny ERP") - - max_hour = reduce(lambda x,y: max(y[1],x), datas, 0) - - date_to_int = lambda x: int(x.ticks()) - int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d %m %Y') - - def _interval_get(*args): - result = [] - for i in range(20): - d = DateTime.localtime(datas[0][0] + (((datas[-1][0]-datas[0][0])/20)*(i+1))) - res = DateTime.DateTime(d.year, d.month, d.day).ticks() - if (not result) or result[-1]<>res: - result.append(res) - return result - - ar = area.T(x_grid_style=line_style.gray50_dash1, - x_axis=axis.X(label="Date", format=int_to_date), - y_axis=axis.Y(label="Burndown Chart - Planned Hours"), - x_grid_interval=_interval_get, - x_range = (datas[0][0],datas[-1][0]), - y_range = (0,max_hour), - legend = None, - size = (680,450)) - ar.add_plot(line_plot.T(data=datas)) - ar.draw(canv) - canv.close() - - self.obj = external_pdf(io.getvalue()) - self.obj.render() - return (self.obj.pdf,'pdf') + def create(self, cr, uid, ids, datas, context={}): + io = StringIO.StringIO() + + if 'date_start' not in datas: + cr.execute('select min(date_start) from project_task where id in ('+','.join(map(str,ids))+')') + dt = cr.fetchone()[0] + if dt: + datas['date_start'] = dt[:10] + else: + datas['date_start'] = time.strftime('%Y-%m-%d') + if 'date_stop' not in datas: + cr.execute('select max(date_start),max(date_close) from project_task where id in ('+','.join(map(str,ids))+')') + res = cr.fetchone() + datas['date_stop'] = (res[0] and res[0][:10]) or time.strftime('%Y-%m-%d') + if res[1] and datas['date_stop']<res[1]: + datas['date_stop'] = res[1][:10] + + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d/%m/%Y') + + datas = _burndown.compute_burndown(cr, uid, ids, datas['date_start'], datas['date_stop']) + + canv = canvas.init(fname=io, format='pdf') + canv.set_author("Tiny ERP") + + max_hour = reduce(lambda x,y: max(y[1],x), datas, 0) + + date_to_int = lambda x: int(x.ticks()) + int_to_date = lambda x: '/a60{}'+DateTime.localtime(x).strftime('%d %m %Y') + + def _interval_get(*args): + result = [] + for i in range(20): + d = DateTime.localtime(datas[0][0] + (((datas[-1][0]-datas[0][0])/20)*(i+1))) + res = DateTime.DateTime(d.year, d.month, d.day).ticks() + if (not result) or result[-1]<>res: + result.append(res) + return result + + ar = area.T(x_grid_style=line_style.gray50_dash1, + x_axis=axis.X(label="Date", format=int_to_date), + y_axis=axis.Y(label="Burndown Chart - Planned Hours"), + x_grid_interval=_interval_get, + x_range = (datas[0][0],datas[-1][0]), + y_range = (0,max_hour), + legend = None, + size = (680,450)) + ar.add_plot(line_plot.T(data=datas)) + ar.draw(canv) + canv.close() + + self.obj = external_pdf(io.getvalue()) + self.obj.render() + return (self.obj.pdf,'pdf') report_tasks('report.project.tasks.burndown') diff --git a/addons/scrum/scrum.py b/addons/scrum/scrum.py index f4d3ab4388a..25497e34f57 100644 --- a/addons/scrum/scrum.py +++ b/addons/scrum/scrum.py @@ -35,197 +35,197 @@ from mx import DateTime import re class scrum_team(osv.osv): - _name = 'scrum.team' - _description = 'Scrum Team' - _columns = { - 'name' : fields.char('Team Name', size=64), - 'users_id' : fields.many2many('res.users', 'scrum_team_users_rel', 'team_id','user_id', 'Users'), - } + _name = 'scrum.team' + _description = 'Scrum Team' + _columns = { + 'name' : fields.char('Team Name', size=64), + 'users_id' : fields.many2many('res.users', 'scrum_team_users_rel', 'team_id','user_id', 'Users'), + } scrum_team() class scrum_project(osv.osv): - _name = 'scrum.project' - _inherit = 'project.project' - _table = 'project_project' - _description = 'Scrum Project' - _columns = { - 'product_owner_id': fields.many2one('res.users', 'Product Owner'), - 'tasks': fields.one2many('scrum.task', 'project_id', 'Scrum Tasks'), - 'sprint_size': fields.integer('Sprint Days'), - 'scrum': fields.integer('Is Scrum'), - 'parent_id': fields.many2one('scrum.project', 'Parent project'), - } - _defaults = { - 'product_owner_id': lambda self,cr,uid,context={}: uid, - 'warn_manager': lambda *a: 1, - 'sprint_size': lambda *a: 14, - 'scrum': lambda *a: 1 - } + _name = 'scrum.project' + _inherit = 'project.project' + _table = 'project_project' + _description = 'Scrum Project' + _columns = { + 'product_owner_id': fields.many2one('res.users', 'Product Owner'), + 'tasks': fields.one2many('scrum.task', 'project_id', 'Scrum Tasks'), + 'sprint_size': fields.integer('Sprint Days'), + 'scrum': fields.integer('Is Scrum'), + 'parent_id': fields.many2one('scrum.project', 'Parent project'), + } + _defaults = { + 'product_owner_id': lambda self,cr,uid,context={}: uid, + 'warn_manager': lambda *a: 1, + 'sprint_size': lambda *a: 14, + 'scrum': lambda *a: 1 + } scrum_project() class scrum_sprint(osv.osv): - _name = 'scrum.sprint' - _description = 'Scrum Sprint' - def _calc_progress(self, cr, uid, ids, name, args, context): - res = {} - for sprint in self.browse(cr, uid, ids): - tot = 0.0 - prog = 0.0 - for bl in sprint.backlog_ids: - tot += bl.planned_hours - prog += bl.planned_hours * bl.progress / 100.0 - res.setdefault(sprint.id, 0.0) - if tot>0: - res[sprint.id] = round(prog/tot*100) - return res - def _calc_effective(self, cr, uid, ids, name, args, context): - res = {} - for sprint in self.browse(cr, uid, ids): - res.setdefault(sprint.id, 0.0) - for bl in sprint.backlog_ids: - res[sprint.id] += bl.effective_hours - return res - def _calc_planned(self, cr, uid, ids, name, args, context): - res = {} - for sprint in self.browse(cr, uid, ids): - res.setdefault(sprint.id, 0.0) - for bl in sprint.backlog_ids: - res[sprint.id] += bl.planned_hours - return res - _columns = { - 'name' : fields.char('Sprint Name', size=64), - 'date_start': fields.date('Starting Date', required=True), - 'date_stop': fields.date('Ending Date', required=True), - 'project_id': fields.many2one('scrum.project', 'Project', required=True, domain=[('scrum','=',1)]), - 'product_owner_id': fields.many2one('res.users', 'Product Owner', required=True), - 'scrum_master_id': fields.many2one('res.users', 'Scrum Master', required=True), - 'meetings_id': fields.one2many('scrum.meeting', 'sprint_id', 'Daily Scrum'), - 'review': fields.text('Sprint Review'), - 'retrospective': fields.text('Sprint Retrospective'), - 'backlog_ids': fields.one2many('scrum.product.backlog', 'sprint_id', 'Sprint Backlog'), - 'progress': fields.function(_calc_progress, method=True, string='Progress (0-100)'), - 'effective_hours': fields.function(_calc_effective, method=True, string='Effective hours'), - 'planned_hours': fields.function(_calc_planned, method=True, string='Planned Hours'), - 'state': fields.selection([('draft','Draft'),('open','Open'),('done','Done')], 'State', required=True), - } - _defaults = { - 'state': lambda *a: 'draft', - 'date_start' : lambda *a:time.strftime('%Y-%m-%d'), - } - def onchange_project_id(self, cr, uid, ids, project_id): - v = {} - if project_id: - proj = self.pool.get('scrum.project').browse(cr, uid, [project_id])[0] - v['product_owner_id']= proj.product_owner_id.id - v['scrum_master_id']= proj.manager.id - v['date_stop'] = (DateTime.now() + DateTime.RelativeDateTime(days=int(proj.sprint_size or 14))).strftime('%Y-%m-%d') - return {'value':v} - + _name = 'scrum.sprint' + _description = 'Scrum Sprint' + def _calc_progress(self, cr, uid, ids, name, args, context): + res = {} + for sprint in self.browse(cr, uid, ids): + tot = 0.0 + prog = 0.0 + for bl in sprint.backlog_ids: + tot += bl.planned_hours + prog += bl.planned_hours * bl.progress / 100.0 + res.setdefault(sprint.id, 0.0) + if tot>0: + res[sprint.id] = round(prog/tot*100) + return res + def _calc_effective(self, cr, uid, ids, name, args, context): + res = {} + for sprint in self.browse(cr, uid, ids): + res.setdefault(sprint.id, 0.0) + for bl in sprint.backlog_ids: + res[sprint.id] += bl.effective_hours + return res + def _calc_planned(self, cr, uid, ids, name, args, context): + res = {} + for sprint in self.browse(cr, uid, ids): + res.setdefault(sprint.id, 0.0) + for bl in sprint.backlog_ids: + res[sprint.id] += bl.planned_hours + return res + _columns = { + 'name' : fields.char('Sprint Name', size=64), + 'date_start': fields.date('Starting Date', required=True), + 'date_stop': fields.date('Ending Date', required=True), + 'project_id': fields.many2one('scrum.project', 'Project', required=True, domain=[('scrum','=',1)]), + 'product_owner_id': fields.many2one('res.users', 'Product Owner', required=True), + 'scrum_master_id': fields.many2one('res.users', 'Scrum Master', required=True), + 'meetings_id': fields.one2many('scrum.meeting', 'sprint_id', 'Daily Scrum'), + 'review': fields.text('Sprint Review'), + 'retrospective': fields.text('Sprint Retrospective'), + 'backlog_ids': fields.one2many('scrum.product.backlog', 'sprint_id', 'Sprint Backlog'), + 'progress': fields.function(_calc_progress, method=True, string='Progress (0-100)'), + 'effective_hours': fields.function(_calc_effective, method=True, string='Effective hours'), + 'planned_hours': fields.function(_calc_planned, method=True, string='Planned Hours'), + 'state': fields.selection([('draft','Draft'),('open','Open'),('done','Done')], 'State', required=True), + } + _defaults = { + 'state': lambda *a: 'draft', + 'date_start' : lambda *a:time.strftime('%Y-%m-%d'), + } + def onchange_project_id(self, cr, uid, ids, project_id): + v = {} + if project_id: + proj = self.pool.get('scrum.project').browse(cr, uid, [project_id])[0] + v['product_owner_id']= proj.product_owner_id.id + v['scrum_master_id']= proj.manager.id + v['date_stop'] = (DateTime.now() + DateTime.RelativeDateTime(days=int(proj.sprint_size or 14))).strftime('%Y-%m-%d') + return {'value':v} + scrum_sprint() class scrum_product_backlog(osv.osv): - _name = 'scrum.product.backlog' - _description = 'Product Backlog' + _name = 'scrum.product.backlog' + _description = 'Product Backlog' - def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - match = re.match('^S\(([0-9]+)\)$', name) - if match: - ids = self.search(cr, uid, [('sprint_id','=', int(match.group(1)))], limit=limit, context=context) - return self.name_get(cr, uid, ids, context=context) - return super(scrum_product_backlog, self).name_search(cr, uid, name, args, operator,context, limit=limit) + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + match = re.match('^S\(([0-9]+)\)$', name) + if match: + ids = self.search(cr, uid, [('sprint_id','=', int(match.group(1)))], limit=limit, context=context) + return self.name_get(cr, uid, ids, context=context) + return super(scrum_product_backlog, self).name_search(cr, uid, name, args, operator,context, limit=limit) - def _calc_progress(self, cr, uid, ids, name, args, context): - res = {} - for bl in self.browse(cr, uid, ids): - tot = 0.0 - prog = 0.0 - for task in bl.tasks_id: - tot += task.planned_hours - prog += task.planned_hours * task.progress / 100.0 - res.setdefault(bl.id, 0.0) - if tot>0: - res[bl.id] = round(prog/tot*100) - return res - def _calc_effective(self, cr, uid, ids, name, args, context): - res = {} - for bl in self.browse(cr, uid, ids): - res.setdefault(bl.id, 0.0) - for task in bl.tasks_id: - res[bl.id] += task.effective_hours - return res - def _calc_planned(self, cr, uid, ids, name, args, context): - res = {} - for bl in self.browse(cr, uid, ids): - res.setdefault(bl.id, 0.0) - for task in bl.tasks_id: - res[bl.id] += task.planned_hours - return res - _columns = { - 'name' : fields.char('Feature', size=64), - 'note' : fields.text('Note'), - 'active' : fields.boolean('Active'), - 'project_id': fields.many2one('scrum.project', 'Scrum Project', required=True, domain=[('scrum','=',1)]), - 'user_id': fields.many2one('res.users', 'User'), - 'sprint_id': fields.many2one('scrum.sprint', 'Sprint'), - 'sequence' : fields.integer('Sequence'), - 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Priority'), - 'tasks_id': fields.one2many('scrum.task', 'product_backlog_id', 'Tasks Details'), - 'state': fields.selection([('draft','Draft'),('open','Open'),('done','Done')], 'State', required=True), - 'progress': fields.function(_calc_progress, method=True, string='Progress (0-100)'), - 'effective_hours': fields.function(_calc_effective, method=True, string='Effective hours'), - 'planned_hours': fields.function(_calc_planned, method=True, string='Planned Hours') - } - _defaults = { - 'priority': lambda *a: '4', - 'state': lambda *a: 'draft', - 'active': lambda *a: 1 - } - _order = "priority,sequence" + def _calc_progress(self, cr, uid, ids, name, args, context): + res = {} + for bl in self.browse(cr, uid, ids): + tot = 0.0 + prog = 0.0 + for task in bl.tasks_id: + tot += task.planned_hours + prog += task.planned_hours * task.progress / 100.0 + res.setdefault(bl.id, 0.0) + if tot>0: + res[bl.id] = round(prog/tot*100) + return res + def _calc_effective(self, cr, uid, ids, name, args, context): + res = {} + for bl in self.browse(cr, uid, ids): + res.setdefault(bl.id, 0.0) + for task in bl.tasks_id: + res[bl.id] += task.effective_hours + return res + def _calc_planned(self, cr, uid, ids, name, args, context): + res = {} + for bl in self.browse(cr, uid, ids): + res.setdefault(bl.id, 0.0) + for task in bl.tasks_id: + res[bl.id] += task.planned_hours + return res + _columns = { + 'name' : fields.char('Feature', size=64), + 'note' : fields.text('Note'), + 'active' : fields.boolean('Active'), + 'project_id': fields.many2one('scrum.project', 'Scrum Project', required=True, domain=[('scrum','=',1)]), + 'user_id': fields.many2one('res.users', 'User'), + 'sprint_id': fields.many2one('scrum.sprint', 'Sprint'), + 'sequence' : fields.integer('Sequence'), + 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Priority'), + 'tasks_id': fields.one2many('scrum.task', 'product_backlog_id', 'Tasks Details'), + 'state': fields.selection([('draft','Draft'),('open','Open'),('done','Done')], 'State', required=True), + 'progress': fields.function(_calc_progress, method=True, string='Progress (0-100)'), + 'effective_hours': fields.function(_calc_effective, method=True, string='Effective hours'), + 'planned_hours': fields.function(_calc_planned, method=True, string='Planned Hours') + } + _defaults = { + 'priority': lambda *a: '4', + 'state': lambda *a: 'draft', + 'active': lambda *a: 1 + } + _order = "priority,sequence" scrum_product_backlog() class scrum_task(osv.osv): - _name = 'scrum.task' - _inherit = 'project.task' - _table = 'project_task' - _description = 'Scrum Task' - _columns = { - 'product_backlog_id': fields.many2one('scrum.product.backlog', 'Product Backlog'), - 'scrum': fields.integer('Is Scrum'), - } - _defaults = { - 'scrum': lambda *a: 1, - } - def onchange_backlog_id(self, cr, uid, backlog_id): - if not backlog_id: - return {} - project_id = self.pool.get('scrum.product.backlog').browse(cr, uid, backlog_id).project_id.id - return {'value': {'project_id': project_id}} + _name = 'scrum.task' + _inherit = 'project.task' + _table = 'project_task' + _description = 'Scrum Task' + _columns = { + 'product_backlog_id': fields.many2one('scrum.product.backlog', 'Product Backlog'), + 'scrum': fields.integer('Is Scrum'), + } + _defaults = { + 'scrum': lambda *a: 1, + } + def onchange_backlog_id(self, cr, uid, backlog_id): + if not backlog_id: + return {} + project_id = self.pool.get('scrum.product.backlog').browse(cr, uid, backlog_id).project_id.id + return {'value': {'project_id': project_id}} scrum_task() class scrum_meeting(osv.osv): - _name = 'scrum.meeting' - _description = 'Scrum Meeting' - _columns = { - 'name' : fields.char('Meeting Name', size=64, required=True), - 'date': fields.date('Meeting Date', required=True), - 'sprint_id': fields.many2one('scrum.sprint', 'Sprint', required=True), - 'question_yesterday': fields.text('Tasks since yesterday'), - 'question_today': fields.text('Tasks for today'), - 'question_blocks': fields.text('Blocks encountered'), - # - # Should be more formal. - # - 'question_backlog': fields.text('Backlog Accurate'), - } - # - # Find the right sprint thanks to users and date - # - _defaults = { - 'date' : lambda *a:time.strftime('%Y-%m-%d'), - } + _name = 'scrum.meeting' + _description = 'Scrum Meeting' + _columns = { + 'name' : fields.char('Meeting Name', size=64, required=True), + 'date': fields.date('Meeting Date', required=True), + 'sprint_id': fields.many2one('scrum.sprint', 'Sprint', required=True), + 'question_yesterday': fields.text('Tasks since yesterday'), + 'question_today': fields.text('Tasks for today'), + 'question_blocks': fields.text('Blocks encountered'), + # + # Should be more formal. + # + 'question_backlog': fields.text('Backlog Accurate'), + } + # + # Find the right sprint thanks to users and date + # + _defaults = { + 'date' : lambda *a:time.strftime('%Y-%m-%d'), + } scrum_meeting() diff --git a/addons/scrum/wizard/backlog_to_task.py b/addons/scrum/wizard/backlog_to_task.py index c36a71968a9..220e3a81afe 100644 --- a/addons/scrum/wizard/backlog_to_task.py +++ b/addons/scrum/wizard/backlog_to_task.py @@ -35,49 +35,49 @@ import netsvc btt_form = """<?xml version="1.0" ?> <form string="Create Tasks"> - <field name="user_id"/> + <field name="user_id"/> </form>""" btt_fields = { - 'user_id' : {'string':'Assign To', 'type':'many2one', 'relation':'res.users'}, + 'user_id' : {'string':'Assign To', 'type':'many2one', 'relation':'res.users'}, } def _do_create(self, cr, uid, data, context): - backlogs = pooler.get_pool(cr.dbname).get('scrum.product.backlog').browse(cr, uid, data['ids']) - ids = [] - for backlog in backlogs: - task = pooler.get_pool(cr.dbname).get('scrum.task') - ids.append(task.create(cr, uid, { - 'product_backlog_id': backlog.id, - 'name': backlog.name, - 'description': backlog.note, - 'project_id': backlog.project_id.id, - 'user_id': (backlog.user_id and backlog.user_id.id) or uid, - 'priority': backlog.priority - })) + backlogs = pooler.get_pool(cr.dbname).get('scrum.product.backlog').browse(cr, uid, data['ids']) + ids = [] + for backlog in backlogs: + task = pooler.get_pool(cr.dbname).get('scrum.task') + ids.append(task.create(cr, uid, { + 'product_backlog_id': backlog.id, + 'name': backlog.name, + 'description': backlog.note, + 'project_id': backlog.project_id.id, + 'user_id': (backlog.user_id and backlog.user_id.id) or uid, + 'priority': backlog.priority + })) - value = { - 'domain': "[('product_backlog_id','in',["+','.join(map(str,data['ids']))+"])]", - 'name': 'Open Backlog Tasks', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'scrum.task', - 'view_id': False, - 'type': 'ir.actions.act_window' - } - return value + value = { + 'domain': "[('product_backlog_id','in',["+','.join(map(str,data['ids']))+"])]", + 'name': 'Open Backlog Tasks', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'scrum.task', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + return value class wiz_btt(wizard.interface): - states = { - 'init':{ - 'actions': [], - 'result': {'type':'form', 'arch':btt_form, 'fields':btt_fields, 'state':[('end', 'Cancel'), ('create', 'Create Tasks')] }, - }, - 'create':{ - 'actions': [], - 'result': {'type':'action', 'action': _do_create, 'state':'end'}, - }, - } + states = { + 'init':{ + 'actions': [], + 'result': {'type':'form', 'arch':btt_form, 'fields':btt_fields, 'state':[('end', 'Cancel'), ('create', 'Create Tasks')] }, + }, + 'create':{ + 'actions': [], + 'result': {'type':'action', 'action': _do_create, 'state':'end'}, + }, + } wiz_btt('scrum.product.backlog.task.create') # vim:noexpandtab:tw=0 diff --git a/addons/stock/__terp__.py b/addons/stock/__terp__.py index 2a2df333745..e211c1def9a 100644 --- a/addons/stock/__terp__.py +++ b/addons/stock/__terp__.py @@ -26,15 +26,15 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Stock Management", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com/module_stock.html", - "depends" : ["product", "account"], - "category" : "Generic Modules/Inventory Control", - "init_xml" : [], - "demo_xml" : ["stock_demo.xml"], - "update_xml" : ["stock_workflow.xml", "stock_data.xml", "stock_incoterms.xml","stock_wizard.xml", "stock_view.xml", "stock_report.xml", "stock_sequence.xml", "product_data.xml",], - "active": False, - "installable": True + "name" : "Stock Management", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_stock.html", + "depends" : ["product", "account"], + "category" : "Generic Modules/Inventory Control", + "init_xml" : [], + "demo_xml" : ["stock_demo.xml"], + "update_xml" : ["stock_workflow.xml", "stock_data.xml", "stock_incoterms.xml","stock_wizard.xml", "stock_view.xml", "stock_report.xml", "stock_sequence.xml", "product_data.xml",], + "active": False, + "installable": True } diff --git a/addons/stock/partner.py b/addons/stock/partner.py index 8af7efe973f..85338867db2 100644 --- a/addons/stock/partner.py +++ b/addons/stock/partner.py @@ -32,28 +32,28 @@ from osv import fields, osv import ir class res_partner(osv.osv): - _name = 'res.partner' - _inherit = 'res.partner' - _columns = { - 'property_stock_customer': fields.property( - 'stock.location', - type='many2one', - relation='stock.location', - string="Customer Location", - method=True, - view_load=True, - group_name="Inventory Properties", - help="This stock location will be used, instead of the default one, as the destination location for goods you send to this partner"), - 'property_stock_supplier': fields.property( - 'stock.location', - type='many2one', - relation='stock.location', - string="Supplier Location", - method=True, - view_load=True, - group_name="Inventory Properties", - help="This stock location will be used, instead of the default one, as the source location for goods you receive from the current partner"), - } + _name = 'res.partner' + _inherit = 'res.partner' + _columns = { + 'property_stock_customer': fields.property( + 'stock.location', + type='many2one', + relation='stock.location', + string="Customer Location", + method=True, + view_load=True, + group_name="Inventory Properties", + help="This stock location will be used, instead of the default one, as the destination location for goods you send to this partner"), + 'property_stock_supplier': fields.property( + 'stock.location', + type='many2one', + relation='stock.location', + string="Supplier Location", + method=True, + view_load=True, + group_name="Inventory Properties", + help="This stock location will be used, instead of the default one, as the source location for goods you receive from the current partner"), + } res_partner() diff --git a/addons/stock/product.py b/addons/stock/product.py index f3ec7e15f6f..cef6595283a 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -32,69 +32,69 @@ from osv import fields, osv class product_product(osv.osv): - _name = 'product.template' - _inherit = 'product.template' - _columns = { - 'property_stock_procurement': fields.property( - 'stock.location', - type='many2one', - relation='stock.location', - string="Procurement Location", - method=True, - view_load=True, - group_name="Inventory Properties", - help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by procurements"), - 'property_stock_production': fields.property( - 'stock.location', - type='many2one', - relation='stock.location', - string="Production Location", - method=True, - view_load=True, - group_name="Inventory Properties", - help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by production orders"), - 'property_stock_inventory': fields.property( - 'stock.location', - type='many2one', - relation='stock.location', - string="Inventory Location", - method=True, - view_load=True, - group_name="Inventory Properties", - help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory"), - 'property_stock_account_input': fields.property('account.account', - type='many2one', relation='account.account', - string='Stock Input Account', method=True, view_load=True, - group_name='Stock Accounting Properties', - help='This account will be used, instead of the default one, to value input stock'), - 'property_stock_account_output': fields.property('account.account', - type='many2one', relation='account.account', - string='Stock Output Account', method=True, view_load=True, - group_name='Stock Accounting Properties', - help='This account will be used, instead of the default one, to value output stock'), - } + _name = 'product.template' + _inherit = 'product.template' + _columns = { + 'property_stock_procurement': fields.property( + 'stock.location', + type='many2one', + relation='stock.location', + string="Procurement Location", + method=True, + view_load=True, + group_name="Inventory Properties", + help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by procurements"), + 'property_stock_production': fields.property( + 'stock.location', + type='many2one', + relation='stock.location', + string="Production Location", + method=True, + view_load=True, + group_name="Inventory Properties", + help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by production orders"), + 'property_stock_inventory': fields.property( + 'stock.location', + type='many2one', + relation='stock.location', + string="Inventory Location", + method=True, + view_load=True, + group_name="Inventory Properties", + help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory"), + 'property_stock_account_input': fields.property('account.account', + type='many2one', relation='account.account', + string='Stock Input Account', method=True, view_load=True, + group_name='Stock Accounting Properties', + help='This account will be used, instead of the default one, to value input stock'), + 'property_stock_account_output': fields.property('account.account', + type='many2one', relation='account.account', + string='Stock Output Account', method=True, view_load=True, + group_name='Stock Accounting Properties', + help='This account will be used, instead of the default one, to value output stock'), + } product_product() class product_category(osv.osv): - _inherit = 'product.category' - _columns = { - 'property_stock_journal': fields.property('account.journal', - relation='account.journal', type='many2one', - string='Stock journal', method=True, view_load=True, - group_name="Accounting Properties", - help="This journal will be used for the accounting move generated by stock move"), - 'property_stock_account_input_categ': fields.property('account.account', - type='many2one', relation='account.account', - string='Stock Input Account', method=True, view_load=True, - group_name='Stock Accounting Properties', - help='This account will be used to value the input stock'), - 'property_stock_account_output_categ': fields.property('account.account', - type='many2one', relation='account.account', - string='Stock Output Account', method=True, view_load=True, - group_name='Stock Accounting Properties', - help='This account will be used to value the output stock'), - } + _inherit = 'product.category' + _columns = { + 'property_stock_journal': fields.property('account.journal', + relation='account.journal', type='many2one', + string='Stock journal', method=True, view_load=True, + group_name="Accounting Properties", + help="This journal will be used for the accounting move generated by stock move"), + 'property_stock_account_input_categ': fields.property('account.account', + type='many2one', relation='account.account', + string='Stock Input Account', method=True, view_load=True, + group_name='Stock Accounting Properties', + help='This account will be used to value the input stock'), + 'property_stock_account_output_categ': fields.property('account.account', + type='many2one', relation='account.account', + string='Stock Output Account', method=True, view_load=True, + group_name='Stock Accounting Properties', + help='This account will be used to value the output stock'), + } product_category() diff --git a/addons/stock/report/lot_location.py b/addons/stock/report/lot_location.py index ddd79004ce5..1fe4743dafd 100755..100644 --- a/addons/stock/report/lot_location.py +++ b/addons/stock/report/lot_location.py @@ -30,35 +30,35 @@ import time from report import report_sxw class lot_location(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(lot_location, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'process':self.process, - }) + def __init__(self, cr, uid, name, context): + super(lot_location, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'process':self.process, + }) - def process(self,location_id): - res = {} - location_obj = pooler.get_pool(self.cr.dbname).get('stock.location') - product_obj = pooler.get_pool(self.cr.dbname).get('product.product') + def process(self,location_id): + res = {} + location_obj = pooler.get_pool(self.cr.dbname).get('stock.location') + product_obj = pooler.get_pool(self.cr.dbname).get('product.product') - res['location_name'] = pooler.get_pool(self.cr.dbname).get('stock.location').read(self.cr, self.uid, [location_id],['name'])[0]['name'] + res['location_name'] = pooler.get_pool(self.cr.dbname).get('stock.location').read(self.cr, self.uid, [location_id],['name'])[0]['name'] - prod_info = location_obj._product_get(self.cr, self.uid, location_id) + prod_info = location_obj._product_get(self.cr, self.uid, location_id) - res['product'] = [] - for prod in product_obj.browse(self.cr, self.uid, prod_info.keys()): - if prod_info[prod.id]: - res['product'].append({'prod_name': prod.name, 'prod_qty': prod_info[prod.id]}) - if not res['product']: - res['product'].append({'prod_name': '', 'prod_qty': ''}) - location_child = location_obj.read(self.cr, self.uid, [location_id], ['child_ids']) - list=[] - list.append(res) - for child_id in location_child[0]['child_ids']: - list.extend(self.process(child_id)) + res['product'] = [] + for prod in product_obj.browse(self.cr, self.uid, prod_info.keys()): + if prod_info[prod.id]: + res['product'].append({'prod_name': prod.name, 'prod_qty': prod_info[prod.id]}) + if not res['product']: + res['product'].append({'prod_name': '', 'prod_qty': ''}) + location_child = location_obj.read(self.cr, self.uid, [location_id], ['child_ids']) + list=[] + list.append(res) + for child_id in location_child[0]['child_ids']: + list.extend(self.process(child_id)) - return list + return list report_sxw.report_sxw('report.lot.location', 'stock.location', 'addons/stock/report/lot_location.rml', parser=lot_location) diff --git a/addons/stock/report/product_location.py b/addons/stock/report/product_location.py index e29d96e9ab1..94287e24a35 100644 --- a/addons/stock/report/product_location.py +++ b/addons/stock/report/product_location.py @@ -34,41 +34,41 @@ import pooler class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context=None): - pool = pooler.get_pool(cr.dbname) - product_obj = pool.get('product.product') - location_obj = pool.get('stock.location') + def create_xml(self, cr, uid, ids, datas, context=None): + pool = pooler.get_pool(cr.dbname) + product_obj = pool.get('product.product') + location_obj = pool.get('stock.location') - products = product_obj.read(cr, uid, ids, ['name','uom_id']) - cr.execute('SELECT id FROM stock_location WHERE usage = %s', - ('internal',)) - location_ids = [ x[0] for x in cr.fetchall() ] - location_ids.sort() - locs_info = {} - locs_name = dict(location_obj.name_get(cr, uid, location_ids)) - for location_id in locs_name.keys(): - locs_info[location_id] = location_obj._product_get(cr, uid, - location_id, ids) + products = product_obj.read(cr, uid, ids, ['name','uom_id']) + cr.execute('SELECT id FROM stock_location WHERE usage = %s', + ('internal',)) + location_ids = [ x[0] for x in cr.fetchall() ] + location_ids.sort() + locs_info = {} + locs_name = dict(location_obj.name_get(cr, uid, location_ids)) + for location_id in locs_name.keys(): + locs_info[location_id] = location_obj._product_get(cr, uid, + location_id, ids) - xml = '<?xml version="1.0" ?><report>' - for p in products: - xml += '<product>' \ - '<name>' + toxml(p['name']) + '</name>' \ - '<unit>' + toxml(p['uom_id'][1]) + '</unit>' \ - '<locations>' - for loc_id in locs_info.keys(): - if locs_info[loc_id].get(p['id']): - xml += '<location>' - xml += '<loc_name>' + toxml(locs_name[loc_id]) \ - + '</loc_name>' - xml += '<loc_qty>' + toxml(locs_info[loc_id].get(p['id'])) \ - + '</loc_qty>' - xml += '</location>' - xml += '</locations>' \ - '</product>' - xml += '</report>' - return self.post_process_xml_data(cr, uid, xml, context) + xml = '<?xml version="1.0" ?><report>' + for p in products: + xml += '<product>' \ + '<name>' + toxml(p['name']) + '</name>' \ + '<unit>' + toxml(p['uom_id'][1]) + '</unit>' \ + '<locations>' + for loc_id in locs_info.keys(): + if locs_info[loc_id].get(p['id']): + xml += '<location>' + xml += '<loc_name>' + toxml(locs_name[loc_id]) \ + + '</loc_name>' + xml += '<loc_qty>' + toxml(locs_info[loc_id].get(p['id'])) \ + + '</loc_qty>' + xml += '</location>' + xml += '</locations>' \ + '</product>' + xml += '</report>' + return self.post_process_xml_data(cr, uid, xml, context) report_custom('report.stock.product.location', 'stock.location', '', - 'addons/stock/report/product_location.xsl') + 'addons/stock/report/product_location.xsl') diff --git a/addons/stock/report/product_stock.py b/addons/stock/report/product_stock.py index 53b60809a83..a4b60e4a68b 100644 --- a/addons/stock/report/product_stock.py +++ b/addons/stock/report/product_stock.py @@ -39,66 +39,66 @@ import pooler import StringIO class external_pdf(render): - def __init__(self, pdf): - render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - - def _render(self): - return self.pdf + def __init__(self, pdf): + render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + + def _render(self): + return self.pdf class report_stock(report_int): - def create(self, cr, uid, ids, datas, context={}): - product_ids = ids - if 'location_id' in context: - location_id = context['location_id'] - else: - warehouse_id = pooler.get_pool(cr.dbname).get('stock.warehouse').search(cr, uid, [])[0] - location_id = pooler.get_pool(cr.dbname).get('stock.warehouse').browse(cr, uid, warehouse_id).lot_stock_id.id - - loc_ids = pooler.get_pool(cr.dbname).get('stock.location').search(cr, uid, [('location_id','child_of',[location_id])]) - - loc_ids_s = ','.join(map(str,loc_ids)) - now = time.strftime('%Y-%m-%d') - dt_from = now - dt_to = now - - names = dict(pooler.get_pool(cr.dbname).get('product.product').name_get(cr, uid, product_ids)) - products = {} - prods = pooler.get_pool(cr.dbname).get('stock.location')._product_all_get(cr, uid, location_id, product_ids) - prod_ids_s = ','.join(map(str,product_ids)) - - for p in prods: - products[p] = [(now,prods[p])] - prods[p] = 0 - - cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id from stock_move r left join product_uom u on (r.product_uom=u.id) where state in ('confirmed','assigned','waiting') and location_id in ("+loc_ids_s+") and product_id in ("+prod_ids_s+") group by date_planned,product_id") - - for (qty, dt, prod_id) in cr.fetchall(): - if dt<=dt_from: - dt= (DateTime.now() + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') - products.setdefault(prod_id, []) - products[prod_id].append((dt,-qty)) - - cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id from stock_move r left join product_uom u on (r.product_uom=u.id) where state in ('confirmed','assigned','waiting') and location_dest_id in ("+loc_ids_s+") and product_id in ("+prod_ids_s+") group by date_planned,product_id") - for (qty, dt, prod_id) in cr.fetchall(): - if dt<=dt_from: - dt= (DateTime.now() + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') - products.setdefault(prod_id, []) - products[prod_id].append((dt,qty)) - - dt = dt_from - qty = 0 - - io = StringIO.StringIO() - gt = stock_graph.stock_graph(io) - for prod_id in products: - gt.add(prod_id, names.get(prod_id, 'Unknown'), products[prod_id]) - gt.draw() - gt.close() - self.obj = external_pdf(io.getvalue()) - self.obj.render() - return (self.obj.pdf, 'pdf') + def create(self, cr, uid, ids, datas, context={}): + product_ids = ids + if 'location_id' in context: + location_id = context['location_id'] + else: + warehouse_id = pooler.get_pool(cr.dbname).get('stock.warehouse').search(cr, uid, [])[0] + location_id = pooler.get_pool(cr.dbname).get('stock.warehouse').browse(cr, uid, warehouse_id).lot_stock_id.id + + loc_ids = pooler.get_pool(cr.dbname).get('stock.location').search(cr, uid, [('location_id','child_of',[location_id])]) + + loc_ids_s = ','.join(map(str,loc_ids)) + now = time.strftime('%Y-%m-%d') + dt_from = now + dt_to = now + + names = dict(pooler.get_pool(cr.dbname).get('product.product').name_get(cr, uid, product_ids)) + products = {} + prods = pooler.get_pool(cr.dbname).get('stock.location')._product_all_get(cr, uid, location_id, product_ids) + prod_ids_s = ','.join(map(str,product_ids)) + + for p in prods: + products[p] = [(now,prods[p])] + prods[p] = 0 + + cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id from stock_move r left join product_uom u on (r.product_uom=u.id) where state in ('confirmed','assigned','waiting') and location_id in ("+loc_ids_s+") and product_id in ("+prod_ids_s+") group by date_planned,product_id") + + for (qty, dt, prod_id) in cr.fetchall(): + if dt<=dt_from: + dt= (DateTime.now() + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') + products.setdefault(prod_id, []) + products[prod_id].append((dt,-qty)) + + cr.execute("select sum(r.product_qty * u.factor), r.date_planned, r.product_id from stock_move r left join product_uom u on (r.product_uom=u.id) where state in ('confirmed','assigned','waiting') and location_dest_id in ("+loc_ids_s+") and product_id in ("+prod_ids_s+") group by date_planned,product_id") + for (qty, dt, prod_id) in cr.fetchall(): + if dt<=dt_from: + dt= (DateTime.now() + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d') + products.setdefault(prod_id, []) + products[prod_id].append((dt,qty)) + + dt = dt_from + qty = 0 + + io = StringIO.StringIO() + gt = stock_graph.stock_graph(io) + for prod_id in products: + gt.add(prod_id, names.get(prod_id, 'Unknown'), products[prod_id]) + gt.draw() + gt.close() + self.obj = external_pdf(io.getvalue()) + self.obj.render() + return (self.obj.pdf, 'pdf') report_stock('report.stock.product.history') diff --git a/addons/stock/report/stock_by_location.py b/addons/stock/report/stock_by_location.py index 66d7578ab9b..7337d4f25d3 100644 --- a/addons/stock/report/stock_by_location.py +++ b/addons/stock/report/stock_by_location.py @@ -35,56 +35,56 @@ from report.interface import toxml #FIXME: we should use toxml class report_custom(report_rml): - def create_xml(self, cr, uid, ids, datas, context={}): - config = """ - <config> - <date>09/09/2005</date> - <PageSize>210.00mm,297.00mm</PageSize> - <PageWidth>595.27</PageWidth> - <PageHeight>841.88</PageHeight> - <tableSize>60.00mm,60.00mm,60.00mm</tableSize> - <report-header>Stock by location</report-header> - <report-footer>Generated by Tiny ERP</report-footer> - </config> - """ - header = """ - <header> - <field>Location</field> - <field>Product name</field> - <field>Product quantity</field> - </header> - """ + def create_xml(self, cr, uid, ids, datas, context={}): + config = """ + <config> + <date>09/09/2005</date> + <PageSize>210.00mm,297.00mm</PageSize> + <PageWidth>595.27</PageWidth> + <PageHeight>841.88</PageHeight> + <tableSize>60.00mm,60.00mm,60.00mm</tableSize> + <report-header>Stock by location</report-header> + <report-footer>Generated by Tiny ERP</report-footer> + </config> + """ + header = """ + <header> + <field>Location</field> + <field>Product name</field> + <field>Product quantity</field> + </header> + """ - def process(location_id, level): - xml = '<row>' - location_name = pooler.get_pool(cr.dbname).get('stock.location').read(cr, uid, [location_id], ['name']) - xml += "<col para='yes' tree='yes' space='" + str(3*level) + "mm'>" - xml += location_name[0]['name'] + '</col>' + def process(location_id, level): + xml = '<row>' + location_name = pooler.get_pool(cr.dbname).get('stock.location').read(cr, uid, [location_id], ['name']) + xml += "<col para='yes' tree='yes' space='" + str(3*level) + "mm'>" + xml += location_name[0]['name'] + '</col>' - prod_info = pooler.get_pool(cr.dbname).get('stock.location')._product_get(cr, uid, location_id) - xml += "<col>" - for prod_id in prod_info.keys(): - if prod_info[prod_id] != 0.0: - prod_name = pooler.get_pool(cr.dbname).get('product.product').read(cr, uid, [prod_id], ['name']) - xml += prod_name[0]['name'] + '\n' - xml += '</col>' + prod_info = pooler.get_pool(cr.dbname).get('stock.location')._product_get(cr, uid, location_id) + xml += "<col>" + for prod_id in prod_info.keys(): + if prod_info[prod_id] != 0.0: + prod_name = pooler.get_pool(cr.dbname).get('product.product').read(cr, uid, [prod_id], ['name']) + xml += prod_name[0]['name'] + '\n' + xml += '</col>' - xml += "<col>" - for prod_id in prod_info.keys(): - if prod_info[prod_id] != 0.0: - xml += str(prod_info[prod_id]) + '\n' - xml += '</col></row>' + xml += "<col>" + for prod_id in prod_info.keys(): + if prod_info[prod_id] != 0.0: + xml += str(prod_info[prod_id]) + '\n' + xml += '</col></row>' - location_child = pooler.get_pool(cr.dbname).get('stock.location').read(cr, uid, [location_id], ['child_ids']) - for child_id in location_child[0]['child_ids']: - xml += process(child_id, level+1) - return xml + location_child = pooler.get_pool(cr.dbname).get('stock.location').read(cr, uid, [location_id], ['child_ids']) + for child_id in location_child[0]['child_ids']: + xml += process(child_id, level+1) + return xml - for location_id in ids: - xml = '<lines>' + process(location_id, 0) + '</lines>' + for location_id in ids: + xml = '<lines>' + process(location_id, 0) + '</lines>' - xml = '<?xml version="1.0" ?>' '<report>'+ config + header + xml + '</report>' + xml = '<?xml version="1.0" ?>' '<report>'+ config + header + xml + '</report>' - return self.post_process_xml_data(cr, uid, xml, context) + return self.post_process_xml_data(cr, uid, xml, context) report_custom('report.stock.lot.location', 'stock.location', '', 'addons/stock/report/stock_by_location.xsl') diff --git a/addons/stock/report/stock_graph.py b/addons/stock/report/stock_graph.py index 8951dd2a574..1dddf1f832e 100644 --- a/addons/stock/report/stock_graph.py +++ b/addons/stock/report/stock_graph.py @@ -35,81 +35,81 @@ from report.misc import choice_colors # dRAw a graph for stocks # class stock_graph(object): - def __init__(self, io): - self._datas = {} - self._canvas = canvas.init(fname=io, format='pdf') - self._canvas.set_author("Tiny ERP") - self._names = {} - self.val_min = '' - self.val_max = '' + def __init__(self, io): + self._datas = {} + self._canvas = canvas.init(fname=io, format='pdf') + self._canvas.set_author("Tiny ERP") + self._names = {} + self.val_min = '' + self.val_max = '' - def add(self, product_id, product_name, datas): - if hasattr(product_name, 'replace'): - product_name=product_name.replace('/', '//') - if product_id not in self._datas: - self._datas[product_id] = {} - self._names[product_id] = product_name - for (dt,stock) in datas: - if not dt in self._datas[product_id]: - self._datas[product_id][dt]=0 - self._datas[product_id][dt]+=stock - if self.val_min: - self.val_min = min(self.val_min,dt) - else: - self.val_min = dt - self.val_max = max(self.val_max,dt) + def add(self, product_id, product_name, datas): + if hasattr(product_name, 'replace'): + product_name=product_name.replace('/', '//') + if product_id not in self._datas: + self._datas[product_id] = {} + self._names[product_id] = product_name + for (dt,stock) in datas: + if not dt in self._datas[product_id]: + self._datas[product_id][dt]=0 + self._datas[product_id][dt]+=stock + if self.val_min: + self.val_min = min(self.val_min,dt) + else: + self.val_min = dt + self.val_max = max(self.val_max,dt) - def draw(self): - colors = choice_colors(len(self._datas.keys())) - user_color = {} - for user in self._datas.keys(): - user_color[user] = colors.pop() + def draw(self): + colors = choice_colors(len(self._datas.keys())) + user_color = {} + for user in self._datas.keys(): + user_color[user] = colors.pop() - val_min = int(time.mktime(time.strptime(self.val_min,'%Y-%m-%d'))) - val_max = int(time.mktime(time.strptime(self.val_max,'%Y-%m-%d'))) + val_min = int(time.mktime(time.strptime(self.val_min,'%Y-%m-%d'))) + val_max = int(time.mktime(time.strptime(self.val_max,'%Y-%m-%d'))) - plots = [] - for product_id in self._datas: - f = fill_style.Plain() - f.bgcolor = user_color[user] - datas = self._datas[product_id].items() - datas = map(lambda x: (int(time.mktime(time.strptime(x[0],'%Y-%m-%d'))),x[1]), datas) - datas.sort() - datas2 = [] - val = 0 - for d in datas: - val+=d[1] + plots = [] + for product_id in self._datas: + f = fill_style.Plain() + f.bgcolor = user_color[user] + datas = self._datas[product_id].items() + datas = map(lambda x: (int(time.mktime(time.strptime(x[0],'%Y-%m-%d'))),x[1]), datas) + datas.sort() + datas2 = [] + val = 0 + for d in datas: + val+=d[1] - if len(datas2): - d2 = d[0]-60*61*24 - if datas2[-1][0]<d2-1000: - datas2.append((d2,datas2[-1][1])) - datas2.append((d[0],val)) - if len(datas2) and datas2[-1][0]<val_max-100: - datas2.append((val_max, datas2[-1][1])) - if len(datas2)==1: - datas2.append( (datas2[0][0]+100, datas2[0][1]) ) - st = line_style.T() - st.color = user_color[product_id] - st.width = 1 - st.cap_style=1 - st.join_style=1 - plot = line_plot.T(label=self._names[product_id], data=datas2, line_style=st) - plots.append(plot) + if len(datas2): + d2 = d[0]-60*61*24 + if datas2[-1][0]<d2-1000: + datas2.append((d2,datas2[-1][1])) + datas2.append((d[0],val)) + if len(datas2) and datas2[-1][0]<val_max-100: + datas2.append((val_max, datas2[-1][1])) + if len(datas2)==1: + datas2.append( (datas2[0][0]+100, datas2[0][1]) ) + st = line_style.T() + st.color = user_color[product_id] + st.width = 1 + st.cap_style=1 + st.join_style=1 + plot = line_plot.T(label=self._names[product_id], data=datas2, line_style=st) + plots.append(plot) - interval = max((val_max-val_min)/15, 86400) - x_axis = axis.X(format=lambda x:'/a60{}'+time.strftime('%Y-%m-%d',time.gmtime(x)), tic_interval=interval, label=None) - ar = area.T(size = (620,435), x_range=(val_min,val_max+1), y_axis = axis.Y(format="%d", label="Virtual Stock (Unit)"), x_axis=x_axis) - for plot in plots: - ar.add_plot(plot) - ar.draw(self._canvas) + interval = max((val_max-val_min)/15, 86400) + x_axis = axis.X(format=lambda x:'/a60{}'+time.strftime('%Y-%m-%d',time.gmtime(x)), tic_interval=interval, label=None) + ar = area.T(size = (620,435), x_range=(val_min,val_max+1), y_axis = axis.Y(format="%d", label="Virtual Stock (Unit)"), x_axis=x_axis) + for plot in plots: + ar.add_plot(plot) + ar.draw(self._canvas) - def close(self): - self._canvas.close() + def close(self): + self._canvas.close() if __name__ == '__main__': - gt = stock_graph('test.pdf') - gt.add(1, 'Pomme', [('2005-07-29', 6), ('2005-07-30', -2), ('2005-07-31', 4)]) - gt.add(2, 'Cailloux', [('2005-07-29', 9), ('2005-07-30', -4), ('2005-07-31', 2)]) - gt.draw() - gt.close() + gt = stock_graph('test.pdf') + gt.add(1, 'Pomme', [('2005-07-29', 6), ('2005-07-30', -2), ('2005-07-31', 4)]) + gt.add(2, 'Cailloux', [('2005-07-29', 9), ('2005-07-30', -4), ('2005-07-31', 2)]) + gt.draw() + gt.close() diff --git a/addons/stock/report/ups.py b/addons/stock/report/ups.py index 04eef836b9f..930cb8083ad 100644 --- a/addons/stock/report/ups.py +++ b/addons/stock/report/ups.py @@ -30,15 +30,15 @@ from report.interface import report_rml class report_custom(report_rml): - def create(self, uid, ids, datas, context): - datas.setdefault('form', {}) - datas['form'].setdefault('weight', 3.0) + def create(self, uid, ids, datas, context): + datas.setdefault('form', {}) + datas['form'].setdefault('weight', 3.0) - datas['model'] = 'stock.move.lot' - datas['ids'] = ids - del datas['id'] + datas['model'] = 'stock.move.lot' + datas['ids'] = ids + del datas['id'] - return (super(report_custom, self).create(uid, ids, datas, context), 'pdf') + return (super(report_custom, self).create(uid, ids, datas, context), 'pdf') report_custom('report.stock.move.lot.ups_xml', 'stock.move.lot', 'addons/stock/report/UPS.xml', None) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 2cef57f04de..02964d1865c 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -40,30 +40,30 @@ from tools.translate import _ # Incoterms #---------------------------------------------------------- class stock_incoterms(osv.osv): - _name = "stock.incoterms" - _description = "Incoterms" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'code': fields.char('Code', size=3, required=True), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: True, - } + _name = "stock.incoterms" + _description = "Incoterms" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=3, required=True), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: True, + } stock_incoterms() #class stock_lot(osv.osv): -# _name = "stock.lot" -# _description = "Lot" -# _columns = { -# 'name': fields.char('Lot Name', size=64, required=True), -# 'active': fields.boolean('Active'), -# 'tracking': fields.char('Tracking', size=64), -# 'move_ids': fields.one2many('stock.move', 'lot_id', 'Move lines'), -# } -# _defaults = { -# 'active': lambda *a: True, -# } +# _name = "stock.lot" +# _description = "Lot" +# _columns = { +# 'name': fields.char('Lot Name', size=64, required=True), +# 'active': fields.boolean('Active'), +# 'tracking': fields.char('Tracking', size=64), +# 'move_ids': fields.one2many('stock.move', 'lot_id', 'Move lines'), +# } +# _defaults = { +# 'active': lambda *a: True, +# } #stock_lot() @@ -71,209 +71,209 @@ stock_incoterms() # Stock Location #---------------------------------------------------------- class stock_location(osv.osv): - _name = "stock.location" - _description = "Location" - _parent_name = "location_id" - _columns = { - 'name': fields.char('Location Name', size=64, required=True), - 'active': fields.boolean('Active'), - 'usage': fields.selection([('supplier','Supplier Location'),('view','View'),('internal','Internal Location'),('customer','Customer Location'),('inventory','Inventory'),('procurement','Procurement'),('production','Production')], 'Location type'), - 'allocation_method': fields.selection([('fifo','FIFO'),('lifo','LIFO'),('nearest','Nearest')], 'Allocation Method', required=True), - - 'account_id': fields.many2one('account.account', string='Inventory Account', domain=[('type','!=','view')]), - 'location_id': fields.many2one('stock.location', 'Parent Location', select=True), - 'child_ids': fields.one2many('stock.location', 'location_id', 'Contains'), - - 'chained_location_id': fields.many2one('stock.location', 'Chained Location If Fixed'), - 'chained_location_type': fields.selection([('','None'),('customer', 'Customer'),('fixed','Fixed Location')], 'Chained Location Type'), - 'chained_auto_packing': fields.boolean('Chained Auto-Packing'), - 'chained_delay': fields.integer('Chained Delay (days)'), - - 'address_id': fields.many2one('res.partner.address', 'Location Address'), - - 'comment': fields.text('Additional Information'), - 'posx': fields.integer('Corridor (X)', required=True), - 'posy': fields.integer('Shelves (Y)', required=True), - 'posz': fields.integer('Height (Z)', required=True), - } - _defaults = { - 'active': lambda *a: 1, - 'usage': lambda *a: 'internal', - 'allocation_method': lambda *a: 'fifo', - 'chained_location_type': lambda *a: '', - 'posx': lambda *a: 0, - 'posy': lambda *a: 0, - 'posz': lambda *a: 0, - } - - def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}): - result = None - if location.chained_location_type=='customer': - result = partner.property_stock_customer - elif location.chained_location_type=='fixed': - result = location.chained_location_id - return result - - - def picking_type_get(self, cr, uid, from_location, to_location, context={}): - result = 'internal' - if (from_location.usage=='internal') and (to_location and to_location.usage in ('customer','supplier')): - result = 'delivery' - elif (from_location.usage in ('supplier','customer')) and (to_location.usage=='internal'): - result = 'in' - return result - - def _product_get_all_report(self, cr, uid, ids, product_ids=False, - context=None): - return self._product_get_report(cr, uid, ids, product_ids, context, - recursive=True) - - def _product_get_report(self, cr, uid, ids, product_ids=False, - context=None, recursive=False): - if context is None: - context = {} - product_obj = self.pool.get('product.product') - if not product_ids: - product_ids = product_obj.search(cr, uid, []) - - products = product_obj.browse(cr, uid, product_ids, context=context) - products_by_uom = {} - products_by_id = {} - for product in products: - products_by_uom.setdefault(product.uom_id.id, []) - products_by_uom[product.uom_id.id].append(product) - products_by_id.setdefault(product.id, []) - products_by_id[product.id] = product - - result = [] - for id in ids: - for uom_id in products_by_uom.keys(): - fnc = self._product_get - if recursive: - fnc = self._product_all_get - ctx = context.copy() - ctx['uom'] = uom_id - qty = fnc(cr, uid, id, [x.id for x in products_by_uom[uom_id]], - context=ctx) - for product_id in qty.keys(): - if not qty[product_id]: - continue - product = products_by_id[product_id] - result.append({ - 'price': product.standard_price, - 'name': product.name, - 'code': product.default_code, # used by lot_overview_all report! - 'variants': product.variants or '', - 'uom': product.uom_id.name, - 'amount': qty[product_id], - }) - return result - - def _product_get_multi_location(self, cr, uid, ids, product_ids=False, context={}, states=['done'], what=('in', 'out')): - product_obj = self.pool.get('product.product') - states_str = ','.join(map(lambda s: "'%s'" % s, states)) - if not product_ids: - product_ids = product_obj.search(cr, uid, []) - res = {} - for id in product_ids: - res[id] = 0.0 - if not ids: - return res - - product2uom = {} - for product in product_obj.browse(cr, uid, product_ids, context=context): - product2uom[product.id] = product.uom_id.id - - prod_ids_str = ','.join(map(str, product_ids)) - location_ids_str = ','.join(map(str, ids)) - results = [] - results2 = [] - if 'in' in what: - # all moves from a location out of the set to a location in the set - cr.execute( - 'select sum(product_qty), product_id, product_uom '\ - 'from stock_move '\ - 'where location_id not in ('+location_ids_str+') '\ - 'and location_dest_id in ('+location_ids_str+') '\ - 'and product_id in ('+prod_ids_str+') '\ - 'and state in ('+states_str+') '\ - 'group by product_id,product_uom' - ) - results = cr.fetchall() - if 'out' in what: - # all moves from a location in the set to a location out of the set - cr.execute( - 'select sum(product_qty), product_id, product_uom '\ - 'from stock_move '\ - 'where location_id in ('+location_ids_str+') '\ - 'and location_dest_id not in ('+location_ids_str+') '\ - 'and product_id in ('+prod_ids_str+') '\ - 'and state in ('+states_str+') '\ - 'group by product_id,product_uom' - ) - results2 = cr.fetchall() - uom_obj = self.pool.get('product.uom') - for amount, prod_id, prod_uom in results: - amount = uom_obj._compute_qty(cr, uid, prod_uom, amount, - context.get('uom', False) or product2uom[prod_id]) - res[prod_id] += amount - for amount, prod_id, prod_uom in results2: - amount = uom_obj._compute_qty(cr, uid, prod_uom, amount, - context.get('uom', False) or product2uom[prod_id]) - res[prod_id] -= amount - return res - - def _product_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): - ids = id and [id] or [] - return self._product_get_multi_location(cr, uid, ids, product_ids, context, states) - - def _product_all_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): - # build the list of ids of children of the location given by id - ids = id and [id] or [] - location_ids = self.search(cr, uid, [('location_id', 'child_of', ids)]) - return self._product_get_multi_location(cr, uid, location_ids, product_ids, context, states) - - def _product_virtual_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): - return self._product_all_get(cr, uid, id, product_ids, context, ['confirmed','waiting','assigned','done']) - - # - # TODO: - # Improve this function - # - # Returns: - # [ (tracking_id, product_qty, location_id) ] - # - def _product_reserve(self, cr, uid, ids, product_id, product_qty, context={}): - result = [] - amount = 0.0 - for id in self.search(cr, uid, [('location_id', 'child_of', ids)]): - cr.execute("select product_uom,sum(product_qty) as product_qty from stock_move where location_dest_id=%d and product_id=%d and state='done' group by product_uom", (id,product_id)) - results = cr.dictfetchall() - cr.execute("select product_uom,-sum(product_qty) as product_qty from stock_move where location_id=%d and product_id=%d and state in ('done', 'assigned') group by product_uom", (id,product_id)) - results += cr.dictfetchall() - - total = 0.0 - results2 = 0.0 - for r in results: - amount = self.pool.get('product.uom')._compute_qty(cr, uid, r['product_uom'],r['product_qty'], context.get('uom',False)) - results2 += amount - total += amount - - if total<=0.0: - continue - - amount = results2 - if amount>0: - if amount>min(total,product_qty): - amount = min(product_qty,total) - result.append((amount,id)) - product_qty -= amount - total -= amount - if product_qty<=0.0: - return result - if total<=0.0: - continue - return False + _name = "stock.location" + _description = "Location" + _parent_name = "location_id" + _columns = { + 'name': fields.char('Location Name', size=64, required=True), + 'active': fields.boolean('Active'), + 'usage': fields.selection([('supplier','Supplier Location'),('view','View'),('internal','Internal Location'),('customer','Customer Location'),('inventory','Inventory'),('procurement','Procurement'),('production','Production')], 'Location type'), + 'allocation_method': fields.selection([('fifo','FIFO'),('lifo','LIFO'),('nearest','Nearest')], 'Allocation Method', required=True), + + 'account_id': fields.many2one('account.account', string='Inventory Account', domain=[('type','!=','view')]), + 'location_id': fields.many2one('stock.location', 'Parent Location', select=True), + 'child_ids': fields.one2many('stock.location', 'location_id', 'Contains'), + + 'chained_location_id': fields.many2one('stock.location', 'Chained Location If Fixed'), + 'chained_location_type': fields.selection([('','None'),('customer', 'Customer'),('fixed','Fixed Location')], 'Chained Location Type'), + 'chained_auto_packing': fields.boolean('Chained Auto-Packing'), + 'chained_delay': fields.integer('Chained Delay (days)'), + + 'address_id': fields.many2one('res.partner.address', 'Location Address'), + + 'comment': fields.text('Additional Information'), + 'posx': fields.integer('Corridor (X)', required=True), + 'posy': fields.integer('Shelves (Y)', required=True), + 'posz': fields.integer('Height (Z)', required=True), + } + _defaults = { + 'active': lambda *a: 1, + 'usage': lambda *a: 'internal', + 'allocation_method': lambda *a: 'fifo', + 'chained_location_type': lambda *a: '', + 'posx': lambda *a: 0, + 'posy': lambda *a: 0, + 'posz': lambda *a: 0, + } + + def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}): + result = None + if location.chained_location_type=='customer': + result = partner.property_stock_customer + elif location.chained_location_type=='fixed': + result = location.chained_location_id + return result + + + def picking_type_get(self, cr, uid, from_location, to_location, context={}): + result = 'internal' + if (from_location.usage=='internal') and (to_location and to_location.usage in ('customer','supplier')): + result = 'delivery' + elif (from_location.usage in ('supplier','customer')) and (to_location.usage=='internal'): + result = 'in' + return result + + def _product_get_all_report(self, cr, uid, ids, product_ids=False, + context=None): + return self._product_get_report(cr, uid, ids, product_ids, context, + recursive=True) + + def _product_get_report(self, cr, uid, ids, product_ids=False, + context=None, recursive=False): + if context is None: + context = {} + product_obj = self.pool.get('product.product') + if not product_ids: + product_ids = product_obj.search(cr, uid, []) + + products = product_obj.browse(cr, uid, product_ids, context=context) + products_by_uom = {} + products_by_id = {} + for product in products: + products_by_uom.setdefault(product.uom_id.id, []) + products_by_uom[product.uom_id.id].append(product) + products_by_id.setdefault(product.id, []) + products_by_id[product.id] = product + + result = [] + for id in ids: + for uom_id in products_by_uom.keys(): + fnc = self._product_get + if recursive: + fnc = self._product_all_get + ctx = context.copy() + ctx['uom'] = uom_id + qty = fnc(cr, uid, id, [x.id for x in products_by_uom[uom_id]], + context=ctx) + for product_id in qty.keys(): + if not qty[product_id]: + continue + product = products_by_id[product_id] + result.append({ + 'price': product.standard_price, + 'name': product.name, + 'code': product.default_code, # used by lot_overview_all report! + 'variants': product.variants or '', + 'uom': product.uom_id.name, + 'amount': qty[product_id], + }) + return result + + def _product_get_multi_location(self, cr, uid, ids, product_ids=False, context={}, states=['done'], what=('in', 'out')): + product_obj = self.pool.get('product.product') + states_str = ','.join(map(lambda s: "'%s'" % s, states)) + if not product_ids: + product_ids = product_obj.search(cr, uid, []) + res = {} + for id in product_ids: + res[id] = 0.0 + if not ids: + return res + + product2uom = {} + for product in product_obj.browse(cr, uid, product_ids, context=context): + product2uom[product.id] = product.uom_id.id + + prod_ids_str = ','.join(map(str, product_ids)) + location_ids_str = ','.join(map(str, ids)) + results = [] + results2 = [] + if 'in' in what: + # all moves from a location out of the set to a location in the set + cr.execute( + 'select sum(product_qty), product_id, product_uom '\ + 'from stock_move '\ + 'where location_id not in ('+location_ids_str+') '\ + 'and location_dest_id in ('+location_ids_str+') '\ + 'and product_id in ('+prod_ids_str+') '\ + 'and state in ('+states_str+') '\ + 'group by product_id,product_uom' + ) + results = cr.fetchall() + if 'out' in what: + # all moves from a location in the set to a location out of the set + cr.execute( + 'select sum(product_qty), product_id, product_uom '\ + 'from stock_move '\ + 'where location_id in ('+location_ids_str+') '\ + 'and location_dest_id not in ('+location_ids_str+') '\ + 'and product_id in ('+prod_ids_str+') '\ + 'and state in ('+states_str+') '\ + 'group by product_id,product_uom' + ) + results2 = cr.fetchall() + uom_obj = self.pool.get('product.uom') + for amount, prod_id, prod_uom in results: + amount = uom_obj._compute_qty(cr, uid, prod_uom, amount, + context.get('uom', False) or product2uom[prod_id]) + res[prod_id] += amount + for amount, prod_id, prod_uom in results2: + amount = uom_obj._compute_qty(cr, uid, prod_uom, amount, + context.get('uom', False) or product2uom[prod_id]) + res[prod_id] -= amount + return res + + def _product_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): + ids = id and [id] or [] + return self._product_get_multi_location(cr, uid, ids, product_ids, context, states) + + def _product_all_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): + # build the list of ids of children of the location given by id + ids = id and [id] or [] + location_ids = self.search(cr, uid, [('location_id', 'child_of', ids)]) + return self._product_get_multi_location(cr, uid, location_ids, product_ids, context, states) + + def _product_virtual_get(self, cr, uid, id, product_ids=False, context={}, states=['done']): + return self._product_all_get(cr, uid, id, product_ids, context, ['confirmed','waiting','assigned','done']) + + # + # TODO: + # Improve this function + # + # Returns: + # [ (tracking_id, product_qty, location_id) ] + # + def _product_reserve(self, cr, uid, ids, product_id, product_qty, context={}): + result = [] + amount = 0.0 + for id in self.search(cr, uid, [('location_id', 'child_of', ids)]): + cr.execute("select product_uom,sum(product_qty) as product_qty from stock_move where location_dest_id=%d and product_id=%d and state='done' group by product_uom", (id,product_id)) + results = cr.dictfetchall() + cr.execute("select product_uom,-sum(product_qty) as product_qty from stock_move where location_id=%d and product_id=%d and state in ('done', 'assigned') group by product_uom", (id,product_id)) + results += cr.dictfetchall() + + total = 0.0 + results2 = 0.0 + for r in results: + amount = self.pool.get('product.uom')._compute_qty(cr, uid, r['product_uom'],r['product_qty'], context.get('uom',False)) + results2 += amount + total += amount + + if total<=0.0: + continue + + amount = results2 + if amount>0: + if amount>min(total,product_qty): + amount = min(product_qty,total) + result.append((amount,id)) + product_qty -= amount + total -= amount + if product_qty<=0.0: + return result + if total<=0.0: + continue + return False stock_location() @@ -282,469 +282,469 @@ stock_location() #---------------------------------------------------------- #class stock_move_lot(osv.osv): -# _name = "stock.move.lot" -# _description = "Move Lot" -# _columns = { -# 'name': fields.char('Move Description', size=64, required=True), -# 'active': fields.boolean('Active'), -# 'state': fields.selection( (('draft','Draft'),('done','Moved')), 'State', readonly=True), -# 'serial': fields.char('Tracking Number', size=32), -# 'date_planned': fields.date('Scheduled date'), -# 'date_moved': fields.date('Actual date'), -# 'lot_id': fields.many2one('stock.lot','Lot', required=True), -# 'loc_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), -# 'address_id': fields.many2one('res.partner.address', 'Destination Address'), -# 'origin': fields.char('Origin', size=64), -# } -# _defaults = { -# 'active': lambda *a: 1, -# 'state': lambda *a: 'draft', -# 'date_planned': lambda *a: time.strftime('%Y-%m-%d'), -# } -# # -# # TODO: test if valid -# # ERROR: does this function should call action_done instead of doing him self on -# # stock.move -# # -# def action_move(self, cr, uid, ids, context={}): -# for move in self.browse(cr, uid, ids, context): -# lot_remove = [] -# for m in move.lot_id.move_ids: -# new_id = self.pool.get('stock.move').copy(cr, uid, m.id, {'location_id': m.location_dest_id.id, 'location_dest_id': move.loc_dest_id.id, 'date_moved': time.strftime('%Y-%m-%d'), 'picking_id': False, 'state':'draft','prodlot_id':False, 'tracking_id':False, 'lot_id': False, 'move_history_ids':[], 'move_history_ids2':[]}) -# self.pool.get('stock.move').action_done(cr, uid, [new_id], context) -# cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%d,%d)', (m.id, new_id)) -# lot_remove.append(m.id) -# self.pool.get('stock.move').write(cr, uid, lot_remove, {'lot_id':False}) -# self.write(cr,uid, ids, {'state':'done','date_moved':time.strftime('%Y-%m-%d')}) -# return True +# _name = "stock.move.lot" +# _description = "Move Lot" +# _columns = { +# 'name': fields.char('Move Description', size=64, required=True), +# 'active': fields.boolean('Active'), +# 'state': fields.selection( (('draft','Draft'),('done','Moved')), 'State', readonly=True), +# 'serial': fields.char('Tracking Number', size=32), +# 'date_planned': fields.date('Scheduled date'), +# 'date_moved': fields.date('Actual date'), +# 'lot_id': fields.many2one('stock.lot','Lot', required=True), +# 'loc_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), +# 'address_id': fields.many2one('res.partner.address', 'Destination Address'), +# 'origin': fields.char('Origin', size=64), +# } +# _defaults = { +# 'active': lambda *a: 1, +# 'state': lambda *a: 'draft', +# 'date_planned': lambda *a: time.strftime('%Y-%m-%d'), +# } +# # +# # TODO: test if valid +# # ERROR: does this function should call action_done instead of doing him self on +# # stock.move +# # +# def action_move(self, cr, uid, ids, context={}): +# for move in self.browse(cr, uid, ids, context): +# lot_remove = [] +# for m in move.lot_id.move_ids: +# new_id = self.pool.get('stock.move').copy(cr, uid, m.id, {'location_id': m.location_dest_id.id, 'location_dest_id': move.loc_dest_id.id, 'date_moved': time.strftime('%Y-%m-%d'), 'picking_id': False, 'state':'draft','prodlot_id':False, 'tracking_id':False, 'lot_id': False, 'move_history_ids':[], 'move_history_ids2':[]}) +# self.pool.get('stock.move').action_done(cr, uid, [new_id], context) +# cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%d,%d)', (m.id, new_id)) +# lot_remove.append(m.id) +# self.pool.get('stock.move').write(cr, uid, lot_remove, {'lot_id':False}) +# self.write(cr,uid, ids, {'state':'done','date_moved':time.strftime('%Y-%m-%d')}) +# return True #stock_move_lot() class stock_tracking(osv.osv): - _name = "stock.tracking" - _description = "Stock Tracking Lots" - - def checksum(sscc): - salt = '31' * 8 + '3' - sum = 0 - for sscc_part, salt_part in zip(sscc, salt): - sum += int(sscc_part) * int(salt_part) - return (10 - (sum % 10)) % 10 - checksum = staticmethod(checksum) - - def make_sscc(self, cr, uid, context={}): - sequence = self.pool.get('ir.sequence').get(cr, uid, 'stock.lot.tracking') - return sequence + str(self.checksum(sequence)) - - _columns = { - 'name': fields.char('Tracking', size=64, required=True), - 'active': fields.boolean('Active'), - 'serial': fields.char('Reference', size=64), - 'move_ids' : fields.one2many('stock.move', 'tracking_id', 'Moves tracked'), - 'date': fields.datetime('Date create', required=True), - } - _defaults = { - 'active': lambda *a: 1, - 'name' : make_sscc, - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } - - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = self.search(cr, user, [('serial','=',name)]+ args, limit=limit, context=context) - ids += self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) - return self.name_get(cr, user, ids, context) - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - res = [(r['id'], r['name']+' ['+(r['serial'] or '')+']') for r in self.read(cr, uid, ids, ['name','serial'], context)] - return res - - def unlink(self, cr ,uid, ids): - raise Exception, _('You can not remove a lot line !') + _name = "stock.tracking" + _description = "Stock Tracking Lots" + + def checksum(sscc): + salt = '31' * 8 + '3' + sum = 0 + for sscc_part, salt_part in zip(sscc, salt): + sum += int(sscc_part) * int(salt_part) + return (10 - (sum % 10)) % 10 + checksum = staticmethod(checksum) + + def make_sscc(self, cr, uid, context={}): + sequence = self.pool.get('ir.sequence').get(cr, uid, 'stock.lot.tracking') + return sequence + str(self.checksum(sequence)) + + _columns = { + 'name': fields.char('Tracking', size=64, required=True), + 'active': fields.boolean('Active'), + 'serial': fields.char('Reference', size=64), + 'move_ids' : fields.one2many('stock.move', 'tracking_id', 'Moves tracked'), + 'date': fields.datetime('Date create', required=True), + } + _defaults = { + 'active': lambda *a: 1, + 'name' : make_sscc, + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = self.search(cr, user, [('serial','=',name)]+ args, limit=limit, context=context) + ids += self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context) + return self.name_get(cr, user, ids, context) + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + res = [(r['id'], r['name']+' ['+(r['serial'] or '')+']') for r in self.read(cr, uid, ids, ['name','serial'], context)] + return res + + def unlink(self, cr ,uid, ids): + raise Exception, _('You can not remove a lot line !') stock_tracking() #---------------------------------------------------------- # Stock Picking #---------------------------------------------------------- class stock_picking(osv.osv): - _name = "stock.picking" - _description = "Packing list" - _columns = { - 'name': fields.char('Packing name', size=64, required=True, select=True), - 'origin': fields.char('Origin', size=64), - 'type': fields.selection([('out','Sending Goods'),('in','Getting Goods'),('internal','Internal'),('delivery','Delivery')], 'Shipping Type', required=True, select=True), - 'active': fields.boolean('Active'), - 'note': fields.text('Notes'), - - 'location_id': fields.many2one('stock.location', 'Location'), - 'location_dest_id': fields.many2one('stock.location', 'Dest. Location'), - - 'move_type': fields.selection([('direct','Direct Delivery'),('one','All at once')],'Delivery Method', required=True), - 'state': fields.selection([ - ('draft','Draft'), - ('auto','Waiting'), - ('confirmed','Confirmed'), - ('assigned','Assigned'), - ('done','Done'), - ('cancel','Cancel'), - ], 'State', readonly=True), - 'date':fields.datetime('Date create'), - - 'move_lines': fields.one2many('stock.move', 'picking_id', 'Move lines'), - - 'auto_picking': fields.boolean('Auto-Packing'), - 'address_id': fields.many2one('res.partner.address', 'Partner'), - 'invoice_state':fields.selection([ - ("invoiced","Invoiced"), - ("2binvoiced","To be invoiced"), - ("none","Not from Packing")], "Invoice state"), - } - _defaults = { - 'name': lambda *a: '/', - 'active': lambda *a: 1, - 'state': lambda *a: 'draft', - 'move_type': lambda *a: 'direct', - 'type': lambda *a: 'in', - 'invoice_state': lambda *a: 'none', - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } - def onchange_partner_in(self, cr, uid, context, partner_id=None): - sid = self.pool.get('res.partner.address').browse(cr, uid, partner_id, context).partner_id.property_stock_supplier.id - return { - 'value': {'location_id':sid} - } - - def action_explode(self, cr, uid, ids, *args): - return True - - def action_confirm(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state': 'confirmed'}) - todo = [] - for picking in self.browse(cr, uid, ids): - if picking.name == self._defaults['name'](): - number = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.%s' % picking.type) - self.write(cr, uid, [picking.id], {'name': number}) - for r in picking.move_lines: - if r.state=='draft': - todo.append(r.id) - if len(todo): - self.pool.get('stock.move').action_confirm(cr,uid, todo) - self.action_explode(cr, uid, ids) - for picking in self.browse(cr, uid, ids): - todo = [] - for r in picking.move_lines: - if r.location_dest_id.chained_location_type: - todo.append(r) - if todo: - loc = self.pool.get('stock.location').chained_location_get(cr, uid, todo[0].location_dest_id, todo[0].picking_id and todo[0].picking_id.address_id and todo[0].picking_id.address_id.partner_id, todo[0].product_id) - ptype = self.pool.get('stock.location').picking_type_get(cr, uid, todo[0].location_dest_id, loc) - pickid = self.pool.get('stock.picking').create(cr, uid, { - 'name': picking.name, - 'origin': str(picking.origin or ''), - 'type': ptype, - 'note': picking.note, - 'move_type': picking.move_type, - 'auto_picking': todo[0].location_dest_id.chained_auto_packing, - 'address_id': picking.address_id.id, - 'invoice_state': 'none' - }) - for move in todo: - loc = self.pool.get('stock.location').chained_location_get(cr, uid, move.location_dest_id, picking.address_id.partner_id, move.product_id).id - new_id = self.pool.get('stock.move').copy(cr, uid, move.id, { - 'location_id': move.location_dest_id.id, - 'location_dest_id': loc, - 'date_moved': time.strftime('%Y-%m-%d'), - 'picking_id': pickid, - 'state':'waiting', - 'prodlot_id':False, - 'tracking_id':False, - 'move_history_ids':[], - 'date_planned': DateTime.strptime(move.date_planned, '%Y-%m-%d') + DateTime.RelativeDateTime(days=move.location_dest_id.chained_delay or 0), - 'move_history_ids2':[]} - ) - self.pool.get('stock.move').write(cr, uid, [move.id], { - 'move_dest_id': new_id, - 'move_history_ids': [(4, new_id)] - }) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr) - return True - - def test_auto_picking(self, cr, uid, ids): - # TODO: Check locations to see if in the same location ? - return True - - def action_assign(self, cr, uid, ids, *args): - for pick in self.browse(cr, uid, ids): - move_ids = [x.id for x in pick.move_lines if x.state=='confirmed'] - self.pool.get('stock.move').action_assign(cr, uid, move_ids) - return True - - def force_assign(self, cr, uid, ids, *args): - wf_service = netsvc.LocalService("workflow") - for pick in self.browse(cr, uid, ids): -# move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed'] - move_ids = [x.id for x in pick.move_lines] - self.pool.get('stock.move').force_assign(cr, uid, move_ids) - wf_service.trg_write(uid, 'stock.picking', pick.id, cr) - return True - - def cancel_assign(self, cr, uid, ids, *args): - wf_service = netsvc.LocalService("workflow") - for pick in self.browse(cr, uid, ids): - move_ids = [x.id for x in pick.move_lines] - self.pool.get('stock.move').cancel_assign(cr, uid, move_ids) - wf_service.trg_write(uid, 'stock.picking', pick.id, cr) - return True - - def action_assign_wkf(self, cr, uid, ids): - self.write(cr, uid, ids, {'state':'assigned'}) - return True - - def test_finnished(self, cr, uid, ids): - move_ids=self.pool.get('stock.move').search(cr,uid,[('picking_id','in',ids)]) - - for move in self.pool.get('stock.move').browse(cr,uid,move_ids): - if move.state not in ('done','cancel') : - if move.product_qty != 0.0: - return False - else: - move.write(cr,uid,[move.id],{'state':'done'}) - return True - - def test_assigned(self, cr, uid, ids): - ok = True - for pick in self.browse(cr, uid, ids): - mt = pick.move_type - for move in pick.move_lines: - if (move.state in ('confirmed','draft')) and (mt=='one'): - return False - if (mt=='direct') and (move.state=='assigned') and (move.product_qty): - return True - ok = ok and (move.state in ('cancel','done','assigned')) - return ok - - def action_cancel(self, cr, uid, ids, context={}): - for pick in self.browse(cr, uid, ids): - ids2 = [move.id for move in pick.move_lines] - self.pool.get('stock.move').action_cancel(cr, uid, ids2, context) - self.write(cr,uid, ids, {'state':'cancel', 'invoice_state':'none'}) - return True - - # - # TODO: change and create a move if not parents - # - def action_done(self, cr, uid, ids, context=None): - self.write(cr,uid, ids, {'state':'done'}) - return True - - def action_move(self, cr, uid, ids, context={}): - for pick in self.browse(cr, uid, ids): - todo = [] - for move in pick.move_lines: - if move.state=='assigned': - todo.append(move.id) - - if len(todo): - self.pool.get('stock.move').action_done(cr, uid, todo, - context=context) - return True - - def _get_address_invoice(self, cursor, user, picking): - '''Return {'contact': address, 'invoice': address} for invoice''' - partner_obj = self.pool.get('res.partner') - partner = picking.address_id.partner_id - - return partner_obj.address_get(cursor, user, [partner.id], - ['contact', 'invoice']) - - def _get_comment_invoice(self, cursor, user, picking): - '''Return comment string for invoice''' - return picking.note or '' - - def _get_price_unit_invoice(self, cursor, user, move_line, type): - '''Return the price unit for the move line''' - if type in ('in_invoice', 'in_refund'): - return move_line.product_id.standard_price - else: - return move_line.product_id.list_price - - def _get_discount_invoice(self, cursor, user, move_line): - '''Return the discount for the move line''' - return 0.0 - - def _get_taxes_invoice(self, cursor, user, move_line, type): - '''Return taxes ids for the move line''' - if type in ('in_invoice', 'in_refund'): - return [x.id for x in move_line.product_id.supplier_taxes_id] - else: - return [x.id for x in move_line.product_id.taxes_id] - - def _get_account_analytic_invoice(self, cursor, user, picking, move_line): - return False - - def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): - '''Call after the creation of the invoice line''' - return - - def _invoice_hook(self, cursor, user, picking, invoice_id): - '''Call after the creation of the invoice''' - return - - def action_invoice_create(self, cursor, user, ids, journal_id=False, - group=False, type='out_invoice', context=None): - '''Return ids of created invoices for the pickings''' - invoice_obj = self.pool.get('account.invoice') - invoice_line_obj = self.pool.get('account.invoice.line') - invoices_group = {} - res = {} - - for picking in self.browse(cursor, user, ids, context=context): - if picking.invoice_state != '2binvoiced': - continue - - partner = picking.address_id.partner_id - if type in ('out_invoice', 'out_refund'): - account_id = partner.property_account_receivable.id - else: - account_id = partner.property_account_payable.id - payment_term_id = False - if partner.property_payment_term: - payment_term_id = partner.property_payment_term.id - - address_contact_id, address_invoice_id = \ - self._get_address_invoice(cursor, user, picking).values() - - comment = self._get_comment_invoice(cursor, user, picking) - - if group and partner.id in invoices_group: - invoice_id = invoices_group[partner.id] - else: - invoice_vals = { - 'name': picking.name, - 'origin': picking.name + ':' + picking.origin, - 'type': type, - 'account_id': account_id, - 'partner_id': partner.id, - 'address_invoice_id': address_invoice_id, - 'address_contact_id': address_contact_id, - 'comment': comment, - 'payment_term': payment_term_id, - } - if journal_id: - invoice_vals['journal_id'] = journal_id - invoice_id = invoice_obj.create(cursor, user, invoice_vals, - context=context) - invoices_group[partner.id] = invoice_id - res[picking.id] = invoice_id - - for move_line in picking.move_lines: - if group: - name = picking.name + '-' + move_line.name - else: - name = move_line.name - - if type in ('out_invoice', 'out_refund'): - account_id = move_line.product_id.product_tmpl_id.\ - property_account_income.id - if not account_id: - account_id = move_line.product_id.categ_id.\ - property_account_income_categ.id - else: - account_id = move_line.product_id.product_tmpl_id.\ - property_account_expense.id - if not account_id: - account_id = move_line.product_id.categ_id.\ - property_account_expense_categ.id - - price_unit = self._get_price_unit_invoice(cursor, user, - move_line, type) - discount = self._get_discount_invoice(cursor, user, move_line) - tax_ids = self._get_taxes_invoice(cursor, user, move_line, type) - account_analytic_id = self._get_account_analytic_invoice(cursor, - user, picking, move_line) - - invoice_line_id = invoice_line_obj.create(cursor, user, { - 'name': name, - 'invoice_id': invoice_id, - 'uos_id': move_line.product_uos.id, - 'product_id': move_line.product_id.id, - 'account_id': account_id, - 'price_unit': price_unit, - 'discount': discount, - 'quantity': move_line.product_uos_qty, - 'invoice_line_tax_id': [(6, 0, tax_ids)], - 'account_analytic_id': account_analytic_id, - }, context=context) - self._invoice_line_hook(cursor, user, move_line, invoice_line_id) - - invoice_obj.button_compute(cursor, user, [invoice_id], context=context, - set_total=(type in ('in_invoice', 'in_refund'))) - self.write(cursor, user, [picking.id], { - 'invoice_state': 'invoiced', - }, context=context) - self._invoice_hook(cursor, user, picking, invoice_id) - self.write(cursor, user, res.keys(), { - 'invoice_state': 'invoiced', - }, context=context) - return res + _name = "stock.picking" + _description = "Packing list" + _columns = { + 'name': fields.char('Packing name', size=64, required=True, select=True), + 'origin': fields.char('Origin', size=64), + 'type': fields.selection([('out','Sending Goods'),('in','Getting Goods'),('internal','Internal'),('delivery','Delivery')], 'Shipping Type', required=True, select=True), + 'active': fields.boolean('Active'), + 'note': fields.text('Notes'), + + 'location_id': fields.many2one('stock.location', 'Location'), + 'location_dest_id': fields.many2one('stock.location', 'Dest. Location'), + + 'move_type': fields.selection([('direct','Direct Delivery'),('one','All at once')],'Delivery Method', required=True), + 'state': fields.selection([ + ('draft','Draft'), + ('auto','Waiting'), + ('confirmed','Confirmed'), + ('assigned','Assigned'), + ('done','Done'), + ('cancel','Cancel'), + ], 'State', readonly=True), + 'date':fields.datetime('Date create'), + + 'move_lines': fields.one2many('stock.move', 'picking_id', 'Move lines'), + + 'auto_picking': fields.boolean('Auto-Packing'), + 'address_id': fields.many2one('res.partner.address', 'Partner'), + 'invoice_state':fields.selection([ + ("invoiced","Invoiced"), + ("2binvoiced","To be invoiced"), + ("none","Not from Packing")], "Invoice state"), + } + _defaults = { + 'name': lambda *a: '/', + 'active': lambda *a: 1, + 'state': lambda *a: 'draft', + 'move_type': lambda *a: 'direct', + 'type': lambda *a: 'in', + 'invoice_state': lambda *a: 'none', + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + def onchange_partner_in(self, cr, uid, context, partner_id=None): + sid = self.pool.get('res.partner.address').browse(cr, uid, partner_id, context).partner_id.property_stock_supplier.id + return { + 'value': {'location_id':sid} + } + + def action_explode(self, cr, uid, ids, *args): + return True + + def action_confirm(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state': 'confirmed'}) + todo = [] + for picking in self.browse(cr, uid, ids): + if picking.name == self._defaults['name'](): + number = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.%s' % picking.type) + self.write(cr, uid, [picking.id], {'name': number}) + for r in picking.move_lines: + if r.state=='draft': + todo.append(r.id) + if len(todo): + self.pool.get('stock.move').action_confirm(cr,uid, todo) + self.action_explode(cr, uid, ids) + for picking in self.browse(cr, uid, ids): + todo = [] + for r in picking.move_lines: + if r.location_dest_id.chained_location_type: + todo.append(r) + if todo: + loc = self.pool.get('stock.location').chained_location_get(cr, uid, todo[0].location_dest_id, todo[0].picking_id and todo[0].picking_id.address_id and todo[0].picking_id.address_id.partner_id, todo[0].product_id) + ptype = self.pool.get('stock.location').picking_type_get(cr, uid, todo[0].location_dest_id, loc) + pickid = self.pool.get('stock.picking').create(cr, uid, { + 'name': picking.name, + 'origin': str(picking.origin or ''), + 'type': ptype, + 'note': picking.note, + 'move_type': picking.move_type, + 'auto_picking': todo[0].location_dest_id.chained_auto_packing, + 'address_id': picking.address_id.id, + 'invoice_state': 'none' + }) + for move in todo: + loc = self.pool.get('stock.location').chained_location_get(cr, uid, move.location_dest_id, picking.address_id.partner_id, move.product_id).id + new_id = self.pool.get('stock.move').copy(cr, uid, move.id, { + 'location_id': move.location_dest_id.id, + 'location_dest_id': loc, + 'date_moved': time.strftime('%Y-%m-%d'), + 'picking_id': pickid, + 'state':'waiting', + 'prodlot_id':False, + 'tracking_id':False, + 'move_history_ids':[], + 'date_planned': DateTime.strptime(move.date_planned, '%Y-%m-%d') + DateTime.RelativeDateTime(days=move.location_dest_id.chained_delay or 0), + 'move_history_ids2':[]} + ) + self.pool.get('stock.move').write(cr, uid, [move.id], { + 'move_dest_id': new_id, + 'move_history_ids': [(4, new_id)] + }) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr) + return True + + def test_auto_picking(self, cr, uid, ids): + # TODO: Check locations to see if in the same location ? + return True + + def action_assign(self, cr, uid, ids, *args): + for pick in self.browse(cr, uid, ids): + move_ids = [x.id for x in pick.move_lines if x.state=='confirmed'] + self.pool.get('stock.move').action_assign(cr, uid, move_ids) + return True + + def force_assign(self, cr, uid, ids, *args): + wf_service = netsvc.LocalService("workflow") + for pick in self.browse(cr, uid, ids): +# move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed'] + move_ids = [x.id for x in pick.move_lines] + self.pool.get('stock.move').force_assign(cr, uid, move_ids) + wf_service.trg_write(uid, 'stock.picking', pick.id, cr) + return True + + def cancel_assign(self, cr, uid, ids, *args): + wf_service = netsvc.LocalService("workflow") + for pick in self.browse(cr, uid, ids): + move_ids = [x.id for x in pick.move_lines] + self.pool.get('stock.move').cancel_assign(cr, uid, move_ids) + wf_service.trg_write(uid, 'stock.picking', pick.id, cr) + return True + + def action_assign_wkf(self, cr, uid, ids): + self.write(cr, uid, ids, {'state':'assigned'}) + return True + + def test_finnished(self, cr, uid, ids): + move_ids=self.pool.get('stock.move').search(cr,uid,[('picking_id','in',ids)]) + + for move in self.pool.get('stock.move').browse(cr,uid,move_ids): + if move.state not in ('done','cancel') : + if move.product_qty != 0.0: + return False + else: + move.write(cr,uid,[move.id],{'state':'done'}) + return True + + def test_assigned(self, cr, uid, ids): + ok = True + for pick in self.browse(cr, uid, ids): + mt = pick.move_type + for move in pick.move_lines: + if (move.state in ('confirmed','draft')) and (mt=='one'): + return False + if (mt=='direct') and (move.state=='assigned') and (move.product_qty): + return True + ok = ok and (move.state in ('cancel','done','assigned')) + return ok + + def action_cancel(self, cr, uid, ids, context={}): + for pick in self.browse(cr, uid, ids): + ids2 = [move.id for move in pick.move_lines] + self.pool.get('stock.move').action_cancel(cr, uid, ids2, context) + self.write(cr,uid, ids, {'state':'cancel', 'invoice_state':'none'}) + return True + + # + # TODO: change and create a move if not parents + # + def action_done(self, cr, uid, ids, context=None): + self.write(cr,uid, ids, {'state':'done'}) + return True + + def action_move(self, cr, uid, ids, context={}): + for pick in self.browse(cr, uid, ids): + todo = [] + for move in pick.move_lines: + if move.state=='assigned': + todo.append(move.id) + + if len(todo): + self.pool.get('stock.move').action_done(cr, uid, todo, + context=context) + return True + + def _get_address_invoice(self, cursor, user, picking): + '''Return {'contact': address, 'invoice': address} for invoice''' + partner_obj = self.pool.get('res.partner') + partner = picking.address_id.partner_id + + return partner_obj.address_get(cursor, user, [partner.id], + ['contact', 'invoice']) + + def _get_comment_invoice(self, cursor, user, picking): + '''Return comment string for invoice''' + return picking.note or '' + + def _get_price_unit_invoice(self, cursor, user, move_line, type): + '''Return the price unit for the move line''' + if type in ('in_invoice', 'in_refund'): + return move_line.product_id.standard_price + else: + return move_line.product_id.list_price + + def _get_discount_invoice(self, cursor, user, move_line): + '''Return the discount for the move line''' + return 0.0 + + def _get_taxes_invoice(self, cursor, user, move_line, type): + '''Return taxes ids for the move line''' + if type in ('in_invoice', 'in_refund'): + return [x.id for x in move_line.product_id.supplier_taxes_id] + else: + return [x.id for x in move_line.product_id.taxes_id] + + def _get_account_analytic_invoice(self, cursor, user, picking, move_line): + return False + + def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id): + '''Call after the creation of the invoice line''' + return + + def _invoice_hook(self, cursor, user, picking, invoice_id): + '''Call after the creation of the invoice''' + return + + def action_invoice_create(self, cursor, user, ids, journal_id=False, + group=False, type='out_invoice', context=None): + '''Return ids of created invoices for the pickings''' + invoice_obj = self.pool.get('account.invoice') + invoice_line_obj = self.pool.get('account.invoice.line') + invoices_group = {} + res = {} + + for picking in self.browse(cursor, user, ids, context=context): + if picking.invoice_state != '2binvoiced': + continue + + partner = picking.address_id.partner_id + if type in ('out_invoice', 'out_refund'): + account_id = partner.property_account_receivable.id + else: + account_id = partner.property_account_payable.id + payment_term_id = False + if partner.property_payment_term: + payment_term_id = partner.property_payment_term.id + + address_contact_id, address_invoice_id = \ + self._get_address_invoice(cursor, user, picking).values() + + comment = self._get_comment_invoice(cursor, user, picking) + + if group and partner.id in invoices_group: + invoice_id = invoices_group[partner.id] + else: + invoice_vals = { + 'name': picking.name, + 'origin': picking.name + ':' + picking.origin, + 'type': type, + 'account_id': account_id, + 'partner_id': partner.id, + 'address_invoice_id': address_invoice_id, + 'address_contact_id': address_contact_id, + 'comment': comment, + 'payment_term': payment_term_id, + } + if journal_id: + invoice_vals['journal_id'] = journal_id + invoice_id = invoice_obj.create(cursor, user, invoice_vals, + context=context) + invoices_group[partner.id] = invoice_id + res[picking.id] = invoice_id + + for move_line in picking.move_lines: + if group: + name = picking.name + '-' + move_line.name + else: + name = move_line.name + + if type in ('out_invoice', 'out_refund'): + account_id = move_line.product_id.product_tmpl_id.\ + property_account_income.id + if not account_id: + account_id = move_line.product_id.categ_id.\ + property_account_income_categ.id + else: + account_id = move_line.product_id.product_tmpl_id.\ + property_account_expense.id + if not account_id: + account_id = move_line.product_id.categ_id.\ + property_account_expense_categ.id + + price_unit = self._get_price_unit_invoice(cursor, user, + move_line, type) + discount = self._get_discount_invoice(cursor, user, move_line) + tax_ids = self._get_taxes_invoice(cursor, user, move_line, type) + account_analytic_id = self._get_account_analytic_invoice(cursor, + user, picking, move_line) + + invoice_line_id = invoice_line_obj.create(cursor, user, { + 'name': name, + 'invoice_id': invoice_id, + 'uos_id': move_line.product_uos.id, + 'product_id': move_line.product_id.id, + 'account_id': account_id, + 'price_unit': price_unit, + 'discount': discount, + 'quantity': move_line.product_uos_qty, + 'invoice_line_tax_id': [(6, 0, tax_ids)], + 'account_analytic_id': account_analytic_id, + }, context=context) + self._invoice_line_hook(cursor, user, move_line, invoice_line_id) + + invoice_obj.button_compute(cursor, user, [invoice_id], context=context, + set_total=(type in ('in_invoice', 'in_refund'))) + self.write(cursor, user, [picking.id], { + 'invoice_state': 'invoiced', + }, context=context) + self._invoice_hook(cursor, user, picking, invoice_id) + self.write(cursor, user, res.keys(), { + 'invoice_state': 'invoiced', + }, context=context) + return res stock_picking() class stock_production_lot(osv.osv): - def name_get(self, cr, uid, ids, context={}): - if not ids: - return [] - reads = self.read(cr, uid, ids, ['name', 'ref'], context) - res=[] - for record in reads: - name=record['name'] - if record['ref']: - name=name+'/'+record['ref'] - res.append((record['id'], name)) - return res - - _name = 'stock.production.lot' - _description = 'Production lot' - - _columns = { - 'name': fields.char('Serial', size=64, required=True), - 'ref': fields.char('Reference', size=64), - 'date': fields.datetime('Date create', required=True), - 'revisions': fields.one2many('stock.production.lot.revision','lot_id','Revisions'), - } - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.lot.serial'), - } - _sql_constraints = [ - ('name_ref_uniq', 'unique (name, ref)', 'The serial/ref must be unique !'), - ] + def name_get(self, cr, uid, ids, context={}): + if not ids: + return [] + reads = self.read(cr, uid, ids, ['name', 'ref'], context) + res=[] + for record in reads: + name=record['name'] + if record['ref']: + name=name+'/'+record['ref'] + res.append((record['id'], name)) + return res + + _name = 'stock.production.lot' + _description = 'Production lot' + + _columns = { + 'name': fields.char('Serial', size=64, required=True), + 'ref': fields.char('Reference', size=64), + 'date': fields.datetime('Date create', required=True), + 'revisions': fields.one2many('stock.production.lot.revision','lot_id','Revisions'), + } + _defaults = { + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.lot.serial'), + } + _sql_constraints = [ + ('name_ref_uniq', 'unique (name, ref)', 'The serial/ref must be unique !'), + ] stock_production_lot() class stock_production_lot_revision(osv.osv): - _name = 'stock.production.lot.revision' - _description = 'Production lot revisions' - _columns = { - 'name': fields.char('Revision name', size=64, required=True), - 'description': fields.text('Description'), - 'date': fields.date('Revision date'), - 'indice': fields.char('Revision', size=16), - 'author_id': fields.many2one('res.users', 'Author'), - 'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True), - } - - _defaults = { - 'author_id': lambda x,y,z,c: z, - 'date': lambda *a: time.strftime('%Y-%m-%d'), - } + _name = 'stock.production.lot.revision' + _description = 'Production lot revisions' + _columns = { + 'name': fields.char('Revision name', size=64, required=True), + 'description': fields.text('Description'), + 'date': fields.date('Revision date'), + 'indice': fields.char('Revision', size=16), + 'author_id': fields.many2one('res.users', 'Author'), + 'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True), + } + + _defaults = { + 'author_id': lambda x,y,z,c: z, + 'date': lambda *a: time.strftime('%Y-%m-%d'), + } stock_production_lot_revision() # ---------------------------------------------------- @@ -753,362 +753,362 @@ stock_production_lot_revision() # # Fields: -# location_dest_id is only used for predicting futur stocks +# location_dest_id is only used for predicting futur stocks # class stock_move(osv.osv): - def _getSSCC(self, cr, uid, context={}): - cr.execute('select id from stock_tracking where create_uid=%d order by id desc limit 1', (uid,)) - res = cr.fetchone() - return (res and res[0]) or False - _name = "stock.move" - _description = "Stock Move" - - _columns = { - 'name': fields.char('Name', size=64, required=True, select=True), - 'priority': fields.selection([('0','Not urgent'),('1','Urgent')], 'Priority'), - - 'date': fields.datetime('Date Created'), - 'date_planned': fields.date('Scheduled date', required=True), - - 'product_id': fields.many2one('product.product', 'Product', required=True), - - 'product_qty': fields.float('Quantity', required=True), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), - 'product_uos_qty': fields.float('Quantity (UOS)'), - 'product_uos': fields.many2one('product.uom', 'Product UOS'), - 'product_packaging' : fields.many2one('product.packaging', 'Packaging'), - - 'location_id': fields.many2one('stock.location', 'Source Location', required=True), - 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True), - 'address_id' : fields.many2one('res.partner.address', 'Dest. Address'), - - 'prodlot_id' : fields.many2one('stock.production.lot', 'Production lot', help="Production lot is used to put a serial number on the production"), - 'tracking_id': fields.many2one('stock.tracking', 'Tracking lot', select=True, help="Tracking lot is the code that will be put on the logistic unit/pallet"), -# 'lot_id': fields.many2one('stock.lot', 'Consumer lot', select=True, readonly=True), - - 'auto_validate': fields.boolean('Auto Validate'), - - 'move_dest_id': fields.many2one('stock.move', 'Dest. Move'), - 'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History'), - 'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History'), - 'picking_id': fields.many2one('stock.picking', 'Packing list', select=True), - - 'note': fields.text('Notes'), - - 'state': fields.selection([('draft','Draft'),('waiting','Waiting'),('confirmed','Confirmed'),('assigned','Assigned'),('done','Done'),('cancel','cancel')], 'State', readonly=True, select=True), - 'price_unit': fields.float('Unit Price', - digits=(16, int(config['price_accuracy']))), - } - _defaults = { - 'state': lambda *a: 'draft', - 'priority': lambda *a: '1', - 'product_qty': lambda *a: 1.0, - 'date_planned': lambda *a: time.strftime('%Y-%m-%d'), - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } - - def _auto_init(self, cursor, context): - super(stock_move, self)._auto_init(cursor, context) - cursor.execute('SELECT indexname \ - FROM pg_indexes \ - WHERE indexname = \'stock_move_location_id_location_dest_id_product_id_state\'') - if not cursor.fetchone(): - cursor.execute('CREATE INDEX stock_move_location_id_location_dest_id_product_id_state \ - ON stock_move (location_id, location_dest_id, product_id, state)') - cursor.commit() - - - def onchange_product_id(self, cr, uid, context, prod_id=False, loc_id=False, loc_dest_id=False): - if not prod_id: - return {} - product = self.pool.get('product.product').browse(cr, uid, [prod_id])[0] - result = { - 'name': product.name, - 'product_uom': product.uom_id.id, - } - if loc_id: - result['location_id'] = loc_id - if loc_dest_id: - result['location_dest_id'] = loc_dest_id - return {'value':result} - - def action_confirm(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state':'confirmed'}) - return True - - def action_assign(self, cr, uid, ids, *args): - todo = [] - for move in self.browse(cr, uid, ids): - if move.state in ('confirmed','waiting'): - todo.append(move.id) - res = self.check_assign(cr, uid, todo) - return res - - def force_assign(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state' : 'assigned'}) - return True - - def cancel_assign(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state': 'confirmed'}) - return True - - # - # Duplicate stock.move - # - def check_assign(self, cr, uid, ids, context={}): - done = [] - count=0 - pickings = {} - for move in self.browse(cr, uid, ids): - if move.product_id.type == 'consu': - if move.state in ('confirmed', 'waiting'): - done.append(move.id) - pickings[move.picking_id.id] = 1 - continue - if move.state in ('confirmed','waiting'): - res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id}) - if res: - done.append(move.id) - pickings[move.picking_id.id] = 1 - r = res.pop(0) - cr.execute('update stock_move set location_id=%d, product_qty=%f where id=%d', (r[1],r[0], move.id)) - - while res: - r = res.pop(0) - move_id = self.copy(cr, uid, move.id, {'product_qty':r[0], 'location_id':r[1]}) - done.append(move_id) - #cr.execute('insert into stock_move_history_ids values (%d,%d)', (move.id,move_id)) - if done: - count += len(done) - self.write(cr, uid, done, {'state':'assigned'}) - - if count: - for pick_id in pickings: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_write(uid, 'stock.picking', pick_id, cr) - return count - - # - # Cancel move => cancel others move and pickings - # - def action_cancel(self, cr, uid, ids, context={}): - if not len(ids): - return True - pickings = {} - for move in self.browse(cr, uid, ids): - if move.state in ('confirmed','waiting','assigned','draft'): - if move.picking_id: - pickings[move.picking_id.id] = True - self.write(cr, uid, ids, {'state':'cancel'}) - - for pick_id in pickings: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, 'stock.picking', pick_id, 'button_cancel', cr) - ids2 = [] - for res in self.read(cr, uid, ids, ['move_dest_id']): - if res['move_dest_id']: - ids2.append(res['move_dest_id'][0]) - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_trigger(uid, 'stock.move', id, cr) - self.action_cancel(cr,uid, ids2, context) - return True - - def action_done(self, cr, uid, ids, context=None): - for move in self.browse(cr, uid, ids): - if move.move_dest_id.id and (move.state != 'done'): - mid = move.move_dest_id.id - if move.move_dest_id.id: - cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%d,%d)', (move.id, move.move_dest_id.id)) - if move.move_dest_id.state in ('waiting','confirmed'): - self.write(cr, uid, [move.move_dest_id.id], {'state':'assigned'}) - if move.move_dest_id.picking_id: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr) - else: - pass - # self.action_done(cr, uid, [move.move_dest_id.id]) - if move.move_dest_id.auto_validate: - self.action_done(cr, uid, [move.move_dest_id.id], context=context) - - # - # Accounting Entries - # - acc_src = None - acc_dest = None - if move.location_id.account_id: - acc_src = move.location_id.account_id.id - if move.location_dest_id.account_id: - acc_dest = move.location_dest_id.account_id.id - if acc_src or acc_dest: - test = [('product.product', move.product_id.id)] - if move.product_id.categ_id: - test.append( ('product.category', move.product_id.categ_id.id) ) - if not acc_src: - acc_src = move.product_id.product_tmpl_id.\ - property_stock_account_input.id - if not acc_src: - acc_src = move.product_id.categ_id.\ - property_stock_account_input_categ.id - if not acc_src: - raise osv.except_osv(_('Error!'), - _('There is no stock input account defined ' \ - 'for this product: "%s" (id: %d)') % \ - (move.product_id.name, - move.product_id.id,)) - if not acc_dest: - acc_dest = move.product_id.product_tmpl_id.\ - property_stock_account_output.id - if not acc_dest: - acc_dest = move.product_id.categ_id.\ - property_stock_account_output_categ.id - if not acc_dest: - raise osv.except_osv(_('Error!'), - _('There is no stock output account defined ' \ - 'for this product: "%s" (id: %d)') % \ - (move.product_id.name, - move.product_id.id,)) - if not move.product_id.categ_id.property_stock_journal.id: - raise osv.except_osv(_('Error!'), - _('There is no journal defined '\ - 'on the product category: "%s" (id: %d)') % \ - (move.product_id.categ_id.name, - move.product_id.categ_id.id,)) - journal_id = move.product_id.categ_id.property_stock_journal.id - if acc_src != acc_dest: - ref = move.picking_id and move.picking_id.name or False - - if move.product_id.cost_method == 'average' and move.price_unit: - amount = move.product_qty * move.price_unit - else: - amount = move.product_qty * move.product_id.standard_price - - date = time.strftime('%Y-%m-%d') - lines = [ - (0, 0, { - 'name': move.name, - 'quantity': move.product_qty, - 'credit': amount, - 'account_id': acc_src, - 'ref': ref, - 'date': date}), - (0, 0, { - 'name': move.name, - 'quantity': move.product_qty, - 'debit': amount, - 'account_id': acc_dest, - 'ref': ref, - 'date': date}) - ] - self.pool.get('account.move').create(cr, uid, - { - 'name': move.name, - 'journal_id': journal_id, - 'line_id': lines, - 'ref': ref, - }) - self.write(cr, uid, ids, {'state':'done'}) - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_trigger(uid, 'stock.move', id, cr) - return True - - - def unlink(self, cr, uid, ids, context=None): - for move in self.browse(cr, uid, ids, context=context): - if move.state != 'draft': - raise osv.except_osv(_('UserError'), - _('You can only delete draft moves.')) - return super(stock_move, self).unlink( - cr, uid, ids, context=context) + def _getSSCC(self, cr, uid, context={}): + cr.execute('select id from stock_tracking where create_uid=%d order by id desc limit 1', (uid,)) + res = cr.fetchone() + return (res and res[0]) or False + _name = "stock.move" + _description = "Stock Move" + + _columns = { + 'name': fields.char('Name', size=64, required=True, select=True), + 'priority': fields.selection([('0','Not urgent'),('1','Urgent')], 'Priority'), + + 'date': fields.datetime('Date Created'), + 'date_planned': fields.date('Scheduled date', required=True), + + 'product_id': fields.many2one('product.product', 'Product', required=True), + + 'product_qty': fields.float('Quantity', required=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_uos_qty': fields.float('Quantity (UOS)'), + 'product_uos': fields.many2one('product.uom', 'Product UOS'), + 'product_packaging' : fields.many2one('product.packaging', 'Packaging'), + + 'location_id': fields.many2one('stock.location', 'Source Location', required=True), + 'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True), + 'address_id' : fields.many2one('res.partner.address', 'Dest. Address'), + + 'prodlot_id' : fields.many2one('stock.production.lot', 'Production lot', help="Production lot is used to put a serial number on the production"), + 'tracking_id': fields.many2one('stock.tracking', 'Tracking lot', select=True, help="Tracking lot is the code that will be put on the logistic unit/pallet"), +# 'lot_id': fields.many2one('stock.lot', 'Consumer lot', select=True, readonly=True), + + 'auto_validate': fields.boolean('Auto Validate'), + + 'move_dest_id': fields.many2one('stock.move', 'Dest. Move'), + 'move_history_ids': fields.many2many('stock.move', 'stock_move_history_ids', 'parent_id', 'child_id', 'Move History'), + 'move_history_ids2': fields.many2many('stock.move', 'stock_move_history_ids', 'child_id', 'parent_id', 'Move History'), + 'picking_id': fields.many2one('stock.picking', 'Packing list', select=True), + + 'note': fields.text('Notes'), + + 'state': fields.selection([('draft','Draft'),('waiting','Waiting'),('confirmed','Confirmed'),('assigned','Assigned'),('done','Done'),('cancel','cancel')], 'State', readonly=True, select=True), + 'price_unit': fields.float('Unit Price', + digits=(16, int(config['price_accuracy']))), + } + _defaults = { + 'state': lambda *a: 'draft', + 'priority': lambda *a: '1', + 'product_qty': lambda *a: 1.0, + 'date_planned': lambda *a: time.strftime('%Y-%m-%d'), + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } + + def _auto_init(self, cursor, context): + super(stock_move, self)._auto_init(cursor, context) + cursor.execute('SELECT indexname \ + FROM pg_indexes \ + WHERE indexname = \'stock_move_location_id_location_dest_id_product_id_state\'') + if not cursor.fetchone(): + cursor.execute('CREATE INDEX stock_move_location_id_location_dest_id_product_id_state \ + ON stock_move (location_id, location_dest_id, product_id, state)') + cursor.commit() + + + def onchange_product_id(self, cr, uid, context, prod_id=False, loc_id=False, loc_dest_id=False): + if not prod_id: + return {} + product = self.pool.get('product.product').browse(cr, uid, [prod_id])[0] + result = { + 'name': product.name, + 'product_uom': product.uom_id.id, + } + if loc_id: + result['location_id'] = loc_id + if loc_dest_id: + result['location_dest_id'] = loc_dest_id + return {'value':result} + + def action_confirm(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state':'confirmed'}) + return True + + def action_assign(self, cr, uid, ids, *args): + todo = [] + for move in self.browse(cr, uid, ids): + if move.state in ('confirmed','waiting'): + todo.append(move.id) + res = self.check_assign(cr, uid, todo) + return res + + def force_assign(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state' : 'assigned'}) + return True + + def cancel_assign(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state': 'confirmed'}) + return True + + # + # Duplicate stock.move + # + def check_assign(self, cr, uid, ids, context={}): + done = [] + count=0 + pickings = {} + for move in self.browse(cr, uid, ids): + if move.product_id.type == 'consu': + if move.state in ('confirmed', 'waiting'): + done.append(move.id) + pickings[move.picking_id.id] = 1 + continue + if move.state in ('confirmed','waiting'): + res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id}) + if res: + done.append(move.id) + pickings[move.picking_id.id] = 1 + r = res.pop(0) + cr.execute('update stock_move set location_id=%d, product_qty=%f where id=%d', (r[1],r[0], move.id)) + + while res: + r = res.pop(0) + move_id = self.copy(cr, uid, move.id, {'product_qty':r[0], 'location_id':r[1]}) + done.append(move_id) + #cr.execute('insert into stock_move_history_ids values (%d,%d)', (move.id,move_id)) + if done: + count += len(done) + self.write(cr, uid, done, {'state':'assigned'}) + + if count: + for pick_id in pickings: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_write(uid, 'stock.picking', pick_id, cr) + return count + + # + # Cancel move => cancel others move and pickings + # + def action_cancel(self, cr, uid, ids, context={}): + if not len(ids): + return True + pickings = {} + for move in self.browse(cr, uid, ids): + if move.state in ('confirmed','waiting','assigned','draft'): + if move.picking_id: + pickings[move.picking_id.id] = True + self.write(cr, uid, ids, {'state':'cancel'}) + + for pick_id in pickings: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', pick_id, 'button_cancel', cr) + ids2 = [] + for res in self.read(cr, uid, ids, ['move_dest_id']): + if res['move_dest_id']: + ids2.append(res['move_dest_id'][0]) + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_trigger(uid, 'stock.move', id, cr) + self.action_cancel(cr,uid, ids2, context) + return True + + def action_done(self, cr, uid, ids, context=None): + for move in self.browse(cr, uid, ids): + if move.move_dest_id.id and (move.state != 'done'): + mid = move.move_dest_id.id + if move.move_dest_id.id: + cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%d,%d)', (move.id, move.move_dest_id.id)) + if move.move_dest_id.state in ('waiting','confirmed'): + self.write(cr, uid, [move.move_dest_id.id], {'state':'assigned'}) + if move.move_dest_id.picking_id: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr) + else: + pass + # self.action_done(cr, uid, [move.move_dest_id.id]) + if move.move_dest_id.auto_validate: + self.action_done(cr, uid, [move.move_dest_id.id], context=context) + + # + # Accounting Entries + # + acc_src = None + acc_dest = None + if move.location_id.account_id: + acc_src = move.location_id.account_id.id + if move.location_dest_id.account_id: + acc_dest = move.location_dest_id.account_id.id + if acc_src or acc_dest: + test = [('product.product', move.product_id.id)] + if move.product_id.categ_id: + test.append( ('product.category', move.product_id.categ_id.id) ) + if not acc_src: + acc_src = move.product_id.product_tmpl_id.\ + property_stock_account_input.id + if not acc_src: + acc_src = move.product_id.categ_id.\ + property_stock_account_input_categ.id + if not acc_src: + raise osv.except_osv(_('Error!'), + _('There is no stock input account defined ' \ + 'for this product: "%s" (id: %d)') % \ + (move.product_id.name, + move.product_id.id,)) + if not acc_dest: + acc_dest = move.product_id.product_tmpl_id.\ + property_stock_account_output.id + if not acc_dest: + acc_dest = move.product_id.categ_id.\ + property_stock_account_output_categ.id + if not acc_dest: + raise osv.except_osv(_('Error!'), + _('There is no stock output account defined ' \ + 'for this product: "%s" (id: %d)') % \ + (move.product_id.name, + move.product_id.id,)) + if not move.product_id.categ_id.property_stock_journal.id: + raise osv.except_osv(_('Error!'), + _('There is no journal defined '\ + 'on the product category: "%s" (id: %d)') % \ + (move.product_id.categ_id.name, + move.product_id.categ_id.id,)) + journal_id = move.product_id.categ_id.property_stock_journal.id + if acc_src != acc_dest: + ref = move.picking_id and move.picking_id.name or False + + if move.product_id.cost_method == 'average' and move.price_unit: + amount = move.product_qty * move.price_unit + else: + amount = move.product_qty * move.product_id.standard_price + + date = time.strftime('%Y-%m-%d') + lines = [ + (0, 0, { + 'name': move.name, + 'quantity': move.product_qty, + 'credit': amount, + 'account_id': acc_src, + 'ref': ref, + 'date': date}), + (0, 0, { + 'name': move.name, + 'quantity': move.product_qty, + 'debit': amount, + 'account_id': acc_dest, + 'ref': ref, + 'date': date}) + ] + self.pool.get('account.move').create(cr, uid, + { + 'name': move.name, + 'journal_id': journal_id, + 'line_id': lines, + 'ref': ref, + }) + self.write(cr, uid, ids, {'state':'done'}) + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_trigger(uid, 'stock.move', id, cr) + return True + + + def unlink(self, cr, uid, ids, context=None): + for move in self.browse(cr, uid, ids, context=context): + if move.state != 'draft': + raise osv.except_osv(_('UserError'), + _('You can only delete draft moves.')) + return super(stock_move, self).unlink( + cr, uid, ids, context=context) stock_move() class stock_inventory(osv.osv): - _name = "stock.inventory" - _description = "Inventory" - _columns = { - 'name': fields.char('Inventory', size=64, required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'date': fields.datetime('Date create', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'date_done': fields.datetime('Date done'), - 'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=True, states={'draft':[('readonly',False)]}), - 'move_ids': fields.many2many('stock.move', 'stock_inventory_move_rel', 'inventory_id', 'move_id', 'Created Moves'), - 'state': fields.selection( (('draft','Draft'),('done','Done')), 'State', readonly=True), - } - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'state': lambda *a: 'draft', - } - # - # Update to support tracking - # - def action_done(self, cr, uid, ids, context=None): - for inv in self.browse(cr,uid,ids): - move_ids = [] - move_line=[] - for line in inv.inventory_line_id: - pid=line.product_id.id - price=line.product_id.standard_price or 0.0 - amount=self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid] - change=line.product_qty-amount - if change: - location_id = line.product_id.product_tmpl_id.property_stock_inventory.id - value = { - 'name': 'INV:'+str(line.inventory_id.id)+':'+line.inventory_id.name, - 'product_id': line.product_id.id, - 'product_uom': line.product_uom.id, - 'date': inv.date, - 'date_planned': inv.date, - 'state': 'assigned' - } - if change>0: - value.update( { - 'product_qty': change, - 'location_id': location_id, - 'location_dest_id': line.location_id.id, - }) - else: - value.update( { - 'product_qty': -change, - 'location_id': line.location_id.id, - 'location_dest_id': location_id, - }) - move_ids.append(self.pool.get('stock.move').create(cr, uid, value)) - if len(move_ids): - self.pool.get('stock.move').action_done(cr, uid, move_ids, - context=context) - self.write(cr, uid, [inv.id], {'state':'done', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S'), 'move_ids': [(6,0,move_ids)]}) - return True - - def action_cancel(self, cr, uid, ids, context={}): - for inv in self.browse(cr,uid,ids): - self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context) - self.write(cr, uid, [inv.id], {'state':'draft'}) - return True + _name = "stock.inventory" + _description = "Inventory" + _columns = { + 'name': fields.char('Inventory', size=64, required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'date': fields.datetime('Date create', required=True, readonly=True, states={'draft':[('readonly',False)]}), + 'date_done': fields.datetime('Date done'), + 'inventory_line_id': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=True, states={'draft':[('readonly',False)]}), + 'move_ids': fields.many2many('stock.move', 'stock_inventory_move_rel', 'inventory_id', 'move_id', 'Created Moves'), + 'state': fields.selection( (('draft','Draft'),('done','Done')), 'State', readonly=True), + } + _defaults = { + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'state': lambda *a: 'draft', + } + # + # Update to support tracking + # + def action_done(self, cr, uid, ids, context=None): + for inv in self.browse(cr,uid,ids): + move_ids = [] + move_line=[] + for line in inv.inventory_line_id: + pid=line.product_id.id + price=line.product_id.standard_price or 0.0 + amount=self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid] + change=line.product_qty-amount + if change: + location_id = line.product_id.product_tmpl_id.property_stock_inventory.id + value = { + 'name': 'INV:'+str(line.inventory_id.id)+':'+line.inventory_id.name, + 'product_id': line.product_id.id, + 'product_uom': line.product_uom.id, + 'date': inv.date, + 'date_planned': inv.date, + 'state': 'assigned' + } + if change>0: + value.update( { + 'product_qty': change, + 'location_id': location_id, + 'location_dest_id': line.location_id.id, + }) + else: + value.update( { + 'product_qty': -change, + 'location_id': line.location_id.id, + 'location_dest_id': location_id, + }) + move_ids.append(self.pool.get('stock.move').create(cr, uid, value)) + if len(move_ids): + self.pool.get('stock.move').action_done(cr, uid, move_ids, + context=context) + self.write(cr, uid, [inv.id], {'state':'done', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S'), 'move_ids': [(6,0,move_ids)]}) + return True + + def action_cancel(self, cr, uid, ids, context={}): + for inv in self.browse(cr,uid,ids): + self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context) + self.write(cr, uid, [inv.id], {'state':'draft'}) + return True stock_inventory() class stock_inventory_line(osv.osv): - _name = "stock.inventory.line" - _description = "Inventory line" - _columns = { - 'inventory_id': fields.many2one('stock.inventory','Inventory', ondelete='cascade', select=True), - 'location_id': fields.many2one('stock.location','Location', required=True), - 'product_id': fields.many2one('product.product', 'Product', required=True ), - 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True ), - 'product_qty': fields.float('Quantity') - } - def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False): - if not product: - return {} - if not uom: - prod = self.pool.get('product.product').browse(cr, uid, [product], {'uom': uom})[0] - uom = prod.uom_id.id - amount=self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], {'uom': uom})[product] - result = {'product_qty':amount, 'product_uom':uom} - return {'value':result} + _name = "stock.inventory.line" + _description = "Inventory line" + _columns = { + 'inventory_id': fields.many2one('stock.inventory','Inventory', ondelete='cascade', select=True), + 'location_id': fields.many2one('stock.location','Location', required=True), + 'product_id': fields.many2one('product.product', 'Product', required=True ), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True ), + 'product_qty': fields.float('Quantity') + } + def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False): + if not product: + return {} + if not uom: + prod = self.pool.get('product.product').browse(cr, uid, [product], {'uom': uom})[0] + uom = prod.uom_id.id + amount=self.pool.get('stock.location')._product_get(cr, uid, location_id, [product], {'uom': uom})[product] + result = {'product_qty':amount, 'product_uom':uom} + return {'value':result} stock_inventory_line() @@ -1116,70 +1116,70 @@ stock_inventory_line() # Stock Warehouse #---------------------------------------------------------- class stock_warehouse(osv.osv): - _name = "stock.warehouse" - _description = "Warehouse" - _columns = { - 'name': fields.char('Name', size=60, required=True), -# 'partner_id': fields.many2one('res.partner', 'Owner'), - 'partner_address_id': fields.many2one('res.partner.address', 'Owner Address'), - 'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True ), - 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True ), - 'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True ), - } + _name = "stock.warehouse" + _description = "Warehouse" + _columns = { + 'name': fields.char('Name', size=60, required=True), +# 'partner_id': fields.many2one('res.partner', 'Owner'), + 'partner_address_id': fields.many2one('res.partner.address', 'Owner Address'), + 'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True ), + 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True ), + 'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True ), + } stock_warehouse() # Product class product_product(osv.osv): - _inherit = "product.product" - # - # Utiliser browse pour limiter les queries ! - # - def view_header_get(self, cr, user, view_id, view_type, context): - if (not context.get('location', False)): - return False - cr.execute('select name from stock_location where id=%d', (context['location'],)) - j = cr.fetchone()[0] - if j: - return 'Products: '+j - return False - - def _get_product_available_func(states, what): - def _product_available(self, cr, uid, ids, name, arg, context={}): - if context.get('shop', False): - cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),)) - res2 = cr.fetchone() - if res2: - context['warehouse'] = res2[0] - - if context.get('warehouse', False): - cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),)) - res2 = cr.fetchone() - if res2: - context['location'] = res2[0] - - if context.get('location', False): - location_ids = [context['location']] - else: - # get the list of ids of the stock location of all warehouses - cr.execute("select lot_stock_id from stock_warehouse") - location_ids = [id for (id,) in cr.fetchall()] - - # build the list of ids of children of the location given by id - location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)]) - res = self.pool.get('stock.location')._product_get_multi_location(cr, uid, location_ids, ids, context, states, what) - for id in ids: - res.setdefault(id, 0.0) - return res - return _product_available - _product_qty_available = _get_product_available_func(('done',), ('in', 'out')) - _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out')) - _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) - _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) - _columns = { - 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'), - 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'), - 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'), - 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'), - } + _inherit = "product.product" + # + # Utiliser browse pour limiter les queries ! + # + def view_header_get(self, cr, user, view_id, view_type, context): + if (not context.get('location', False)): + return False + cr.execute('select name from stock_location where id=%d', (context['location'],)) + j = cr.fetchone()[0] + if j: + return 'Products: '+j + return False + + def _get_product_available_func(states, what): + def _product_available(self, cr, uid, ids, name, arg, context={}): + if context.get('shop', False): + cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),)) + res2 = cr.fetchone() + if res2: + context['warehouse'] = res2[0] + + if context.get('warehouse', False): + cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),)) + res2 = cr.fetchone() + if res2: + context['location'] = res2[0] + + if context.get('location', False): + location_ids = [context['location']] + else: + # get the list of ids of the stock location of all warehouses + cr.execute("select lot_stock_id from stock_warehouse") + location_ids = [id for (id,) in cr.fetchall()] + + # build the list of ids of children of the location given by id + location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)]) + res = self.pool.get('stock.location')._product_get_multi_location(cr, uid, location_ids, ids, context, states, what) + for id in ids: + res.setdefault(id, 0.0) + return res + return _product_available + _product_qty_available = _get_product_available_func(('done',), ('in', 'out')) + _product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out')) + _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) + _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) + _columns = { + 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'), + 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'), + 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'), + 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'), + } product_product() diff --git a/addons/stock/wizard/stock_traceability.py b/addons/stock/wizard/stock_traceability.py index 5e765b2a428..dded36505df 100644 --- a/addons/stock/wizard/stock_traceability.py +++ b/addons/stock/wizard/stock_traceability.py @@ -35,56 +35,56 @@ import pooler from osv import osv def action_traceability(type='move_history_ids', field='tracking_id'): - def open_tab(self, cr, uid, data, context): - obj = pooler.get_pool(cr.dbname).get('stock.move') - ids = obj.search(cr, uid, [(field, 'in', data['ids'])]) - cr.execute('select id from ir_ui_view where model=%s and field_parent=%s and type=%s', ('stock.move', type, 'tree')) - view_id = cr.fetchone()[0] - value = { - 'domain': "[('id','in',["+','.join(map(str,ids))+"])]", - 'name': ((type=='move_history_ids') and 'Upstream Traceability') or 'Downstream Traceability', - 'view_type': 'tree', - 'res_model': 'stock.move', - 'field_parent': type, - 'view_id': (view_id,'View'), - 'type': 'ir.actions.act_window' - } - return value - return open_tab + def open_tab(self, cr, uid, data, context): + obj = pooler.get_pool(cr.dbname).get('stock.move') + ids = obj.search(cr, uid, [(field, 'in', data['ids'])]) + cr.execute('select id from ir_ui_view where model=%s and field_parent=%s and type=%s', ('stock.move', type, 'tree')) + view_id = cr.fetchone()[0] + value = { + 'domain': "[('id','in',["+','.join(map(str,ids))+"])]", + 'name': ((type=='move_history_ids') and 'Upstream Traceability') or 'Downstream Traceability', + 'view_type': 'tree', + 'res_model': 'stock.move', + 'field_parent': type, + 'view_id': (view_id,'View'), + 'type': 'ir.actions.act_window' + } + return value + return open_tab class wiz_journal(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': action_traceability(), 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': action_traceability(), 'state':'end'} + } + } wiz_journal('stock.traceability.aval') class wiz_journal2(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': action_traceability('move_history_ids2'), 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': action_traceability('move_history_ids2'), 'state':'end'} + } + } wiz_journal2('stock.traceability.amont') class wiz_journal3(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': action_traceability(field='prodlot_id'), 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': action_traceability(field='prodlot_id'), 'state':'end'} + } + } wiz_journal3('stock.traceability.lot.amont') class wiz_journal4(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'action', 'action': action_traceability('move_history_ids2', 'prodlot_id'), 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'action', 'action': action_traceability('move_history_ids2', 'prodlot_id'), 'state':'end'} + } + } wiz_journal4('stock.traceability.lot.aval') diff --git a/addons/stock/wizard/wizard_invoice_onshipping.py b/addons/stock/wizard/wizard_invoice_onshipping.py index 3bc1ea98d47..47b99a74315 100644 --- a/addons/stock/wizard/wizard_invoice_onshipping.py +++ b/addons/stock/wizard/wizard_invoice_onshipping.py @@ -36,119 +36,119 @@ import netsvc invoice_form = """<?xml version="1.0"?> <form string="Create invoices"> - <separator colspan="4" string="Create invoices" /> - <field name="journal_id"/> - <newline/> - <field name="group"/> - <newline/> - <field name="type"/> + <separator colspan="4" string="Create invoices" /> + <field name="journal_id"/> + <newline/> + <field name="group"/> + <newline/> + <field name="type"/> </form> """ invoice_fields = { - 'journal_id': { - 'string': 'Destination Journal', - 'type': 'many2one', - 'relation': 'account.journal', - 'required': True - }, - 'group': { - 'string': 'Group by partner', - 'type':'boolean' - }, - 'type': { - 'string': 'Type', - 'type': 'selection', - 'selection': [ - ('out_invoice', 'Customer Invoice'), - ('in_invoice', 'Supplier Invoice'), - ('out_refund', 'Customer Refund'), - ('in_refund', 'Supplier Refund'), - ], - 'required': True - }, + 'journal_id': { + 'string': 'Destination Journal', + 'type': 'many2one', + 'relation': 'account.journal', + 'required': True + }, + 'group': { + 'string': 'Group by partner', + 'type':'boolean' + }, + 'type': { + 'string': 'Type', + 'type': 'selection', + 'selection': [ + ('out_invoice', 'Customer Invoice'), + ('in_invoice', 'Supplier Invoice'), + ('out_refund', 'Customer Refund'), + ('in_refund', 'Supplier Refund'), + ], + 'required': True + }, } def _get_type(obj, cr, uid, data, context): - picking_obj=pooler.get_pool(cr.dbname).get('stock.picking') - - pick=picking_obj.browse(cr, uid, data['id']) - if pick.loc_move_id: - usage=pick.loc_move_id.usage - else: - usage=pick.move_lines[0].location_id.usage - - if pick.type == 'out' and usage == 'supplier': - type = 'in_refund' - elif pick.type == 'out' and usage == 'customer': - type = 'out_invoice' - elif pick.type == 'in' and usage == 'supplier': - type = 'in_invoice' - elif pick.type == 'in' and usage == 'customer': - type = 'out_refund' - else: - type = 'out_invoice' - return {'type': type} + picking_obj=pooler.get_pool(cr.dbname).get('stock.picking') + + pick=picking_obj.browse(cr, uid, data['id']) + if pick.loc_move_id: + usage=pick.loc_move_id.usage + else: + usage=pick.move_lines[0].location_id.usage + + if pick.type == 'out' and usage == 'supplier': + type = 'in_refund' + elif pick.type == 'out' and usage == 'customer': + type = 'out_invoice' + elif pick.type == 'in' and usage == 'supplier': + type = 'in_invoice' + elif pick.type == 'in' and usage == 'customer': + type = 'out_refund' + else: + type = 'out_invoice' + return {'type': type} def _create_invoice(obj, cr, uid, data, context): - if context and context.get('new_picking',False): - data['id'] = context['new_picking'] - data['ids'] = [context['new_picking']] - pool = pooler.get_pool(cr.dbname) - picking_obj = pooler.get_pool(cr.dbname).get('stock.picking') - mod_obj = pool.get('ir.model.data') - act_obj = pool.get('ir.actions.act_window') + if context and context.get('new_picking',False): + data['id'] = context['new_picking'] + data['ids'] = [context['new_picking']] + pool = pooler.get_pool(cr.dbname) + picking_obj = pooler.get_pool(cr.dbname).get('stock.picking') + mod_obj = pool.get('ir.model.data') + act_obj = pool.get('ir.actions.act_window') - type = data['form']['type'] + type = data['form']['type'] - res = picking_obj.action_invoice_create(cr, uid, data['ids'], - journal_id=data['form']['journal_id'],group=data['form']['group'], - type=type, context= context) + res = picking_obj.action_invoice_create(cr, uid, data['ids'], + journal_id=data['form']['journal_id'],group=data['form']['group'], + type=type, context= context) - invoice_ids = res.values() + invoice_ids = res.values() - if not invoice_ids: - return {} + if not invoice_ids: + return {} - if type == 'out_invoice': - xml_id = 'action_invoice_tree5' - elif type == 'in_invoice': - xml_id = 'action_invoice_tree8' - elif type == 'out_refund': - xml_id = 'action_invoice_tree10' - else: - xml_id = 'action_invoice_tree12' + if type == 'out_invoice': + xml_id = 'action_invoice_tree5' + elif type == 'in_invoice': + xml_id = 'action_invoice_tree8' + elif type == 'out_refund': + xml_id = 'action_invoice_tree10' + else: + xml_id = 'action_invoice_tree12' - result = mod_obj._get_id(cr, uid, 'account', xml_id) - id = mod_obj.read(cr, uid, result, ['res_id'])['res_id'] - result = act_obj.read(cr, uid, id) - result['res_id'] = invoice_ids - return result + result = mod_obj._get_id(cr, uid, 'account', xml_id) + id = mod_obj.read(cr, uid, result, ['res_id'])['res_id'] + result = act_obj.read(cr, uid, id) + result['res_id'] = invoice_ids + return result class make_invoice_onshipping(wizard.interface): - states = { - 'init': { - 'actions': [_get_type], - 'result': { - 'type': 'form', - 'arch': invoice_form, - 'fields': invoice_fields, - 'state': [ - ('end', 'Cancel'), - ('create_invoice', 'Create invoice') - ] - } - }, - 'create_invoice': { - 'actions': [], - 'result': { - 'type': 'action', - 'action': _create_invoice, - 'state': 'end' - } - }, - } + states = { + 'init': { + 'actions': [_get_type], + 'result': { + 'type': 'form', + 'arch': invoice_form, + 'fields': invoice_fields, + 'state': [ + ('end', 'Cancel'), + ('create_invoice', 'Create invoice') + ] + } + }, + 'create_invoice': { + 'actions': [], + 'result': { + 'type': 'action', + 'action': _create_invoice, + 'state': 'end' + } + }, + } make_invoice_onshipping("stock.invoice_onshipping") diff --git a/addons/stock/wizard/wizard_partial_picking.py b/addons/stock/wizard/wizard_partial_picking.py index a9017cbcde7..9f7d9566a11 100644 --- a/addons/stock/wizard/wizard_partial_picking.py +++ b/addons/stock/wizard/wizard_partial_picking.py @@ -40,193 +40,193 @@ _moves_fields = UpdateableDict() _moves_arch_end = '''<?xml version="1.0"?> <form string="Packing result"> - <label string="The packing has been successfully made !" colspan="4"/> + <label string="The packing has been successfully made !" colspan="4"/> </form>''' _moves_fields_end = {} def make_default(val): - def fct(uid, data, state): - return val - return fct + def fct(uid, data, state): + return val + return fct def _to_xml(s): - return (s or '').replace('&','&').replace('<','<').replace('>','>') + return (s or '').replace('&','&').replace('<','<').replace('>','>') def _get_moves(self, cr, uid, data, context): - pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') - pick = pick_obj.browse(cr, uid, [data['id']])[0] - res = {} + pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') + pick = pick_obj.browse(cr, uid, [data['id']])[0] + res = {} - _moves_fields.clear() - _moves_arch_lst = ['<?xml version="1.0"?>', '<form string="Make packing">'] + _moves_fields.clear() + _moves_arch_lst = ['<?xml version="1.0"?>', '<form string="Make packing">'] - for m in pick.move_lines: - quantity = m.product_qty - if m.state<>'assigned': - quantity = 0 + for m in pick.move_lines: + quantity = m.product_qty + if m.state<>'assigned': + quantity = 0 - _moves_arch_lst.append('<field name="move%s" />' % (m.id,)) - _moves_fields['move%s' % m.id] = { - 'string': '%s - %s' % (_to_xml(m.product_id.code or '/'), _to_xml(m.product_id.name)), - 'type' : 'float', 'required' : True, 'default' : make_default(quantity)} + _moves_arch_lst.append('<field name="move%s" />' % (m.id,)) + _moves_fields['move%s' % m.id] = { + 'string': '%s - %s' % (_to_xml(m.product_id.code or '/'), _to_xml(m.product_id.name)), + 'type' : 'float', 'required' : True, 'default' : make_default(quantity)} - if (pick.type == 'in') and (m.product_id.cost_method == 'average'): - price=0 - if hasattr(m, 'purchase_line_id') and m.purchase_line_id: - price=m.purchase_line_id.price_unit + if (pick.type == 'in') and (m.product_id.cost_method == 'average'): + price=0 + if hasattr(m, 'purchase_line_id') and m.purchase_line_id: + price=m.purchase_line_id.price_unit - currency=0 - if hasattr(pick, 'purchase_id') and pick.purchase_id: - currency=pick.purchase_id.pricelist_id.currency_id.id + currency=0 + if hasattr(pick, 'purchase_id') and pick.purchase_id: + currency=pick.purchase_id.pricelist_id.currency_id.id - _moves_arch_lst.append('<group col="6"><field name="uom%s" nolabel="1"/>\ - <field name="price%s"/>' % (m.id,m.id,)) + _moves_arch_lst.append('<group col="6"><field name="uom%s" nolabel="1"/>\ + <field name="price%s"/>' % (m.id,m.id,)) - _moves_fields['price%s' % m.id] = {'string': 'Unit Price', - 'type': 'float', 'required': True, 'default': make_default(price)} + _moves_fields['price%s' % m.id] = {'string': 'Unit Price', + 'type': 'float', 'required': True, 'default': make_default(price)} - _moves_fields['uom%s' % m.id] = {'string': 'UOM', 'type': 'many2one', - 'relation': 'product.uom', 'required': True, - 'default': make_default(m.product_uom.id)} + _moves_fields['uom%s' % m.id] = {'string': 'UOM', 'type': 'many2one', + 'relation': 'product.uom', 'required': True, + 'default': make_default(m.product_uom.id)} - _moves_arch_lst.append('<field name="currency%d" nolabel="1"/></group>' % (m.id,)) - _moves_fields['currency%s' % m.id] = {'string': 'Currency', - 'type': 'many2one', 'relation': 'res.currency', - 'required': True, 'default': make_default(currency)} + _moves_arch_lst.append('<field name="currency%d" nolabel="1"/></group>' % (m.id,)) + _moves_fields['currency%s' % m.id] = {'string': 'Currency', + 'type': 'many2one', 'relation': 'res.currency', + 'required': True, 'default': make_default(currency)} - _moves_arch_lst.append('<newline/>') - res.setdefault('moves', []).append(m.id) + _moves_arch_lst.append('<newline/>') + res.setdefault('moves', []).append(m.id) - _moves_arch_lst.append('</form>') - _moves_arch.string = '\n'.join(_moves_arch_lst) - return res + _moves_arch_lst.append('</form>') + _moves_arch.string = '\n'.join(_moves_arch_lst) + return res def _do_split(self, cr, uid, data, context): - move_obj = pooler.get_pool(cr.dbname).get('stock.move') - pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') - pick = pick_obj.browse(cr, uid, [data['id']])[0] - new_picking = None - new_moves = [] - - complete, too_many, too_few = [], [], [] - for move in move_obj.browse(cr, uid, data['form'].get('moves',[])): - if move.product_qty == data['form']['move%s' % move.id]: - complete.append(move) - elif move.product_qty > data['form']['move%s' % move.id]: - too_few.append(move) - else: - too_many.append(move) - - # Average price computation - if (pick.type == 'in') and (move.product_id.cost_method == 'average'): - product_obj = pooler.get_pool(cr.dbname).get('product.product') - currency_obj = pooler.get_pool(cr.dbname).get('res.currency') - users_obj = pooler.get_pool(cr.dbname).get('res.users') - uom_obj = pooler.get_pool(cr.dbname).get('product.uom') - - product = product_obj.browse(cr, uid, [move.product_id.id])[0] - user = users_obj.browse(cr, uid, [uid])[0] - - qty = data['form']['move%s' % move.id] - uom = data['form']['uom%s' % move.id] - price = data['form']['price%s' % move.id] - currency = data['form']['currency%s' % move.id] - - qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id) - - if qty > 0: - new_price = currency_obj.compute(cr, uid, currency, - user.company_id.currency_id.id, price) - new_price = uom_obj._compute_price(cr, uid, uom, new_price, - product.uom_id.id) - new_std_price = ((product.standard_price * product.qty_available)\ - + (new_price * qty))/(product.qty_available + qty) - - product_obj.write(cr, uid, [product.id], - {'standard_price': new_std_price}) - move_obj.write(cr, uid, [move.id], {'price_unit': new_price}) - - for move in too_few: - if not new_picking: - new_picking = pick_obj.copy(cr, uid, pick.id, - { - 'name' : '%s (splitted)' % pick.name, - 'move_lines' : [], - 'state':'draft' - }) - new_obj = move_obj.copy(cr, uid, move.id, - { - 'product_qty' : data['form']['move%s' % move.id], - 'product_uos_qty':data['form']['move%s' % move.id], - 'picking_id' : new_picking, - 'state': 'assigned', - 'move_dest_id': False, - 'price_unit': move.price_unit, - }) - move_obj.write(cr, uid, [move.id], - { - 'product_qty' : move.product_qty - data['form']['move%s' % move.id], - 'product_uos_qty':move.product_qty - data['form']['move%s' % move.id], - }) - - if new_picking: - move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking}) - for move in too_many: - move_obj.write(cr, uid, [move.id], - { - 'product_qty' : data['form']['move%s' % move.id], - 'product_uos_qty': data['form']['move%s' % move.id], - 'picking_id': new_picking, - }) - else: - for move in too_many: - move_obj.write(cr, uid, [move.id], - { - 'product_qty': data['form']['move%s' % move.id], - 'product_uos_qty': data['form']['move%s' % move.id] - }) - - # At first we confirm the new picking (if necessary) - wf_service = netsvc.LocalService("workflow") - if new_picking: - wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr) - # Then we finish the good picking - if new_picking: - pick_obj.action_move(cr, uid, [new_picking]) - wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr) - wf_service.trg_write(uid, 'stock.picking', pick.id, cr) - else: - pick_obj.action_move(cr, uid, [pick.id]) - wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr) - return {'new_picking':new_picking or False} + move_obj = pooler.get_pool(cr.dbname).get('stock.move') + pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') + pick = pick_obj.browse(cr, uid, [data['id']])[0] + new_picking = None + new_moves = [] + + complete, too_many, too_few = [], [], [] + for move in move_obj.browse(cr, uid, data['form'].get('moves',[])): + if move.product_qty == data['form']['move%s' % move.id]: + complete.append(move) + elif move.product_qty > data['form']['move%s' % move.id]: + too_few.append(move) + else: + too_many.append(move) + + # Average price computation + if (pick.type == 'in') and (move.product_id.cost_method == 'average'): + product_obj = pooler.get_pool(cr.dbname).get('product.product') + currency_obj = pooler.get_pool(cr.dbname).get('res.currency') + users_obj = pooler.get_pool(cr.dbname).get('res.users') + uom_obj = pooler.get_pool(cr.dbname).get('product.uom') + + product = product_obj.browse(cr, uid, [move.product_id.id])[0] + user = users_obj.browse(cr, uid, [uid])[0] + + qty = data['form']['move%s' % move.id] + uom = data['form']['uom%s' % move.id] + price = data['form']['price%s' % move.id] + currency = data['form']['currency%s' % move.id] + + qty = uom_obj._compute_qty(cr, uid, uom, qty, product.uom_id.id) + + if qty > 0: + new_price = currency_obj.compute(cr, uid, currency, + user.company_id.currency_id.id, price) + new_price = uom_obj._compute_price(cr, uid, uom, new_price, + product.uom_id.id) + new_std_price = ((product.standard_price * product.qty_available)\ + + (new_price * qty))/(product.qty_available + qty) + + product_obj.write(cr, uid, [product.id], + {'standard_price': new_std_price}) + move_obj.write(cr, uid, [move.id], {'price_unit': new_price}) + + for move in too_few: + if not new_picking: + new_picking = pick_obj.copy(cr, uid, pick.id, + { + 'name' : '%s (splitted)' % pick.name, + 'move_lines' : [], + 'state':'draft' + }) + new_obj = move_obj.copy(cr, uid, move.id, + { + 'product_qty' : data['form']['move%s' % move.id], + 'product_uos_qty':data['form']['move%s' % move.id], + 'picking_id' : new_picking, + 'state': 'assigned', + 'move_dest_id': False, + 'price_unit': move.price_unit, + }) + move_obj.write(cr, uid, [move.id], + { + 'product_qty' : move.product_qty - data['form']['move%s' % move.id], + 'product_uos_qty':move.product_qty - data['form']['move%s' % move.id], + }) + + if new_picking: + move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking}) + for move in too_many: + move_obj.write(cr, uid, [move.id], + { + 'product_qty' : data['form']['move%s' % move.id], + 'product_uos_qty': data['form']['move%s' % move.id], + 'picking_id': new_picking, + }) + else: + for move in too_many: + move_obj.write(cr, uid, [move.id], + { + 'product_qty': data['form']['move%s' % move.id], + 'product_uos_qty': data['form']['move%s' % move.id] + }) + + # At first we confirm the new picking (if necessary) + wf_service = netsvc.LocalService("workflow") + if new_picking: + wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr) + # Then we finish the good picking + if new_picking: + pick_obj.action_move(cr, uid, [new_picking]) + wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr) + wf_service.trg_write(uid, 'stock.picking', pick.id, cr) + else: + pick_obj.action_move(cr, uid, [pick.id]) + wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr) + return {'new_picking':new_picking or False} class partial_picking(wizard.interface): - states = { - 'init': { - 'actions': [ _get_moves ], - 'result': {'type': 'form', 'arch': _moves_arch, 'fields': _moves_fields, - 'state' : ( - ('end', 'Cancel'), - ('split', 'Make Picking') - ) - }, - }, - 'split': { - 'actions': [ _do_split ], - 'result': {'type': 'state', 'state': 'end'}, - }, - 'end2': { - 'actions': [ ], - 'result': {'type': 'form', 'arch': _moves_arch_end, - 'fields': _moves_fields_end, - 'state': ( - ('end', 'Close'), - ) - }, - }, - } + states = { + 'init': { + 'actions': [ _get_moves ], + 'result': {'type': 'form', 'arch': _moves_arch, 'fields': _moves_fields, + 'state' : ( + ('end', 'Cancel'), + ('split', 'Make Picking') + ) + }, + }, + 'split': { + 'actions': [ _do_split ], + 'result': {'type': 'state', 'state': 'end'}, + }, + 'end2': { + 'actions': [ ], + 'result': {'type': 'form', 'arch': _moves_arch_end, + 'fields': _moves_fields_end, + 'state': ( + ('end', 'Close'), + ) + }, + }, + } partial_picking('stock.partial_picking') diff --git a/addons/stock/wizard/wizard_picking_make.py b/addons/stock/wizard/wizard_picking_make.py index 833a3b468ff..545ad9ed389 100644 --- a/addons/stock/wizard/wizard_picking_make.py +++ b/addons/stock/wizard/wizard_picking_make.py @@ -33,64 +33,64 @@ import netsvc ARCH = '''<?xml version="1.0"?> <form string="Make packing"> - <field name="pickings" nolabel="1" colspan="4" - width="600" height="300"/> + <field name="pickings" nolabel="1" colspan="4" + width="600" height="300"/> </form>''' FIELDS = { - 'pickings': { - 'string': 'Packings', - 'type': 'one2many', - 'relation': 'stock.picking', - 'readonly': True, - }, + 'pickings': { + 'string': 'Packings', + 'type': 'one2many', + 'relation': 'stock.picking', + 'readonly': True, + }, } def _get_value(obj, cursor, user, data, context): - pool = pooler.get_pool(cursor.dbname) - picking_obj = pool.get('stock.picking') + pool = pooler.get_pool(cursor.dbname) + picking_obj = pool.get('stock.picking') - picking_ids = picking_obj.search(cursor, user, [ - ('id', 'in', data['ids']), - ('state', '<>', 'done'), - ('state', '<>', 'cancel')], context=context) - return {'pickings': picking_ids} + picking_ids = picking_obj.search(cursor, user, [ + ('id', 'in', data['ids']), + ('state', '<>', 'done'), + ('state', '<>', 'cancel')], context=context) + return {'pickings': picking_ids} def _make_packing(obj, cursor, user, data, context): - wkf_service = netsvc.LocalService('workflow') - pool = pooler.get_pool(cursor.dbname) - picking_obj = pool.get('stock.picking') - ids = [x[1] for x in data['form']['pickings']] + wkf_service = netsvc.LocalService('workflow') + pool = pooler.get_pool(cursor.dbname) + picking_obj = pool.get('stock.picking') + ids = [x[1] for x in data['form']['pickings']] - picking_obj.force_assign(cursor, user, ids) - picking_obj.action_move(cursor, user, ids) - for picking_id in ids: - wkf_service.trg_validate(user, 'stock.picking', picking_id, - 'button_done', cursor) - return {} + picking_obj.force_assign(cursor, user, ids) + picking_obj.action_move(cursor, user, ids) + for picking_id in ids: + wkf_service.trg_validate(user, 'stock.picking', picking_id, + 'button_done', cursor) + return {} class stock_picking_make(wizard.interface): - states = { - 'init': { - 'actions': [_get_value], - 'result': { - 'type': 'form', - 'arch': ARCH, - 'fields': FIELDS, - 'state': [ - ('end', 'Cancel', 'gtk-cancel'), - ('make', 'Ok', 'gtk-ok', True) - ], - }, - }, - 'make': { - 'actions': [_make_packing], - 'result': { - 'type': 'state', - 'state':'end', - }, - }, - } + states = { + 'init': { + 'actions': [_get_value], + 'result': { + 'type': 'form', + 'arch': ARCH, + 'fields': FIELDS, + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('make', 'Ok', 'gtk-ok', True) + ], + }, + }, + 'make': { + 'actions': [_make_packing], + 'result': { + 'type': 'state', + 'state':'end', + }, + }, + } stock_picking_make('stock.picking.make') diff --git a/addons/stock/wizard/wizard_replacement.py b/addons/stock/wizard/wizard_replacement.py index e4b998cc614..a8e02474f94 100644 --- a/addons/stock/wizard/wizard_replacement.py +++ b/addons/stock/wizard/wizard_replacement.py @@ -31,35 +31,35 @@ import wizard class wizard_replacement(wizard.interface): - def getComposant(self, cr, uid, data, context): - return {} + def getComposant(self, cr, uid, data, context): + return {} - def replaceComposant(self, cr, uid, data, context): - return {} + def replaceComposant(self, cr, uid, data, context): + return {} - comp_form = '''<?xml version="1.0"?><form string="Replace a component"><label string="Component" colspan="4"/></form>''' - comp_fields = {} + comp_form = '''<?xml version="1.0"?><form string="Replace a component"><label string="Component" colspan="4"/></form>''' + comp_fields = {} - replace_form = '''<?xml version="1.0"?><form string="Replace result"><label string="Replacing successful !" colspan="4" /></form>''' - replace_fields = {} + replace_form = '''<?xml version="1.0"?><form string="Replace result"><label string="Replacing successful !" colspan="4" /></form>''' + replace_fields = {} - states = { - 'init' : { - 'actions' : [getComposant], - 'result' : { - 'type' : 'form', - 'arch' : comp_form, - 'fields' : comp_fields, - 'state' : [('end', 'Cancel'), ('replace', 'Replace')]} - }, - 'replace' : { - 'action' : [replaceComposant], - 'result' : { - 'type' : 'form', - 'arch' : replace_form, - 'fields' : replace_fields, - 'state' : [('end', 'Ok')]} - }, - } + states = { + 'init' : { + 'actions' : [getComposant], + 'result' : { + 'type' : 'form', + 'arch' : comp_form, + 'fields' : comp_fields, + 'state' : [('end', 'Cancel'), ('replace', 'Replace')]} + }, + 'replace' : { + 'action' : [replaceComposant], + 'result' : { + 'type' : 'form', + 'arch' : replace_form, + 'fields' : replace_fields, + 'state' : [('end', 'Ok')]} + }, + } wizard_replacement('stock.move.replace') diff --git a/addons/stock/wizard/wizard_return.py b/addons/stock/wizard/wizard_return.py index 015ed81ba65..8fe6bd3f3b4 100644 --- a/addons/stock/wizard/wizard_return.py +++ b/addons/stock/wizard/wizard_return.py @@ -38,95 +38,95 @@ arch=UpdateableStr() fields={} def make_default(val): - def fct(obj, cr, uid): - return val - return fct + def fct(obj, cr, uid): + return val + return fct def _get_returns(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - pick_obj=pool.get('stock.picking') - pick=pick_obj.browse(cr, uid, [data['id']])[0] - res={} - fields.clear() - arch_lst=['<?xml version="1.0"?>', '<form string="Return lines">', '<label string="Indicate here the quantity of the return line." colspan="4"/>'] - for m in [line for line in pick.move_lines]: - quantity=m.product_qty - arch_lst.append('<field name="return%s"/>\n<newline/>' % (m.id,)) - fields['return%s' % m.id]={'string':m.product_id.name, 'type':'float', 'required':True, 'default':make_default(quantity)} - res.setdefault('returns', []).append(m.id) - arch_lst.append('<field name="invoice_state"/>\n<newline/>') - if pick.invoice_state=='invoiced': - new_invoice_state='2binvoiced' - else: - new_invoice_state=pick.invoice_state - fields['invoice_state']={'string':'Invoice state', 'type':'selection', 'default':make_default(new_invoice_state), 'selection':[('2binvoiced', 'to be invoiced'), ('none', 'None')]} - arch_lst.append('</form>') - arch.string='\n'.join(arch_lst) - return res + pool = pooler.get_pool(cr.dbname) + pick_obj=pool.get('stock.picking') + pick=pick_obj.browse(cr, uid, [data['id']])[0] + res={} + fields.clear() + arch_lst=['<?xml version="1.0"?>', '<form string="Return lines">', '<label string="Indicate here the quantity of the return line." colspan="4"/>'] + for m in [line for line in pick.move_lines]: + quantity=m.product_qty + arch_lst.append('<field name="return%s"/>\n<newline/>' % (m.id,)) + fields['return%s' % m.id]={'string':m.product_id.name, 'type':'float', 'required':True, 'default':make_default(quantity)} + res.setdefault('returns', []).append(m.id) + arch_lst.append('<field name="invoice_state"/>\n<newline/>') + if pick.invoice_state=='invoiced': + new_invoice_state='2binvoiced' + else: + new_invoice_state=pick.invoice_state + fields['invoice_state']={'string':'Invoice state', 'type':'selection', 'default':make_default(new_invoice_state), 'selection':[('2binvoiced', 'to be invoiced'), ('none', 'None')]} + arch_lst.append('</form>') + arch.string='\n'.join(arch_lst) + return res def _create_returns(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - move_obj = pool.get('stock.move') - pick_obj = pool.get('stock.picking') - uom_obj = pool.get('product.uom') + pool = pooler.get_pool(cr.dbname) + move_obj = pool.get('stock.move') + pick_obj = pool.get('stock.picking') + uom_obj = pool.get('product.uom') - pick=pick_obj.browse(cr, uid, [data['id']])[0] - new_picking=None - date_cur=time.strftime('%Y-%m-%d %H:%M:%S') + pick=pick_obj.browse(cr, uid, [data['id']])[0] + new_picking=None + date_cur=time.strftime('%Y-%m-%d %H:%M:%S') - for move in move_obj.browse(cr, uid, data['form'].get('returns',[])): - if not new_picking: - if pick.type=='out': - new_type='in' - elif pick.type=='in': - new_type='out' - else: - new_type='internal' - new_picking=pick_obj.copy(cr, uid, pick.id, {'name':'%s (return)' % pick.name, - 'move_lines':[], 'state':'draft', 'type':new_type, 'loc_move_id':False, - 'date':date_cur, 'invoice_state':data['form']['invoice_state'],}) - if pick.loc_move_id: - new_location=pick.loc_move_id.id - else: - new_location=move.location_dest_id.id + for move in move_obj.browse(cr, uid, data['form'].get('returns',[])): + if not new_picking: + if pick.type=='out': + new_type='in' + elif pick.type=='in': + new_type='out' + else: + new_type='internal' + new_picking=pick_obj.copy(cr, uid, pick.id, {'name':'%s (return)' % pick.name, + 'move_lines':[], 'state':'draft', 'type':new_type, 'loc_move_id':False, + 'date':date_cur, 'invoice_state':data['form']['invoice_state'],}) + if pick.loc_move_id: + new_location=pick.loc_move_id.id + else: + new_location=move.location_dest_id.id - new_move=move_obj.copy(cr, uid, move.id, { - 'product_qty': data['form']['return%s' % move.id], - 'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id, - data['form']['return%s' % move.id], move.product_uos.id), - 'picking_id':new_picking, 'state':'draft', - 'location_id':new_location, 'location_dest_id':move.location_id.id, - 'date':date_cur, 'date_planned':date_cur,}) - if new_picking: - wf_service = netsvc.LocalService("workflow") - if new_picking: - wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr) - pick_obj.force_assign(cr, uid, [new_picking], context) - return new_picking + new_move=move_obj.copy(cr, uid, move.id, { + 'product_qty': data['form']['return%s' % move.id], + 'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id, + data['form']['return%s' % move.id], move.product_uos.id), + 'picking_id':new_picking, 'state':'draft', + 'location_id':new_location, 'location_dest_id':move.location_id.id, + 'date':date_cur, 'date_planned':date_cur,}) + if new_picking: + wf_service = netsvc.LocalService("workflow") + if new_picking: + wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr) + pick_obj.force_assign(cr, uid, [new_picking], context) + return new_picking def _action_open_window(self, cr, uid, data, context): - res=_create_returns(self, cr, uid, data, context) - if not res: - return {} - return { - 'domain': "[('id', 'in', ["+str(res)+"])]", - 'name': 'Packing List', - 'view_type':'form', - 'view_mode':'tree,form', - 'res_model':'stock.picking', - 'view_id':False, - 'type':'ir.actions.act_window', - } + res=_create_returns(self, cr, uid, data, context) + if not res: + return {} + return { + 'domain': "[('id', 'in', ["+str(res)+"])]", + 'name': 'Packing List', + 'view_type':'form', + 'view_mode':'tree,form', + 'res_model':'stock.picking', + 'view_id':False, + 'type':'ir.actions.act_window', + } class wizard_return_picking(wizard.interface): - states={ - 'init':{ - 'actions':[_get_returns], - 'result':{'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('return','Return')]} - }, - 'return':{ - 'actions':[], - 'result':{'type':'action', 'action':_action_open_window, 'state':'end'} - } - } + states={ + 'init':{ + 'actions':[_get_returns], + 'result':{'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('return','Return')]} + }, + 'return':{ + 'actions':[], + 'result':{'type':'action', 'action':_action_open_window, 'state':'end'} + } + } wizard_return_picking('stock.return.picking') diff --git a/addons/stock/wizard/wizard_split_lot_line.py b/addons/stock/wizard/wizard_split_lot_line.py index 42763ad9c2f..3fd2792f44c 100644 --- a/addons/stock/wizard/wizard_split_lot_line.py +++ b/addons/stock/wizard/wizard_split_lot_line.py @@ -39,47 +39,47 @@ arch = UpdateableStr() fields = {} def make_default(val): - def fct(obj, cr, uid): - return val - return fct + def fct(obj, cr, uid): + return val + return fct def _get_moves(self, cr, uid, data, context): - pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') - pick = pick_obj.browse(cr, uid, [data['id']])[0] - res = {} - fields.clear() - arch_lst = ['<?xml version="1.0"?>', '<form string="Split lines">', '<label string="Indicate here the quantity of the new line. A quantity of zero will not split the line." colspan="4"/>'] - for m in [line for line in pick.move_lines]: - quantity = m.product_qty - arch_lst.append('<field name="move%s" />\n<newline />' % (m.id,)) - fields['move%s' % m.id] = {'string' : m.product_id.name, 'type' : 'float', 'required' : True, 'default' : make_default(quantity)} - res.setdefault('moves', []).append(m.id) - arch_lst.append('</form>') - arch.string = '\n'.join(arch_lst) - return res + pick_obj = pooler.get_pool(cr.dbname).get('stock.picking') + pick = pick_obj.browse(cr, uid, [data['id']])[0] + res = {} + fields.clear() + arch_lst = ['<?xml version="1.0"?>', '<form string="Split lines">', '<label string="Indicate here the quantity of the new line. A quantity of zero will not split the line." colspan="4"/>'] + for m in [line for line in pick.move_lines]: + quantity = m.product_qty + arch_lst.append('<field name="move%s" />\n<newline />' % (m.id,)) + fields['move%s' % m.id] = {'string' : m.product_id.name, 'type' : 'float', 'required' : True, 'default' : make_default(quantity)} + res.setdefault('moves', []).append(m.id) + arch_lst.append('</form>') + arch.string = '\n'.join(arch_lst) + return res def _split_lines(self, cr, uid, data, context): - move_obj = pooler.get_pool(cr.dbname).get('stock.move') - for move in move_obj.browse(cr, uid, data['form']['moves']): - quantity = data['form']['move%s' % move.id] - if 0 < quantity < move.product_qty: - new_qty = move.product_qty - quantity - new_uos_qty = new_qty / move.product_qty * move.product_uos_qty - new_obj = move_obj.copy(cr, uid, move.id, {'product_qty' : new_qty, 'product_uos_qty': new_uos_qty, 'state':move.state}) - uos_qty = quantity / move.product_qty * move.product_uos_qty - move_obj.write(cr, uid, [move.id], {'product_qty' : quantity, 'product_uos_qty': uos_qty}) - return {} + move_obj = pooler.get_pool(cr.dbname).get('stock.move') + for move in move_obj.browse(cr, uid, data['form']['moves']): + quantity = data['form']['move%s' % move.id] + if 0 < quantity < move.product_qty: + new_qty = move.product_qty - quantity + new_uos_qty = new_qty / move.product_qty * move.product_uos_qty + new_obj = move_obj.copy(cr, uid, move.id, {'product_qty' : new_qty, 'product_uos_qty': new_uos_qty, 'state':move.state}) + uos_qty = quantity / move.product_qty * move.product_uos_qty + move_obj.write(cr, uid, [move.id], {'product_qty' : quantity, 'product_uos_qty': uos_qty}) + return {} class wizard_split_move(wizard.interface): - states = { - 'init': { - 'actions': [_get_moves], - 'result': {'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('split','Split')]} - }, - 'split': { - 'actions': [_split_lines], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_moves], + 'result': {'type':'form', 'arch':arch, 'fields':fields, 'state':[('end','Cancel'),('split','Split')]} + }, + 'split': { + 'actions': [_split_lines], + 'result': {'type':'state', 'state':'end'} + } + } wizard_split_move('stock.move.split') diff --git a/addons/stock/wizard/wizard_track_line.py b/addons/stock/wizard/wizard_track_line.py index 5b82dd45662..4ac00a08e3f 100644 --- a/addons/stock/wizard/wizard_track_line.py +++ b/addons/stock/wizard/wizard_track_line.py @@ -43,91 +43,91 @@ track_form = '''<?xml version="1.0"?> </form> ''' fields = { - 'tracking_prefix': { - 'string': 'Tracking prefix', - 'type': 'char', - 'size': 64, - }, - 'quantity': { - 'string': 'Quantity per lot', - 'type': 'float', - 'default': 1, - } + 'tracking_prefix': { + 'string': 'Tracking prefix', + 'type': 'char', + 'size': 64, + }, + 'quantity': { + 'string': 'Quantity per lot', + 'type': 'float', + 'default': 1, + } } def _track_lines(self, cr, uid, data, context): - move_id = data['id'] + move_id = data['id'] - pool = pooler.get_pool(cr.dbname) - prodlot_obj = pool.get('stock.production.lot') - move_obj = pool.get('stock.move') - production_obj = pool.get('mrp.production') - ir_sequence_obj = pool.get('ir.sequence') + pool = pooler.get_pool(cr.dbname) + prodlot_obj = pool.get('stock.production.lot') + move_obj = pool.get('stock.move') + production_obj = pool.get('mrp.production') + ir_sequence_obj = pool.get('ir.sequence') - sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial') - if not sequence: - raise wizard.except_wizard(_('Error!'), _('No production sequence defined')) - if data['form']['tracking_prefix']: - sequence=data['form']['tracking_prefix']+'/'+(sequence or '') + sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial') + if not sequence: + raise wizard.except_wizard(_('Error!'), _('No production sequence defined')) + if data['form']['tracking_prefix']: + sequence=data['form']['tracking_prefix']+'/'+(sequence or '') - move = move_obj.browse(cr, uid, [move_id])[0] - quantity=data['form']['quantity'] - if quantity <= 0 or move.product_qty == 0: - return {} - uos_qty=quantity/move.product_qty*move.product_uos_qty + move = move_obj.browse(cr, uid, [move_id])[0] + quantity=data['form']['quantity'] + if quantity <= 0 or move.product_qty == 0: + return {} + uos_qty=quantity/move.product_qty*move.product_uos_qty - quantity_rest = move.product_qty%quantity - uos_qty_rest = quantity_rest/move.product_qty*move.product_uos_qty + quantity_rest = move.product_qty%quantity + uos_qty_rest = quantity_rest/move.product_qty*move.product_uos_qty - update_val = { - 'product_qty': quantity, - 'product_uos_qty': uos_qty, - } - new_move = [] - for idx in range(int(move.product_qty//quantity)): - if idx: - current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) - new_move.append(current_move) - else: - current_move = move.id - new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}, {'product_id': move.product_id.id}) - update_val['prodlot_id'] = new_prodlot - move_obj.write(cr, uid, [current_move], update_val) - production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) - - if quantity_rest > 0: - idx = int(move.product_qty//quantity) - update_val['product_qty']=quantity_rest - update_val['product_uos_qty']=uos_qty_rest - if idx: - current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) - new_move.append(current_move) - else: - current_move = move.id - new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}) - update_val['prodlot_id'] = new_prodlot - move_obj.write(cr, uid, [current_move], update_val) + update_val = { + 'product_qty': quantity, + 'product_uos_qty': uos_qty, + } + new_move = [] + for idx in range(int(move.product_qty//quantity)): + if idx: + current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) + new_move.append(current_move) + else: + current_move = move.id + new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}, {'product_id': move.product_id.id}) + update_val['prodlot_id'] = new_prodlot + move_obj.write(cr, uid, [current_move], update_val) + production_ids = production_obj.search(cr, uid, [('move_lines', 'in', [move.id])]) + + if quantity_rest > 0: + idx = int(move.product_qty//quantity) + update_val['product_qty']=quantity_rest + update_val['product_uos_qty']=uos_qty_rest + if idx: + current_move = move_obj.copy(cr, uid, move.id, {'state': move.state, 'production_id': move.production_id.id}) + new_move.append(current_move) + else: + current_move = move.id + new_prodlot = prodlot_obj.create(cr, uid, {'name': sequence, 'ref': '%d'%idx}) + update_val['prodlot_id'] = new_prodlot + move_obj.write(cr, uid, [current_move], update_val) - products = production_obj.read(cr, uid, production_ids, ['move_lines']) - for p in products: - for new in new_move: - if new not in p['move_lines']: - p['move_lines'].append(new) - production_obj.write(cr, uid, [p['id']], {'move_lines': [(6, 0, p['move_lines'])]}) + products = production_obj.read(cr, uid, production_ids, ['move_lines']) + for p in products: + for new in new_move: + if new not in p['move_lines']: + p['move_lines'].append(new) + production_obj.write(cr, uid, [p['id']], {'move_lines': [(6, 0, p['move_lines'])]}) - return {} + return {} class wizard_track_move(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': track_form, 'fields': fields, 'state': [('end', 'Cancel', 'gtk-cancel'), ('track', 'Ok', 'gtk-ok')]}, - }, - 'track': { - 'actions': [_track_lines], - 'result': {'type':'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': track_form, 'fields': fields, 'state': [('end', 'Cancel', 'gtk-cancel'), ('track', 'Ok', 'gtk-ok')]}, + }, + 'track': { + 'actions': [_track_lines], + 'result': {'type':'state', 'state':'end'} + } + } wizard_track_move('stock.move.track') diff --git a/addons/stock/wizard/wizard_ups.py b/addons/stock/wizard/wizard_ups.py index 88bfe761e2d..5bd0450fba5 100644 --- a/addons/stock/wizard/wizard_ups.py +++ b/addons/stock/wizard/wizard_ups.py @@ -36,56 +36,56 @@ import time _ups_form = '''<?xml version="1.0"?> <form string="UPS XML generator"> <separator string="UPS generator" colspan="4"/> - <field name="weight" /> + <field name="weight" /> </form>''' _ups_fields = { - 'weight' : { 'string' : 'Lot weight', 'type' : 'float', 'default' : lambda *a: 3.0, 'required' : True }, + 'weight' : { 'string' : 'Lot weight', 'type' : 'float', 'default' : lambda *a: 3.0, 'required' : True }, } _ups_finalform = '''<?xml version="1.0"?> <form string="UPS XML generator"> - <separator string="Save the attached file" colspan="4" /> - <field name="xmlfile" /> + <separator string="Save the attached file" colspan="4" /> + <field name="xmlfile" /> </form>''' _ups_finalfields = { - 'xmlfile' : { 'string' : 'XML File', 'type' : 'binary' }, + 'xmlfile' : { 'string' : 'XML File', 'type' : 'binary' }, } _ups_uploadform = '''<?xml version="1.0"?> <form string="UPS XML generator"> - <separator string="File uploaded" colspan="4" /> + <separator string="File uploaded" colspan="4" /> </form>''' _ups_uploadfields = {} def create_xmlfile(self, cr, uid, data, context): - report = netsvc._group['report']['report.stock.move.lot.ups_xml'] - data['report_type'] = 'raw' - return {'xmlfile' : report.create(uid, data['ids'], data, {})} + report = netsvc._group['report']['report.stock.move.lot.ups_xml'] + data['report_type'] = 'raw' + return {'xmlfile' : report.create(uid, data['ids'], data, {})} def upload_xmlfile(self, cr, uid, data, context): - report = netsvc._group['report']['report.stock.move.lot.ups_xml'] - data['report_type'] = 'raw' -#FIXME: this seems unfinished - fp = file('/tmp/test.xml', 'w').write(report.create(uid, data['ids'], data, {})) - return {} + report = netsvc._group['report']['report.stock.move.lot.ups_xml'] + data['report_type'] = 'raw' +#FIXME: this seems unfinished + fp = file('/tmp/test.xml', 'w').write(report.create(uid, data['ids'], data, {})) + return {} class wiz_ups(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch' : _ups_form, 'fields' : _ups_fields, 'state':[('end','Cancel'),('ups_save','Get xml file'), ('ups_upload', 'Upload xml file')]} - }, - 'ups_save': { - 'actions': [create_xmlfile], - 'result': {'type': 'form', 'arch' : _ups_finalform, 'fields' : _ups_finalfields, 'state':[('end', 'End')]} - }, - 'ups_upload' : { - 'actions' : [upload_xmlfile], - 'result' : {'type' : 'form', 'arch' : _ups_uploadform, 'fields' : _ups_uploadfields, 'state' : [('end', 'End')]} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch' : _ups_form, 'fields' : _ups_fields, 'state':[('end','Cancel'),('ups_save','Get xml file'), ('ups_upload', 'Upload xml file')]} + }, + 'ups_save': { + 'actions': [create_xmlfile], + 'result': {'type': 'form', 'arch' : _ups_finalform, 'fields' : _ups_finalfields, 'state':[('end', 'End')]} + }, + 'ups_upload' : { + 'actions' : [upload_xmlfile], + 'result' : {'type' : 'form', 'arch' : _ups_uploadform, 'fields' : _ups_uploadfields, 'state' : [('end', 'End')]} + }, + } wiz_ups('stock.ups_xml'); diff --git a/addons/subscription/__terp__.py b/addons/subscription/__terp__.py index 37e059b3773..221e4b44dce 100644 --- a/addons/subscription/__terp__.py +++ b/addons/subscription/__terp__.py @@ -26,14 +26,14 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Subscription and recurring operations", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Others", - "depends" : ["base"], - "init_xml" : [], - "demo_xml" : [], - "update_xml" : ["subscription_view.xml"], - "active": False, - "installable": True + "name" : "Subscription and recurring operations", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Others", + "depends" : ["base"], + "init_xml" : [], + "demo_xml" : [], + "update_xml" : ["subscription_view.xml"], + "active": False, + "installable": True } diff --git a/addons/subscription/subscription.py b/addons/subscription/subscription.py index 7fe7c264d28..457e3a05de0 100644 --- a/addons/subscription/subscription.py +++ b/addons/subscription/subscription.py @@ -28,7 +28,7 @@ ############################################################################## # TODO: -# Error treatment: exception, request, ... -> send request to user_id +# Error treatment: exception, request, ... -> send request to user_id from mx import DateTime import time @@ -36,126 +36,126 @@ from osv import fields,osv from tools.translate import _ class subscription_document(osv.osv): - _name = "subscription.document" - _description = "Subscription document" - _columns = { - 'name': fields.char('Name', size=60, required=True), - 'active': fields.boolean('Active'), - 'model': fields.many2one('ir.model', 'Model', required=True), - 'field_ids': fields.one2many('subscription.document.fields', 'document_id', 'Fields') - } - _defaults = { - 'active' : lambda *a: True, - } + _name = "subscription.document" + _description = "Subscription document" + _columns = { + 'name': fields.char('Name', size=60, required=True), + 'active': fields.boolean('Active'), + 'model': fields.many2one('ir.model', 'Model', required=True), + 'field_ids': fields.one2many('subscription.document.fields', 'document_id', 'Fields') + } + _defaults = { + 'active' : lambda *a: True, + } subscription_document() class subscription_document_fields(osv.osv): - _name = "subscription.document.fields" - _description = "Subscription document fields" - _rec_name = 'field' - _columns = { - 'field': fields.many2one('ir.model.fields', 'Field', domain="[('model_id', '=', parent.model)]", required=True), - 'value': fields.selection([('false','False'),('date','Current Date')], 'Default Value', size=40), - 'document_id': fields.many2one('subscription.document', 'Subscription Document', ondelete='cascade'), - } - _defaults = {} + _name = "subscription.document.fields" + _description = "Subscription document fields" + _rec_name = 'field' + _columns = { + 'field': fields.many2one('ir.model.fields', 'Field', domain="[('model_id', '=', parent.model)]", required=True), + 'value': fields.selection([('false','False'),('date','Current Date')], 'Default Value', size=40), + 'document_id': fields.many2one('subscription.document', 'Subscription Document', ondelete='cascade'), + } + _defaults = {} subscription_document_fields() def _get_document_types(self, cr, uid, context={}): - cr.execute('select m.model, s.name from subscription_document s, ir_model m WHERE s.model = m.id order by s.name') - return cr.fetchall() + cr.execute('select m.model, s.name from subscription_document s, ir_model m WHERE s.model = m.id order by s.name') + return cr.fetchall() class subscription_subscription(osv.osv): - _name = "subscription.subscription" - _description = "Subscription" - _columns = { - 'name': fields.char('Name', size=60, required=True), - 'active': fields.boolean('Active'), - 'partner_id': fields.many2one('res.partner', 'Partner'), - 'notes': fields.text('Notes'), - 'user_id': fields.many2one('res.users', 'User', required=True), - 'interval_number': fields.integer('Interval Qty'), - 'interval_type': fields.selection([('days', 'Days'), ('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'), - 'exec_init': fields.integer('Number of documents'), - 'date_init': fields.datetime('First Date'), - 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'State'), - 'doc_source': fields.reference('Source Document', required=True, selection=_get_document_types, size=128), - 'doc_lines': fields.one2many('subscription.subscription.history', 'subscription_id', 'Documents created', readonly=True), - 'cron_id': fields.many2one('ir.cron', 'Cron Job') - } - _defaults = { - 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'user_id': lambda obj,cr,uid,context: uid, - 'active': lambda *a: True, - 'interval_number': lambda *a: 1, - 'interval_type': lambda *a: 'months', - 'doc_source': lambda *a: False, - 'state': lambda *a: 'draft' - } + _name = "subscription.subscription" + _description = "Subscription" + _columns = { + 'name': fields.char('Name', size=60, required=True), + 'active': fields.boolean('Active'), + 'partner_id': fields.many2one('res.partner', 'Partner'), + 'notes': fields.text('Notes'), + 'user_id': fields.many2one('res.users', 'User', required=True), + 'interval_number': fields.integer('Interval Qty'), + 'interval_type': fields.selection([('days', 'Days'), ('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'), + 'exec_init': fields.integer('Number of documents'), + 'date_init': fields.datetime('First Date'), + 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'State'), + 'doc_source': fields.reference('Source Document', required=True, selection=_get_document_types, size=128), + 'doc_lines': fields.one2many('subscription.subscription.history', 'subscription_id', 'Documents created', readonly=True), + 'cron_id': fields.many2one('ir.cron', 'Cron Job') + } + _defaults = { + 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'user_id': lambda obj,cr,uid,context: uid, + 'active': lambda *a: True, + 'interval_number': lambda *a: 1, + 'interval_type': lambda *a: 'months', + 'doc_source': lambda *a: False, + 'state': lambda *a: 'draft' + } - def set_process(self, cr, uid, ids, context={}): - for row in self.read(cr, uid, ids): - mapping = {'name':'name','interval_number':'interval_number','interval_type':'interval_type','exec_init':'numbercall','date_init':'nextcall'} - res = {'model':'subscription.subscription', 'args': repr([[row['id']]]), 'function':'model_copy', 'priority':6, 'user_id':row['user_id'] and row['user_id'][0]} - for key,value in mapping.items(): - res[value] = row[key] - id = self.pool.get('ir.cron').create(cr, uid, res) - self.write(cr, uid, [row['id']], {'cron_id':id, 'state':'running'}) - return True + def set_process(self, cr, uid, ids, context={}): + for row in self.read(cr, uid, ids): + mapping = {'name':'name','interval_number':'interval_number','interval_type':'interval_type','exec_init':'numbercall','date_init':'nextcall'} + res = {'model':'subscription.subscription', 'args': repr([[row['id']]]), 'function':'model_copy', 'priority':6, 'user_id':row['user_id'] and row['user_id'][0]} + for key,value in mapping.items(): + res[value] = row[key] + id = self.pool.get('ir.cron').create(cr, uid, res) + self.write(cr, uid, [row['id']], {'cron_id':id, 'state':'running'}) + return True - def model_copy(self, cr, uid, ids, context={}): - for row in self.read(cr, uid, ids): - cron_ids = [row['cron_id'][0]] - remaining = self.pool.get('ir.cron').read(cr, uid, cron_ids, ['numbercall'])[0]['numbercall'] - try: - (model_name, id) = row['doc_source'].split(',') - id = int(id) - model = self.pool.get(model_name) - except: - raise osv.except_osv(_('Wrong Source Document !'), _('Please provide another source document.\nThis one does not exist !')) + def model_copy(self, cr, uid, ids, context={}): + for row in self.read(cr, uid, ids): + cron_ids = [row['cron_id'][0]] + remaining = self.pool.get('ir.cron').read(cr, uid, cron_ids, ['numbercall'])[0]['numbercall'] + try: + (model_name, id) = row['doc_source'].split(',') + id = int(id) + model = self.pool.get(model_name) + except: + raise osv.except_osv(_('Wrong Source Document !'), _('Please provide another source document.\nThis one does not exist !')) - default = {'state':'draft'} - doc_obj = self.pool.get('subscription.document') - document_ids = doc_obj.search(cr, uid, [('model.model','=',model_name)]) - doc = doc_obj.browse(cr, uid, document_ids)[0] - for f in doc.field_ids: - if f.value=='date': - value = time.strftime('%Y-%m-%d') - else: - value = False - default[f.field.name] = value + default = {'state':'draft'} + doc_obj = self.pool.get('subscription.document') + document_ids = doc_obj.search(cr, uid, [('model.model','=',model_name)]) + doc = doc_obj.browse(cr, uid, document_ids)[0] + for f in doc.field_ids: + if f.value=='date': + value = time.strftime('%Y-%m-%d') + else: + value = False + default[f.field.name] = value - state = 'running' + state = 'running' - # if there was only one remaining document to generate - # the subscription is over and we mark it as being done - if remaining == 1: - state = 'done' - id = self.pool.get(model_name).copy(cr, uid, id, default, context) - self.pool.get('subscription.subscription.history').create(cr, uid, {'subscription_id': row['id'], 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'document_id': model_name+','+str(id)}) - self.write(cr, uid, [row['id']], {'state':state}) - return True + # if there was only one remaining document to generate + # the subscription is over and we mark it as being done + if remaining == 1: + state = 'done' + id = self.pool.get(model_name).copy(cr, uid, id, default, context) + self.pool.get('subscription.subscription.history').create(cr, uid, {'subscription_id': row['id'], 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'document_id': model_name+','+str(id)}) + self.write(cr, uid, [row['id']], {'state':state}) + return True - def set_done(self, cr, uid, ids, context={}): - res = self.read(cr,uid, ids, ['cron_id']) - ids2 = [x['cron_id'][0] for x in res if x['id']] - self.pool.get('ir.cron').write(cr, uid, ids2, {'active':False}) - self.write(cr, uid, ids, {'state':'done'}) - return True + def set_done(self, cr, uid, ids, context={}): + res = self.read(cr,uid, ids, ['cron_id']) + ids2 = [x['cron_id'][0] for x in res if x['id']] + self.pool.get('ir.cron').write(cr, uid, ids2, {'active':False}) + self.write(cr, uid, ids, {'state':'done'}) + return True - def set_draft(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state':'draft'}) - return True + def set_draft(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state':'draft'}) + return True subscription_subscription() class subscription_subscription_history(osv.osv): - _name = "subscription.subscription.history" - _description = "Subscription history" - _rec_name = 'date' - _columns = { - 'date': fields.datetime('Date'), - 'subscription_id': fields.many2one('subscription.subscription', 'Subscription', ondelete='cascade'), - 'document_id': fields.reference('Source Document', required=True, selection=[('account.invoice','Invoice'),('sale.order','Sale Order')], size=128), - } + _name = "subscription.subscription.history" + _description = "Subscription history" + _rec_name = 'date' + _columns = { + 'date': fields.datetime('Date'), + 'subscription_id': fields.many2one('subscription.subscription', 'Subscription', ondelete='cascade'), + 'document_id': fields.reference('Source Document', required=True, selection=[('account.invoice','Invoice'),('sale.order','Sale Order')], size=128), + } subscription_subscription_history() |
