Merge lp:~vrt-openerp/openobject-addons/vrt-openerp into lp:openobject-addons

Proposed by Stuart Longland
Status: Superseded
Proposed branch: lp:~vrt-openerp/openobject-addons/vrt-openerp
Merge into: lp:openobject-addons
Diff against target: 517 lines (+241/-55)
8 files modified
crm/report/crm_lead_report.py (+4/-0)
crm/report/crm_lead_report_view.xml (+93/-3)
hr_timesheet_sheet/hr_timesheet_sheet_view.xml (+1/-1)
project/project.py (+7/-2)
project/project_view.xml (+3/-0)
project_long_term/project_long_term.py (+24/-2)
resource/faces/pcalendar.py (+10/-1)
resource/resource.py (+99/-46)
To merge this branch: bzr merge lp:~vrt-openerp/openobject-addons/vrt-openerp
Reviewer Review Type Date Requested Status
Olivier Dony (Odoo) branch content Needs Resubmitting
Review via email: mp+93763@code.launchpad.net

This proposal has been superseded by a proposal from 2012-03-23.

Description of the change

This branch includes a number of in-house changes to OpenERP 6.1 trunk that fix some issues discovered in the move across. Updates include:

- Long term planning:
  - Fix to working hours; converting the user's working hours to UTC for scheduling in Faces
  - Allowing the "Schedule Phases" to also schedule tasks within each phase
  - Enable assignment of a work-load for a task in percent; this is passed through to Faces for scheduling purposes
- CRM module:
  - Create a new "Leads" field (taken from partner_name), for leads that do not have a partner defined yet (otherwise all these "leads" get filed under the "Undefined" partner)

To post a comment you must log in.
6624. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6625. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6626. By Jean-Christophe VASSORT

[FIX] opw 383657: hr_expense: When unit quantity or factor value is zero > get divide by zero error

6627. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6628. By Raphael Collet (OpenERP)

[MERGE] lp:931523 (fix error message when pushing on Documents)

6629. By Quentin (OpenERP) <email address hidden>

[MERGE] merge the branch from Numérigraphe with a better label for the address of direct delivery on PO.

6630. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6631. By Nicolas Vanhoren (OpenERP)

[merge] refactoring in web client

6632. By Quentin (OpenERP) <email address hidden>

[FIX] account, cash statement: creation through web client

6633. By Quentin (OpenERP) <email address hidden>

[MERGE] lp:909124. Conflict between account_bank_statement_extension and point_of_sale

6634. By Quentin (OpenERP) <email address hidden>

[FIX] account: cannot iter on a boolean. stupid me

6635. By Fabien Meghazi (OpenERP)

[FIX] Edi: webclient's template name has been changed from 'Interface' to 'WebClient'

6636. By Quentin (OpenERP) <email address hidden>

[IMP] account_payment: improved the module description to tackle the most common pitfall linked to this module + rst compliancy of few other modules

6637. By Quentin (OpenERP) <email address hidden>

[IMP] account_payment: improved the module description to tackle the most common pitfall linked to this module

6638. By Quentin (OpenERP) <email address hidden>

[MERGE] lp:930528

6639. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6640. By Quentin (OpenERP) <email address hidden>

[MERGE] lp:880844

6641. By Quentin (OpenERP) <email address hidden>

[REM] hr_timesheet_invoice: removed unused report cost ledger. It was duplicated code of the same report defined in account module AND calling its rml.

6642. By Quentin (OpenERP) <email address hidden>

[MERGE] fix of lp:901089

6643. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6644. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6645. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6646. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6647. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6648. By Olivier Dony (Odoo)

[MERGE] Latest bugfixes from 6.1

6649. By Nicolas Vanhoren (OpenERP)

[imp] adaptation to .js due to changes in web

6650. By Fabien Meghazi (OpenERP)

[FIX] Fixed broken web addon due to webclient's new layout

6651. By Fabien Meghazi (OpenERP)

[FIX] web_livechat: Fixed broken addon due to webclient's layout change

6652. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6653. By Fabien Meghazi (OpenERP)

[IMP] web addons: Use webclient's Systray tooltips

6654. By Quentin (OpenERP) <email address hidden>

