From 904de67cf5acf691f50995198dd255b7f13c0306 Mon Sep 17 00:00:00 2001 From: "HDA (OpenERP)" Date: Fri, 5 Feb 2010 20:39:27 +0530 Subject: [PATCH] rename report_analytic_planing to project_planing and removed dependancy of account from project and add analytic module + improvement in account_anglo_saxon bzr revid: hda@tinyerp.com-20100205150927-ztj0oaw3d3mv4f34 --- addons/account/__terp__.py | 2 +- .../process/customer_invoice_process.xml | 2 +- .../process/supplier_invoice_process.xml | 2 +- addons/account/project/project.py | 233 ---------------- addons/account/security/ir.model.access.csv | 4 +- addons/account_anglo_saxon/__terp__.py | 13 +- addons/account_anglo_saxon/invoice.py | 24 +- addons/account_anglo_saxon/stock.py | 12 + addons/analytic/__init__.py | 25 ++ addons/analytic/__terp__.py | 41 +++ addons/analytic/project.py | 262 ++++++++++++++++++ addons/analytic/security/ir.model.access.csv | 3 + addons/board_project/__terp__.py | 2 +- .../board_project_manager_view.xml | 2 +- addons/project/__terp__.py | 2 +- addons/project/process/task_process.xml | 20 -- addons/project/project.py | 5 +- addons/project/security/ir.model.access.csv | 4 +- .../__init__.py | 2 +- .../__terp__.py | 6 +- .../i18n/ar.po | 0 .../i18n/bg.po | 0 .../i18n/bs.po | 0 .../i18n/ca.po | 0 .../i18n/cs.po | 0 .../i18n/de.po | 0 .../i18n/el.po | 0 .../i18n/es.po | 0 .../i18n/es_AR.po | 0 .../i18n/et.po | 0 .../i18n/fr.po | 0 .../i18n/hr.po | 0 .../i18n/hu.po | 0 .../i18n/id.po | 0 .../i18n/it.po | 0 .../i18n/ko.po | 0 .../i18n/ko_KO.po | 0 .../i18n/lt.po | 0 .../i18n/nl.po | 0 .../i18n/nl_BE.po | 0 .../i18n/pl.po | 0 .../i18n/pt.po | 0 .../i18n/pt_BR.po | 0 .../i18n/report_analytic_planning.pot | 0 .../i18n/ro.po | 0 .../i18n/ru.po | 0 .../i18n/sl.po | 0 .../i18n/sq.po | 0 .../i18n/sv.po | 0 .../i18n/tlh.po | 0 .../i18n/tr.po | 0 .../i18n/uk.po | 0 .../i18n/vi.po | 0 .../i18n/zh_CN.po | 0 .../i18n/zh_TW.po | 0 .../project_planning.py} | 0 .../project_planning_demo.xml} | 0 .../project_planning_report.xml} | 2 +- .../project_planning_view.xml} | 0 .../report/__init__.py | 0 .../report/report_planning.rml | 0 .../report_account_analytic.planning.csv | 0 .../security/ir.model.access.csv | 0 .../process/project_timesheet_process.xml | 24 ++ 64 files changed, 412 insertions(+), 280 deletions(-) create mode 100644 addons/analytic/__init__.py create mode 100644 addons/analytic/__terp__.py create mode 100644 addons/analytic/project.py create mode 100644 addons/analytic/security/ir.model.access.csv rename addons/{report_analytic_planning => project_planning}/__init__.py (97%) rename addons/{report_analytic_planning => project_planning}/__terp__.py (94%) rename addons/{report_analytic_planning => project_planning}/i18n/ar.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/bg.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/bs.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/ca.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/cs.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/de.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/el.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/es.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/es_AR.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/et.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/fr.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/hr.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/hu.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/id.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/it.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/ko.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/ko_KO.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/lt.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/nl.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/nl_BE.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/pl.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/pt.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/pt_BR.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/report_analytic_planning.pot (100%) rename addons/{report_analytic_planning => project_planning}/i18n/ro.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/ru.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/sl.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/sq.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/sv.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/tlh.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/tr.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/uk.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/vi.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/zh_CN.po (100%) rename addons/{report_analytic_planning => project_planning}/i18n/zh_TW.po (100%) rename addons/{report_analytic_planning/report_analytic_planning.py => project_planning/project_planning.py} (100%) rename addons/{report_analytic_planning/report_analytic_planning_demo.xml => project_planning/project_planning_demo.xml} (100%) rename addons/{report_analytic_planning/report_analytic_planning_report.xml => project_planning/project_planning_report.xml} (74%) rename addons/{report_analytic_planning/report_analytic_planning_view.xml => project_planning/project_planning_view.xml} (100%) rename addons/{report_analytic_planning => project_planning}/report/__init__.py (100%) rename addons/{report_analytic_planning => project_planning}/report/report_planning.rml (100%) rename addons/{report_analytic_planning => project_planning}/report_account_analytic.planning.csv (100%) rename addons/{report_analytic_planning => project_planning}/security/ir.model.access.csv (100%) diff --git a/addons/account/__terp__.py b/addons/account/__terp__.py index c7f1dc9e149..921dc8c8cb9 100644 --- a/addons/account/__terp__.py +++ b/addons/account/__terp__.py @@ -23,7 +23,7 @@ { "name" : "Accounting and Financial Management", "version" : "1.1", - "depends" : ["product", "base", "process"], + "depends" : ["product", "analytic", "process"], "author" : "Tiny", "description": """Financial and accounting module that covers: General accounting diff --git a/addons/account/process/customer_invoice_process.xml b/addons/account/process/customer_invoice_process.xml index 117b7723c68..b9710276509 100755 --- a/addons/account/process/customer_invoice_process.xml +++ b/addons/account/process/customer_invoice_process.xml @@ -18,7 +18,7 @@ - + diff --git a/addons/account/process/supplier_invoice_process.xml b/addons/account/process/supplier_invoice_process.xml index 80d57de1f41..26e1e9862e7 100755 --- a/addons/account/process/supplier_invoice_process.xml +++ b/addons/account/process/supplier_invoice_process.xml @@ -18,7 +18,7 @@ - + diff --git a/addons/account/project/project.py b/addons/account/project/project.py index a381f587f8a..7ad98b04b32 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -29,239 +29,6 @@ from osv import osv # Object definition # -class account_analytic_account(osv.osv): - _name = 'account.analytic.account' - _description = 'Analytic Accounts' - - def _credit_calc(self, cr, uid, ids, name, arg, context={}): - - where_date = '' - if context.get('from_date',False): - where_date += " AND l.date >= '" + context['from_date'] + "'" - if context.get('to_date',False): - where_date += " AND l.date <= '" + context['to_date'] + "'" - - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount<0 and a.id =ANY(%s) GROUP BY a.id",(ids,)) - r = dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - - def _debit_calc(self, cr, uid, ids, name, arg, context={}): - - where_date = '' - if context.get('from_date',False): - where_date += " AND l.date >= '" + context['from_date'] + "'" - if context.get('to_date',False): - where_date += " AND l.date <= '" + context['to_date'] + "'" - - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount>0 and a.id =ANY(%s) GROUP BY a.id" ,(ids,)) - r= dict(cr.fetchall()) - for i in ids: - r.setdefault(i,0.0) - return r - - def _balance_calc(self, cr, uid, ids, name, arg, context={}): - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - for i in ids: - res.setdefault(i,0.0) - if not ids2: - return res - - where_date = '' - if context.get('from_date',False): - where_date += " AND l.date >= '" + context['from_date'] + "'" - if context.get('to_date',False): - where_date += " AND l.date <= '" + context['to_date'] + "'" - - cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE a.id =ANY(%s) GROUP BY a.id",(ids2,)) - - for account_id, sum in cr.fetchall(): - res[account_id] = sum - - cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id =ANY(%s)",(ids2,)) - - currency= dict(cr.fetchall()) - - res_currency= self.pool.get('res.currency') - for id in ids: - if id not in ids2: - continue - for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): - if child <> id: - res.setdefault(id, 0.0) - if currency[child]<>currency[id]: - res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) - else: - res[id] += res.get(child, 0.0) - - cur_obj = res_currency.browse(cr,uid,currency.values(),context) - cur_obj = dict([(o.id, o) for o in cur_obj]) - for id in ids: - if id in ids2: - res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) - - return dict([(i, res[i]) for i in ids ]) - - def _quantity_calc(self, cr, uid, ids, name, arg, context={}): - #XXX must convert into one uom - res = {} - ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) - acc_set = ",".join(map(str, ids2)) - - for i in ids: - res.setdefault(i,0.0) - - if not acc_set: - return res - - where_date = '' - if context.get('from_date',False): - where_date += " AND l.date >= '" + context['from_date'] + "'" - if context.get('to_date',False): - where_date += " AND l.date <= '" + context['to_date'] + "'" - - cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \ - FROM account_analytic_account a \ - LEFT JOIN account_analytic_line l ON (a.id = l.account_id ' + where_date + ') \ - WHERE a.id =ANY(%s) GROUP BY a.id',(ids2,)) - - for account_id, sum in cr.fetchall(): - res[account_id] = sum - - for id in ids: - if id not in ids2: - continue - for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): - if child <> id: - res.setdefault(id, 0.0) - res[id] += res.get(child, 0.0) - return dict([(i, res[i]) for i in ids]) - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','parent_id'], context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res - - def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res = self.name_get(cr, uid, ids) - return dict(res) - - def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): - result = {} - for rec in self.browse(cr, uid, ids, context): - result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False - return result - - _columns = { - 'name' : fields.char('Account Name', size=128, required=True), - 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'), - 'code' : fields.char('Account Code', size=24), -# 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the analytic account without removing it."), - 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type'), - 'description' : fields.text('Description'), - 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2), - 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'), - 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'), - 'balance' : fields.function(_balance_calc, method=True, type='float', string='Balance'), - 'debit' : fields.function(_debit_calc, method=True, type='float', string='Debit'), - 'credit' : fields.function(_credit_calc, method=True, type='float', string='Credit'), - 'quantity': fields.function(_quantity_calc, method=True, type='float', string='Quantity'), - 'quantity_max': fields.float('Maximum Quantity', help='Sets the higher limit of quantity of hours.'), - 'partner_id' : fields.many2one('res.partner', 'Associated Partner'), - 'contact_id' : fields.many2one('res.partner.address', 'Contact'), - 'user_id' : fields.many2one('res.users', 'Account Manager'), - 'date_start': fields.date('Date Start'), - 'date': fields.date('Date End'), - 'company_id': fields.many2one('res.company', 'Company', required=True), - 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'), - 'state': fields.selection([('draft','Draft'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Close'),('template', 'Template')], 'State', required=True,readonly=True, - help='* When an account is created its in \'Draft\' state.\ - \n* If any associated partner is there, it can be in \'Open\' state.\ - \n* If any pending balance is there it can be in \'Pending\'. \ - \n* And finally when all the transactions are over, it can be in \'Close\' state. \ - \n* The project can be in either if the states \'Template\' and \'Running\'.\n If it is template then we can make projects based on the template projects. If its in \'Running\' state it is a normal project.\ - \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'), - } - - def _default_company(self, cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id: - return user.company_id.id - return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] - _defaults = { -# 'active' : lambda *a : True, - 'type' : lambda *a : 'normal', - 'company_id': _default_company, - 'state' : lambda *a : 'open', - 'user_id' : lambda self,cr,uid,ctx : uid, - 'partner_id': lambda self,cr, uid, ctx: ctx.get('partner_id', False), - 'contact_id': lambda self,cr, uid, ctx: ctx.get('contact_id', False), - 'date_start': lambda *a: time.strftime('%Y-%m-%d') - } - - def check_recursion(self, cr, uid, ids, parent=None): - return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent) - - _order = 'parent_id desc,code' - _constraints = [ - (check_recursion, 'Error! You can not create recursive analytic accounts.', ['parent_id']) - ] - - def create(self, cr, uid, vals, context=None): - parent_id = vals.get('parent_id', 0) - if ('code' not in vals or not vals['code']) and not parent_id: - vals['code'] = self.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account') - return super(account_analytic_account, self).create(cr, uid, vals, context=context) - - def copy(self, cr, uid, id, default=None, context={}): - if not default: - default = {} - default['code'] = False - default['line_ids'] = [] - return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) - - - def on_change_parent(self, cr, uid, id, parent_id): - if not parent_id: - return {} - parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0] - childs = self.search(cr, uid, [('parent_id', '=', parent_id), ('active', 'in', [True, False])]) - numchild = len(childs) - if parent['partner_id']: - partner = parent['partner_id'][0] - else: - partner = False - res = {'value' : {'code' : '%s - %03d' % (parent['code'] or '', numchild + 1),}} - if partner: - res['value']['partner_id'] = partner - return res - - def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): - if not args: - args=[] - if not context: - context={} - account = self.search(cr, uid, [('code', '=', name)]+args, limit=limit, context=context) - if not account: - account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)]+args, limit=limit, context=context) - newacc = account - while newacc: - newacc = self.search(cr, uid, [('parent_id', 'in', newacc)]+args, limit=limit, context=context) - account+=newacc - return self.name_get(cr, uid, account, context=context) - -account_analytic_account() - - class account_analytic_journal(osv.osv): _name = 'account.analytic.journal' _columns = { diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index bb7ffbae946..46b27595402 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -34,7 +34,7 @@ "access_account_bank_statement_line","account.bank.statement.line","model_account_bank_statement_line","account.group_account_user",1,1,1,1 "access_account_analytic_line","account.analytic.line","model_account_analytic_line","account.group_account_user",1,1,1,1 "access_report_hr_timesheet_invoice_journal","report.hr.timesheet.invoice.journal","model_report_hr_timesheet_invoice_journal","account.group_account_manager",1,0,0,0 -"access_account_analytic_account","account.analytic.account","model_account_analytic_account","base.group_user",1,0,0,0 +"access_account_analytic_account","account.analytic.account","analytic.model_account_analytic_account","base.group_user",1,0,0,0 "access_account_analytic_journal","account.analytic.journal","model_account_analytic_journal","account.group_account_user",1,0,0,0 "access_account_invoice_uinvoice","account.invoice","model_account_invoice","account.group_account_invoice",1,1,1,1 "access_account_invoice_line_uinvoice","account.invoice.line","model_account_invoice_line","account.group_account_invoice",1,1,1,1 @@ -58,7 +58,7 @@ "access_account_tax_code_manager","account.tax.code","model_account_tax_code","account.group_account_manager",1,1,1,1 "access_account_tax_manager","account.tax","model_account_tax","account.group_account_manager",1,1,1,1 "access_account_invoice_group_invoice","account.invoice group invoice","model_account_invoice","account.group_account_invoice",1,1,1,1 -"access_account_analytic_account_manager","account.analytic.account","model_account_analytic_account","account.group_account_manager",1,1,1,1 +"access_account_analytic_account_manager","account.analytic.account","analytic.model_account_analytic_account","account.group_account_manager",1,1,1,1 "access_account_analytic_journal_manager","account.analytic.journal","model_account_analytic_journal","account.group_account_manager",1,1,1,1 "access_account_fiscalyear","account.fiscalyear","model_account_fiscalyear","account.group_account_manager",1,1,1,1 "access_account_fiscalyear_user","account.fiscalyear.user","model_account_fiscalyear","account.group_account_user",1,0,0,0 diff --git a/addons/account_anglo_saxon/__terp__.py b/addons/account_anglo_saxon/__terp__.py index 31818e213ef..a601d56b1d1 100644 --- a/addons/account_anglo_saxon/__terp__.py +++ b/addons/account_anglo_saxon/__terp__.py @@ -18,17 +18,18 @@ # ############################################################################## { - "name" : "Stock Account", - "version" : "1.1", - "author" : "Tiny", - "website" : "http://tinyerp.com", + "name" : "Stock Accounting for Anglo Saxon countries", + "version" : "1.2", + "author" : "Tiny, Veritos", + "website" : "http://tinyerp.com - http://veritos.nl", "description" : """This module will support the Anglo-Saxons accounting methodology by changing the accounting logic with stock transactions. The difference between the Anglo-Saxon accounting countries and the Rhine or also called Continental accounting countries is the moment of taking the Cost of Goods Sold versus Cost of Sales. Anglo-Saxons accounting does take the cost when sales invoice is created, Continental accounting will take the cost at he moment the goods are shipped. This module will add this functionality by using a interim account, to store the value of shipped goods and will contra book this interim account - when the invoice is created to transfer this amount to the debtor or creditor account.""", - "depends" : ["product", "account", "sale", "purchase"], + when the invoice is created to transfer this amount to the debtor or creditor account. + Secondly, price differences between actual purchase price and fixed product standard price are booked on a seperate account""", + "depends" : ["product", "account", "sale", "purchase", "stock"], "category" : "Generic Modules/Inventory Control", "init_xml" : [], "demo_xml" : [], diff --git a/addons/account_anglo_saxon/invoice.py b/addons/account_anglo_saxon/invoice.py index 3904cd6b01e..7c1ecbab758 100644 --- a/addons/account_anglo_saxon/invoice.py +++ b/addons/account_anglo_saxon/invoice.py @@ -1,7 +1,10 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). +# Copyright (C) +# 2004-2010 Tiny SPRL (). +# 2009-2010 Veritos (http://veritos.nl). +# All Rights Reserved # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -30,14 +33,20 @@ class account_invoice_line(osv.osv): for i_line in inv.invoice_line: if i_line.product_id: if inv.type == 'out_invoice': + # debit account dacc will be the output account + # first check the product, if empty check the category dacc = i_line.product_id.product_tmpl_id.property_stock_account_output and i_line.product_id.product_tmpl_id.property_stock_account_output.id if not dacc: dacc = i_line.product_id.categ_id.property_stock_account_output_categ and i_line.product_id.categ_id.property_stock_account_output_categ.id else: + # = out_refund + # debit account dacc will be the input account + # first check the product, if empty check the category dacc = i_line.product_id.product_tmpl_id.property_stock_account_input and i_line.product_id.product_tmpl_id.property_stock_account_input.id if not dacc: dacc = i_line.product_id.categ_id.property_stock_account_input_categ and i_line.product_id.categ_id.property_stock_account_input_categ.id - + # in both cases the credit account cacc will be the expense account + # first check the product, if empty check the category cacc = i_line.product_id.product_tmpl_id.property_account_expense and i_line.product_id.product_tmpl_id.property_account_expense.id if not cacc: cacc = i_line.product_id.categ_id.property_account_expense_categ and i_line.product_id.categ_id.property_account_expense_categ.id @@ -71,27 +80,36 @@ class account_invoice_line(osv.osv): for i_line in inv.invoice_line: if i_line.product_id: if i_line.product_id.product_tmpl_id.type != 'service': + # get the price difference account at the product acc = i_line.product_id.product_tmpl_id.property_account_creditor_price_difference and i_line.product_id.product_tmpl_id.property_account_creditor_price_difference.id if not acc: + # if not found on the product get the price difference account at the category acc = i_line.product_id.categ_id.property_account_creditor_price_difference_categ and i_line.product_id.categ_id.property_account_creditor_price_difference_categ.id a = None if inv.type == 'in_invoice': + # oa will be the stock input account + # first check the product, if empty check the category oa = i_line.product_id.product_tmpl_id.property_stock_account_input and i_line.product_id.product_tmpl_id.property_stock_account_input.id if not oa: oa = i_line.product_id.categ_id.property_stock_account_input_categ and i_line.product_id.categ_id.property_stock_account_input_categ.id else: + # = in_refund + # oa will be the stock output account + # first check the product, if empty check the category oa = i_line.product_id.product_tmpl_id.property_stock_account_output and i_line.product_id.product_tmpl_id.property_stock_account_output.id if not oa: oa = i_line.product_id.categ_id.property_stock_account_output_categ and i_line.product_id.categ_id.property_stock_account_output_categ.id if oa: + # get the fiscal position fpos = i_line.invoice_id.fiscal_position or False a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, oa) diff_res = [] + # calculate and write down the possible price difference between invoice price and product price for line in res: if a == line['account_id'] and i_line.product_id.id == line['product_id']: uom = i_line.product_id.uos_id or i_line.product_id.uom_id standard_price = self.pool.get('product.uom')._compute_price(cr, uid, uom.id, i_line.product_id.product_tmpl_id.standard_price, i_line.uos_id.id) - if standard_price != i_line.price_unit and line['price'] == i_line.price_unit and acc: + if standard_price != i_line.price_unit and line['price_unit'] == i_line.price_unit and acc: price_diff = i_line.price_unit - standard_price line.update({'price':standard_price * line['quantity']}) diff_res.append({ diff --git a/addons/account_anglo_saxon/stock.py b/addons/account_anglo_saxon/stock.py index ad091364fd8..ac0dbeadcec 100644 --- a/addons/account_anglo_saxon/stock.py +++ b/addons/account_anglo_saxon/stock.py @@ -43,6 +43,18 @@ class stock_picking(osv.osv): fpos = ol.invoice_id.fiscal_position or False a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, oa) self.pool.get('account.invoice.line').write(cr, uid, [ol.id], {'account_id': a}) + + elif type == 'in_invoice': + for inv in self.pool.get('account.invoice').browse(cr, uid, res.values(), context=context): + for ol in inv.invoice_line: + if ol.product_id: + oa = ol.product_id.product_tmpl_id.property_stock_account_input and ol.product_id.product_tmpl_id.property_stock_account_input.id + if not oa: + oa = ol.product_id.categ_id.property_stock_account_input_categ and ol.product_id.categ_id.property_stock_account_input_categ.id + if oa: + fpos = ol.invoice_id.fiscal_position or False + a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, oa) + self.pool.get('account.invoice.line').write(cr, uid, [ol.id], {'account_id': a}) return res stock_picking() diff --git a/addons/analytic/__init__.py b/addons/analytic/__init__.py new file mode 100644 index 00000000000..dc48b6f4001 --- /dev/null +++ b/addons/analytic/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import project + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + diff --git a/addons/analytic/__terp__.py b/addons/analytic/__terp__.py new file mode 100644 index 00000000000..6ccdf3564d8 --- /dev/null +++ b/addons/analytic/__terp__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +{ + "name" : "Analytic Account", + "version": "1.1", + "author" : "Tiny", + "website" : "http://www.openerp.com", + "category" : "Generic Modules/Projects & Services", + "depends" : ["base"], + "description": """Module for defining analytic accounting object. + """, + "init_xml" : [], + "update_xml": ["security/ir.model.access.csv", + ], + 'demo_xml': [ + ], + 'installable': True, + 'active': False, + 'certificate': '', +} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/analytic/project.py b/addons/analytic/project.py new file mode 100644 index 00000000000..7e0ab40c63a --- /dev/null +++ b/addons/analytic/project.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import time +import operator + +from osv import fields +from osv import osv + +# +# Object definition +# + +class account_analytic_account(osv.osv): + _name = 'account.analytic.account' + _description = 'Analytic Accounts' + + def _credit_calc(self, cr, uid, ids, name, arg, context={}): + + where_date = '' + if context.get('from_date',False): + where_date += " AND l.date >= '" + context['from_date'] + "'" + if context.get('to_date',False): + where_date += " AND l.date <= '" + context['to_date'] + "'" + + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount<0 and a.id =ANY(%s) GROUP BY a.id",(ids,)) + r = dict(cr.fetchall()) + for i in ids: + r.setdefault(i,0.0) + return r + + def _debit_calc(self, cr, uid, ids, name, arg, context={}): + + where_date = '' + if context.get('from_date',False): + where_date += " AND l.date >= '" + context['from_date'] + "'" + if context.get('to_date',False): + where_date += " AND l.date <= '" + context['to_date'] + "'" + + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE l.amount>0 and a.id =ANY(%s) GROUP BY a.id" ,(ids,)) + r= dict(cr.fetchall()) + for i in ids: + r.setdefault(i,0.0) + return r + + def _balance_calc(self, cr, uid, ids, name, arg, context={}): + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + for i in ids: + res.setdefault(i,0.0) + if not ids2: + return res + + where_date = '' + if context.get('from_date',False): + where_date += " AND l.date >= '" + context['from_date'] + "'" + if context.get('to_date',False): + where_date += " AND l.date <= '" + context['to_date'] + "'" + + cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id "+where_date+") WHERE a.id =ANY(%s) GROUP BY a.id",(ids2,)) + + for account_id, sum in cr.fetchall(): + res[account_id] = sum + + cr.execute("SELECT a.id, r.currency_id FROM account_analytic_account a INNER JOIN res_company r ON (a.company_id = r.id) where a.id =ANY(%s)",(ids2,)) + + currency= dict(cr.fetchall()) + + res_currency= self.pool.get('res.currency') + for id in ids: + if id not in ids2: + continue + for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): + if child <> id: + res.setdefault(id, 0.0) + if currency[child]<>currency[id]: + res[id] += res_currency.compute(cr, uid, currency[child], currency[id], res.get(child, 0.0), context=context) + else: + res[id] += res.get(child, 0.0) + + cur_obj = res_currency.browse(cr,uid,currency.values(),context) + cur_obj = dict([(o.id, o) for o in cur_obj]) + for id in ids: + if id in ids2: + res[id] = res_currency.round(cr,uid,cur_obj[currency[id]],res.get(id,0.0)) + + return dict([(i, res[i]) for i in ids ]) + + def _quantity_calc(self, cr, uid, ids, name, arg, context={}): + #XXX must convert into one uom + res = {} + ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)]) + acc_set = ",".join(map(str, ids2)) + + for i in ids: + res.setdefault(i,0.0) + + if not acc_set: + return res + + where_date = '' + if context.get('from_date',False): + where_date += " AND l.date >= '" + context['from_date'] + "'" + if context.get('to_date',False): + where_date += " AND l.date <= '" + context['to_date'] + "'" + + cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \ + FROM account_analytic_account a \ + LEFT JOIN account_analytic_line l ON (a.id = l.account_id ' + where_date + ') \ + WHERE a.id =ANY(%s) GROUP BY a.id',(ids2,)) + + for account_id, sum in cr.fetchall(): + res[account_id] = sum + + for id in ids: + if id not in ids2: + continue + for child in self.search(cr, uid, [('parent_id', 'child_of', [id])]): + if child <> id: + res.setdefault(id, 0.0) + res[id] += res.get(child, 0.0) + return dict([(i, res[i]) for i in ids]) + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','parent_id'], context) + res = [] + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1]+' / '+name + res.append((record['id'], name)) + return res + + def _complete_name_calc(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res = self.name_get(cr, uid, ids) + return dict(res) + + def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}): + result = {} + for rec in self.browse(cr, uid, ids, context): + result[rec.id] = (rec.company_id.currency_id.id,rec.company_id.currency_id.code) or False + return result + + _columns = { + 'name' : fields.char('Account Name', size=128, required=True), + 'complete_name': fields.function(_complete_name_calc, method=True, type='char', string='Full Account Name'), + 'code' : fields.char('Account Code', size=24), +# 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the analytic account without removing it."), + 'type': fields.selection([('view','View'), ('normal','Normal')], 'Account Type'), + 'description' : fields.text('Description'), + 'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2), + 'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'), + 'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'), + 'balance' : fields.function(_balance_calc, method=True, type='float', string='Balance'), + 'debit' : fields.function(_debit_calc, method=True, type='float', string='Debit'), + 'credit' : fields.function(_credit_calc, method=True, type='float', string='Credit'), + 'quantity': fields.function(_quantity_calc, method=True, type='float', string='Quantity'), + 'quantity_max': fields.float('Maximum Quantity', help='Sets the higher limit of quantity of hours.'), + 'partner_id' : fields.many2one('res.partner', 'Associated Partner'), + 'contact_id' : fields.many2one('res.partner.address', 'Contact'), + 'user_id' : fields.many2one('res.users', 'Account Manager'), + 'date_start': fields.date('Date Start'), + 'date': fields.date('Date End'), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'), + 'state': fields.selection([('draft','Draft'),('open','Open'), ('pending','Pending'),('cancelled', 'Cancelled'),('close','Close'),('template', 'Template')], 'State', required=True,readonly=True, + help='* When an account is created its in \'Draft\' state.\ + \n* If any associated partner is there, it can be in \'Open\' state.\ + \n* If any pending balance is there it can be in \'Pending\'. \ + \n* And finally when all the transactions are over, it can be in \'Close\' state. \ + \n* The project can be in either if the states \'Template\' and \'Running\'.\n If it is template then we can make projects based on the template projects. If its in \'Running\' state it is a normal project.\ + \n If it is to be reviewed then the state is \'Pending\'.\n When the project is completed the state is set to \'Done\'.'), + } + + def _default_company(self, cr, uid, context={}): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if user.company_id: + return user.company_id.id + return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + _defaults = { +# 'active' : lambda *a : True, + 'type' : lambda *a : 'normal', + 'company_id': _default_company, + 'state' : lambda *a : 'open', + 'user_id' : lambda self,cr,uid,ctx : uid, + 'partner_id': lambda self,cr, uid, ctx: ctx.get('partner_id', False), + 'contact_id': lambda self,cr, uid, ctx: ctx.get('contact_id', False), + 'date_start': lambda *a: time.strftime('%Y-%m-%d') + } + + def check_recursion(self, cr, uid, ids, parent=None): + return super(account_analytic_account, self).check_recursion(cr, uid, ids, parent=parent) + + _order = 'parent_id desc,code' + _constraints = [ + (check_recursion, 'Error! You can not create recursive analytic accounts.', ['parent_id']) + ] + + def create(self, cr, uid, vals, context=None): + parent_id = vals.get('parent_id', 0) + if ('code' not in vals or not vals['code']) and not parent_id: + vals['code'] = self.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account') + return super(account_analytic_account, self).create(cr, uid, vals, context=context) + + def copy(self, cr, uid, id, default=None, context={}): + if not default: + default = {} + default['code'] = False + default['line_ids'] = [] + return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) + + + def on_change_parent(self, cr, uid, id, parent_id): + if not parent_id: + return {} + parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0] + childs = self.search(cr, uid, [('parent_id', '=', parent_id), ('active', 'in', [True, False])]) + numchild = len(childs) + if parent['partner_id']: + partner = parent['partner_id'][0] + else: + partner = False + res = {'value' : {'code' : '%s - %03d' % (parent['code'] or '', numchild + 1),}} + if partner: + res['value']['partner_id'] = partner + return res + + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100): + if not args: + args=[] + if not context: + context={} + account = self.search(cr, uid, [('code', '=', name)]+args, limit=limit, context=context) + if not account: + account = self.search(cr, uid, [('name', 'ilike', '%%%s%%' % name)]+args, limit=limit, context=context) + newacc = account + while newacc: + newacc = self.search(cr, uid, [('parent_id', 'in', newacc)]+args, limit=limit, context=context) + account+=newacc + return self.name_get(cr, uid, account, context=context) + +account_analytic_account() diff --git a/addons/analytic/security/ir.model.access.csv b/addons/analytic/security/ir.model.access.csv new file mode 100644 index 00000000000..5af39c15bed --- /dev/null +++ b/addons/analytic/security/ir.model.access.csv @@ -0,0 +1,3 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_account_analytic_account_manager","account.analytic.account","model_account_analytic_account","project.group_project_user",1,1,1,1 +"access_account_analytic_account","account.analytic.account","model_account_analytic_account","project.group_project_user",1,0,0,0 \ No newline at end of file diff --git a/addons/board_project/__terp__.py b/addons/board_project/__terp__.py index 558b633d36e..c2667fd113a 100644 --- a/addons/board_project/__terp__.py +++ b/addons/board_project/__terp__.py @@ -36,7 +36,7 @@ This module implements a dashboard for project member that includes: 'project', 'report_timesheet', 'board', - 'report_analytic_planning', + 'project_planning', 'report_analytic_line', 'report_task', 'hr_timesheet_sheet', diff --git a/addons/board_project/board_project_manager_view.xml b/addons/board_project/board_project_manager_view.xml index 3baca91bce6..893a3a7df68 100644 --- a/addons/board_project/board_project_manager_view.xml +++ b/addons/board_project/board_project_manager_view.xml @@ -64,7 +64,7 @@