From a268fad012aba95b9d72b9b9fd05854eb9da43e3 Mon Sep 17 00:00:00 2001 From: Sofia Date: Tue, 16 Sep 2008 17:15:13 +0530 Subject: [PATCH] The following modules are merged:: addons-extra/product_procurement -> mrp module addons-extra/inventory_merge -> stock addons-extra/product_pricelist_print -> product addons-extra/invoice_payment -> account bzr revid: sahibsofia@gmail.com-20080916114513-7an9yz59hucjmjk8 --- addons/.project | 17 ++ addons/account/account_invoice_view.xml | 26 +- addons/account/invoice.py | 22 +- .../analytic_journal_billing_rate/__init__.py | 4 + .../analytic_journal_billing_rate/__terp__.py | 25 ++ .../analytic_journal_billing_rate.py | 103 +++++++ .../analytic_journal_billing_rate_view.xml | 83 ++++++ addons/analytic_user_function/__init__.py | 5 + addons/analytic_user_function/__terp__.py | 25 ++ .../analytic_user_function.py | 123 ++++++++ .../analytic_user_function_view.xml | 109 +++++++ addons/audittrail/__init__.py | 5 + addons/audittrail/__terp__.py | 20 ++ addons/audittrail/audittrail.py | 281 ++++++++++++++++++ addons/audittrail/audittrail_demo.xml | 10 + addons/audittrail/audittrail_view.xml | 126 ++++++++ addons/audittrail/wizard/__init__.py | 4 + addons/audittrail/wizard/wizard_view_log.py | 61 ++++ addons/mrp/__terp__.py | 2 +- addons/mrp/mrp_wizard.xml | 9 +- addons/mrp/wizard/__init__.py | 1 + addons/mrp/wizard/make_procurement.py | 108 +++++++ addons/product/__init__.py | 1 + addons/product/__terp__.py | 2 +- addons/product/product_wizard.xml | 11 + addons/product/report/__init__.py | 2 +- addons/product/report/pricelist.py | 127 ++++++++ addons/product/report/product_price.xsl | 104 +++++++ addons/product/report/product_pricelist.py | 156 ++++++++++ addons/product/report/product_pricelist.rml | 115 +++++++ addons/product/wizard/__init__.py | 32 ++ addons/product/wizard/wizard_price.py | 67 +++++ addons/stock/stock_wizard.xml | 17 +- addons/stock/wizard/__init__.py | 2 + addons/stock/wizard/inventory_merge.py | 104 +++++++ addons/stock/wizard/inventory_merge_zero.py | 94 ++++++ addons/stock_no_autopicking/__init__.py | 32 ++ addons/stock_no_autopicking/__terp__.py | 23 ++ .../stock_no_autopicking.py | 50 ++++ .../stock_no_autopicking_view.xml | 18 ++ 40 files changed, 2116 insertions(+), 10 deletions(-) create mode 100644 addons/.project create mode 100644 addons/analytic_journal_billing_rate/__init__.py create mode 100644 addons/analytic_journal_billing_rate/__terp__.py create mode 100644 addons/analytic_journal_billing_rate/analytic_journal_billing_rate.py create mode 100644 addons/analytic_journal_billing_rate/analytic_journal_billing_rate_view.xml create mode 100644 addons/analytic_user_function/__init__.py create mode 100644 addons/analytic_user_function/__terp__.py create mode 100644 addons/analytic_user_function/analytic_user_function.py create mode 100644 addons/analytic_user_function/analytic_user_function_view.xml create mode 100644 addons/audittrail/__init__.py create mode 100644 addons/audittrail/__terp__.py create mode 100644 addons/audittrail/audittrail.py create mode 100644 addons/audittrail/audittrail_demo.xml create mode 100644 addons/audittrail/audittrail_view.xml create mode 100644 addons/audittrail/wizard/__init__.py create mode 100644 addons/audittrail/wizard/wizard_view_log.py create mode 100644 addons/mrp/wizard/make_procurement.py create mode 100644 addons/product/product_wizard.xml create mode 100644 addons/product/report/pricelist.py create mode 100644 addons/product/report/product_price.xsl create mode 100644 addons/product/report/product_pricelist.py create mode 100644 addons/product/report/product_pricelist.rml create mode 100644 addons/product/wizard/__init__.py create mode 100644 addons/product/wizard/wizard_price.py create mode 100644 addons/stock/wizard/inventory_merge.py create mode 100644 addons/stock/wizard/inventory_merge_zero.py create mode 100644 addons/stock_no_autopicking/__init__.py create mode 100644 addons/stock_no_autopicking/__terp__.py create mode 100644 addons/stock_no_autopicking/stock_no_autopicking.py create mode 100644 addons/stock_no_autopicking/stock_no_autopicking_view.xml diff --git a/addons/.project b/addons/.project new file mode 100644 index 00000000000..5a28b1de850 --- /dev/null +++ b/addons/.project @@ -0,0 +1,17 @@ + + + addons + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 8dff066a29d..46239314c40 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -209,8 +209,17 @@ - - + + + + + + + + + + + @@ -283,8 +292,17 @@ - - + + + + + + + + + + + diff --git a/addons/account/invoice.py b/addons/account/invoice.py index d8d18e3eb57..983790e2b7f 100644 --- a/addons/account/invoice.py +++ b/addons/account/invoice.py @@ -126,6 +126,26 @@ class account_invoice(osv.osv): l = map(lambda x: x.id, ids_line) res[id]=[x for x in l if x <> line.id] return res + + def _compute_lines(self, cr, uid, ids, name, args, context={}): + result = {} + print 'ICI 0' + for invoice in self.browse(cr, uid, ids, context): + moves = self.move_line_id_payment_get(cr, uid, [invoice.id]) + src = [] + print 'ICI 1' + lines = [] + for m in self.pool.get('account.move.line').browse(cr, uid, moves, context): + print 'ICI 2' + if m.reconcile_id: + lines += map(lambda x: x.id, m.reconcile_id.line_id) + elif m.reconcile_partial_id: + lines += map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids) + src.append(m.id) + print 'ICI 3' + lines = filter(lambda x: x not in src, lines) + result[invoice.id] = lines + return result _name = "account.invoice" _description = 'Invoice' @@ -182,7 +202,7 @@ class account_invoice(osv.osv): help='The bank account to pay to or to be paid from'), 'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Move Lines'), 'residual': fields.function(_amount_residual, method=True, digits=(16,2),string='Residual', store=True), - + 'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'), } _defaults = { 'type': _get_type, diff --git a/addons/analytic_journal_billing_rate/__init__.py b/addons/analytic_journal_billing_rate/__init__.py new file mode 100644 index 00000000000..c597addd202 --- /dev/null +++ b/addons/analytic_journal_billing_rate/__init__.py @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- +import analytic_journal_billing_rate +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_journal_billing_rate/__terp__.py b/addons/analytic_journal_billing_rate/__terp__.py new file mode 100644 index 00000000000..5ee1e35b8a8 --- /dev/null +++ b/addons/analytic_journal_billing_rate/__terp__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +{ + "name" : "Analytic Journal Billing Rate", + "version" : "1.0", + "depends" : ["analytic_user_function", "account", "hr_timesheet_invoice"], + "author" : "Tiny", + "description": """ + + This module allows you to define what is the defaut invoicing rate for a specific journal on a given account. This is mostly used when a user encode his timesheet: the values are retrieved and the fields are auto-filled... but the possibility to change these values is still available. + + Obviously if no data has been recorded for the current account, the default value is given as usual by the account data so that this module is perfectly compatible with older configurations. + + """, + "website" : "http://tinyerp.com/", + "category" : "Generic Modules/Others", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : [ + "analytic_journal_billing_rate_view.xml", + ], + "active": False, + "installable": True +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_journal_billing_rate/analytic_journal_billing_rate.py b/addons/analytic_journal_billing_rate/analytic_journal_billing_rate.py new file mode 100644 index 00000000000..1a1a8d0b62d --- /dev/null +++ b/addons/analytic_journal_billing_rate/analytic_journal_billing_rate.py @@ -0,0 +1,103 @@ +# -*- encoding: utf-8 -*- +from osv import fields,osv +from osv import orm + +class analytic_journal_rate_grid(osv.osv): + + _name="analytic_journal_rate_grid" + _description= "Relation table between journals and billing rates" + _columns={ + 'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal',required=True,), + 'account_id': fields.many2one("account.analytic.account", "Analytic Account",required=True,), + 'rate_id': fields.many2one("hr_timesheet_invoice.factor", "Invoicing Rate",), + } + +analytic_journal_rate_grid() + +class account_analytic_account(osv.osv): + + _inherit = "account.analytic.account" + _columns = { + 'journal_rate_ids' : fields.one2many('analytic_journal_rate_grid', 'account_id', 'Invoicing Rate per Journal'), + } + +account_analytic_account() + +class hr_analytic_timesheet(osv.osv): + + _inherit = "hr.analytic.timesheet" + + + def on_change_account_id(self, cr, uid, ids,user_id, account_id, journal_id,unit_amount=0): + res = {} + if not (account_id): + #avoid a useless call to super + return res + + if not (journal_id): + return super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,user_id, account_id, unit_amount) + + #get the browse record related to journal_id and account_id + temp = self.pool.get('analytic_journal_rate_grid').search(cr, uid, [('journal_id', '=', journal_id),('account_id', '=', account_id) ]) + + if not temp: + #if there isn't any record for this journal_id and account_id + return super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,user_id, account_id, unit_amount) + else: + #get the old values from super and add the value from the new relation analytic_journal_rate_grid + r = self.pool.get('analytic_journal_rate_grid').browse(cr, uid, temp)[0] + res.setdefault('value',{}) + res['value']= super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,user_id, account_id,unit_amount)['value'] + if r.rate_id.id: + res['value']['to_invoice'] = r.rate_id.id + + return res + + + def on_change_journal_id(self, cr, uid, ids,journal_id, account_id): + res = {} + if not (journal_id and account_id): + return res + + #get the browse record related to journal_id and account_id + temp = self.pool.get('analytic_journal_rate_grid').search(cr, uid, [('journal_id', '=', journal_id),('account_id', '=', account_id) ]) + if temp: + #add the value from the new relation analytic_user_funct_grid + r = self.pool.get('analytic_journal_rate_grid').browse(cr, uid, temp)[0] + res.setdefault('value',{}) + if r.rate_id.id: + res['value']['to_invoice'] = r.rate_id.id + return res + to_invoice = self.pool.get('account.analytic.account').read(cr, uid, [account_id], ['to_invoice'])[0]['to_invoice'] + if to_invoice: + res.setdefault('value',{}) + res['value']['to_invoice'] = to_invoice[0] + + return res + +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] + for il in iml: + if il['account_analytic_id']: + + #get the browse record related to journal_id and account_id + journal_id = il['analytic_lines'][0][2]['journal_id'] + account_id = il['analytic_lines'][0][2]['account_id'] + if journal_id and account_id: + temp = self.pool.get('analytic_journal_rate_grid').search(cr, uid, [('journal_id', '=', journal_id),('account_id', '=', account_id) ]) + + if temp: + r = self.pool.get('analytic_journal_rate_grid').browse(cr, uid, temp)[0] + il['analytic_lines'][0][2]['to_invoice'] = r.rate_id.id + return iml + +account_invoice() +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_journal_billing_rate/analytic_journal_billing_rate_view.xml b/addons/analytic_journal_billing_rate/analytic_journal_billing_rate_view.xml new file mode 100644 index 00000000000..626710597d2 --- /dev/null +++ b/addons/analytic_journal_billing_rate/analytic_journal_billing_rate_view.xml @@ -0,0 +1,83 @@ + + + + + + + analytic_journal_rate_grid.tree + analytic_journal_rate_grid + tree + + + + + + + + + + analytic_journal_rate_grid.form + analytic_journal_rate_grid + form + +
+ + + + +
+ + + + account.analytic.account.form + account.analytic.account + form + + + + + + + + + + + + hr.timesheet.sheet.form + hr_timesheet_sheet.sheet + form + + + + + + + + + + + hr.analytic.timesheet.form + hr.analytic.timesheet + form + + + + + + + + + + hr.analytic.timesheet.form + hr.analytic.timesheet + form + + + + + + + + +
+
diff --git a/addons/analytic_user_function/__init__.py b/addons/analytic_user_function/__init__.py new file mode 100644 index 00000000000..c0f4bdd4e89 --- /dev/null +++ b/addons/analytic_user_function/__init__.py @@ -0,0 +1,5 @@ +# -*- encoding: utf-8 -*- +import analytic_user_function + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_user_function/__terp__.py b/addons/analytic_user_function/__terp__.py new file mode 100644 index 00000000000..6208cd43226 --- /dev/null +++ b/addons/analytic_user_function/__terp__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +{ + "name" : "Analytic User Function", + "version" : "1.0", + "depends" : ["hr_timesheet_sheet"], + "author" : "Tiny", + "description": """ + + This module allows you to define what is the defaut function of a specific user on a given account. This is mostly used when a user encode his timesheet: the values are retrieved and the fields are auto-filled... but the possibility to change these values is still available. + + Obviously if no data has been recorded for the current account, the default value is given as usual by the employee data so that this module is perfectly compatible with older configurations. + + """, + "website" : "http://tinyerp.com/", + "category" : "Generic Modules/Others", + "init_xml" : [], + "demo_xml" : [], + "update_xml" : [ + "analytic_user_function_view.xml", + ], + "active": False, + "installable": True +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py new file mode 100644 index 00000000000..0d5e2200265 --- /dev/null +++ b/addons/analytic_user_function/analytic_user_function.py @@ -0,0 +1,123 @@ +# -*- encoding: utf-8 -*- +from osv import fields,osv +from osv import orm + +class analytic_user_funct_grid(osv.osv): + + _name="analytic_user_funct_grid" + _description= "Relation table between users and products on a analytic account" + _columns={ + 'user_id': fields.many2one("res.users","User",required=True,), + 'product_id': fields.many2one("product.product","Product",required=True,), + 'account_id': fields.many2one("account.analytic.account", "Analytic Account",required=True,), + } + +analytic_user_funct_grid() + + +class account_analytic_account(osv.osv): + + _inherit = "account.analytic.account" + _columns = { + 'user_product_ids' : fields.one2many('analytic_user_funct_grid', 'account_id', 'Users/Products Rel.'), + } + +account_analytic_account() + + +class hr_analytic_timesheet(osv.osv): + + _inherit = "hr.analytic.timesheet" + + + # Look in account, if no value for the user => look in parent until there is no more parent to look + # Take the first found... if nothing found => return False + def _get_related_user_account_recursiv(self,cr,uid,user_id,account_id): + + temp=self.pool.get('analytic_user_funct_grid').search(cr, uid, [('user_id', '=', user_id),('account_id', '=', account_id) ]) + account=self.pool.get('account.analytic.account').browse(cr,uid,account_id) + if temp: + return temp + else: + if account.parent_id: + return self._get_related_user_account_recursiv(cr,uid,user_id,account.parent_id.id) + else: + return False + + + def on_change_account_id(self, cr, uid, ids,user_id, account_id, unit_amount=0): + #{'value': {'to_invoice': False, 'amount': (-162.0,), 'product_id': 7, 'general_account_id': (5,)}} + res = {} + if not (account_id): + #avoid a useless call to super + return res + + if not (user_id): + return super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,account_id) + + #get the browse record related to user_id and account_id + temp = self._get_related_user_account_recursiv(cr,uid,user_id,account_id) + # temp = self.pool.get('analytic_user_funct_grid').search(cr, uid, [('user_id', '=', user_id),('account_id', '=', account_id) ]) + if not temp: + #if there isn't any record for this user_id and account_id + return super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,account_id) + else: + #get the old values from super and add the value from the new relation analytic_user_funct_grid + r = self.pool.get('analytic_user_funct_grid').browse(cr, uid, temp)[0] + res.setdefault('value',{}) + res['value']= super(hr_analytic_timesheet, self).on_change_account_id(cr, uid, ids,account_id)['value'] + res['value']['product_id'] = r.product_id.id + res['value']['product_uom_id'] = r.product_id.product_tmpl_id.uom_id.id + + #the change of product has to impact the amount, uom and general_account_id + a = r.product_id.product_tmpl_id.property_account_expense.id + if not a: + a = r.product_id.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)' % \ + (r.product_id.name, r.product_id.id,)) + amount = unit_amount * r.product_id.uom_id._compute_price(cr, uid, + r.product_id.uom_id.id, r.product_id.standard_price, False) + res ['value']['amount']= - round(amount, 2) + res ['value']['general_account_id']= a + return res + + def on_change_user_id(self, cr, uid, ids,user_id, account_id, unit_amount=0): + res = {} + if not (user_id): + #avoid a useless call to super + return res + + #get the old values from super + res = super(hr_analytic_timesheet, self).on_change_user_id(cr, uid, ids,user_id) + + if account_id: + #get the browse record related to user_id and account_id + # temp = self.pool.get('analytic_user_funct_grid').search(cr, uid, [('user_id', '=', user_id),('account_id', '=', account_id) ]) + temp = self._get_related_user_account_recursiv(cr,uid,user_id,account_id) + if temp: + #add the value from the new relation analytic_user_funct_grid + r = self.pool.get('analytic_user_funct_grid').browse(cr, uid, temp)[0] + res['value']['product_id'] = r.product_id.id + + #the change of product has to impact the amount, uom and general_account_id + a = r.product_id.product_tmpl_id.property_account_expense.id + if not a: + a = r.product_id.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)' % \ + (r.product_id.name, r.product_id.id,)) + amount = unit_amount * r.product_id.uom_id._compute_price(cr, uid, + r.product_id.uom_id.id, r.product_id.standard_price, False) + res ['value']['amount']= - round(amount, 2) + res ['value']['general_account_id']= a + return res + +hr_analytic_timesheet() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic_user_function/analytic_user_function_view.xml b/addons/analytic_user_function/analytic_user_function_view.xml new file mode 100644 index 00000000000..340d255f182 --- /dev/null +++ b/addons/analytic_user_function/analytic_user_function_view.xml @@ -0,0 +1,109 @@ + + + + + + + analytic_user_funct_grid.tree + analytic_user_funct_grid + tree + + + + + + + + + + analytic_user_funct_grid.form + analytic_user_funct_grid + form + +
+ + + + +
+ + + + account.analytic.account.form + account.analytic.account + form + + + + + + + + + + + + + hr.timesheet.sheet.form + hr_timesheet_sheet.sheet + form + + + + + + + + + + + + hr.analytic.timesheet.form + hr.analytic.timesheet + form + + + + + + + + + + hr.analytic.timesheet.form + hr.analytic.timesheet + form + + + + + + + + + + hr.analytic.timesheet.tree + hr.analytic.timesheet + tree + + + + + + + + + + hr.analytic.timesheet.tree + hr.analytic.timesheet + tree + + + + + + + + +
+
diff --git a/addons/audittrail/__init__.py b/addons/audittrail/__init__.py new file mode 100644 index 00000000000..43ae9448433 --- /dev/null +++ b/addons/audittrail/__init__.py @@ -0,0 +1,5 @@ +# -*- encoding: utf-8 -*- +import audittrail +import wizard +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/audittrail/__terp__.py b/addons/audittrail/__terp__.py new file mode 100644 index 00000000000..a91f827cb73 --- /dev/null +++ b/addons/audittrail/__terp__.py @@ -0,0 +1,20 @@ +# -*- encoding: utf-8 -*- +# +# you must set the depends variable on modules you plan to audit ! +# +{ + "name" : "Audit Trail", + "version" : "1.0", + "depends" : ["base","account","purchase","mrp"], + "website" : "http://tinyerp.com", + "author" : "Tiny", + "init_xml" : [], + "description": "Allows the administrator to track every user operations on all objects of the system.", + "category" : "Generic Modules/Others", + "update_xml" : ["audittrail_view.xml"], + "demo_xml" : ["audittrail_demo.xml"], + "active" : False, + "installable": True +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/audittrail/audittrail.py b/addons/audittrail/audittrail.py new file mode 100644 index 00000000000..f9ffffac8d0 --- /dev/null +++ b/addons/audittrail/audittrail.py @@ -0,0 +1,281 @@ +# -*- encoding: utf-8 -*- + +from osv import osv, fields +import time, pooler, copy +import ir +class audittrail_rule(osv.osv): + _name = 'audittrail.rule' + _columns = { + "name": fields.char("Rule Name", size=32, required=True), + "object_id": fields.many2one('ir.model', 'Object', required=True), + "user_id": fields.many2many('res.users', 'audittail_rules_users', 'user_id', 'rule_id', 'Users'), + "log_read": fields.boolean("Log reads"), + "log_write": fields.boolean("Log writes"), + "log_unlink": fields.boolean("Log deletes"), + "log_create": fields.boolean("Log creates"), + "state": fields.selection((("draft", "Draft"),("subscribed", "Subscribed")), "State", required=True), + "action_id":fields.many2one('ir.actions.act_window',"Action ID"), + } + + _defaults = { + 'state': lambda *a: 'draft', + 'log_create': lambda *a: 1, + 'log_unlink': lambda *a: 1, + 'log_write': lambda *a: 1, + } + + _sql_constraints = [ + ('model_uniq', 'unique (object_id)', """There is a rule defined on this object\n You can not define other on the same!""") + ] + __functions = {} + + def __init__(self,pool,cr=None): + for obj_name in pool.obj_list(): + obj=pool.get(obj_name) + for field in ('read','write','create','unlink'): + setattr(obj, field, self.logging_fct(getattr(obj,field), obj)) + super(audittrail_rule, self).__init__(pool,cr) + + def subscribe(self, cr, uid, ids, *args): + for thisrule in self.browse(cr, uid, ids): + obj = self.pool.get(thisrule.object_id.model) + if not obj: + raise osv.except_osv( + 'WARNING:audittrail is not part of the pool', + 'Change audittrail depends -- Setting rule as DRAFT') + self.write(cr, uid, [thisrule.id], {"state": "draft"}) + val={ + "name":'View Log', + "res_model":'audittrail.log', + "src_model":thisrule.object_id.model, + "domain":"[('res_id', '=', active_id)]" + + } + id=self.pool.get('ir.actions.act_window').create(cr, uid, val) + self.write(cr, uid, ids, {"state": "subscribed","action_id":id}) + keyword = 'client_action_relate' + value = 'ir.actions.act_window,'+str(id) + res=self.pool.get('ir.model.data').ir_set(cr, uid, 'action', keyword,'View_log_'+thisrule.object_id.model, [thisrule.object_id.model], value, replace=True, isobject=True, xml_id=False) + return True + + + def logging_fct(self, fct_src, obj): + object_name=obj._name + object=None + logged_uids = [] + def get_value_text(cr, uid, field_name,values,object, context={}): + f_id= self.pool.get('ir.model.fields').search(cr, uid,[('name','=',field_name),('model_id','=',object.id)]) + if f_id: + field=self.pool.get('ir.model.fields').read(cr, uid,f_id)[0] + model=field['relation'] + + if field['ttype']=='many2one': + if values: + if type(values)==tuple: + values=values[0] + val=self.pool.get(model).read(cr,uid,[values],['name']) + if len(val): + return val[0]['name'] + + elif field['ttype'] == 'many2many': + value=[] + if values: + for id in values: + val=self.pool.get(model).read(cr,uid,[id],['name']) + if len(val): + value.append(val[0]['name']) + return value + + elif field['ttype'] == 'one2many': + + if values: + value=[] + for id in values: + val=self.pool.get(model).read(cr,uid,[id],['name']) + if len(val): + value.append(val[0]['name']) + return value + return values + + def create_log_line(cr,uid,id,object,lines=[]): + for line in lines: + f_id= self.pool.get('ir.model.fields').search(cr, uid,[('name','=',line['name']),('model_id','=',object.id)]) + if len(f_id): + fields=self.pool.get('ir.model.fields').read(cr, uid,f_id) + old_value='old_value' in line and line['old_value'] or '' + new_value='new_value' in line and line['new_value'] or '' + old_value_text='old_value_text' in line and line['old_value_text'] or '' + new_value_text='new_value_text' in line and line['new_value_text'] or '' + + if fields[0]['ttype']== 'many2one': + if type(old_value)==tuple: + old_value=old_value[0] + if type(new_value)==tuple: + new_value=new_value[0] + self.pool.get('audittrail.log.line').create(cr, uid, {"log_id": id, "field_id": f_id[0] ,"old_value":old_value ,"new_value":new_value,"old_value_text":old_value_text ,"new_value_text":new_value_text,"field_description":fields[0]['field_description']}) + return True + + def my_fct( cr, uid, *args, **args2): + obj_ids= self.pool.get('ir.model').search(cr, uid,[('model','=',object_name)]) + if not len(obj_ids): + return fct_src(cr, uid, *args, **args2) + object=self.pool.get('ir.model').browse(cr,uid,obj_ids)[0] + rule_ids=self.search(cr, uid, [('object_id','=',obj_ids[0]),('state','=','subscribed')]) + if not len(rule_ids): + return fct_src(cr, uid, *args, **args2) + + field=fct_src.__name__ + for thisrule in self.browse(cr, uid, rule_ids): + if not getattr(thisrule, 'log_'+field): + return fct_src(cr, uid, *args, **args2) + self.__functions.setdefault(thisrule.id, []) + self.__functions[thisrule.id].append( (obj,field, getattr(obj,field)) ) + for user in thisrule.user_id: + logged_uids.append(user.id) + + if fct_src.__name__ in ('create'): + res_id =fct_src( cr, uid, *args, **args2) + new_value=self.pool.get(object.model).read(cr,uid,[res_id],args[0].keys())[0] + if 'id' in new_value: + del new_value['id'] + if not len(logged_uids) or uid in logged_uids: + id=self.pool.get('audittrail.log').create(cr, uid, {"method": fct_src.__name__, "object_id": object.id, "user_id": uid, "res_id": res_id,"name": "%s %s %s" % (fct_src.__name__, object.id, time.strftime("%Y-%m-%d %H:%M:%S"))}) + lines=[] + for field in new_value: + if new_value[field]: + line={ + 'name':field, + 'new_value':new_value[field], + 'new_value_text':get_value_text(cr,uid,field,new_value[field],object) + } + lines.append(line) + create_log_line(cr,uid,id,object,lines) + return res_id + + if fct_src.__name__ in ('write'): + res_ids=args[0] + for res_id in res_ids: + old_values=self.pool.get(object.model).read(cr,uid,res_id,args[1].keys()) + old_values_text={} + for field in args[1].keys(): + old_values_text[field]=get_value_text(cr,uid,field,old_values[field],object) + res =fct_src( cr, uid, *args, **args2) + if res: + new_values=self.pool.get(object.model).read(cr,uid,res_ids,args[1].keys())[0] + if not len(logged_uids) or uid in logged_uids: + id=self.pool.get('audittrail.log').create(cr, uid, {"method": fct_src.__name__, "object_id": object.id, "user_id": uid, "res_id": res_id,"name": "%s %s %s" % (fct_src.__name__, object.id, time.strftime("%Y-%m-%d %H:%M:%S"))}) + lines=[] + for field in args[1].keys(): + if args[1].keys(): + line={ + 'name':field, + 'new_value':field in new_values and new_values[field] or '', + 'old_value':field in old_values and old_values[field] or '', + 'new_value_text':get_value_text(cr,uid,field,new_values[field],object), + 'old_value_text':old_values_text[field] + } + lines.append(line) + create_log_line(cr,uid,id,object,lines) + return res + + if fct_src.__name__ in ('read'): + res_ids=args[0] + old_values={} + res =fct_src( cr, uid,*args, **args2) + if type(res)==list: + + for v in res: + old_values[v['id']]=v + else: + + old_values[res['id']]=res + for res_id in old_values: + if not len(logged_uids) or uid in logged_uids: + id=self.pool.get('audittrail.log').create(cr, uid, {"method": fct_src.__name__, "object_id": object.id, "user_id": uid, "res_id": res_id,"name": "%s %s %s" % (fct_src.__name__, object.id, time.strftime("%Y-%m-%d %H:%M:%S"))}) + lines=[] + for field in old_values[res_id]: + if old_values[res_id][field]: + line={ + 'name':field, + 'old_value':old_values[res_id][field], + 'old_value_text':get_value_text(cr,uid,field,old_values[res_id][field],object) + } + lines.append(line) + create_log_line(cr,uid,id,object,lines) + return res + if fct_src.__name__ in ('unlink'): + res_ids=args[0] + old_values={} + for res_id in res_ids: + old_values[res_id]=self.pool.get(object.model).read(cr,uid,res_id,[]) + + for res_id in res_ids: + if not len(logged_uids) or uid in logged_uids: + id=self.pool.get('audittrail.log').create(cr, uid, {"method": fct_src.__name__, "object_id": object.id, "user_id": uid, "res_id": res_id,"name": "%s %s %s" % (fct_src.__name__, object.id, time.strftime("%Y-%m-%d %H:%M:%S"))}) + lines=[] + for field in old_values[res_id]: + if old_values[res_id][field]: + line={ + 'name':field, + 'old_value':old_values[res_id][field], + 'old_value_text':get_value_text(cr,uid,field,old_values[res_id][field],object) + } + lines.append(line) + create_log_line(cr,uid,id,object,lines) + res =fct_src( cr, uid,*args, **args2) + return res + + return my_fct + + def unsubscribe(self, cr, uid, ids, *args): + for thisrule in self.browse(cr, uid, ids): + if thisrule.id in self.__functions : + for function in self.__functions[thisrule.id]: + setattr(function[0], function[1], function[2]) + w_id=self.pool.get('ir.actions.act_window').search(cr, uid, [('name','=','View Log'),('res_model','=','audittrail.log'),('src_model','=',thisrule.object_id.model)]) + self.pool.get('ir.actions.act_window').unlink(cr, uid,w_id ) + val_obj=self.pool.get('ir.values') + value="ir.actions.act_window"+','+str(w_id[0]) + val_id=val_obj.search(cr, uid, [('model','=',thisrule.object_id.model),('value','=',value)]) + if val_id: + res = ir.ir_del(cr, uid, val_id[0]) + self.write(cr, uid, ids, {"state": "draft"}) + return True + +audittrail_rule() + + +class audittrail_log(osv.osv): + _name = 'audittrail.log' + _columns = { + "name": fields.char("Name", size=32), + "object_id": fields.many2one('ir.model', 'Object'), + "user_id": fields.many2one('res.users', 'User'), + "method": fields.selection((('read', 'Read'), ('write', 'Write'), ('unlink', 'Delete'), ('create', 'Create')), "Method"), + "timestamp": fields.datetime("Date"), + "res_id":fields.integer('Resource Id'), + "line_ids":fields.one2many('audittrail.log.line','log_id','Log lines') + + } + _defaults = { + "timestamp": lambda *a: time.strftime("%Y-%m-%d %H:%M:%S") + } + +audittrail_log() + +class audittrail_log_line(osv.osv): + _name='audittrail.log.line' + _columns={ + 'field_id': fields.many2one('ir.model.fields','Fields', required=True), + 'log_id':fields.many2one('audittrail.log','Log'), + 'log':fields.integer("Log ID"), + 'old_value':fields.text("Old Value"), + 'new_value':fields.text("New Value"), + 'old_value_text':fields.text('Old value Text' ), + 'new_value_text':fields.text('New value Text' ), + 'field_description':fields.char('Field Description' ,size=64), + } + +audittrail_log_line() +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/audittrail/audittrail_demo.xml b/addons/audittrail/audittrail_demo.xml new file mode 100644 index 00000000000..e1c4343597e --- /dev/null +++ b/addons/audittrail/audittrail_demo.xml @@ -0,0 +1,10 @@ + + + + + Audit on Partners + + + + + diff --git a/addons/audittrail/audittrail_view.xml b/addons/audittrail/audittrail_view.xml new file mode 100644 index 00000000000..ed7ff7294b2 --- /dev/null +++ b/addons/audittrail/audittrail_view.xml @@ -0,0 +1,126 @@ + + + + + audittrail.rule.form + audittrail.rule + form + +
+ + + + + + + + + + +