[MERGE] merged the branch containing the refactoring of event module and the new modules event_moodle and event_sale

6655. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6656. By Launchpad Translations on behalf of openerp

Launchpad automatic translations update.

6671. By Stuart Longland

Rewrite and Merge with upstream changes.

6672. By Stuart Longland

Rewrite and Merge with upstream changes.

6673. By Stuart Longland

hr_timesheet_sheet: Fix user_id default value

OpenERP 6.1-1 currently fails to fill in the user_id field. On submission of a
timesheet line, the end user finds the system rejects the entered data on the
basis that the user_id field is invalid. Being a hidden field, it is
impossible for the end user to correct this.

The following seems to fix the problem here.

6674. By Stuart Longland

Rewrite and Merge with upstream changes.

6675. By Stuart Longland

crm: Hide "Lead" in search view of Opportunities

They don't have "lead" fields, as the lead has been already converted to
a partner by this stage.

6676. By Stuart Longland

crm: Allow lead report to show individual leads

This allows the end user to navigate to an individual lead (for now via
an intermediate form). The form will need some prettying up, but at
least now if there's a heap of leads in one group, we can differentiate
them now.

This also reverts my earlier commit, which broke things.

6677. By Stuart Longland

crm_lead_report: Fix broken init function

SQL was complaining we specified id twice, so we call the latter
occurance lead_id. That keeps it happy.

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Hello Stuart,

The proposed patches sound good, but the merge proposal is hard to review because it seems to contain a certain number of unrelated changes (CRM stuff). As a rule we prefer to keep only related changesets in each merge proposal, especially for bugfix branches - so they can be independently reviewed and merged.
Perhaps the easiest course is to apply your patch for bug 932584 on a fresh copy of our official addons branch, and submit that as a separate merge proposal.

If you'd like more details, our merge proposal documentation includes a step-by-step guide for this process [1].

Thanks for your contributions to OpenERP!

[1] http://doc.openerp.com/v6.0/contribute/15_guidelines/contribution_guidelines.html#merge-proposal-guidelines

review: Needs Resubmitting (branch content)
Revision history for this message
Stuart Longland (redhatter) wrote :

No worries, I'll split this up.

I've just spent what seems like 10 minutes hunting around for the "Register Branch" link to do exactly this, and Launchpad has "helpfully" removed it and not told anyone (thanks Launchpad).

I'm creating a fresh branch of the trunk code -- when this is done, I should be able to cherry-pick the relevant commits from it and push those into a separate new branch.

Unmerged revisions

6677. By Stuart Longland

crm_lead_report: Fix broken init function

SQL was complaining we specified id twice, so we call the latter
occurance lead_id. That keeps it happy.

6676. By Stuart Longland

crm: Allow lead report to show individual leads

This allows the end user to navigate to an individual lead (for now via
an intermediate form). The form will need some prettying up, but at
least now if there's a heap of leads in one group, we can differentiate
them now.

This also reverts my earlier commit, which broke things.

6675. By Stuart Longland

crm: Hide "Lead" in search view of Opportunities

They don't have "lead" fields, as the lead has been already converted to
a partner by this stage.

6674. By Stuart Longland

Rewrite and Merge with upstream changes.

6673. By Stuart Longland

hr_timesheet_sheet: Fix user_id default value

OpenERP 6.1-1 currently fails to fill in the user_id field. On submission of a
timesheet line, the end user finds the system rejects the entered data on the
basis that the user_id field is invalid. Being a hidden field, it is
impossible for the end user to correct this.

The following seems to fix the problem here.

6672. By Stuart Longland

Rewrite and Merge with upstream changes.

6671. By Stuart Longland

Rewrite and Merge with upstream changes.

6670. By Stuart Longland

Rewrite and Merge with upstream changes.

6669. By Stuart Longland

project_long_term: Skip cancelled or done tasks.

These are skipped when generating the project description for faces to manage,
therefore they should also be skipped when collecting results.

6668. By Stuart Longland

project_long_term.schedule_phases: Skip projects with no phases

Projects that have no phases by definition cannot be scheduled via the "Schedule Phases" algorithm. When one attempts to do this, the following is emitted to Faces:

from resource.faces import *
import datetime

def Project():

