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
+
+
+
+
+
+ audittrail.rule.tree
+ audittrail.rule
+ tree
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ audittrail.rule
+ ir.actions.act_window
+ form
+ tree,form
+
+
+
+
+
+
+
+ audittrail.rule
+ form
+ tree,form
+ [('state','=','subscribed')]
+
+
+
+
+ audittrail.log.form
+ audittrail.log
+ form
+
+
+
+
+
+
+ audittrail.log.tree
+ audittrail.log
+ tree
+
+
+
+
+
+
+
+
+
+
+
+ audittrail.log
+ form
+
+
+
+
+
+
+
+
diff --git a/addons/audittrail/wizard/__init__.py b/addons/audittrail/wizard/__init__.py
new file mode 100644
index 00000000000..417f74a908c
--- /dev/null
+++ b/addons/audittrail/wizard/__init__.py
@@ -0,0 +1,4 @@
+# -*- encoding: utf-8 -*-
+import wizard_view_log
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/audittrail/wizard/wizard_view_log.py b/addons/audittrail/wizard/wizard_view_log.py
new file mode 100644
index 00000000000..463c701e18c
--- /dev/null
+++ b/addons/audittrail/wizard/wizard_view_log.py
@@ -0,0 +1,61 @@
+# -*- encoding: utf-8 -*-
+import wizard
+import pooler
+import time
+
+class wizard_view_log(wizard.interface):
+
+ form1 = '''
+
+
+
+
+
'''
+
+ form1_fields = {
+ 'from': {
+ 'string': 'Log From',
+ 'type': 'datetime',
+
+ },
+ 'to': {
+ 'string': 'Log To',
+ 'type': 'datetime',
+ 'default': lambda *a: time.strftime("%Y-%m-%d %H:%M:%S"),
+ 'required':True
+ },
+ }
+
+
+ def _log_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, 'audittrail', 'action_audittrail_log_tree')
+ id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
+ result = act_obj.read(cr, uid, [id])[0]
+ log_obj= pooler.get_pool(cr.dbname).get(result['res_model'])
+ log_id = log_obj.search(cr, uid, [])
+ log_model=log_obj.read(cr, uid,log_id,['object_id'])
+ if not data['form']['from']:
+ if data['form']['to'] <> time.strftime("%Y-%m-%d %H:%M:%S"):
+ result['domain'] = str([('timestamp', '<',data['form']['to'])])
+ else:
+ pass
+ else:
+ result['domain'] = str([('timestamp', '>',data['form']['from']),('timestamp', '<',data['form']['to'])])
+
+ return result
+
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel'), ('open', 'Open Logs')]}
+ },
+ 'open': {
+ 'actions': [],
+ 'result': {'type': 'action', 'action':_log_open_window, 'state':'end'}
+ }
+ }
+wizard_view_log('audittrail.view.log')
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/mrp/__terp__.py b/addons/mrp/__terp__.py
index 0a5220eddaf..8fa15f145fd 100644
--- a/addons/mrp/__terp__.py
+++ b/addons/mrp/__terp__.py
@@ -69,7 +69,7 @@
"mrp_view.xml",
"mrp_wizard.xml",
"mrp_report.xml",
- "company_view.xml"
+ "company_view.xml",
],
"active": False,
"installable": True
diff --git a/addons/mrp/mrp_wizard.xml b/addons/mrp/mrp_wizard.xml
index be3eca8eed2..cc35b509d5a 100644
--- a/addons/mrp/mrp_wizard.xml
+++ b/addons/mrp/mrp_wizard.xml
@@ -29,6 +29,13 @@
-
+
+
+
diff --git a/addons/mrp/wizard/__init__.py b/addons/mrp/wizard/__init__.py
index 285376d20ec..998736b6722 100644
--- a/addons/mrp/wizard/__init__.py
+++ b/addons/mrp/wizard/__init__.py
@@ -34,6 +34,7 @@ import wizard_schedulers_all
import wizard_price
import wizard_workcenter_load
import wizard_track_prod
+import make_procurement
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mrp/wizard/make_procurement.py b/addons/mrp/wizard/make_procurement.py
new file mode 100644
index 00000000000..5477c7754e3
--- /dev/null
+++ b/addons/mrp/wizard/make_procurement.py
@@ -0,0 +1,108 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2007 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import wizard
+import pooler
+import netsvc
+
+import time
+
+def _get_default(obj, cr, uid, data, context=None):
+ pool = pooler.get_pool(cr.dbname)
+ product = pool.get('product.product').browse(cr, uid, data['id'], context)
+ return {'product_id': product.id, 'uom_id':product.uom_id.id, 'qty':1.0}
+
+def make_procurement(obj, cr, uid, data, context=None):
+ '''Create procurement'''
+ pool = pooler.get_pool(cr.dbname)
+ wh = pool.get('stock.warehouse').browse(cr, uid, data['form']['warehouse_id'], context)
+ user = pool.get('res.users').browse(cr, uid, uid, context)
+ procure_id = pool.get('mrp.procurement').create(cr, uid, {
+ 'name':'INT:'+str(user.login),
+ 'date_planned':data['form']['date_planned'],
+ 'product_id':data['form']['product_id'],
+ 'product_qty':data['form']['qty'],
+ 'product_uom':data['form']['uom_id'],
+ 'location_id':wh.lot_stock_id.id,
+ 'procure_method':'make_to_order',
+ }, context=context)
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'mrp.procurement', procure_id, 'button_confirm', cr)
+ return {}
+
+
+class MakeProcurement(wizard.interface):
+ '''Wizard that create a procurement from a product form'''
+
+ done_form = """
+
+
+
"""
+ procurement_form = """
+
+
+
+
+
+
+
"""
+ procurement_fields = {
+ 'qty': {'string': 'Quantity', 'type': 'float', 'digits':(16,2), 'required': True},
+ 'product_id': {'string': 'product', 'type': 'many2one', 'relation': 'product.product', 'required': True, 'readonly':1},
+ 'uom_id': {'string': 'Unit of Measure', 'type': 'many2one', 'relation': 'product.uom', 'required':True},
+ 'warehouse_id': {'string': 'Location', 'type': 'many2one', 'relation':'stock.warehouse', 'required':True},
+ 'date_planned': {'string': 'Planned Date', 'type': 'date', 'required':True, 'default': lambda *args: time.strftime('%Y-%m-%d')}
+ }
+
+ states = {
+ 'init': {
+ 'actions': [_get_default],
+ 'result': {'type': 'form', 'arch': procurement_form, 'fields': procurement_fields,
+ 'state': [
+ ('end', 'Cancel'),
+ ('create', 'Ask New Products')
+ ]
+ }
+ },
+ 'done': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch': done_form, 'fields': {},
+ 'state': [
+ ('end', 'Close'),
+ ]
+ }
+ },
+ 'create': {
+ 'actions': [],
+ 'result': {'type': 'action', 'action': make_procurement, 'state': 'done'}
+ }
+ }
+
+MakeProcurement('product.product.procurement')
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/product/__init__.py b/addons/product/__init__.py
index ce5f1fd7466..c6819fba1ee 100644
--- a/addons/product/__init__.py
+++ b/addons/product/__init__.py
@@ -30,6 +30,7 @@ import product
import pricelist
import report
import partner
+import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/product/__terp__.py b/addons/product/__terp__.py
index 7f19e6bf961..34cd432b10e 100644
--- a/addons/product/__terp__.py
+++ b/addons/product/__terp__.py
@@ -57,7 +57,7 @@
"security/ir.model.access.csv",
"product_data.xml","product_report.xml",
"product_view.xml", "pricelist_view.xml",
- "partner_view.xml"],
+ "partner_view.xml", "product_wizard.xml"],
"active": False,
"installable": True
}
diff --git a/addons/product/product_wizard.xml b/addons/product/product_wizard.xml
new file mode 100644
index 00000000000..f226cd4e543
--- /dev/null
+++ b/addons/product/product_wizard.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/addons/product/report/__init__.py b/addons/product/report/__init__.py
index e80c8a0a075..ea40408cc86 100644
--- a/addons/product/report/__init__.py
+++ b/addons/product/report/__init__.py
@@ -27,7 +27,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
-
+import product_pricelist
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/product/report/pricelist.py b/addons/product/report/pricelist.py
new file mode 100644
index 00000000000..8725d31e411
--- /dev/null
+++ b/addons/product/report/pricelist.py
@@ -0,0 +1,127 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import datetime
+from report.interface import report_rml
+from report.interface import toxml
+import pooler
+from osv import osv
+import datetime
+
+class report_custom(report_rml):
+
+ def create_xml(self, cr, uid, ids, data, context):
+ pool = pooler.get_pool(cr.dbname)
+
+ price_list_id = data['form']['price_list']
+
+ product_categ_id =pool.get('product.category').search(cr,uid,[])
+ currency = pool.get('product.pricelist').read(cr,uid,[price_list_id],['currency_id','name'])[0]
+
+
+ qty =[]
+
+ for i in range(1,6):
+ q = 'qty%d'%i
+ if data['form'][q]:
+ qty.append(data['form'][q])
+
+ if not qty:
+ qty.append(1)
+
+ product_xml = []
+ cols = ''
+ cols = cols+'6cm'
+ title =''
+ i=1
+ for q in qty:
+ cols = cols+',2.5cm'
+ if q==1:
+ title+=''%(q,i)
+ else:
+ title+=''%(q,i)
+ i+=1
+ date = datetime.date.today()
+ str_date=date.strftime("%d/%m/%Y")
+ product_xml.append(''+cols+'')
+ product_xml.append(' %s '%currency['name'])
+ product_xml.append(' %s '%currency['currency_id'][1])
+ product_xml.append(' %s '%str_date)
+ product_xml.append("")
+
+ for p_categ_id in product_categ_id:
+ product_ids = pool.get('product.product').search(cr,uid,[('id','in',ids),('categ_id','=',p_categ_id)])
+ if product_ids:
+ categ_name = pool.get('product.category').read(cr,uid,[p_categ_id],['name'])
+ products = pool.get('product.product').read(cr,uid,product_ids,['id','name','code'])
+ pro = []
+ i=0
+ pro.append('' % (categ_name[0]['name']))
+ temp = []
+ for q in qty:
+ temp.append('')
+ pro.extend(temp)
+ pro.append('')
+ for x in products:
+ #Replacement of special characters with their code html for allowing reporting - Edited by Hasa
+ x['name'] = x['name'].replace("&","&")
+ x['name'] = x['name'].replace("\"",""")
+ if x['code']:
+ pro.append('' % (x['code'], x['name']))
+ else:
+ pro.append('' % (x['name']))
+ temp = []
+ for q in qty:
+ price_dict = pool.get('product.pricelist').price_get(cr,uid,[price_list_id],x['id'],q)
+ if price_dict[price_list_id]:
+ price = price_dict[price_list_id]
+ else:
+ res = pool.get('product.product').read(cr, uid,[x['id']])
+ price = res[0]['list_price']
+# temp.append(''%(price_list[i][x['id']]*q[2]['name'],currency['currency_id'][1]))
+
+ temp.append(''%(price))
+ i+=1
+ pro.extend(temp)
+ pro.append('')
+# categ.extend(pro)
+# categ.append('')
+ product_xml.extend(pro)
+
+ product_xml.append('')
+
+ xml = '''
+
+ %s
+
+ ''' % (title+'\n'.join(product_xml))
+ return self.post_process_xml_data(cr, uid, xml, context)
+
+report_custom('report.pricelist.pricelist', 'product.product','','addons/product_pricelist_print/report/product_price.xsl')
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/product/report/product_price.xsl b/addons/product/report/product_price.xsl
new file mode 100644
index 00000000000..5442092fdcf
--- /dev/null
+++ b/addons/product/report/product_price.xsl
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #e6e6e6
+
+
+ ,0
+
+
+
+ ,0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ main_title
+
Products Price List
+
+
+ Price List Name :
+ Currency :
+ Printing Date :
+
+
+ product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/product/report/product_pricelist.py b/addons/product/report/product_pricelist.py
new file mode 100644
index 00000000000..17c7823c1bd
--- /dev/null
+++ b/addons/product/report/product_pricelist.py
@@ -0,0 +1,156 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import time
+from report import report_sxw
+from osv import osv
+import pooler
+parents = {
+ 'tr':1,
+ 'li':1,
+ 'story': 0,
+ 'section': 0
+}
+class product_pricelist(report_sxw.rml_parse):
+ def __init__(self, cr, uid, name, context):
+ super(product_pricelist, self).__init__(cr, uid, name, context)
+ self.pricelist=False
+ self.quantity=[]
+ self.localcontext.update({
+ 'time': time,
+ 'get_pricelist': self._get_pricelist,
+ 'get_currency': self._get_currency,
+ 'get_categories': self._get_categories,
+ 'get_price': self._get_price,
+ 'get_quantity':self._get_quantity,
+ 'qty_header':self._qty_header,
+ })
+ def _qty_header(self,form):
+ qty=[]
+ self.pricelist=form['price_list']
+ for i in range(1,6):
+ q = 'qty%d'%i
+ if form[q]:
+ self.quantity.append(form[q])
+ if form[q]==1:
+ qty.append('%d unit'%(form[q]))
+ else:
+ qty.append('%d units'%(form[q]))
+ return qty
+
+
+ def _get_quantity(self,form):
+ qty=[]
+ for i in range(1,6):
+ q = 'qty%d'%i
+ if form[q]:
+ qty.append(form[q])
+ return qty
+ def _get_pricelist(self, pricelist_id):
+ pool = pooler.get_pool(self.cr.dbname)
+ pricelist = pool.get('product.pricelist').read(self.cr,self.uid,[pricelist_id],['name'])[0]
+ return pricelist['name']
+ def _get_currency(self, pricelist_id):
+ pool = pooler.get_pool(self.cr.dbname)
+ pricelist = pool.get('product.pricelist').read(self.cr,self.uid,[pricelist_id],['currency_id'])[0]
+ return pricelist['currency_id'][1]
+ def _get_categories(self, products):
+ cat_ids=[]
+ res=[]
+ pool = pooler.get_pool(self.cr.dbname)
+ pro_ids=[]
+ for product in products:
+ pro_ids.append(product.id)
+ if product.categ_id.id not in cat_ids:
+ cat_ids.append(product.categ_id.id)
+ cats=pool.get('product.category').browse(self.cr,self.uid,cat_ids)
+ for cat in cats:
+ product_ids=pool.get('product.product').search(self.cr,self.uid,[('id','in',pro_ids),('categ_id','=',cat.id)])
+ products = []
+ for product in pool.get('product.product').browse(self.cr,self.uid,product_ids):
+ val={
+ 'id':product.id,
+ 'name':product.name,
+ 'code':product.code
+ }
+ for qty in self.quantity:
+ val[str(qty)]=self._get_price(self.pricelist,product.id,qty)
+ products.append(val)
+
+ res.append({'name':cat.name,'products':products})
+ return res
+
+ def _get_price(self,pricelist_id, product_id,qty):
+ pool = pooler.get_pool(self.cr.dbname)
+ price_dict = pool.get('product.pricelist').price_get(self.cr,self.uid,[pricelist_id],product_id,qty)
+ if price_dict[pricelist_id]:
+ price = self.formatLang(price_dict[pricelist_id])
+ else:
+ res = pool.get('product.product').read(self.cr, self.uid,[product_id])
+ price = self.formatLang(res[0]['list_price'])
+ return price
+
+ def repeatIn(self, lst, name, nodes_parent=False,value=[],width=False,type=False):
+ self._node.data = ''
+ node = self._find_parent(self._node, nodes_parent or parents)
+ ns = node.nextSibling
+ if not lst:
+ lst.append(1)
+ for ns in node.childNodes :
+ if ns and ns.nodeName!='#text' and ns.tagName=='blockTable' and len(value) :
+ width_str = ns._attrs['colWidths'].nodeValue
+ ns.removeAttribute('colWidths')
+ if not width or width=='':
+ width=30
+ if type.lower() in ('title'):
+ width=width*len(value)
+ width_str= '%d'%(float(width_str)+width)
+ ns.setAttribute('colWidths',width_str)
+ else:
+ for v in value:
+ width_str +=',%d'%width
+
+ ns.setAttribute('colWidths',width_str)
+ child_list = ns.childNodes
+ for child in child_list:
+ if child.nodeName=='tr':
+ lc = child.childNodes[1]
+ t=0
+ for v in value:
+ newnode = lc.cloneNode(1)
+ if type.lower() in ('string'):
+ newnode.childNodes[1].lastChild.data="[[ %s['%s'] ]]"%(name,v)
+ elif type.lower() in ('label'):
+ newnode.childNodes[1].lastChild.data= "%s"%(v)
+ child.appendChild(newnode)
+ newnode=False
+ return super(product_pricelist,self).repeatIn(lst, name, nodes_parent=False)
+#end
+report_sxw.report_sxw('report.product.pricelist','product.product','addons/product_pricelist_print/report/product_pricelist.rml',parser=product_pricelist)
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/product/report/product_pricelist.rml b/addons/product/report/product_pricelist.rml
new file mode 100644
index 00000000000..ddeb5023768
--- /dev/null
+++ b/addons/product/report/product_pricelist.rml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [[ p['code'] and '[' + p['code'] + '] ' or '' ]] [[ p['name'] ]]
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/product/wizard/__init__.py b/addons/product/wizard/__init__.py
new file mode 100644
index 00000000000..58234dafab3
--- /dev/null
+++ b/addons/product/wizard/__init__.py
@@ -0,0 +1,32 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved.
+# Fabien Pinckaers
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+import wizard_price
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/product/wizard/wizard_price.py b/addons/product/wizard/wizard_price.py
new file mode 100644
index 00000000000..b4e5ee271ef
--- /dev/null
+++ b/addons/product/wizard/wizard_price.py
@@ -0,0 +1,67 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+# Fabien Pinckaers
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import wizard
+
+qty1_form = '''
+
+
+
+
+
+
+
+
+
'''
+qty1_fields = {
+ 'price_list' : {'string' : 'PriceList', 'type' : 'many2one', 'relation' : 'product.pricelist', 'required':True },
+ 'qty1': {'string':'Quantity-1', 'type':'integer', 'default':0},
+ 'qty2': {'string':'Quantity-2', 'type':'integer', 'default':0},
+ 'qty3': {'string':'Quantity-3', 'type':'integer', 'default':0},
+ 'qty4': {'string':'Quantity-4', 'type':'integer', 'default':0},
+ 'qty5': {'string':'Quantity-5', 'type':'integer', 'default':0},
+}
+
+
+class wizard_qty(wizard.interface):
+
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type':'form', 'arch':qty1_form, 'fields':qty1_fields, 'state':[('end','Cancel'),('price','Print')]}
+ },
+ 'price': {
+ 'actions': [],
+ 'result': {'type':'print', 'report':'product.pricelist', 'state':'end'}
+ }
+
+ }
+wizard_qty('product.price_list')
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock/stock_wizard.xml b/addons/stock/stock_wizard.xml
index 10e0859295e..fa46831cf2c 100644
--- a/addons/stock/stock_wizard.xml
+++ b/addons/stock/stock_wizard.xml
@@ -18,6 +18,21 @@
-
+
+
+
+
+
diff --git a/addons/stock/wizard/__init__.py b/addons/stock/wizard/__init__.py
index 330999d3748..b18ad592fb3 100644
--- a/addons/stock/wizard/__init__.py
+++ b/addons/stock/wizard/__init__.py
@@ -37,6 +37,8 @@ import wizard_split_lot_line
import wizard_track_line
import wizard_ups
import wizard_invoice_onshipping
+import inventory_merge
+import inventory_merge_zero
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/inventory_merge.py b/addons/stock/wizard/inventory_merge.py
new file mode 100644
index 00000000000..30edbbca7ad
--- /dev/null
+++ b/addons/stock/wizard/inventory_merge.py
@@ -0,0 +1,104 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+
+import wizard
+import pooler
+
+
+_form = """
+
+
+
+
+"""
+
+
+def do_merge(self, cr, uid, data, context):
+
+ invent_obj = pooler.get_pool(cr.dbname).get('stock.inventory')
+ invent_line_obj = pooler.get_pool(cr.dbname).get('stock.inventory.line')
+
+ invent_lines = {}
+
+ if len(data['ids']) < 2:
+ raise wizard.except_wizard("Warning",
+ "Please select at least two inventories.")
+
+
+
+ for inventory in invent_obj.browse(cr, uid, data['ids'], context=context):
+ if inventory.state == "done":
+ raise wizard.except_wizard("Warning",
+ "Merging is only allowed on draft inventories.")
+
+ for line in inventory.inventory_line_id:
+ key = (line.location_id.id, line.product_id.id, line.product_uom.id)
+ if key in invent_lines:
+ invent_lines[key] += line.product_qty
+ else:
+ invent_lines[key] = line.product_qty
+
+
+ new_invent = invent_obj.create(cr, uid, {
+ 'name': 'Merged inventory'
+ }, context=context)
+
+ for key, quantity in invent_lines.items():
+ invent_line_obj.create(cr, uid, {
+ 'inventory_id': new_invent,
+ 'location_id': key[0],
+ 'product_id': key[1],
+ 'product_uom': key[2],
+ 'product_qty': quantity,
+ })
+
+ return {}
+
+
+class merge_inventory(wizard.interface):
+ states = {
+ 'init' : {
+ 'actions' : [],
+ 'result' : {'type' : 'form',
+ 'arch' : _form,
+ 'fields' : {},
+ 'state' : [('end', 'Cancel'),
+ ('merge', 'Yes') ]}
+ },
+ 'merge' : {
+ 'actions' : [],
+ 'result' : {'type' : 'action',
+ 'action': do_merge,
+ 'state' : 'end'}
+ },
+ }
+merge_inventory("inventory.merge")
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock/wizard/inventory_merge_zero.py b/addons/stock/wizard/inventory_merge_zero.py
new file mode 100644
index 00000000000..a9cc90b534b
--- /dev/null
+++ b/addons/stock/wizard/inventory_merge_zero.py
@@ -0,0 +1,94 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+
+import wizard
+import pooler
+
+
+_form = """
+
+
+
+
+"""
+
+
+def do_merge(self, cr, uid, data, context):
+ invent_obj = pooler.get_pool(cr.dbname).get('stock.inventory')
+ invent_line_obj = pooler.get_pool(cr.dbname).get('stock.inventory.line')
+ prod_obj = pooler.get_pool(cr.dbname).get('product.product')
+
+ if len(data['ids']) <> 1:
+ raise wizard.except_wizard("Warning",
+ "Please select one and only one inventory!")
+
+ cr.execute('select distinct location_id from stock_inventory_line where inventory_id=%d', (data['ids'][0],))
+ loc_ids = map(lambda x: x[0], cr.fetchall())
+ locs = ','.join(map(lambda x: str(x), loc_ids))
+
+ cr.execute('select distinct location_id,product_id from stock_inventory_line where inventory_id=%d', (data['ids'][0],))
+ inv = cr.fetchall()
+
+ cr.execute('select distinct product_id from stock_move where (location_dest_id in ('+locs+')) or (location_id in ('+locs+'))')
+ stock = cr.fetchall()
+ for s in stock:
+ for loc in loc_ids:
+ if (loc,s[0]) not in inv:
+ p = prod_obj.browse(cr, uid, s[0])
+ invent_line_obj.create(cr, uid, {
+ 'inventory_id': data['ids'][0],
+ 'location_id': loc,
+ 'product_id': s[0],
+ 'product_uom': p.uom_id.id,
+ 'product_qty': 0.0,
+ })
+ return {}
+
+
+class merge_inventory(wizard.interface):
+ states = {
+ 'init' : {
+ 'actions' : [],
+ 'result' : {'type' : 'form',
+ 'arch' : _form,
+ 'fields' : {},
+ 'state' : [('end', 'Cancel'),
+ ('merge', 'Set to Zero') ]}
+ },
+ 'merge' : {
+ 'actions' : [],
+ 'result' : {'type' : 'action',
+ 'action': do_merge,
+ 'state' : 'end'}
+ },
+ }
+merge_inventory("inventory.merge.stock.zero")
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock_no_autopicking/__init__.py b/addons/stock_no_autopicking/__init__.py
new file mode 100644
index 00000000000..6c209ea1c94
--- /dev/null
+++ b/addons/stock_no_autopicking/__init__.py
@@ -0,0 +1,32 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+# Fabien Pinckaers
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import stock_no_autopicking
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock_no_autopicking/__terp__.py b/addons/stock_no_autopicking/__terp__.py
new file mode 100644
index 00000000000..aad199f2965
--- /dev/null
+++ b/addons/stock_no_autopicking/__terp__.py
@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+{
+ "name":"Stock No Auto-Picking",
+ "version":"1.0",
+ "author":"Tiny",
+ "category":"Generic Modules/Production",
+ "description": """
+ This module allows an intermediate picking process to provide raw materials
+ to production orders.
+
+ One example of usage of this module is to manage production made by your
+ suppliers (sub-contracting). To achieve this, set the assembled product
+ which is sub-contracted to "No Auto-Picking" and put the location of the
+ supplier in the routing of the assembly operation.
+ """,
+ "depends":["mrp"],
+ "demo_xml":[],
+ "update_xml":["stock_no_autopicking_view.xml"],
+ "active":False,
+ "installable":True,
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock_no_autopicking/stock_no_autopicking.py b/addons/stock_no_autopicking/stock_no_autopicking.py
new file mode 100644
index 00000000000..e765054cd02
--- /dev/null
+++ b/addons/stock_no_autopicking/stock_no_autopicking.py
@@ -0,0 +1,50 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# $Id: stock.py 1005 2005-07-25 08:41:42Z nicoe $
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# 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
+# 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.
+#
+##############################################################################
+
+import netsvc
+from osv import fields,osv
+
+class product(osv.osv):
+ _inherit = "product.product"
+ _columns = {
+ 'auto_pick': fields.boolean('Auto Picking', help="Auto picking for raw materials of production orders.")
+ }
+ _defaults = {
+ 'auto_pick': lambda *args: True
+ }
+product()
+
+class mrp_production(osv.osv):
+ _inherit = "mrp.production"
+ def _get_auto_picking(self, cr, uid, production):
+ return production.product_id.auto_pick
+mrp_production()
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/stock_no_autopicking/stock_no_autopicking_view.xml b/addons/stock_no_autopicking/stock_no_autopicking_view.xml
new file mode 100644
index 00000000000..c24888245b3
--- /dev/null
+++ b/addons/stock_no_autopicking/stock_no_autopicking_view.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ product.normal.auto_pick.form
+ product.product
+
+ form
+
+
+
+
+
+
+
+
+