diff --git a/addons/account/project/project.py b/addons/account/project/project.py index f6c58ee3ea2..e39e5b00d77 100644 --- a/addons/account/project/project.py +++ b/addons/account/project/project.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -41,7 +41,7 @@ class account_analytic_account(osv.osv): 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 %s) WHERE l.amount<0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r = dict(cr.fetchall()) for i in ids: @@ -51,13 +51,13 @@ class account_analytic_account(osv.osv): def _debit_calc(self, cr, uid, ids, name, arg, context={}): acc_set = ",".join(map(str, ids)) - + 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 %s) WHERE l.amount>0 and a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) r= dict(cr.fetchall()) for i in ids: @@ -68,21 +68,21 @@ class account_analytic_account(osv.osv): 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.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id %s) WHERE a.id IN (%s) GROUP BY a.id" % (where_date,acc_set)) - + for account_id, sum in cr.fetchall(): res[account_id] = sum @@ -115,19 +115,19 @@ class account_analytic_account(osv.osv): 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 + ') \ @@ -168,10 +168,10 @@ class account_analytic_account(osv.osv): return result _columns = { - 'name' : fields.char('Account Name', size=64, required=True), + '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."), +# '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), @@ -189,11 +189,13 @@ class account_analytic_account(osv.osv): 'date': fields.date('Date End'), 'company_id': fields.many2one('res.company', 'Company', required=True), 'company_currency_id': fields.function(_get_company_currency, method=True, type='many2one', relation='res.currency', string='Currency'), - 'state': fields.selection([('draft','Draft'), ('open','Open'), ('pending','Pending'), ('close','Close'),], 'State', required=True, + '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* 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={}): @@ -202,13 +204,14 @@ class account_analytic_account(osv.osv): return user.company_id.id return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] _defaults = { - 'active' : lambda *a : True, +# 'active' : lambda *a : True, 'type' : lambda *a : 'normal', 'company_id': _default_company, - 'state' : lambda *a : 'draft', + '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): diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index 7ffb93fb73b..1123b122eaf 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -76,7 +76,7 @@ - + diff --git a/addons/hr/__terp__.py b/addons/hr/__terp__.py index 6f405e2a03d..f78bdc4ef18 100644 --- a/addons/hr/__terp__.py +++ b/addons/hr/__terp__.py @@ -36,7 +36,7 @@ """, 'author': 'Tiny', 'website': 'http://www.openerp.com', - 'depends': ['base', 'process'], + 'depends': ['base', 'process', 'resource'], 'init_xml': [], 'update_xml': [ 'security/hr_security.xml', @@ -46,7 +46,8 @@ 'process/hr_process.xml', 'hr_installer.xml' ], - 'demo_xml': ['hr_demo.xml', 'hr_department_demo.xml'], + 'demo_xml': ['hr_demo.xml', + 'hr_department_demo.xml'], 'installable': True, 'active': False, 'certificate': '0086710558965', diff --git a/addons/hr/hr.py b/addons/hr/hr.py old mode 100644 new mode 100755 index 0d4d5f5bad0..a49bad405dc --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -26,78 +26,16 @@ import math from osv import fields, osv from tools.translate import _ -class hr_timesheet_group(osv.osv): - _name = "hr.timesheet.group" - _description = "Working Time" - _columns = { - 'name' : fields.char("Group name", size=64, required=True), - 'timesheet_id' : fields.one2many('hr.timesheet', 'tgroup_id', 'Working Time'), - 'manager' : fields.many2one('res.users', 'Workgroup manager'), - } - def interval_min_get(self, cr, uid, id, dt_from, hours): - if not id: - return [(dt_from-DateTime.RelativeDateTime(hours=int(hours)*3), dt_from)] - todo = hours - cycle = 0 - result = [] - maxrecur = 100 - current_hour = dt_from.hour - while (todo>0) and maxrecur: - cr.execute("select hour_from,hour_to from hr_timesheet where dayofweek='%s' and tgroup_id=%s order by hour_from desc", (dt_from.day_of_week,id)) - for (hour_from,hour_to) in cr.fetchall(): - if (hour_from0): - m = min(hour_to, current_hour) - if (m-hour_from)>todo: - hour_from = m-todo - d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_from)),int((hour_from%1) * 60)) - d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - result.append((d1, d2)) - current_hour = hour_from - todo -= (m-hour_from) - dt_from -= DateTime.RelativeDateTime(days=1) - current_hour = 24 - maxrecur -= 1 - result.reverse() - return result - - def interval_get(self, cr, uid, id, dt_from, hours, byday=True): - if not id: - return [(dt_from,dt_from+DateTime.RelativeDateTime(hours=int(hours)*3))] - todo = hours - cycle = 0 - result = [] - maxrecur = 100 - current_hour = dt_from.hour - while (todo>0) and maxrecur: - cr.execute("select hour_from,hour_to from hr_timesheet where dayofweek='%s' and tgroup_id=%s order by hour_from", (dt_from.day_of_week,id)) - for (hour_from,hour_to) in cr.fetchall(): - if (hour_to>current_hour) and (todo>0): - m = max(hour_from, current_hour) - if (hour_to-m)>todo: - hour_to = m+todo - d1 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(m)),int((m%1) * 60)) - d2 = DateTime.DateTime(dt_from.year,dt_from.month,dt_from.day,int(math.floor(hour_to)),int((hour_to%1) * 60)) - result.append((d1, d2)) - current_hour = hour_to - todo -= (hour_to - m) - dt_from += DateTime.RelativeDateTime(days=1) - current_hour = 0 - maxrecur -= 1 - return result - -hr_timesheet_group() - - class hr_employee_category(osv.osv): _name = "hr.employee.category" _description = "Employee Category" - + _columns = { 'name' : fields.char("Category", size=64, required=True), 'parent_id': fields.many2one('hr.employee.category', 'Parent Category', select=True), 'child_ids': fields.one2many('hr.employee.category', 'parent_id', 'Child Categories') } - + def _check_recursion(self, cr, uid, ids): level = 100 while len(ids): @@ -107,23 +45,18 @@ class hr_employee_category(osv.osv): return False level -= 1 return True - + _constraints = [ (_check_recursion, 'Error ! You cannot create recursive Categories.', ['parent_id']) ] - + hr_employee_category() class hr_employee(osv.osv): _name = "hr.employee" _description = "Employee" - + _inherits = {'resource.resource':"resource_id"} _columns = { - 'name' : fields.char("Employee's Name", size=128, required=True), - 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the employee record without removing it."), - 'company_id': fields.many2one('res.company', 'Company'), - 'user_id' : fields.many2one('res.users', 'Related User', help='Related user name for an employee to manage his access rights.'), - 'country_id' : fields.many2one('res.country', 'Nationality'), 'birthday' : fields.date("Birthday"), 'ssnid': fields.char('SSN No', size=32, help='Social Security Number'), @@ -142,11 +75,12 @@ class hr_employee(osv.osv): 'parent_id': fields.many2one('hr.employee', 'Manager', select=True), 'category_id' : fields.many2one('hr.employee.category', 'Category'), 'child_ids': fields.one2many('hr.employee', 'parent_id','Subordinates'), + 'resource_id': fields.many2one('resource.resource','Resource',ondelete='cascade'), } _defaults = { 'active' : lambda *a: True, } - + def _check_recursion(self, cr, uid, ids): level = 100 while len(ids): @@ -156,26 +90,10 @@ class hr_employee(osv.osv): return False level -= 1 return True - + _constraints = [ (_check_recursion, 'Error ! You cannot create recursive Hierarchy of Employees.', ['parent_id']) ] - + hr_employee() - -class hr_timesheet(osv.osv): - _name = "hr.timesheet" - _description = "Timesheet Line" - _columns = { - 'name' : fields.char("Name", size=64, required=True), - 'dayofweek': fields.selection([('0','Monday'),('1','Tuesday'),('2','Wednesday'),('3','Thursday'),('4','Friday'),('5','Saturday'),('6','Sunday')], 'Day of week'), - 'date_from' : fields.date('Starting date'), - 'hour_from' : fields.float('Work from', size=8, required=True), - 'hour_to' : fields.float("Work to", size=8, required=True), - 'tgroup_id' : fields.many2one("hr.timesheet.group", "Employee's timesheet group", select=True), - } - _order = 'dayofweek, hour_from' -hr_timesheet() - - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml index 3b1a3edfed7..fc01109d86c 100644 --- a/addons/hr/hr_demo.xml +++ b/addons/hr/hr_demo.xml @@ -1,90 +1,90 @@ - + - - + + 38 Hours/Week - + Fabien Pinckaers - - + + Monday morning 0 08 12 - + - + Monday evening 0 13 18 - + - + Tuesday morning 1 08 12 - + - + Tuesday evening 1 13 18 - + - + Wednesday morning 2 08 12 - + - + Wednesday evening 2 13 18 - + - + Thursday morning 3 08 12 - + - + Thursday evening 13 18 3 - + - + Friday morning 4 08 12 - + - + Friday evening 4 13 18 - + - + diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index de573597095..3789fe184eb 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -12,8 +12,12 @@ id="menu_hr_configuration" name="Configuration" parent="hr.menu_hr_root" - sequence="50" groups="group_hr_manager"/> - + sequence="50" /> + - - hr.timesheet.group.form - hr.timesheet.group - form - -
- - - - - - -
- - Working Schedules - hr.timesheet.group - form - - - - - - - hr.timesheet.tree - hr.timesheet - tree - - - - - - - - - - - hr.timesheet.form - hr.timesheet - form - -
- - - - - - - -
- = date_trunc('day',a.name) AND s.date_from <= a.name) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 8d2a65bb725..6e3c8c39004 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -46,7 +46,7 @@
- + hr.timesheet.sheet.form hr_timesheet_sheet.sheet @@ -266,7 +266,7 @@ - + diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 4d155ea7b8e..df41baf7bc2 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -40,21 +40,14 @@ from tools.translate import _ class mrp_workcenter(osv.osv): _name = 'mrp.workcenter' _description = 'Work Center' + _inherits = {'resource.resource':"resource_id"} _columns = { - 'name': fields.char('Work Center Name', size=64, required=True), - 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the work center without removing it."), - 'type': fields.selection([('machine','Machine'),('hr','Human Resource'),('tool','Tool')], 'Type', required=True), - 'code': fields.char('Code', size=16), - 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working Time', help="The normal working time of the workcenter."), +# 'name': fields.char('Work Center Name', size=64, required=True), 'note': fields.text('Description', help="Description of the workcenter. Explain here what's a cycle according to this workcenter."), - 'capacity_per_cycle': fields.float('Capacity per Cycle', help="Number of operations this workcenter can do in parallel. If this workcenter represents a team of 5 workers, the capacity per cycle is 5."), - 'time_cycle': fields.float('Time for 1 cycle (hour)', help="Time in hours for doing one cycle."), 'time_start': fields.float('Time before prod.', help="Time in hours for the setup."), 'time_stop': fields.float('Time after prod.', help="Time in hours for the cleaning."), - 'time_efficiency': fields.float('Efficiency factor', help="Factor to adjust the work center cycle and before and after production durations"), - 'costs_hour': fields.float('Cost per hour'), 'costs_hour_account_id': fields.many2one('account.analytic.account', 'Hour Account', domain=[('type','<>','view')], help="Complete this only if you want automatic analytic accounting entries on production orders."), @@ -63,15 +56,12 @@ class mrp_workcenter(osv.osv): help="Complete this only if you want automatic analytic accounting entries on production orders."), 'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'), 'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('type','<>','view')]), - 'company_id': fields.many2one('res.company','Company',required=True), +# 'company_id': fields.many2one('res.company','Company',required=True), + 'resource_id': fields.many2one('resource.resource','Resource',ondelete='cascade'), } _defaults = { - 'active': lambda *a: 1, - 'type': lambda *a: 'machine', - 'time_efficiency': lambda *a: 1.0, 'capacity_per_cycle': lambda *a: 1.0, - 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.workcenter', context=c) - } + } mrp_workcenter() @@ -130,7 +120,7 @@ class mrp_routing_workcenter(osv.osv): 'cycle_nbr': fields.float('Number of Cycles', required=True, help="Time in hours for doing one cycle."), 'hour_nbr': fields.float('Number of Hours', required=True, help="Cost per hour"), - 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', + 'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade', help="Routing indicates all the workcenters used, for how long and/or cycles." \ "If Routing is indicated then,the third tab of a production order (workcenters) will be automatically pre-completed."), 'note': fields.text('Description') @@ -175,7 +165,7 @@ class mrp_bom(osv.osv): 'name': fields.char('Name', size=64, required=True), 'code': fields.char('Code', size=16), 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the bills of material without removing it."), - 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', required=True, + 'type': fields.selection([('normal','Normal BoM'),('phantom','Sets / Phantom')], 'BoM Type', required=True, help= "If a sub-product is used in several products, it can be useful to create its own BoM."\ "Though if you don't want separated production orders for this sub-product, select Set/Phantom as BoM type."\ "If a Phantom BoM is used for a root product, it will be sold and shipped as a set of components, instead of being produced."), @@ -833,7 +823,7 @@ class mrp_procurement(osv.osv): help='When a procurement is created the state is set to \'Draft\'.\n If the procurement is confirmed, the state is set to \'Confirmed\'.\ \nAfter confirming the state is set to \'Running\'.\n If any exception arises in the order then the state is set to \'Exception\'.\n Once the exception is removed the state becomes \'Ready\'.\n It is in \'Waiting\'. state when the procurement is waiting for another one to finish.'), 'note' : fields.text('Note'), - 'company_id': fields.many2one('res.company','Company',required=True), + 'company_id': fields.many2one('res.company','Company',required=True), } _defaults = { 'state': lambda *a: 'draft', @@ -1205,7 +1195,7 @@ class stock_warehouse_orderpoint(osv.osv): 'qty_multiple': fields.integer('Qty Multiple', required=True, help="The requisition quantity will by rounded up to this multiple."), 'procurement_id': fields.many2one('mrp.procurement', 'Purchase Order'), - 'company_id': fields.many2one('res.company','Company',required=True), + 'company_id': fields.many2one('res.company','Company',required=True), } _defaults = { 'active': lambda *a: 1, diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index fadc0443e0d..40145749a22 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -120,8 +120,8 @@ - - + + diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv index 3767ed3935f..f3cae96266c 100644 --- a/addons/mrp/security/ir.model.access.csv +++ b/addons/mrp/security/ir.model.access.csv @@ -27,6 +27,6 @@ "access_account_account","account.account mrp_worker","account.model_account_account","mrp.group_mrp_user",1,0,0,0 "access_purchase_order_mrp_worker","purchase.order mrp_worker","purchase.model_purchase_order","mrp.group_mrp_user",1,0,0,0 "access_purchase_order_line_mrp_worker","purchase.order.line mrp_worker","purchase.model_purchase_order_line","mrp.group_mrp_user",1,0,0,0 -"access_hr_timesheet_group_mrp_worker","hr.timesheet.group mrp_worker","hr.model_hr_timesheet_group","mrp.group_mrp_user",1,0,0,0 +"access_hr_timesheet_group_mrp_worker","resource.calendar mrp_worker","resource.model_resource_calendar","mrp.group_mrp_user",1,0,0,0 "access_mrp_procurement_stock_worker","mrp.procurement stock_worker","model_mrp_procurement","stock.group_stock_user",1,1,1,1 "access_mrp_production_stock_worker","mrp.production stock_worker","model_mrp_production","stock.group_stock_user",1,0,0,0 diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index bf76f834daf..1848470ad15 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -60,7 +60,7 @@ class mrp_production_workcenter_line(osv.osv): res[op.id]= False if op.date_planned: d = DateTime.strptime(op.date_planned,'%Y-%m-%d %H:%M:%S') - i = self.pool.get('hr.timesheet.group').interval_get(cr, uid, op.workcenter_id.timesheet_id.id or False, d, op.hour or 0.0) + i = self.pool.get('resource.calendar').interval_get(cr, uid, op.workcenter_id.calendar_id.id or False, d, op.hour or 0.0) if i: res[op.id] = i[-1][1].strftime('%Y-%m-%d %H:%M:%S') else: @@ -204,10 +204,10 @@ class mrp_production(osv.osv): self.pool.get('mrp.production.workcenter.line').write(cr, uid, [wc.id], { 'date_planned':dt.strftime('%Y-%m-%d %H:%M:%S') }, context=context, update=False) - i = self.pool.get('hr.timesheet.group').interval_get( + i = self.pool.get('resource.calendar').interval_get( cr, uid, - wc.workcenter_id.timesheet_id and wc.workcenter_id.timesheet_id.id or False, + wc.workcenter_id.calendar_id and wc.workcenter_id.calendar_id.id or False, dt, wc.hour or 0.0 ) @@ -235,10 +235,10 @@ class mrp_production(osv.osv): if l.production_id and (l.production_id.date_finnished>dt): if l.production_id.state not in ('done','cancel'): for wc in l.production_id.workcenter_lines: - i = self.pool.get('hr.timesheet.group').interval_min_get( + i = self.pool.get('resource.calendar').interval_min_get( cr, uid, - wc.workcenter_id.timesheet_id.id or False, + wc.workcenter_id.calendar_id.id or False, dt, wc.hour or 0.0 ) dt = i[0][0] diff --git a/addons/product/product.py b/addons/product/product.py index 08729a44d7d..b2e70cab81d 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -245,7 +245,7 @@ class product_template(osv.osv): 'description': fields.text('Description',translate=True), 'description_purchase': fields.text('Purchase Description',translate=True), 'description_sale': fields.text('Sale Description',translate=True), - 'type': fields.selection([('product','Stockable Product'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Will change the way requisitions are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."), + 'type': fields.selection([('product','Stockable Product'),('rima','Rima'),('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Will change the way requisitions are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system."), 'supply_method': fields.selection([('produce','Produce'),('buy','Buy')], 'Supply method', required=True, help="Produce will generate production order or tasks, according to the product type. Purchase will trigger purchase orders when requested."), 'sale_delay': fields.float('Customer Lead Time', help="This is the average time between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."), 'produce_delay': fields.float('Manufacturing Lead Time', help="Average time to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different lead times will be summed for all levels and purchase orders."), diff --git a/addons/project/__terp__.py b/addons/project/__terp__.py index 40d41a8fdd1..02910352a9a 100644 --- a/addons/project/__terp__.py +++ b/addons/project/__terp__.py @@ -41,7 +41,9 @@ works done on tasks, eso. It is able to render planning, order tasks, eso. "process/task_process.xml", "project_installer.xml", ], - 'demo_xml': ['project_demo.xml'], + 'demo_xml': [#'project_demo.xml' + + ], 'installable': True, 'active': False, 'certificate': '0075116868317', diff --git a/addons/project/project.py b/addons/project/project.py index 886a32e4890..051958557ac 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -20,11 +20,10 @@ ############################################################################## from lxml import etree -from mx import DateTime -from mx.DateTime import now +import mx.DateTime +import datetime import time from tools.translate import _ - from osv import fields, osv from tools.translate import _ @@ -46,7 +45,7 @@ project_task_type() class project(osv.osv): _name = "project.project" _description = "Project" - + _inherits = {'account.analytic.account':"category_id"} def _complete_name(self, cr, uid, ids, name, args, context): res = {} for m in self.browse(cr, uid, ids, context=context): @@ -88,7 +87,7 @@ class project(osv.osv): tocompute = [project] while tocompute: p = tocompute.pop() - tocompute += p.child_id + tocompute += p.child_ids for i in range(3): s[i] += progress.get(p.id, (0.0,0.0,0.0))[i] res[project.id] = { @@ -105,52 +104,60 @@ class project(osv.osv): raise osv.except_osv(_('Operation Not Permitted !'), _('You can not delete a project with tasks. I suggest you to deactivate it.')) return super(project, self).unlink(cr, uid, ids, *args, **kwargs) _columns = { - 'name': fields.char("Project Name", size=128, required=True), - 'complete_name': fields.function(_complete_name, method=True, string="Project Name", type='char', size=128), +# 'name': fields.char("Project Name", size=128, required=True), +# 'complete_name': fields.function(_complete_name, method=True, string="Project Name", type='char', size=128), 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the project without removing it."), 'category_id': fields.many2one('account.analytic.account','Analytic Account', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc."), 'priority': fields.integer('Sequence'), - 'manager': fields.many2one('res.users', 'Project Manager'), +# 'manager': fields.many2one('res.users', 'Project Manager'), 'warn_manager': fields.boolean('Warn Manager', help="If you check this field, the project manager will receive a request each time a task is completed by his team."), - 'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members', help="Project's member. Not used in any computation, just for information purpose."), + 'resource_ids': fields.many2many('resource.resource', 'project_resource_rel', 'project_id', 'resource_id', 'Project Members', help="Project's member. Not used in any computation, just for information purpose."), 'tasks': fields.one2many('project.task', 'project_id', "Project tasks"), - 'parent_id': fields.many2one('project.project', 'Parent Project',\ - help="If you have [?] in the name, it means there are no analytic account linked to project."), - 'child_id': fields.one2many('project.project', 'parent_id', 'Subproject'), +# 'parent_id': fields.many2one('project.project', 'Parent Project',\ +# help="If you have [?] in the name, it means there are no analytic account linked to project."), +# 'child_id': fields.one2many('project.project', 'parent_id', 'Subproject'), 'planned_hours': fields.function(_progress_rate, multi="progress", method=True, string='Planned Time', help="Sum of planned hours of all tasks related to this project."), 'effective_hours': fields.function(_progress_rate, multi="progress", method=True, string='Time Spent', help="Sum of spent hours of all tasks related to this project."), 'total_hours': fields.function(_progress_rate, multi="progress", method=True, string='Total Time', help="Sum of total hours of all tasks related to this project."), 'progress_rate': fields.function(_progress_rate, multi="progress", method=True, string='Progress', type='float', help="Percent of tasks closed according to the total of tasks todo."), - 'date_start': fields.date('Starting Date'), - 'date_end': fields.date('Expected End'), - 'partner_id': fields.many2one('res.partner', 'Partner'), - 'contact_id': fields.many2one('res.partner.address', 'Contact'), +# 'partner_id': fields.many2one('res.partner', 'Partner'), +# 'contact_id': fields.many2one('res.partner.address', 'Contact'), 'warn_customer': fields.boolean('Warn Partner', help="If you check this, the user will have a popup when closing a task that propose a message to send by email to the customer."), 'warn_header': fields.text('Mail Header', help="Header added at the beginning of the email for the warning message sent to the customer when a task is closed."), 'warn_footer': fields.text('Mail Footer', help="Footer added at the beginning of the email for the warning message sent to the customer when a task is closed."), - 'notes': fields.text('Notes', help="Internal description of the project."), - 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"), - 'state': fields.selection([('template', 'Template'), ('open', 'Running'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', required=True, readonly=True, - help='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\'.'), - 'company_id': fields.many2one('res.company', 'Company'), +# 'notes': fields.text('Notes', help="Internal description of the project."), + 'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"), +# 'state': fields.selection([('template', 'Template'), ('open', 'Running'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', required=True, readonly=True, +# help='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\'.'), +# 'company_id': fields.many2one('res.company', 'Company'), +# 'timesheet_id': fields.many2one('hr.timesheet.group', 'Working Time', help="Timetable working hours to adjust the gantt diagram report"), 'allowed_task_type': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Allowed Task Types'), } _defaults = { 'active': lambda *a: True, - 'manager': lambda object,cr,uid,context: uid, +# 'manager': lambda object,cr,uid,context: uid, 'priority': lambda *a: 1, - 'date_start': lambda *a: time.strftime('%Y-%m-%d'), - 'state': lambda *a: 'open', - 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.project', context=c) +# 'state': lambda *a: 'open', +# 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.project', context=c) } + def _check_dates(self, cr, uid, ids): + leave = self.read(cr, uid, ids[0],['date_start','date']) + if leave['date_start'] and leave['date']: + if leave['date_start'] > leave['date']: + return False + return True - _order = "parent_id,priority,name" _constraints = [ - (check_recursion, 'Error ! You can not create recursive projects.', ['parent_id']) + (_check_dates, 'Error! project start-date must be lower then project end-date.', ['date_start', 'date']) ] +# _order = "parent_id,priority,name" +# _constraints = [ +# (check_recursion, 'Error ! You can not create recursive projects.', ['parent_id']) +# ] + # toggle activity of projects, their sub projects and their tasks def set_template(self, cr, uid, ids, context={}): res = self.setActive(cr, uid, ids, value=False, context=context) @@ -226,6 +233,29 @@ class task(osv.osv): _name = "project.task" _description = "Tasks" _date_name = "date_start" + +# def compute_date(self,cr,uid): +# project_id = self.pool.get('project.project').search(cr,uid,[]) +# for i in range(len(project_id)): +# task_ids = self.pool.get('project.task').search(cr,uid,[('project_id','=',project_id[i])]) +# if task_ids: +# task_obj = self.pool.get('project.task').browse(cr,uid,task_ids) +# task_1 = task_obj[0] +# task_1.date_start = self.pool.get('project.project').browse(cr,uid,project_id[i]).date_start +# dt = mx.DateTime.strptime(task_1.date_start,"%Y-%m-%d").strftime("%Y-%m-%d") +# def Project_1(): +# title = "New Project" +# start = dt +# +# def task1(): +# start = dt +# effort = task_1.planned_hours +# title = "Task 1" +## project_1 = BalancedProject(Project_1) +## for t in project_1: +## print 'details:::',t.indent_name(), t.start, t.end, t.effort + + def _str_get(self, task, level=0, border='***', context={}): return border+' '+(task.user_id and task.user_id.name.upper() or '')+(level and (': L'+str(level)) or '')+(' - %.1fh / %.1fh'%(task.effective_hours or 0.0,task.planned_hours))+' '+border+'\n'+ \ border[0]+' '+(task.name or '')+'\n'+ \ @@ -235,12 +265,12 @@ class task(osv.osv): result = {} for task in self.browse(cr, uid, ids, context=context): result[task.id] = self._str_get(task, border='===') - t2 = task.parent_id + t2 = task.parent_ids level = 0 while t2: level -= 1 result[task.id] = self._str_get(t2, level) + result[task.id] - t2 = t2.parent_id + t2 = t2.parent_ids t3 = map(lambda x: (x,1), task.child_ids) while t3: t2 = t3.pop(0) @@ -265,8 +295,19 @@ class task(osv.osv): res[task.id]['delay_hours'] = res[task.id]['total_hours'] - task.planned_hours return res - def onchange_planned(self, cr, uid, ids, planned, effective=0.0): - return {'value':{'remaining_hours': planned-effective}} + def onchange_planned(self, cr, uid, ids, planned, effective, date_start,occupation_rate=0.0): + result = {} + for res in self.browse(cr, uid, ids): + if date_start and planned: + resource_id = self.pool.get('resource.resource').search(cr,uid,[('user_id','=',res.user_id.id)]) + resource_obj = self.pool.get('resource.resource').browse(cr,uid,resource_id)[0] + d = mx.DateTime.strptime(date_start,'%Y-%m-%d %H:%M:%S') + hrs = (planned)/(occupation_rate) + work_times = self.pool.get('resource.calendar').interval_get(cr, uid, resource_obj.calendar_id.id or False, d, hrs or 0.0, resource_obj.id) + result['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S') + result['remaining_hours'] = planned-effective + return {'value':result} + def _default_project(self, cr, uid, context={}): if 'project_id' in context and context['project_id']: @@ -282,6 +323,13 @@ class task(osv.osv): default['work_ids'] = [] return super(task, self).copy_data(cr, uid, id, default, context) + def _check_date(self,cr,uid,ids): + for res in self.browse(cr,uid,ids): + if res.date_start and res.date_end: + if res.date_start > res.date_end: + return False + return True + _columns = { 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the task without removing it."), 'name': fields.char('Task Summary', size=128, required=True), @@ -293,15 +341,16 @@ class task(osv.osv): help='If the task is created the state \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\ \n If the task is over, the states is set to \'Done\'.'), 'date_start': fields.datetime('Starting Date'), + 'date_end': fields.datetime('Ending Date'), 'date_deadline': fields.datetime('Deadline'), 'date_close': fields.datetime('Date Closed', readonly=True), 'project_id': fields.many2one('project.project', 'Project', ondelete='cascade', help="If you have [?] in the project name, it means there are no analytic account linked to this project."), - 'parent_id': fields.many2one('project.task', 'Parent Task'), - 'child_ids': fields.one2many('project.task', 'parent_id', 'Delegated Tasks'), + 'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'), + 'child_ids': fields.many2many('project.task', 'project_task_child_rel', 'task_id', 'child_id', 'Delegated Tasks'), 'history': fields.function(_history_get, method=True, string="Task Details", type="text"), 'notes': fields.text('Notes'), - + 'occupation_rate': fields.float('Occupation Rate', help='The occupation rate fields indicates how much of his time a user is working on a task. A 100% occupation rate means the user works full time on the tasks. The ending date of a task is computed like this: Starting Date + Duration / Occupation Rate.'), 'planned_hours': fields.float('Planned Hours', required=True, help='Estimated time to do the task, usually set by the project manager when the task is in draft state.'), 'effective_hours': fields.function(_hours_get, method=True, string='Hours Spent', multi='hours', store=True, help="Computed using the sum of the task work done."), 'remaining_hours': fields.float('Remaining Hours', digits=(16,4), help="Total remaining time, can be re-estimated periodically by the assignee of the task."), @@ -325,6 +374,7 @@ class task(osv.osv): 'active': lambda *a: True, 'date_start': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'project_id': _default_project, + 'occupation_rate':lambda *a: '1', 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c) } _order = "sequence, priority, date_deadline, id" @@ -366,24 +416,24 @@ class task(osv.osv): for task in tasks: project = task.project_id if project: - if project.warn_manager and project.manager and (project.manager.id != uid): + if project.warn_manager and project.user_id and (project.user_id.id != uid): request.create(cr, uid, { 'name': _("Task '%s' closed") % task.name, 'state': 'waiting', 'act_from': uid, - 'act_to': project.manager.id, + 'act_to': project.user_id.id, 'ref_partner_id': task.partner_id.id, 'ref_doc1': 'project.task,%d'% (task.id,), 'ref_doc2': 'project.project,%d'% (project.id,), }) self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0}) - if task.parent_id and task.parent_id.state in ('pending','draft'): + if task.parent_ids and task.parent_ids.state in ('pending','draft'): reopen = True - for child in task.parent_id.child_ids: + for child in task.parent_ids.child_ids: if child.id != task.id and child.state not in ('done','cancelled'): reopen = False if reopen: - self.do_reopen(cr, uid, [task.parent_id.id]) + self.do_reopen(cr, uid, [task.parent_ids.id]) return True def do_reopen(self, cr, uid, ids, *args): @@ -391,12 +441,12 @@ class task(osv.osv): tasks = self.browse(cr, uid, ids) for task in tasks: project = task.project_id - if project and project.warn_manager and project.manager.id and (project.manager.id != uid): + if project and project.warn_manager and project.user_id.id and (project.user_id.id != uid): request.create(cr, uid, { 'name': _("Task '%s' set in progress") % task.name, 'state': 'waiting', 'act_from': uid, - 'act_to': project.manager.id, + 'act_to': project.user_id.id, 'ref_partner_id': task.partner_id.id, 'ref_doc1': 'project.task,%d' % task.id, 'ref_doc2': 'project.project,%d' % project.id, @@ -410,12 +460,12 @@ class task(osv.osv): tasks = self.browse(cr, uid, ids) for task in tasks: project = task.project_id - if project.warn_manager and project.manager and (project.manager.id != uid): + if project.warn_manager and project.user_id and (project.user_id.id != uid): request.create(cr, uid, { 'name': _("Task '%s' cancelled") % task.name, 'state': 'waiting', 'act_from': uid, - 'act_to': project.manager.id, + 'act_to': project.user_id.id, 'ref_partner_id': task.partner_id.id, 'ref_doc1': 'project.task,%d' % task.id, 'ref_doc2': 'project.project,%d' % project.id, diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 50e8d58ba10..f0a0d846e4b 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -30,11 +30,11 @@
- - - + + + - + @@ -42,14 +42,14 @@ - + - + - +