resulting in the following stack trace:

[2012-02-17 02:07:29,630][development61] ERROR:web-services:Uncaught exception
Traceback (most recent call last):
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/osv/osv.py", line 120, in wrapper
    return f(self, dbname, *args, **kwargs)
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/osv/osv.py", line 175, in execute
    res = self.execute_cr(cr, uid, obj, method, *args, **kw)
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/osv/osv.py", line 163, in execute_cr
    return getattr(object, method)(cr, uid, *args, **kw)
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/addons/project_long_term/wizard/project_compute_phases.py", line 39, in check_selection
    return self.compute_date(cr, uid, ids, context=context)
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/addons/project_long_term/wizard/project_compute_phases.py", line 55, in compute_date
    project_pool.schedule_phases(cr, uid, project_ids, context=context)
  File "/usr/lib64/python2.7/site-packages/openerp-6.1rc1_20120112_173907-py2.7.egg/openerp/addons/project_long_term/project_long_term.py", line 238, in schedule_phases
    exec result in local_dict
  File "<string>", line 5

   ^
IndentationError: expected an indented block

This is a partial fix. The user should be warned as to what projects have been
skipped or at least, given the opportunity to schedule the tasks for those
projects.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'crm/report/crm_lead_report.py'
2--- crm/report/crm_lead_report.py 2012-01-31 13:36:57 +0000
3+++ crm/report/crm_lead_report.py 2012-03-06 23:39:19 +0000
4@@ -74,6 +74,7 @@
5 'delay_close': fields.float('Delay to Close',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
6 'delay_expected': fields.float('Overpassed Deadline',digits=(16,2),readonly=True, group_operator="avg"),
7
8+ 'lead_id':fields.many2one('crm.lead', 'Lead', readonly=True),
9 'user_id':fields.many2one('res.users', 'User', readonly=True),
10 'country_id':fields.many2one('res.country', 'Country', readonly=True),
11 'section_id':fields.many2one('crm.case.section', 'Sales Team', readonly=True),
12@@ -89,6 +90,7 @@
13 domain="['|',('section_id','=',False),('section_id','=',section_id)]" , readonly=True),
14 'stage_id': fields.many2one ('crm.case.stage', 'Stage', readonly=True, domain="[('section_ids', '=', section_id)]"),
15 'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True),
16+ 'partner_name': fields.char("Lead", size=64,help='The name of the future partner that will be created while converting the lead into an opportunity', readonly=True),
17 'nbr': fields.integer('# of Cases', readonly=True),
18 'company_id': fields.many2one('res.company', 'Company', readonly=True),
19 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
20@@ -124,6 +126,7 @@
21 to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
22 to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
23
24+ c.id as lead_id,
25 c.state,
26 c.user_id,
27 c.probability,
28@@ -136,6 +139,7 @@
29 c.type_id,
30 c.categ_id,
31 c.partner_id,
32+ c.partner_name,
33 c.country_id,
34 c.planned_revenue,
35 c.planned_revenue*(c.probability/100) as probable_revenue,
36
37=== modified file 'crm/report/crm_lead_report_view.xml'
38--- crm/report/crm_lead_report_view.xml 2011-11-14 09:27:08 +0000
39+++ crm/report/crm_lead_report_view.xml 2012-03-06 23:39:19 +0000
40@@ -25,16 +25,52 @@
41 <field name="user_id" invisible="1"/>
42 <field name="company_id" invisible="1"/>
43 <field name="partner_id" invisible="1"/>
44+ <field name="partner_name" invisible="1"/>
45 <field name="country_id" invisible="1"/>
46 <field name="nbr" sum="# Leads"/>
47 <field name="email" sum="# Mails"/>
48 <field name="delay_open"/>
49 <field name="delay_close"/>
50 <field name="planned_revenue"/>
51+ <field name="lead_id"/>
52 </tree>
53 </field>
54 </record>
55
56+<!-- Leads by user and section Form View -->
57+
58+ <record id="view_report_crm_lead_form" model="ir.ui.view">
59+ <field name="name">crm.lead.report.form</field>
60+ <field name="model">crm.lead.report</field>
61+ <field name="type">form</field>
62+ <field name="arch" type="xml">
63+ <form string="Lead Analysis">
64+ <field name="lead_id" colspan="4"/>
65+ <field name="creation_year"/>
66+ <field name="creation_month"/>
67+ <field name="creation_day"/>
68+ <field name="deadline_month"/>
69+ <field name="state"/>
70+ <field name="stage_id"/>
71+ <field name="categ_id"/>
72+ <field name="type_id"/>
73+ <field name="channel_id"/>
74+ <field name="type"/>
75+ <field name="priority"/>
76+ <field name="section_id"/>
77+ <field name="user_id"/>
78+ <field name="company_id"/>
79+ <field name="partner_id"/>
80+ <field name="partner_name"/>
81+ <field name="country_id"/>
82+ <field name="email"/>
83+ <field name="delay_open"/>
84+ <field name="delay_close"/>
85+ <field name="planned_revenue"/>
86+ </form>
87+ </field>
88+ </record>
89+
90 <!-- Leads by user and section Graph View -->
91
92 <record id="view_report_crm_lead_graph" model="ir.ui.view">
93@@ -126,6 +162,7 @@
94 <newline/>
95 <group expand="0" string="Extended Filters..." groups="base.group_extended">
96 <field name="partner_id"/>
97+ <field name="partner_name"/>
98 <separator orientation="vertical"/>
99 <field name="stage_id" widget="selection" domain="[('section_ids', '=', 'section_id')]" />
100 <field name="categ_id" widget="selection"/>
101@@ -146,6 +183,7 @@
102 domain="[]"
103 context="{'group_by':'section_id'}" />
104 <filter string="Partner" icon="terp-partner" context="{'group_by':'partner_id'}" />
105+ <filter string="Lead" icon="terp-partner" context="{'group_by':'partner_name'}"/>
106 <filter string="Country" icon="terp-go-home" context="{'group_by':'country_id'}" />
107 <filter string="Company" icon="terp-go-home"
108 domain="[]"
109@@ -193,6 +231,7 @@
110 <field name="section_id" invisible="1" groups="base.group_extended"/>
111 <field name="user_id" invisible="1"/>
112 <field name="partner_id" invisible="1"/>
113+ <field name="partner_name" invisible="1"/>
114 <field name="country_id" invisible="1"/>
115 <field name="state" invisible="1"/>
116 <field name="stage_id" invisible="1"/>
117@@ -210,18 +249,56 @@
118 <field name="delay_expected"/>
119 <field name="probability" widget="progressbar"/>
120 <field name="probable_revenue"/>
121+ <field name="lead_id"/>
122 </tree>
123 </field>
124 </record>
125
126+<!-- Opportunity form view -->
127+
128+ <record id="view_report_crm_opportunity_form" model="ir.ui.view">
129+ <field name="name">crm.lead.report.form</field>
130+ <field name="model">crm.lead.report</field>
131+ <field name="type">form</field>
132+ <field name="arch" type="xml">
133+ <form string="Opportunities Analysis">
134+ <field name="lead_id" colspan="4"/>
135+ <field name="creation_year"/>
136+ <field name="creation_month"/>
137+ <field name="creation_day"/>
138+ <field name="deadline_month"/>
139+ <field name="section_id"/>
140+ <field name="user_id"/>
141+ <field name="partner_id"/>
142+ <field name="partner_name"/>
143+ <field name="country_id"/>
144+ <field name="state" />
145+ <field name="stage_id" />
146+ <field name="priority" />
147+ <field name="categ_id"/>
148+ <field name="type_id"/>
149+ <field name="channel_id"/>
150+ <field name="type"/>
151+ <field name="company_id" />
152+ <field name="planned_revenue" />
153+ <field name="email" string="# of Emails"/>
154+ <field name="delay_open"/>
155+ <field name="delay_close"/>
156+ <field name="delay_expected"/>
157+ <field name="probability" widget="progressbar"/>
158+ <field name="probable_revenue"/>
159+ </form>
160+ </field>
161+ </record>
162+
163 <!-- Leads by user and section Action -->
164
165 <record id="action_report_crm_lead" model="ir.actions.act_window">
166 <field name="name">Leads Analysis</field>
167 <field name="res_model">crm.lead.report</field>
168 <field name="view_type">form</field>
169- <field name="context">{'search_default_year': 1,'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by_no_leaf':1, 'group_by':[]}</field>
170- <field name="view_mode">tree,graph</field>
171+ <field name="context">{'search_default_year': 1,'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by':[]}</field>
172+ <field name="view_mode">tree,graph,form</field>
173 <field name="domain">[]</field>
174 <field name="help">Leads Analysis allows you to check different CRM related information. Check for treatment delays, number of responses given and emails sent. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
175 </record>
176@@ -237,12 +314,18 @@
177 <field name="view_id" ref="view_report_crm_lead_graph"/>
178 <field name="act_window_id" ref="action_report_crm_lead"/>
179 </record>
180+ <record model="ir.actions.act_window.view" id="action_report_crm_lead_form">
181+ <field name="sequence" eval="3"/>
182+ <field name="view_mode">form</field>
183+ <field name="view_id" ref="view_report_crm_lead_form"/>
184+ <field name="act_window_id" ref="action_report_crm_lead"/>
185+ </record>
186
187 <record id="action_report_crm_opportunity" model="ir.actions.act_window">
188 <field name="name">Opportunities Analysis</field>
189 <field name="res_model">crm.lead.report</field>
190 <field name="view_type">form</field>
191- <field name="context">{"search_default_year":1,"search_default_opportunity":1, "search_default_user":1,"search_default_this_month":1,'group_by_no_leaf':1,'group_by':[]}</field>
192+ <field name="context">{"search_default_year":1,"search_default_opportunity":1, "search_default_user":1,"search_default_this_month":1,'group_by':[]}</field>
193 <field name="view_mode">tree,graph</field>
194 <field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
195 </record>
196@@ -261,6 +344,13 @@
197 <field name="act_window_id" ref="action_report_crm_opportunity"/>
198 </record>
199
200+ <record model="ir.actions.act_window.view" id="action_report_crm_opportunity_form">
201+ <field name="sequence" eval="3"/>
202+ <field name="view_mode">form</field>
203+ <field name="view_id" ref="view_report_crm_opportunity_form"/>
204+ <field name="act_window_id" ref="action_report_crm_opportunity"/>
205+ </record>
206+
207 <menuitem name="Leads Analysis" id="menu_report_crm_leads_tree"
208 groups="base.group_extended"
209 parent="base.next_id_64" action="action_report_crm_lead" sequence="3"/>
210
211=== modified file 'hr_timesheet_sheet/hr_timesheet_sheet_view.xml'
212--- hr_timesheet_sheet/hr_timesheet_sheet_view.xml 2012-02-13 15:27:55 +0000
213+++ hr_timesheet_sheet/hr_timesheet_sheet_view.xml 2012-03-06 23:39:19 +0000
214@@ -95,7 +95,7 @@
215 <button name="sign_out" string="Sign Out" type="object" icon="terp-gtk-jump-to-rtl"/>
216 <field name="total_attendance_day" widget="float_time" colspan="4"/>
217 </group>
218- <field colspan="4" context="{'date':date_current,'user_id':user_id}" domain="[('name','=',date_current)]" name="timesheet_ids" nolabel="1">
219+ <field colspan="4" context="{'date':date_current,'user_id':uid}" domain="[('name','=',date_current)]" name="timesheet_ids" nolabel="1">
220 <tree editable="top" string="Timesheet Lines">
221 <field invisible="1" name="date"/>
222 <field domain="[('type','=','normal'), ('state', '&lt;&gt;', 'close')]" name="account_id" on_change="on_change_account_id(account_id)"/>
223
224=== modified file 'project/project.py'
225--- project/project.py 2012-02-28 14:08:16 +0000
226+++ project/project.py 2012-03-06 23:39:19 +0000
227@@ -666,6 +666,7 @@
228 'id': fields.integer('ID', readonly=True),
229 'color': fields.integer('Color Index'),
230 'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
231+ 'work_load': fields.integer('Workload (%)', help='Indicates how much time each person is to spend working on this task as a percentage of their total working hours. The default is 100%. The value must be greater than 0, else it will be treated as 100%.'),
232 }
233
234 _defaults = {
235@@ -676,7 +677,7 @@
236 'sequence': 10,
237 'active': True,
238 'user_id': lambda obj, cr, uid, context: uid,
239- 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c)
240+ 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c),
241 }
242
243 _order = "priority, sequence, date_start, name, id"
244@@ -1067,8 +1068,12 @@
245 continue
246 result += '''
247 %sdef Task_%s():
248+%s load = %4.2f
249 %s todo = \"%.2fH\"
250-%s effort = \"%.2fH\"''' % (ident,task.id, ident,task.remaining_hours, ident,task.total_hours)
251+%s effort = \"%.2fH\"''' % ( ident,task.id,
252+ ident,((task.work_load / 100.0) if task.work_load else 1.0),
253+ ident,task.remaining_hours,
254+ ident,task.total_hours )
255 start = []
256 for t2 in task.parent_ids:
257 start.append("up.Task_%s.end" % (t2.id,))
258
259=== modified file 'project/project_view.xml'
260--- project/project_view.xml 2012-02-13 15:27:55 +0000
261+++ project/project_view.xml 2012-03-06 23:39:19 +0000
262@@ -228,6 +228,9 @@
263 <field name="total_hours" widget="float_time"/>
264 <field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
265 <field name="user_id" select="1" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
266+ <field name="work_load"/>
267+ </group>
268+ <group colspan="4">
269 <field name="progress" widget="progressbar"/>
270 </group>
271 <notebook colspan="4">
272
273=== modified file 'project_long_term/project_long_term.py'
274--- project_long_term/project_long_term.py 2012-01-31 13:36:57 +0000
275+++ project_long_term/project_long_term.py 2012-03-06 23:39:19 +0000
276@@ -224,9 +224,16 @@
277 ids = [ids]
278 projects = self.browse(cr, uid, ids, context=context)
279 result = self._schedule_header(cr, uid, ids, context=context)
280+ do_schedule = False
281 for project in projects:
282- result += self._schedule_project(cr, uid, project, context=context)
283- result += self.pool.get('project.phase').generate_phase(cr, uid, project.phase_ids, context=context)
284+ if len(project.phase_ids) > 0:
285+ result += self._schedule_project(cr, uid, project, context=context)
286+ result += self.pool.get('project.phase').generate_phase(cr, uid, project.phase_ids, context=context)
287+ do_schedule = True
288+
289+ if not do_schedule:
290+ # Nothing to do here
291+ return True
292
293 local_dict = {}
294 exec result in local_dict
295@@ -256,6 +263,21 @@
296 'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
297 'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
298 }, context=context)
299+
300+ # Schedule sub-tasks
301+ for task in phase.task_ids:
302+ if task.state in ('done','cancelled'):
303+ continue
304+
305+ t = getattr(p, 'Task_%d' % (task.id,))
306+ self.pool.get('project.task').write(cr, uid, [task.id], {
307+ 'date_start': t.start.strftime('%Y-%m-%d %H:%M:%S'),
308+ 'date_end': t.end.strftime('%Y-%m-%d %H:%M:%S'),
309+ }, context=context)
310+ if (not task.user_id) and (t.booked_resource):
311+ self.pool.get('project.task').write(cr, uid, [task.id], {
312+ 'user_id': int(t.booked_resource[0].name[5:]),
313+ }, context=context)
314 return True
315 project()
316
317
318=== modified file 'resource/faces/pcalendar.py'
319--- resource/faces/pcalendar.py 2011-12-19 16:54:40 +0000
320+++ resource/faces/pcalendar.py 2012-03-06 23:39:19 +0000
321@@ -895,7 +895,16 @@
322 def _recalc_working_time(self):
323 def slot_sum_time(day):
324 slots = self.working_times.get(day, DEFAULT_WORKING_DAYS[day])
325- return sum(map(lambda slot: slot[1] - slot[0], slots))
326+ def time_diff(times):
327+ (start, end) = times
328+ if end == start:
329+ return 24*60 # 24 hours
330+
331+ diff = end - start
332+ if end < start:
333+ diff += (24*60)
334+ return diff
335+ return sum(map(time_diff, slots))
336
337 self.day_times = map(slot_sum_time, range(0, 7))
338 self.week_time = sum(self.day_times)
339
340=== modified file 'resource/resource.py'
341--- resource/resource.py 2011-12-21 12:37:24 +0000
342+++ resource/resource.py 2012-03-06 23:39:19 +0000
343@@ -19,7 +19,7 @@
344 #
345 ##############################################################################
346
347-from datetime import datetime, timedelta
348+from datetime import datetime, timedelta, time, date
349 import math
350 from faces import *
351 from osv import fields, osv
352@@ -28,6 +28,7 @@
353 from itertools import groupby
354 from operator import itemgetter
355
356+import pytz
357
358 class resource_calendar(osv.osv):
359 _name = "resource.calendar"
360@@ -279,8 +280,9 @@
361 hour_part = split_list[0]
362 mins_part = split_list[1]
363 round_mins = int(round(float(mins_part) * 60,-2))
364- converted_string = hour_part + ':' + str(round_mins)[0:2]
365- return converted_string
366+ return time(int(hour_part), round_mins)
367+ #converted_string = hour_part + ':' + str(round_mins)[0:2]
368+ #return converted_string
369
370 class resource_resource(osv.osv):
371 _name = "resource.resource"
372@@ -366,51 +368,102 @@
373 Change the format of working calendar from 'Openerp' format to bring it into 'Faces' format.
374 @param calendar_id : working calendar of the project
375 """
376+ tz = pytz.utc
377+ if context and ('tz' in context):
378+ tz = pytz.timezone(context['tz'])
379+
380+ wktime_local = None
381+ week_days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']
382 if not calendar_id:
383- # Calendar is not specified: working days: 24/7
384- return [('fri', '8:0-12:0','13:0-17:0'), ('thu', '8:0-12:0','13:0-17:0'), ('wed', '8:0-12:0','13:0-17:0'),
385- ('mon', '8:0-12:0','13:0-17:0'), ('tue', '8:0-12:0','13:0-17:0')]
386- resource_attendance_pool = self.pool.get('resource.calendar.attendance')
387- time_range = "8:00-8:00"
388- non_working = ""
389- week_days = {"0": "mon", "1": "tue", "2": "wed","3": "thu", "4": "fri", "5": "sat", "6": "sun"}
390- wk_days = {}
391- wk_time = {}
392- wktime_list = []
393+ # Calendar is not specified: working days: some sane default
394+ wktime_local = [
395+ (0, time(8,0), time(12,0)),
396+ (0, time(13,0), time(17,0)),
397+ (1, time(8,0), time(12,0)),
398+ (1, time(13,0), time(17,0)),
399+ (2, time(8,0), time(12,0)),
400+ (2, time(13,0), time(17,0)),
401+ (3, time(8,0), time(12,0)),
402+ (3, time(13,0), time(17,0)),
403+ (4, time(8,0), time(12,0)),
404+ (4, time(13,0), time(17,0)),
405+ ]
406+ #return [('fri', '8:0-12:0','13:0-17:0'), ('thu', '8:0-12:0','13:0-17:0'), ('wed', '8:0-12:0','13:0-17:0'),
407+ # ('mon', '8:0-12:0','13:0-17:0'), ('tue', '8:0-12:0','13:0-17:0')]
408+ else:
409+ resource_attendance_pool = self.pool.get('resource.calendar.attendance')
410+ non_working = ""
411+ wktime_local = []
412+
413+ week_ids = resource_attendance_pool.search(cr, uid, [('calendar_id', '=', calendar_id)], context=context)
414+ weeks = resource_attendance_pool.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'], context=context)
415+ # Convert time formats into appropriate format required
416+ # and create lists inside wktime_local.
417+ for week in weeks:
418+ res_str = ""
419+ day = None
420+ if week['dayofweek']:
421+ day = int(week['dayofweek'])
422+ else:
423+ raise osv.except_osv(_('Configuration Error!'),_('Make sure the Working time has been configured with proper week days!'))
424+ hour_from = convert_timeformat(week['hour_from'])
425+ hour_to = convert_timeformat(week['hour_to'])
426+ wktime_local.append((day, hour_from, hour_to))
427+
428+ # We now have working hours _in local time_. Non-working days are an
429+ # empty list, while working days are a list of tuples, consisting of a
430+ # start time and an end time. We will convert these to UTC for time
431+ # calculation purposes.
432+
433+ # We need to get this into a dict
434+ # which will be in the following format:
435+ # { 'day': [(time(9,0), time(17,0)), ...], ... }
436+ wktime_utc = {}
437+
438+ # NOTE: This may break with regards to DST!
439+ for (day, start, end) in wktime_local:
440+ # Convert start time to UTC
441+ start_dt_local = datetime.combine(date.today(), start.replace(tzinfo=tz))
442+ start_dt_utc = start_dt_local.astimezone(pytz.utc)
443+ start_dt_day = (day + (start_dt_utc.date() - start_dt_local.date()).days) % 7
444+
445+ # Convert end time to UTC
446+ end_dt_local = datetime.combine(date.today(), end.replace(tzinfo=tz))
447+ end_dt_utc = end_dt_local.astimezone(pytz.utc)
448+ end_dt_day = (day + (end_dt_utc.date() - end_dt_local.date()).days) % 7
449+
450+ # Are start and end still on the same day?
451+ if start_dt_day == end_dt_day:
452+ day_name = week_days[start_dt_day]
453+ if day_name not in wktime_utc:
454+ wktime_utc[day_name] = []
455+ wktime_utc[day_name].append((start_dt_utc.time(), end_dt_utc.time()))
456+ else:
457+ day_start_name = week_days[start_dt_day]
458+ if day_start_name not in wktime_utc:
459+ wktime_utc[day_start_name] = []
460+ # We go until midnight that day
461+ wktime_utc[day_start_name].append((start_dt_utc.time(), time(0,0)))
462+
463+ day_end_name = week_days[end_dt_day]
464+ if day_end_name not in wktime_utc:
465+ wktime_utc[day_end_name] = []
466+ # Then resume from midnight that day
467+ wktime_utc[day_end_name].append((time(0,0), end_dt_utc.time()))
468+
469+ # Now having gotten a list of times together, generate the final output
470 wktime_cal = []
471- week_ids = resource_attendance_pool.search(cr, uid, [('calendar_id', '=', calendar_id)], context=context)
472- weeks = resource_attendance_pool.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'], context=context)
473- # Convert time formats into appropriate format required
474- # and create a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
475- for week in weeks:
476- res_str = ""
477- day = None
478- if week_days.get(week['dayofweek'],False):
479- day = week_days[week['dayofweek']]
480- wk_days[week['dayofweek']] = week_days[week['dayofweek']]
481- else:
482- raise osv.except_osv(_('Configuration Error!'),_('Make sure the Working time has been configured with proper week days!'))
483- hour_from_str = convert_timeformat(week['hour_from'])
484- hour_to_str = convert_timeformat(week['hour_to'])
485- res_str = hour_from_str + '-' + hour_to_str
486- wktime_list.append((day, res_str))
487- # Convert into format like [('mon', '8:00-12:00', '13:00-18:00')]
488- for item in wktime_list:
489- if wk_time.has_key(item[0]):
490- wk_time[item[0]].append(item[1])
491- else:
492- wk_time[item[0]] = [item[0]]
493- wk_time[item[0]].append(item[1])
494- for k,v in wk_time.items():
495- wktime_cal.append(tuple(v))
496- # Add for the non-working days like: [('sat, sun', '8:00-8:00')]
497- for k, v in wk_days.items():
498- if week_days.has_key(k):
499- week_days.pop(k)
500- for v in week_days.itervalues():
501- non_working += v + ','
502- if non_working:
503- wktime_cal.append((non_working[:-1], time_range))
504+ for day, times in wktime_utc.iteritems():
505+ # Sort the times
506+ times.sort()
507+ wktime = ['{0}-{1}'.format(s.strftime('%H:%M'), e.strftime('%H:%M')) for (s, e) in times]
508+ wktime.insert(0, day)
509+ wktime_cal.append(tuple(wktime))
510+ # Finally, add in non-working days
511+ for day in week_days:
512+ if day not in wktime_utc:
513+ wktime_cal.append((day, None))
514+
515 return wktime_cal
516
517 resource_resource()

Subscribers

People subscribed via source and target branches

to all changes: