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.py b/addons/account/account.py
index 865d18cec61..b47b4c14bd9 100644
--- a/addons/account/account.py
+++ b/addons/account/account.py
@@ -1758,7 +1758,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
'company_id':fields.many2one('res.company','Company',required=True),
'chart_template_id': fields.many2one('account.chart.template','Chart Template',required=True),
'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Bank Accounts',required=True),
- 'code_digits':fields.integer('No. of Digits for Account Code',required=True),
+ 'code_digits':fields.integer('# of Digits',required=True,help="No. of Digits to use for account code"),
}
def action_create(self, cr, uid, ids, context=None):
@@ -1980,7 +1980,22 @@ class wizard_multi_charts_accounts(osv.osv_memory):
#create the property
property_obj.create(cr, uid, vals)
- return {}
+ return {
+ 'view_type': 'form',
+ "view_mode": 'form',
+ 'res_model': 'ir.module.module.configuration.wizard',
+ 'type': 'ir.actions.act_window',
+ 'target':'new',
+ }
+ def action_cancel(self,cr,uid,ids,conect=None):
+ return {
+ 'view_type': 'form',
+ "view_mode": 'form',
+ 'res_model': 'ir.module.module.configuration.wizard',
+ 'type': 'ir.actions.act_window',
+ 'target':'new',
+ }
+
wizard_multi_charts_accounts()
diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml
index 481fc04912d..46239314c40 100644
--- a/addons/account/account_invoice_view.xml
+++ b/addons/account/account_invoice_view.xml
@@ -3,8 +3,8 @@
+ Invoices
+ -->
account.invoice.calendar
@@ -17,17 +17,17 @@
-
- account.invoice.graph
- account.invoice
- graph
-
-
-
-
-
-
-
+
+ account.invoice.graph
+ account.invoice
+ graph
+
+
+
+
+
+
+ account.invoice.line.treeaccount.invoice.line
@@ -209,7 +209,16 @@
-
+
+
+
+
+
+
+
+
+
+
@@ -283,7 +292,16 @@
-
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml
index 97064ac62e6..638ba88f44a 100644
--- a/addons/account/account_view.xml
+++ b/addons/account/account_view.xml
@@ -1336,7 +1336,7 @@
-
+
@@ -1525,13 +1525,13 @@
- Account Tax Templates
+ Tax Templatesaccount.tax.templateformtree,form
-
+
@@ -1565,26 +1565,27 @@
- Account Tax Code Templates
+ Tax Code Templatesaccount.tax.code.templateformtree,form
-
+
- Create Multiple Charts of Accounts
+ Generate Chart of Accounts from a Chart Templatewizard.multi.charts.accountsform
-
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/account/product_view.xml b/addons/account/product_view.xml
index 431bdd9e35a..cff72df1505 100644
--- a/addons/account/product_view.xml
+++ b/addons/account/product_view.xml
@@ -9,8 +9,8 @@
-
-
+
+
@@ -31,8 +31,8 @@
-
-
+
+
diff --git a/addons/account/wizard/wizard_account_chart.py b/addons/account/wizard/wizard_account_chart.py
index 1a0e0dcfa18..1a1dcd51b8d 100644
--- a/addons/account/wizard/wizard_account_chart.py
+++ b/addons/account/wizard/wizard_account_chart.py
@@ -69,7 +69,8 @@ class wizard_account_chart(wizard.interface):
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
result = act_obj.read(cr, uid, [id])[0]
result['context'] = str({'fiscalyear': data['form']['fiscalyear'],'target_move':data['form']['target_move']})
- result['name']+=':'+pooler.get_pool(cr.dbname).get('account.fiscalyear').read(cr,uid,[data['form']['fiscalyear']])[0]['code']
+ if data['form']['fiscalyear']:
+ result['name']+=':'+pooler.get_pool(cr.dbname).get('account.fiscalyear').read(cr,uid,[data['form']['fiscalyear']])[0]['code']
return result
states = {
diff --git a/addons/account/wizard/wizard_statement_from_invoice.py b/addons/account/wizard/wizard_statement_from_invoice.py
index 1e231569758..b32d4933ad6 100644
--- a/addons/account/wizard/wizard_statement_from_invoice.py
+++ b/addons/account/wizard/wizard_statement_from_invoice.py
@@ -147,7 +147,7 @@ class PopulateStatementFromInv(wizard.interface):
'fields':START_FIELD,
'state': [
('end', '_Cancel'),
- ('customer', 'C_ustomer invoices', '', True),
+ ('customer', '_Customer invoices', '', True),
('supplier', '_Supplier invoices', '', True)
]
},
diff --git a/addons/account_balance/account_move_line.py b/addons/account_balance/account_move_line.py
index b5a4cab7960..817f09ee194 100644
--- a/addons/account_balance/account_move_line.py
+++ b/addons/account_balance/account_move_line.py
@@ -39,45 +39,54 @@ class account_move_line(osv.osv):
_description = "Entry lines"
def _query_get(self, cr, uid, obj='l', context={}):
-
- if not 'fiscalyear' in context:
- context['fiscalyear'] = self.pool.get('account.fiscalyear').find(cr, uid, exception=False)
-
- strQuery = ""
- if context.get('periods', False):
- ids = ','.join([str(x) for x in context['periods']])
-
- if not 'period_manner' in context:
- strQuery = obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id=%d)" % (context['fiscalyear'],)
- else:
- if context['period_manner']=='actual':
-
- strQuery = obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id=%d AND id in (%s))" % (context['fiscalyear'], ids)
- else:
-# p_id="in (SELECT id from account_period WHERE fiscalyear_id=%d AND id in (%s)))" % (context['fiscalyear'], ids)
- strQuery = obj+".active AND "+obj+".state<>'draft' AND("
-
- p_id=self.pool.get('account.period').search(cr,uid,[('fiscalyear_id','=',context['fiscalyear']),('id','in',context['periods'])])
-
- periods = self.pool.get('account.period').read(cr,uid,p_id,['date_start','date_stop'])
-
- count=1
- len_periods=len(p_id)
-
-
- for period in periods:
- strQuery += "("+obj+".create_date between to_date('" + period['date_start'] + "','yyyy-mm-dd') and to_date('" + period['date_stop'] + "','yyyy-mm-dd'))"
- if len_periods!=1 and count!=len_periods:
- strQuery+=" OR "
- count=count+1
- if p_id==[]:
- strQuery+=obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id=%d))" % (context['fiscalyear'],)
+ query = super(account_move_line, self)._query_get(cr, uid, obj, context)
+ if 'period_manner' in context:
+ if context['period_manner'] == 'created':
+ if 'periods' in context:
+ #the query have to be build with no reference to periods but thanks to the creation date
+ if context['periods']:
+ #if one or more period are given, use them
+ p_ids = self.pool.get('account.period').search(cr,uid,[('id','in',context['periods'])])
else:
- strQuery+=")"
- else:
- strQuery = obj+".active AND "+obj+".state<>'draft' AND "+obj+".period_id in (SELECT id from account_period WHERE fiscalyear_id=%d)" % (context['fiscalyear'],)
+ #else we have to consider all the periods of the selected fiscal year(s)
+ fiscalyear_obj = self.pool.get('account.fiscalyear')
+ if not context.get('fiscalyear', False):
- return strQuery
+ #if there is no fiscal year, take all the fiscal years
+ fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('state', '=', 'draft')])
+ else:
+ fiscalyear_ids = [context['fiscalyear']]
+ p_ids = self.pool.get('account.period').search(cr,uid,[('fiscalyear_id','in',fiscalyear_ids)])
+
+ if p_ids == []:
+ return query
+
+ #remove from the old query the clause related to the period selection
+ res = ''
+ count = 1
+ clause_list = query.split('AND')
+ ref_string = ' '+obj+'.period_id in'
+ for clause in clause_list:
+ if count != 1 and not clause.startswith(ref_string):
+ res += "AND"
+ if not clause.startswith(ref_string):
+ res += clause
+ count += 1
+
+ #add to 'res' a new clause containing the creation date criterion
+ count = 1
+ res += " AND ("
+ periods = self.pool.get('account.period').read(cr,uid,p_ids,['date_start','date_stop'])
+ for period in periods:
+ if count != 1:
+ res += " OR "
+ #creation date criterion: the creation date of the move_line has to be
+ # between the date_start and the date_stop of the selected periods
+ res += "("+obj+".create_date between to_date('" + period['date_start'] + "','yyyy-mm-dd') and to_date('" + period['date_stop'] + "','yyyy-mm-dd'))"
+ count += 1
+ res += ")"
+ return res
+ return query
account_move_line()
diff --git a/addons/account_balance/report/account_balance.py b/addons/account_balance/report/account_balance.py
index 85a548461f8..1bfc55034b6 100644
--- a/addons/account_balance/report/account_balance.py
+++ b/addons/account_balance/report/account_balance.py
@@ -396,7 +396,7 @@ class account_balance(report_sxw.rml_parse):
ctx['fiscalyear'] = id
ctx['periods'] = form['periods'][0][2]
- ctx['period_manner']=form['select_periods']
+ ctx['period_manner']=form['period_manner']
tmp = self.pool.get('account.account').browse(self.cr, self.uid, ids, ctx.copy())
if len(tmp):
@@ -454,7 +454,7 @@ class account_balance(report_sxw.rml_parse):
ctx['fiscalyear'] = id
ctx['periods'] = form['periods'][0][2]
- ctx['period_manner']=form['select_periods']
+ ctx['period_manner']=form['period_manner']
tmp1 = self.pool.get('account.account').browse(self.cr, self.uid, ids,ctx.copy())
diff --git a/addons/account_balance/wizard/wizard_account_balance_report.py b/addons/account_balance/wizard/wizard_account_balance_report.py
index 1e6a88db9bc..58e855d3717 100644
--- a/addons/account_balance/wizard/wizard_account_balance_report.py
+++ b/addons/account_balance/wizard/wizard_account_balance_report.py
@@ -55,7 +55,7 @@ dates_form = '''
-
+
@@ -70,18 +70,18 @@ dates_fields = {
'landscape': {'string': 'Show Report in Landscape Form', 'type': 'boolean'},
'format_perc': {'string': 'Show Comparision in %', 'type': 'boolean'},
'compare_pattern':{'string':"Compare Selected Years In Terms Of",'type':'selection','selection':[('bal_cash','Cash'),('bal_perc','Percentage'),('none','Don'+ "'" +'t Compare')]},
- 'select_periods':{'string':"Select Invoices Based on Their",'type':'selection','selection':[('actual','Actual Period (Duration)'),('created','Creation Date')]},
+ 'period_manner':{'string':"Entries Selection Based on",'type':'selection','selection':[('actual','Financial Period'),('created','Creation Date')]},
'periods': {'string': 'Periods', 'type': 'many2many', 'relation': 'account.period', 'help': 'All periods if empty'}
}
back_form='''
-
+
-
-
-
-
+
+
+
+
'''
back_fields={
@@ -146,7 +146,7 @@ class wizard_report(wizard.interface):
# data['form']['periods']=p_ids
data['form']['compare_pattern']='none'
data['form']['account_choice']='moves'
- data['form']['select_periods']='actual'
+ data['form']['period_manner']='actual'
return data['form']
diff --git a/addons/account_payment/account_move_line.py b/addons/account_payment/account_move_line.py
index 884a8408956..23a0afc6173 100644
--- a/addons/account_payment/account_move_line.py
+++ b/addons/account_payment/account_move_line.py
@@ -104,11 +104,13 @@ class account_move_line(osv.osv):
line2bank[line.id] = line.invoice.partner_bank.id
elif line.partner_id:
if not line.partner_id.bank_ids:
- raise osv.except_osv(_('Error !'), _('Partner '+ line.partner_id.name+ ' has no bank account defined'))
- for bank in line.partner_id.bank_ids:
- if bank.state in bank_type:
- line2bank[line.id] = bank.id
- break
+ #raise osv.except_osv(_('Error !'), _('Partner '+ line.partner_id.name+ ' has no bank account defined'))
+ line2bank[line.id] = False
+ else:
+ for bank in line.partner_id.bank_ids:
+ if bank.state in bank_type:
+ line2bank[line.id] = bank.id
+ break
if line.id not in line2bank and line.partner_id.bank_ids:
line2bank[line.id] = line.partner_id.bank_ids[0].id
else:
diff --git a/addons/account_payment/payment_view.xml b/addons/account_payment/payment_view.xml
index ef6773c480a..102b4eccd10 100644
--- a/addons/account_payment/payment_view.xml
+++ b/addons/account_payment/payment_view.xml
@@ -97,7 +97,7 @@
-
+
diff --git a/addons/account_payment/wizard/wizard_payment_order.py b/addons/account_payment/wizard/wizard_payment_order.py
index 76e26f7ea27..be87f0488a7 100644
--- a/addons/account_payment/wizard/wizard_payment_order.py
+++ b/addons/account_payment/wizard/wizard_payment_order.py
@@ -75,7 +75,7 @@ def search_entries(self, cr, uid, data, context):
''' % (','.join([str(x) for x in line_ids]), ctx)
- return {'entries': line_ids}
+ return {}
def create_payment(self, cr, uid, data, context):
line_ids= data['form']['entries'][0][2]
@@ -88,7 +88,7 @@ def create_payment(self, cr, uid, data, context):
payment = order_obj.browse(cr, uid, data['id'],
context=context)
t = payment.mode and payment.mode.type.id or None
- line2bank= pool.get('account.move.line').line2bank(cr, uid,
+ line2bank = pool.get('account.move.line').line2bank(cr, uid,
line_ids, t, context)
## Finally populate the current payment with new lines:
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/association_vertical/__terp__.py b/addons/association_vertical/__terp__.py
index 060e60b01fa..a185405fdf9 100644
--- a/addons/association_vertical/__terp__.py
+++ b/addons/association_vertical/__terp__.py
@@ -8,8 +8,8 @@
"description": """Simplification of the interface for associations.""",
"depends" : ["hr","crm_configuration","event"],
"init_xml" : [],
- "demo_xml" : ["crm_fund_data.xml"],
- "update_xml" : ["crm_fund_demo.xml",
+ "demo_xml" : [],
+ "update_xml" : [
"aso_data.xml",
"aso_vertical_view.xml",
],
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
+
+
+
+
-
- Process Node
- process.node
- form
- tree,form
-
-
-
-
-
-
- Process Transitions
- process.transition
- form
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Process Transitions
- process.transition
- tree
-
-
-
-
-
-
-
-
-
-
- Process Transitions
- process.transition
- form
-
-
+
+ Process Transitions
+ process.transition
+ form
+ tree,form
+
+
diff --git a/addons/process/report/ds.ttf b/addons/process/report/ds.ttf
deleted file mode 100644
index ffbce87ab78..00000000000
Binary files a/addons/process/report/ds.ttf and /dev/null differ
diff --git a/addons/process/report/gtk-help.png b/addons/process/report/gtk-help.png
deleted file mode 100644
index 791a108319f..00000000000
Binary files a/addons/process/report/gtk-help.png and /dev/null differ
diff --git a/addons/process/report/gtk-new.png b/addons/process/report/gtk-new.png
deleted file mode 100644
index b679e037ca3..00000000000
Binary files a/addons/process/report/gtk-new.png and /dev/null differ
diff --git a/addons/process/report/gtk-open.png b/addons/process/report/gtk-open.png
deleted file mode 100644
index 79cfedac988..00000000000
Binary files a/addons/process/report/gtk-open.png and /dev/null differ
diff --git a/addons/process/report/gtk-print.png b/addons/process/report/gtk-print.png
deleted file mode 100644
index 3fb674288e2..00000000000
Binary files a/addons/process/report/gtk-print.png and /dev/null differ
diff --git a/addons/process/report/process_print.py b/addons/process/report/process_print.py
deleted file mode 100644
index 491a0f8e019..00000000000
--- a/addons/process/report/process_print.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# -*- encoding: utf-8 -*-
-import Image
-import ImageDraw
-import ImageFont
-import math
-
-import addons
-
-ROUNDED = 30
-BGCOLOR = (228,233,237)
-TITLECOLOR = (150,70,70)
-FONT = addons.get_module_resource('process', 'report/sb.ttf')
-BOXSIZE = (160,120)
-
-size = 800,600
-
-class draw(object):
- def _rounding_box(self, x, y, width, height, title=None, bgcolor=BGCOLOR):
- d = ImageDraw.Draw(self.img)
- DR = ROUNDED/2
- d.polygon( (x+DR,y,x+width-DR,y,x+width,y+DR,x+width,y+height-DR,x+width-DR,y+height,x+DR,y+height,x,y+height-DR,x,y+DR), fill=bgcolor)
- d.pieslice((x,y,x+ROUNDED,y+ROUNDED),180,270,fill=bgcolor)
- d.pieslice((x+width-ROUNDED,y,x+width,y+ROUNDED),270,0,fill=title and TITLECOLOR or bgcolor)
- d.pieslice((x+width-ROUNDED,y+height-ROUNDED,x+width,y+height),0,90,fill=bgcolor)
- d.pieslice((x,y+height-ROUNDED,x+ROUNDED,y+height),90,180,fill=bgcolor)
- if title:
- d.polygon( (x+width/5, y, x+width-DR, y, x+width, y+DR, x+width, y+20, x+width/5+15, y+20), fill=TITLECOLOR)
- self.draw_text(x+width/5+13, y+4, 10, title, (255,255,255), width*4/5-14)
-
- def node(self, x, y, width, height, title=None, start_color=BGCOLOR):
- self._rounding_box(x,y,BOXSIZE[0]-16,BOXSIZE[1], bgcolor=start_color)
- self._rounding_box(x+12,y,BOXSIZE[0]-16,BOXSIZE[1], bgcolor=(255,255,255))
- self._rounding_box(x+16,y,BOXSIZE[0]-16,BOXSIZE[1], title)
-
- def angle(self, arrow):
- if not arrow[1]-arrow[3]:
- angle = 180
- else:
- angle = math.atan(-(arrow[2]-arrow[0]) / (arrow[1]-arrow[3])) * 180 / math.pi
- angle = 270 - angle
- if arrow[3]
-
-
+
+
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index e394f521e56..ef9d24fc05a 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -74,7 +74,7 @@
-
+
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/report_stock/__init__.py b/addons/product/wizard/__init__.py
similarity index 90%
rename from addons/report_stock/__init__.py
rename to addons/product/wizard/__init__.py
index 587d4bda4b5..58234dafab3 100644
--- a/addons/report_stock/__init__.py
+++ b/addons/product/wizard/__init__.py
@@ -1,9 +1,8 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
-#
-# $Id$
+# 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
@@ -27,8 +26,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
-
-import report_stock
+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 = '''
+
@@ -204,7 +204,7 @@
-
+
diff --git a/addons/scrum/scrum_view.xml b/addons/scrum/scrum_view.xml
index 2b7c16a66bd..aac00a39c9d 100644
--- a/addons/scrum/scrum_view.xml
+++ b/addons/scrum/scrum_view.xml
@@ -381,8 +381,8 @@
-->
- project.task.form
- project.task
+ scrum.task.form
+ scrum.taskform
@@ -396,7 +396,7 @@
All Tasks
- project.task
+ scrum.taskform[('scrum','=',1)]
@@ -404,7 +404,7 @@
My tasks
- project.task
+ scrum.taskformtree,form[('user_id', '=', uid),('scrum','=',1)]
@@ -413,7 +413,7 @@
My opened tasks
- project.task
+ scrum.taskformtree,form[('user_id','=',uid),('state','=','open'),('scrum','=',1)]
@@ -422,11 +422,12 @@
Opened tasks
- project.task
+ scrum.taskformtree,form[('state','=','open'),('scrum','=',1)]
+
diff --git a/addons/stock/__init__.py b/addons/stock/__init__.py
index ceeb7d56e8d..55b009811cf 100644
--- a/addons/stock/__init__.py
+++ b/addons/stock/__init__.py
@@ -34,6 +34,7 @@ import product
import report
import wizard
+import report_stock
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/__terp__.py b/addons/stock/__terp__.py
index adf8f825ab4..e80312a2097 100644
--- a/addons/stock/__terp__.py
+++ b/addons/stock/__terp__.py
@@ -48,6 +48,7 @@
"product_data.xml",
"product_view.xml",
"partner_view.xml",
+ "report_stock_view.xml",
],
"active": False,
"installable": True
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index 4359f2360e4..d4b1e9c7cf4 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -28,7 +28,7 @@
-
+
@@ -80,7 +80,7 @@
-
+
diff --git a/addons/stock/report/__init__.py b/addons/stock/report/__init__.py
index 2474083afdf..a9039818672 100644
--- a/addons/stock/report/__init__.py
+++ b/addons/stock/report/__init__.py
@@ -30,7 +30,6 @@
#import lot_overview
import product_stock
-import product_location
import lot_location
import ups
import picking
diff --git a/addons/stock/report/product_location.py b/addons/stock/report/product_location.py
deleted file mode 100644
index 1128d056575..00000000000
--- a/addons/stock/report/product_location.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- encoding: utf-8 -*-
-##############################################################################
-#
-# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
-#
-# $Id$
-#
-# 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.
-#
-##############################################################################
-
-from report.interface import report_rml
-from report.interface import toxml
-import pooler
-
-
-class report_custom(report_rml):
-
- def create_xml(self, cr, uid, ids, datas, context=None):
- pool = pooler.get_pool(cr.dbname)
- product_obj = pool.get('product.product')
- location_obj = pool.get('stock.location')
-
- products = product_obj.read(cr, uid, ids, ['name','uom_id'])
- cr.execute('SELECT id FROM stock_location WHERE usage = %s',
- ('internal',))
- location_ids = [ x[0] for x in cr.fetchall() ]
- location_ids.sort()
- locs_info = {}
- locs_name = dict(location_obj.name_get(cr, uid, location_ids))
- for location_id in locs_name.keys():
- locs_info[location_id] = location_obj._product_get(cr, uid,
- location_id, ids)
-
- xml = ''
- for p in products:
- xml += '' \
- '' + toxml(p['name']) + '' \
- '' + toxml(p['uom_id'][1]) + '' \
- ''
- for loc_id in locs_info.keys():
- if locs_info[loc_id].get(p['id']):
- xml += ''
- xml += '' + toxml(locs_name[loc_id]) \
- + ''
- xml += '' + toxml(locs_info[loc_id].get(p['id'])) \
- + ''
- xml += ''
- xml += '' \
- ''
- xml += ''
- return self.post_process_xml_data(cr, uid, xml, context)
-
-report_custom('report.stock.product.location', 'stock.location', '',
- 'addons/stock/report/product_location.xsl')
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/stock/report/product_location.xsl b/addons/stock/report/product_location.xsl
deleted file mode 100644
index d5e1731a4f7..00000000000
--- a/addons/stock/report/product_location.xsl
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
- a4_normal
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
Location
-
Quantity
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/report_stock/report_stock.py b/addons/stock/report_stock.py
similarity index 67%
rename from addons/report_stock/report_stock.py
rename to addons/stock/report_stock.py
index 0ad2f980ec2..36334cc5ddf 100644
--- a/addons/report_stock/report_stock.py
+++ b/addons/stock/report_stock.py
@@ -30,8 +30,11 @@
from osv import osv, fields
-class report_stock_prodlots(osv.osv):
- _name = "report.stock.prodlots"
+#
+# Check if it works with UoM ???
+#
+class stock_report_prodlots(osv.osv):
+ _name = "stock.report.prodlots"
_description = "Stock report by production lots"
_auto = False
_columns = {
@@ -42,7 +45,7 @@ class report_stock_prodlots(osv.osv):
}
def init(self, cr):
cr.execute("""
- create or replace view report_stock_prodlots as (
+ create or replace view stock_report_prodlots as (
select max(id) as id,
location_id,
product_id,
@@ -58,7 +61,6 @@ class report_stock_prodlots(osv.osv):
left join stock_location sl
on (sl.id = sm.location_id)
where state = 'done'
- and sl.usage = 'internal'
group by sm.location_id, sm.product_id, sm.product_uom, sm.prodlot_id
union all
select max(sm.id) as id,
@@ -70,43 +72,9 @@ class report_stock_prodlots(osv.osv):
left join stock_location sl
on (sl.id = sm.location_dest_id)
where sm.state = 'done'
- and sl.usage = 'internal'
group by sm.location_dest_id, sm.product_id, sm.product_uom, sm.prodlot_id
) as report
group by location_id, product_id, prodlot_id
)""")
-report_stock_prodlots()
-
-
-class report_inventory_latest(osv.osv):
- _name = "report.inventory.latest"
- _description = "Latest inventories by product."
- _auto = False
- _columns = {
- 'name': fields.datetime('Latest Date',readonly=True),
- 'product': fields.many2one('product.product', 'Product', readonly=True, select=True),
- 'inventory': fields.many2one('stock.inventory', 'Inventory Name', readonly=True, select=True),
- 'qty': fields.float('Quantity', readonly=True, select=True),
- 'uom' : fields.many2one('product.uom', 'UoM', readonly=True),
-# 'date' : fields.datetime('Latest Date',readonly=True),
- }
- _order = 'name desc'
- def init(self, cr):
- cr.execute("""
- create or replace view report_inventory_latest as (
- select max(l.id) as id,
- l.product_id as product,
- l.product_qty as qty,
- l.product_uom as uom ,
- min(l.inventory_id) as inventory,
- i.date as name
- from stock_inventory_line l
- join stock_inventory i on (l.inventory_id=i.id)
- group by l.product_uom,l.product_qty,l.product_id,i.date
-
- )""")
-report_inventory_latest()
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+stock_report_prodlots()
diff --git a/addons/stock/report_stock_view.xml b/addons/stock/report_stock_view.xml
new file mode 100644
index 00000000000..0c28d69f8cb
--- /dev/null
+++ b/addons/stock/report_stock_view.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+ stock.report.prodlots.view
+ stock.report.prodlots
+ tree
+
+
+
+
+
+
+
+
+
+
+ Stock by Lots
+ ir.actions.act_window
+ stock.report.prodlots
+ form
+ form,tree
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index 5d47e01ecf9..da20aa0d1fe 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -13,3 +13,4 @@ access_stock_production_lot_revision,stock.production.lot.revision,model_stock_p
access_stock_move,stock.move,model_stock_move,stock.group_stock_user,1,1,1,0
access_stock_inventory,stock.inventory,model_stock_inventory,stock.group_stock_user,1,1,1,1
access_stock_inventory_line,stock.inventory.line,model_stock_inventory_line,stock.group_stock_user,1,1,1,1
+access_stock_report_prodlots,stock.report.prodlots,model_stock_report_prodlots,stock.group_stock_user,1,0,0,0
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index d066bd8af35..7f74511491f 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -35,6 +35,7 @@ from osv import fields,osv
import ir
from tools import config
from tools.translate import _
+import tools
#----------------------------------------------------------
@@ -60,14 +61,44 @@ class stock_location(osv.osv):
_name = "stock.location"
_description = "Location"
_parent_name = "location_id"
+ _parent_store = True
+ _parent_order = 'name'
+ _order = 'parent_left'
+
+ def _complete_name(self, cr, uid, ids, name, args, context):
+ def _get_one_full_name(location, level=4):
+ if location.location_id:
+ parent_path = _get_one_full_name(location.location_id, level-1) + "/"
+ else:
+ parent_path = ''
+ return parent_path + location.name
+ res = {}
+ for m in self.browse(cr, uid, ids, context=context):
+ res[m.id] = _get_one_full_name(m)
+ return res
+
+ def _product_qty_available(self, cr, uid, ids, name, arg, context={}):
+ res = {}.fromkeys(ids, 0.0)
+ if 'product_id' not in context:
+ return res
+ for id in ids:
+ location_ids = self.search(cr, uid, [('location_id', 'child_of', [id])])
+ res[id] = self._product_get_multi_location(cr, uid, location_ids, [context['product_id']], context, ('done',), ('in','out'))[context['product_id']]
+ return res
+
_columns = {
'name': fields.char('Location Name', size=64, required=True, translate=True),
'active': fields.boolean('Active'),
- 'usage': fields.selection([('supplier','Supplier Location'),('view','View'),('internal','Internal Location'),('customer','Customer Location'),('inventory','Inventory'),('procurement','Procurement'),('production','Production')], 'Location type'),
+ 'usage': fields.selection([('supplier','Supplier Location'),('view','View'),('internal','Internal Location'),('customer','Customer Location'),('inventory','Inventory'),('procurement','Procurement'),('production','Production')], 'Location type', required=True),
'allocation_method': fields.selection([('fifo','FIFO'),('lifo','LIFO'),('nearest','Nearest')], 'Allocation Method', required=True),
+ 'complete_name': fields.function(_complete_name, method=True, type='char', size=100, string="Location Name"),
+
+ 'stock_real': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'),
+ 'stock_virtual': fields.function(_product_qty_available, method=True, type='float', string='Virtual Stock'),
+
'account_id': fields.many2one('account.account', string='Inventory Account', domain=[('type','!=','view')]),
- 'location_id': fields.many2one('stock.location', 'Parent Location', select=True),
+ 'location_id': fields.many2one('stock.location', 'Parent Location', select=True, ondelete='cascade'),
'child_ids': fields.one2many('stock.location', 'location_id', 'Contains'),
'chained_location_id': fields.many2one('stock.location', 'Chained Location If Fixed'),
@@ -75,20 +106,23 @@ class stock_location(osv.osv):
'chained_auto_packing': fields.selection(
[('auto','Automatic Move'), ('manual','Manual Operation'),('transparent','Automatic No Step Added')],
'Automatic Move',
- required=True, select=1,
+ required=True,
help="This is used only if you selected a chained location type.\n" \
"The 'Automatic Move' value will create a stock move after the current one that will be "\
"validated automatically. With 'Manual Operation', the stock move has to be validated "\
"by a worker. With 'Automatic No Step Added', the location is replaced in the original move."
),
'chained_delay': fields.integer('Chained Delay (days)'),
-
'address_id': fields.many2one('res.partner.address', 'Location Address'),
+ 'icon': fields.selection(tools.icons, 'Icon', size=64),
'comment': fields.text('Additional Information'),
'posx': fields.integer('Corridor (X)'),
'posy': fields.integer('Shelves (Y)'),
'posz': fields.integer('Height (Z)'),
+
+ 'parent_left': fields.integer('Left Parent', select=1),
+ 'parent_right': fields.integer('Right Parent', select=1),
}
_defaults = {
'active': lambda *a: 1,
@@ -99,6 +133,7 @@ class stock_location(osv.osv):
'posx': lambda *a: 0,
'posy': lambda *a: 0,
'posz': lambda *a: 0,
+ 'icon': lambda *a: False
}
def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}):
@@ -325,49 +360,55 @@ stock_tracking()
class stock_picking(osv.osv):
_name = "stock.picking"
_description = "Packing list"
- def _set_minimum_date(self, cr, uid, ids, name, value, arg, context):
- if value!=False:
- cr.execute("update stock_picking set min_date='%s' where id in (%d)"%(value,ids))
- def get_minimum_date(self, cr, uid, ids, field_name, arg, context={}):
- res = {}
- for pick in self.browse(cr, uid, ids):
- res[pick.id] = None
- cr.execute("select min_date from stock_picking where id =%d"%pick.id)
- min_date = cr.fetchone()[0]
- if not min_date:
- move_ids = [x.id for x in pick.move_lines ]
- if len(move_ids):
- cr.execute("select min(date_planned) from stock_move where id in (" + ','.join(map(str, move_ids)) + ")")
- res[pick.id] = cr.fetchone()[0] or None
- else:
- res[pick.id]=min_date
-
- return res
-
def _set_maximum_date(self, cr, uid, ids, name, value, arg, context):
- if value!=False:
- cr.execute("update stock_picking set max_date='%s' where id in (%d)"%(value,ids))
- def get_maximum_date(self, cr, uid, ids, field_name, arg, context={}):
- res = {}
- for pick in self.browse(cr, uid, ids):
- res[pick.id] = None
- cr.execute("select max_date from stock_picking where id =%d"%pick.id)
- max_date = cr.fetchone()[0]
- if not max_date:
- move_ids = [x.id for x in pick.move_lines ]
- if len(move_ids):
- cr.execute("select max(date_planned) from stock_move where id in (" + ','.join(map(str, move_ids)) + ")")
- res[pick.id] = cr.fetchone()[0] or None
- else:
- res[pick.id]=max_date
+ print 'max', ids, name, value, arg, context
+ if not value: return False
+ for pick in self.browse(cr, uid, ids, context):
+ cr.execute("""update stock_move set
+ date_planned=%s
+ where
+ picking_id=%d and
+ (date_planned=%s or date_planned>%s)""", (value,pick.id,pick.max_date,value))
+ print 'Ok'
+ return True
+ def _set_minimum_date(self, cr, uid, ids, name, value, arg, context):
+ print 'min', ids, name, value, arg, context
+ if not value: return False
+ for pick in self.browse(cr, uid, ids, context):
+ cr.execute("""update stock_move set
+ date_planned=%s
+ where
+ picking_id=%d and
+ (date_planned=%s or date_planned<%s)""", (value,pick.id,pick.min_date,value))
+ print 'Ok'
+ return True
+
+ def get_min_max_date(self, cr, uid, ids, field_name, arg, context={}):
+ res = {}
+ for id in ids:
+ res[id] = {'min_date':False, 'max_date': False}
+ if not ids:
+ return res
+ cr.execute("""select
+ picking_id,
+ min(date_planned),
+ max(date_planned)
+ from
+ stock_move
+ where
+ picking_id in (""" + ','.join(map(str, ids)) + """)
+ group by
+ picking_id""")
+ for pick, dt1,dt2 in cr.fetchall():
+ res[pick]['min_date'] = dt1
+ res[pick]['max_date'] = dt2
+ print res, ids
return res
-
-
-
+
_columns = {
'name': fields.char('Reference', size=64, required=True, select=True),
- 'origin': fields.char('Origin', size=64),
+ 'origin': fields.char('Origin Reference', size=64),
'backorder_id': fields.many2one('stock.picking', 'Back Order'),
'type': fields.selection([('out','Sending Goods'),('in','Getting Goods'),('internal','Internal'),('delivery','Delivery')], 'Shipping Type', required=True, select=True),
'active': fields.boolean('Active'),
@@ -384,12 +425,11 @@ class stock_picking(osv.osv):
('done','Done'),
('cancel','Cancel'),
], 'Status', readonly=True, select=True),
- 'min_date': fields.function(get_minimum_date, fnct_inv=_set_minimum_date,
- method=True,store=True, type='date', string='Min. Date', select=True),
+ 'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
+ method=True,store=True, type='datetime', string='Planned Date', select=1),
'date':fields.datetime('Date create'),
-
- 'max_date': fields.function(get_maximum_date, fnct_inv=_set_maximum_date,
- method=True,store=True, type='date', string='Max. Date', select=True),
+ 'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
+ method=True,store=True, type='datetime', string='Max. Planned Date', select=2),
'move_lines': fields.one2many('stock.move', 'picking_id', 'Move lines'),
'auto_picking': fields.boolean('Auto-Packing'),
@@ -398,7 +438,7 @@ class stock_picking(osv.osv):
("invoiced","Invoiced"),
("2binvoiced","To be invoiced"),
("none","Not from Packing")], "Invoice Status",
- select=True),
+ select=True, required=True),
}
_defaults = {
'name': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'stock.picking'),
@@ -722,15 +762,38 @@ class stock_production_lot(osv.osv):
_name = 'stock.production.lot'
_description = 'Production lot'
+ def _get_stock(self, cr, uid, ids, field_name, arg, context={}):
+ if 'location_id' not in context:
+ locations = self.pool.get('stock.location').search(cr, uid, [('usage','=','internal')], context=context)
+ else:
+ locations = self.pool.get('stock.location').search(cr, uid, [('location_id','child_of', [context['location_id']])], context=context)
+ res = {}.fromkeys(ids, 0.0)
+ cr.execute('''select
+ prodlot_id,
+ sum(name)
+ from
+ stock_report_prodlots
+ where
+ location_id in ('''+','.join(map(str, locations))+''' and
+ prodlot_id in ('''+','.join(map(str, ids))+'''
+ group by
+ prodlot_id
+ ''')
+ res.update(dict(cr.fetchall()))
+ return res
+
_columns = {
'name': fields.char('Serial', size=64, required=True),
- 'ref': fields.char('Reference', size=64),
+ 'ref': fields.char('Internal Ref.', size=64),
+ 'product_id': fields.many2one('product.product','Product',required=True),
'date': fields.datetime('Date create', required=True),
+ 'stock_available': fields.function(_get_stock, method=True, type="float", string="Available", select="2"),
'revisions': fields.one2many('stock.production.lot.revision','lot_id','Revisions'),
}
_defaults = {
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'name': lambda x,y,z,c: x.pool.get('ir.sequence').get(y,z,'stock.lot.serial'),
+ 'product_id': lambda x,y,z,c: c.get('product_id',False),
}
_sql_constraints = [
('name_ref_uniq', 'unique (name, ref)', 'The serial/ref must be unique !'),
@@ -747,7 +810,7 @@ class stock_production_lot_revision(osv.osv):
'date': fields.date('Revision date'),
'indice': fields.char('Revision', size=16),
'author_id': fields.many2one('res.users', 'Author'),
- 'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True),
+ 'lot_id': fields.many2one('stock.production.lot', 'Production lot', select=True, ondelete='cascade'),
}
_defaults = {
@@ -777,7 +840,7 @@ class stock_move(osv.osv):
'priority': fields.selection([('0','Not urgent'),('1','Urgent')], 'Priority'),
'date': fields.datetime('Date Created'),
- 'date_planned': fields.date('Scheduled date', required=True),
+ 'date_planned': fields.datetime('Scheduled date', required=True),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
@@ -831,7 +894,7 @@ class stock_move(osv.osv):
'state': lambda *a: 'draft',
'priority': lambda *a: '1',
'product_qty': lambda *a: 1.0,
- 'date_planned': lambda *a: time.strftime('%Y-%m-%d'),
+ 'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
}
@@ -867,7 +930,7 @@ class stock_move(osv.osv):
if dest:
if dest[1]=='transparent':
self.write(cr, uid, [m.id], {
- 'date_planned': (DateTime.strptime(m.date_planned, '%Y-%m-%d') + \
+ 'date_planned': (DateTime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
DateTime.RelativeDateTime(days=dest[2] or 0)).strftime('%Y-%m-%d'),
'location_dest_id': dest[0].id})
else:
@@ -899,7 +962,7 @@ class stock_move(osv.osv):
'picking_id': pickid,
'state':'waiting',
'move_history_ids':[],
- 'date_planned': (DateTime.strptime(move.date_planned, '%Y-%m-%d') + DateTime.RelativeDateTime(days=delay or 0)).strftime('%Y-%m-%d'),
+ 'date_planned': (DateTime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + DateTime.RelativeDateTime(days=delay or 0)).strftime('%Y-%m-%d'),
'move_history_ids2':[]}
)
self.pool.get('stock.move').write(cr, uid, [move.id], {
diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml
index 5a752f612bb..5a85898bfe4 100644
--- a/addons/stock/stock_data.xml
+++ b/addons/stock/stock_data.xml
@@ -15,14 +15,17 @@
Physical Locationsview
+ terp-stockPartner Locationsview
+ terp-partnerVirtual Locationsview
+ terp-mrp
diff --git a/addons/stock/stock_report.xml b/addons/stock/stock_report.xml
index 2a35a89513f..a7bf9eaf91e 100644
--- a/addons/stock/stock_report.xml
+++ b/addons/stock/stock_report.xml
@@ -13,8 +13,6 @@
-
-
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 2836f8742bd..6ddc37e2f63 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -3,7 +3,7 @@
-
+
=============================
Inventory Line
=============================
@@ -33,7 +33,7 @@
-
+
=============================
Inventory
=============================
@@ -64,11 +64,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -80,22 +80,29 @@
-
-
- Inventory Lines
+
+
+ Draft Physical Inventoriesir.actions.act_window
- stock.inventory.line
+ stock.inventoryform
- tree,form
-
+ [('state','=','draft')]
-
-
-
+
+
+ New Physical Inventory
+ ir.actions.act_window
+ stock.inventory
+ form
+ form,tree
+
+
+
+
=============================
Lot
=============================
-
+
stock.tracking.formstock.tracking
@@ -131,7 +138,7 @@
-
+
stock.tracking.treestock.tracking
@@ -145,7 +152,7 @@
-
+
stock.production.lot.revision.formstock.production.lot.revision
@@ -182,9 +189,11 @@
-
-
-
+
+
+
Getting Productsstock.picking
@@ -1004,9 +1016,9 @@
-
+
-
+
Packings to Processstock.picking
@@ -1029,7 +1041,7 @@
-
+
New Reception Packingstock.picking
@@ -1040,20 +1052,19 @@
[('type','=','in')]
-
+ tree
-
+ form
-
-
+
Internal Packingsstock.picking
@@ -1064,8 +1075,8 @@
{'contact_display': 'partner'}
-
-
+
+
Assigned Packingsstock.picking
@@ -1076,7 +1087,7 @@
{'contact_display': 'partner'}
-
+
Confirmed Packings Waiting Assignationstock.picking
@@ -1087,7 +1098,7 @@
{'contact_display': 'partner'}
-
+
Draft Internal Packingsstock.picking
@@ -1098,16 +1109,16 @@
{'contact_display': 'partner'}
-
+
=============================
Move Tracking
=============================
-
-
+
+
=============================
Move
=============================
-
+
stock.move.treestock.move
@@ -1115,20 +1126,20 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
stock.move.formstock.move
@@ -1148,7 +1159,7 @@
-
+
@@ -1172,7 +1183,7 @@
-
+
Draft Movesstock.move
@@ -1182,7 +1193,7 @@
-
+
Assigned Movesstock.move
@@ -1192,7 +1203,7 @@
-
+
Packingsstock.picking
@@ -1202,8 +1213,8 @@
{'contact_display': 'partner'}
-
-
+
+
# -------------------------------------------------------------
# Stock incoterms
# -------------------------------------------------------------
@@ -1238,30 +1249,18 @@
tree,form
-
-
+
+
+
-
+
tree_but_openstock.locationProducts
-
-
-
-
-
- tree_but_open
- stock.location
- Open Moves
-
-
-
-
-
+
-
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/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/process/report/__init__.py b/addons/stock_no_autopicking/__init__.py
similarity index 89%
rename from addons/process/report/__init__.py
rename to addons/stock_no_autopicking/__init__.py
index e1a4a6f4026..6c209ea1c94 100644
--- a/addons/process/report/__init__.py
+++ b/addons/stock_no_autopicking/__init__.py
@@ -1,7 +1,8 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2005-TODAY TINY SPRL. (http://tiny.be) All Rights Reserved.
+# 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
@@ -26,7 +27,6 @@
#
##############################################################################
-import report_process
-
+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/report_stock/__terp__.py b/addons/stock_no_autopicking/stock_no_autopicking.py
similarity index 63%
rename from addons/report_stock/__terp__.py
rename to addons/stock_no_autopicking/stock_no_autopicking.py
index 5e929a4e163..e765054cd02 100644
--- a/addons/report_stock/__terp__.py
+++ b/addons/stock_no_autopicking/stock_no_autopicking.py
@@ -1,9 +1,9 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2004-2008 Tiny SPRL (http://tiny.be) All Rights Reserved.
+# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
-# $Id$
+# $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
@@ -25,30 +25,26 @@
# 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.
-###############################################################################
-{
- "name" : "Stock report",
- "version" : "1.0",
- "author" : "Tiny",
- "website" : "http://tinyerp.com/",
- "depends" : ["stock", "product",],
- "category" : "Generic Modules/Inventory Control",
- "description" : """
- This module adds new reports based on the stock module.
+#
+##############################################################################
- It create a new menu to get products by production lots within
- the different locations:
- Inventory Control/Reporting/Traceability/Stock by production lots
- """,
- "init_xml" : [],
- "demo_xml" : [],
- "update_xml" : [
- "security/ir.model.access.csv",
- "report_stock_view.xml",
- ],
- "active": False,
- "installable": True
-}
+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
+
+
+
+
+
+
+
+
+