[IMP] Bck not running

bzr revid: jke@openerp.com-20131123113627-csxj0qx8s030dhi7
This commit is contained in:
jke-openerp 2013-11-23 12:36:27 +01:00
commit 5f9ee3b2d2
17 changed files with 1956 additions and 1872 deletions

View File

@ -20,7 +20,6 @@
##############################################################################
import base_calendar
import crm_meeting
import controllers
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -42,10 +42,8 @@ If you need to manage your meetings, you should install the CRM module.
'data': [
'security/calendar_security.xml',
'security/ir.model.access.csv',
#'base_calendar_view.xml',
'crm_meeting_view.xml',
#'base_calendar_data.xml',
'crm_meeting_data.xml',
'base_calendar_data.xml'
],
'js': [
'static/src/js/*.js'

File diff suppressed because it is too large Load Diff

View File

@ -2,118 +2,375 @@
<openerp>
<data noupdate="1">
<record model="res.request.link" id="request_link_event">
<record model="res.request.link" id="request_link_event">
<field name="name">Event</field>
<field name="object">calendar.event</field>
</record>
<record model="res.alarm" id="alarm2">
<field name="name">5 minutes before</field>
<record model="calendar.alarm" id="alarm_notif_1">
<field name="name">15 min notif</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="5" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm3">
<field name="name">10 minutes before</field>
<field name="duration" eval="15" />
<field name="interval">minutes</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_2">
<field name="name">30 min notif</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="10" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm4">
<field name="name">15 minutes before</field>
<field name="duration" eval="30" />
<field name="interval">minutes</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_3">
<field name="name">1 hour notif</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="15" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm5">
<field name="name">30 minutes before</field>
<field name="duration" eval="1" />
<field name="interval">hours</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_4">
<field name="name">2 hours notif</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="30" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm6">
<field name="name">45 minutes before</field>
<field name="duration" eval="2" />
<field name="interval">hours</field>
<field name="type">notification</field>
</record>
<record model="calendar.alarm" id="alarm_notif_5">
<field name="name">1 day notif</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="45" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
<field name="duration" eval="1" />
<field name="interval">days</field>
<field name="type">notification</field>
</record>
<record model="res.alarm" id="alarm7">
<field name="name">1 hour before</field>
<record model="calendar.alarm" id="alarm_mail_1">
<field name="name">15 min mail</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="1" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm8">
<field name="name">2 hours before</field>
<field name="duration" eval="15" />
<field name="interval">minutes</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_2">
<field name="name">30 min mail</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="2" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm9">
<field name="name">3 hours before</field>
<field name="duration" eval="30" />
<field name="interval">minutes</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_3">
<field name="name">1 hour mail</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="3" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm10">
<field name="name">4 hours before</field>
<field name="duration" eval="1" />
<field name="interval">hours</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_4">
<field name="name">2 hours mail</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="4" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm11">
<field name="name">24 hours before</field>
<field name="duration" eval="2" />
<field name="interval">hours</field>
<field name="type">email</field>
</record>
<record model="calendar.alarm" id="alarm_mail_5">
<field name="name">1 day mail</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="24" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
<field name="duration" eval="1" />
<field name="interval">days</field>
<field name="type">email</field>
</record>
<!-- Scheduler for Event Alarm-->
<record forcecreate="True" id="ir_cron_scheduler_alarm"
model="ir.cron">
<record forcecreate="True" id="ir_cron_scheduler_alarm" model="ir.cron">
<field name="name">Run Event Reminder</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">5</field>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field eval="'calendar.alarm'" name="model" />
<field eval="'do_run_scheduler'" name="function" />
<field eval="'calendar.alarm_manager'" name="model" />
<field eval="'do_run_scheduler_mail'" name="function" />
<field eval="'(False,)'" name="args" />
</record>
<record model="crm.meeting.type" id="categ_meet1">
<field name="name">Customer Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet2">
<field name="name">Internal Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet3">
<field name="name">Off-site Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet4">
<field name="name">Open Discussion</field>
</record>
<record model="crm.meeting.type" id="categ_meet5">
<field name="name">Feedback Meeting</field>
</record>
<record model="res.request.link" id="request_link_meeting">
<field name="name">Meeting</field>
<field name="object">crm.meeting</field>
</record>
<record id="crm_email_template_meeting_invitation" model="email.template">
<field name="name">Meeting Invitation</field>
<field name="email_from">${object.user_id.email or ''}</field>
<field name="subject">${object.name}</field>
<field name="email_to" >${ctx['att_obj'].email}</field>
<field name="model_id" ref="base_calendar.model_crm_meeting"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>${object.name}</title>
</head>
<body>
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
<div style="height:auto;text-align: center;font-size : 30px;color: #8A89BA;">
<strong>${object.name}</strong>
</div>
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
<strong style="margin-left:12px">Hello ${ctx['att_obj'].cn}</strong> ,<br/><p style="margin-left:12px">${object.organizer} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.</p>
</div>
<div style="height: auto;margin-left:12px;margin-top:30px;">
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.get_interval(object.date, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.get_interval(object.date,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.get_interval(object.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${object.get_interval(object.date, 'time')}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
% if object.location :
<tr style=" height: 30px;">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1" style="vertical-align:top;">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px" >
: ${object.location}
<span style= "color:#A9A9A9; ">(<a href="http://maps.google.com/maps?oi=map&q=${object.location}">View Map</a>)
</span>
</div>
</td>
</tr>
% endif
% if not object.location :
<tr style=" height: 30px;color:#909090">
<td>
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px;" >
: -
</div>
</td>
</tr>
% endif
% if object.description :
<tr style=" height:auto;">
<td style="vertical-align:top;">
<div style="height:auto; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: ${object.description or ''}
</div>
</td>
</tr>
% endif
% if not object.description :
<tr style=" height: 30px;color:#909090">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: -
</div>
</td>
</tr>
% endif
<tr style=" height: 30px;">
<td style="height: 25px;width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
<div>
Attendees
</div>
</td>
<td colspan="3">
: <div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:grey;'></div>
<span style="margin-left:5px">You</span>
% for attendee in object.attendee_ids:
% if attendee.cn != ctx['att_obj'].cn:
<div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:${ctx['color'][attendee.state]};'></div>
<span style="margin-left:5px">${attendee.cn}</span>
% endif
% endfor
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div style="height: auto;width:300px; margin:0 auto;padding-top:20px;">
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#8A89BA;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/accept?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Accept</a>
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#808080;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/decline?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Decline</a>
</div>
<div style="padding-top:10px;">
-- </br> Sent by ${object.user_id.name} from ${object.user_id.company_id.name}. View this meeting detail <a href="${ctx['base_url']}/meeting_invitation/view?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">directly in OpenERP.</a>
</div>
</div>
</body>
</html>
]]></field>
</record>
<record id="crm_email_template_meeting_reminder" model="email.template">
<field name="name">Meeting Invitation</field>
<field name="email_from">${object.user_id.email or ''}</field>
<field name="subject">${object.name}</field>
<field name="email_to" >${ctx['att_obj'].email}</field>
<field name="model_id" ref="base_calendar.model_crm_meeting"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>${object.name}</title>
</head>
<body>
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
<div style="height:auto;text-align: center;font-size : 30px;color: #8A89BA;">
<strong>REMINDER : ${object.name}</strong>
</div>
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
<strong style="margin-left:12px">Hello ${ctx['att_obj'].cn}</strong> ,<br/><p style="margin-left:12px">${object.organizer} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.</p>
</div>
<div style="height: auto;margin-left:12px;margin-top:30px;">
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.get_interval(object.date, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.get_interval(object.date,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.get_interval(object.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${object.get_interval(object.date, 'time')}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
% if object.location :
<tr style=" height: 30px;">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1" style="vertical-align:top;">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px" >
: ${object.location}
<span style= "color:#A9A9A9; ">(<a href="http://maps.google.com/maps?oi=map&q=${object.location}">View Map</a>)
</span>
</div>
</td>
</tr>
% endif
% if not object.location :
<tr style=" height: 30px;color:#909090">
<td>
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px;" >
: -
</div>
</td>
</tr>
% endif
% if object.description :
<tr style=" height:auto;">
<td style="vertical-align:top;">
<div style="height:auto; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: ${object.description or ''}
</div>
</td>
</tr>
% endif
% if not object.description :
<tr style=" height: 30px;color:#909090">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: -
</div>
</td>
</tr>
% endif
<tr style=" height: 30px;">
<td style="height: 25px;width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
<div>
Attendees
</div>
</td>
<td colspan="3">
: <div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:grey;'></div>
<span style="margin-left:5px">You</span>
% for attendee in object.attendee_ids:
% if attendee.cn != ctx['att_obj'].cn:
<div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:${ctx['color'][attendee.state]};'></div>
<span style="margin-left:5px">${attendee.cn}</span>
% endif
% endfor
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>
]]></field>
</record>
</data>
</openerp>

View File

@ -1,289 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Calendar's menu -->
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="50" groups="base.group_no_one"/>
<!--
Alarm form view
<record id="res_alarm_form_view" model="ir.ui.view">
<field name="name">res.alarm.form</field>
<field name="model">res.alarm</field>
<field name="arch" type="xml">
<form string="Reminder details" version="7.0">
<group col="4">
<field name="name"/>
<field name="active"/>
<separator string="Reminder Details" colspan="4"/>
<field name="trigger_duration"/>
<field name="trigger_interval"/>
<field name="trigger_occurs"/>
<field name="trigger_related"/>
</group>
</form>
</field>
</record>
-->
<!-- Alarm list view
<record id="res_alarm_tree_view" model="ir.ui.view">
<field name="name">res.alarm.tree</field>
<field name="model">res.alarm</field>
<field name="arch" type="xml">
<tree string="Reminder details">
<field name="name"/>
<field name="trigger_interval"/>
<field name="trigger_duration"/>
<field name="trigger_occurs"/>
<field name="trigger_related"/>
</tree>
</field>
</record>
<record id="action_res_alarm_view" model="ir.actions.act_window">
<field name="name">Alarms</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.alarm</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to setup a new alarm type.
</p><p>
You can define a customized type of calendar alarm that may be
assigned to calendar events or meetings.
</p>
</field>
</record>
-->
<!-- Alarms menu
<menuitem id="menu_crm_meeting_avail_alarm"
groups="base.group_no_one"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" sequence="5"/>
-->
<!-- Event form view -->
<record model="ir.ui.view" id="event_form_view">
<field name="name">Event Form</field>
<field name="model">calendar.event</field>
<field name="arch" type="xml">
<form string="Events" version="7.0">
<header>
<button name="do_confirm" string="Confirm" states="tentative,cancelled" type="object" class="oe_highlight"/>
<button name="do_tentative" states="confirmed,cancelled" string="Uncertain" type="object" class="oe_highlight"/>
<button name="do_cancel" string="Cancel Event" states="tentative,confirmed" type="object"/>
<field name="state" widget="statusbar"
statusbar_visible="tentative,confirmed" statusbar_colors='{"proforma":"blue"}'/>
</header>
<sheet>
<group col="6">
<field name="name" string="Summary USEDDDDDDDDDDDDDDDDDDDDD"
colspan="4" required="1"/>
<field name="allday" colspan="2" on_change="onchange_dates(date,False,False,allday)"/>
<newline/>
<field name="date" string="Start Date" required="1"
on_change="onchange_dates(date,duration,False,allday)"/>
<field name="duration" widget="float_time"
on_change="onchange_dates(date,duration,False,allday)" attrs="{'invisible': [('allday', '=', True)]}"/>
<field name="date_deadline" string="End Date" required="1" on_change="onchange_dates(date,False,date_deadline)"/>
<field name="location"/>
<field name="alarm_ids" string="Reminder" />
<group colspan="2" col="4" attrs="{'readonly': [('state','=','done')]}">
<field name="recurrency"/>
</group>
</group>
<notebook>
<page string="Event">
<group col="6" colspan="4">
<separator colspan="6" string="Visibility"/>
<field name="show_as" string="Show Time as"/>
<field name="class" string="Privacy"/>
<field name="recurrent_id_date" invisible="1"/>
<field name="recurrent_id" invisible="1"/>
</group>
<separator string="Description"/>
<field name="description"/>
</page>
<page string="Invitation Detail">
<field name="attendee_ids" colspan="4"
nolabel="1" widget="one2many" mode="tree">
<tree string="Invitation details" editable="top">
<field name="email"/>
<field name="state"/>
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm"/>
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" icon="gtk-apply"/>
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel"/>
</tree>
<form string="Invitation details" version="7.0">
<notebook colspan="4">
<page string="Details">
<group col="4">
<field name="email"/>
<field name="rsvp"/>
<field name="cutype"/>
</group>
<group col="4">
<field name="state"/>
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain"
type="object"
icon="terp-crm"/>
<button name="do_accept"
string="Accept"
states="needs-action,tentative,declined"
type="object"
icon="gtk-apply"/>
<button name="do_decline"
string="Decline"
states="needs-action,tentative,accepted"
type="object"
icon="gtk-cancel"/>
</group>
</page>
</notebook>
</form>
</field>
</page>
<page string="Recurrency Option" attrs="{'invisible': [('recurrency','=',False)]}">
<group col="4" colspan="4" name="rrule">
<group col="4" colspan="4">
<field name="rrule_type" string="Recurrency period"
attrs="{'readonly':[('recurrent_id','!=',False)]}"/>
<field name="interval"/>
<separator string="End of Recurrence" colspan="4"/>
<field name="end_type"/>
<label string=" " colspan="2"/>
<newline/>
<field name="count" attrs="{'invisible' : [('end_type', '!=', 'count')] }"/>
<label string=" " colspan="2"/>
<newline/>
<field name="end_date" attrs="{'invisible' : [('end_type', '!=', 'end_date')] }"/>
<newline/>
</group>
<group col="8" colspan="4" name="Select weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}">
<separator string="Choose day where repeat the meeting" colspan="8"/>
<field name="mo" colspan="1"/>
<field name="tu" colspan="1"/>
<field name="we" colspan="1"/>
<field name="th" colspan="1"/>
<newline/>
<field name="fr" colspan="1"/>
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline/>
</group>
<group col="10" colspan="4"
attrs="{'invisible' : [('rrule_type','!=','monthly')]}">
<separator string="Choose day in the month where repeat the meeting" colspan="12"/>
<group col="2" colspan="1">
<field name="month_by"/>
</group>
<group col="2" colspan="1">
<field name="day"
attrs="{'required' : [('month_by','=','date'), ('rrule_type','=','monthly')],
'invisible' : ['|', ('month_by','=','day'), ('rrule_type','!=','monthly')]}"/>
</group>
<group col="3" colspan="1"
attrs="{'invisible' : ['|', ('month_by','=','date'), ('rrule_type','!=','monthly')]}">
<field name="byday" string="The"
attrs="{'required' : [('month_by','=','day'), ('rrule_type','=','monthly')]}"/>
<field name="week_list" nolabel="1"
attrs="{'required' : [('month_by','=','day'), ('rrule_type','=','monthly')]}"/>
</group>
</group>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!-- Event list view -->
<record model="ir.ui.view" id="event_tree_view">
<field name="name">Event Tree</field>
<field name="model">calendar.event</field>
<field name="arch" type="xml">
<tree string="Events">
<field name="name" string="Subject"/>
<field name="date" string="Event Date"/>
<field name="location"/>
<field name="show_as"/>
<field name="class" string="Privacy"/>
<field name="user_id" invisible="1"/>
<field name="state" invisible="1"/>
</tree>
</field>
</record>
<!-- Event calendar view -->
<record model="ir.ui.view" id="event_calendar_view">
<field name="name">Events Calendar</field>
<field name="model">calendar.event</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Events" date_start="date" color="show_as" date_delay="duration" all_day="allday">
<field name="name"/>
<field name="class"/>
<field name="show_as"/>
</calendar>
</field>
</record>
<!-- Event search view -->
<record id="view_calendar_event_filter" model="ir.ui.view">
<field name="name">Calendar Events Search</field>
<field name="model">calendar.event</field>
<field name="arch" type="xml">
<search string="Search Events">
<field name="name" filter_domain="['|',('name','ilike',self),('location','ilike',self)]" string="Event"/>
<field name="show_as"/>
<field name="class" string="Privacy"/>
<filter icon="terp-go-today" string="My Events" domain="[('user_id','=',uid)]" help="My Events"/>
<separator/>
<filter icon="terp-check" string="Confirmed" domain="[('state','=','confirmed')]" help="Confirmed Events"/>
<field name="user_id"/>
<group expand="0" string="Group By...">
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'show_as'}"/>
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Event Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Start Date of Event by Month"/>
</group>
</search>
</field>
</record>
<!-- Event action -->
<record id="action_view_event" model="ir.actions.act_window">
<field name="name">Events</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">calendar.event</field>
<field name="view_type">form</field>
<field name="view_mode">calendar,tree,form</field>
<field name="search_view_id" ref="view_calendar_event_filter"/>
</record>
<!-- Event menu -->
<menuitem id="menu_events"
name="Events" parent="base.menu_calendar_configuration"
sequence="15" action="action_view_event"/>
</data>
</openerp>

View File

@ -77,5 +77,17 @@ class meetting_invitation(http.Controller):
'modules': simplejson.dumps(webmain.module_boot(db)),
'init': "s.base_calendar.event('%s', '%s', '%s', '%s' , '%s');" % (db, action, id, 'form', json.dumps(attendee_data)),
}
@http.route('/calendar/NextNotify', type='json', auth="none")
def NextNotify(self, type=''):
registry = openerp.modules.registry.RegistryManager.get(request.session.db)
uid = request.session.uid
context = request.session.context
with registry.cursor() as cr:
if type=='GET':
res = registry.get("calendar.alarm_manager").get_next_event(cr,uid,context=context)
return res
elif type=="UPDATE":
res = registry.get("res.partner").update_cal_last_event(cr,uid,context=context)
return res

View File

@ -1,959 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import time
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
from base_calendar import get_real_ids, base_calendar_id2real_id,get_recurrent_dates
from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta, date
import pytz
from openerp import tools, SUPERUSER_ID
import openerp
import hashlib
import re
#
# crm.meeting is defined here so that it may be used by modules other than crm,
# without forcing the installation of crm.
#
class crm_meeting_type(osv.Model):
_name = 'crm.meeting.type'
_description = 'Meeting Type'
_columns = {
'name': fields.char('Name', size=64, required=True, translate=True),
}
class crm_meeting(osv.Model):
""" Model for CRM meetings """
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
_inherit = ["mail.thread", "ir.needaction_mixin"]
def _get_recurrency_end_date(self, data, context=None):
if data.get('recurrency') and data.get('end_type') in ('count', unicode('count')):
data_date_deadline = datetime.strptime(data.get('date_deadline'), '%Y-%m-%d %H:%M:%S')
if data.get('rrule_type') in ('daily', unicode('count')):
rel_date = relativedelta(days=data.get('count')+1)
elif data.get('rrule_type') in ('weekly', unicode('weekly')):
rel_date = relativedelta(days=(data.get('count')+1)*7)
elif data.get('rrule_type') in ('monthly', unicode('monthly')):
rel_date = relativedelta(months=data.get('count')+1)
elif data.get('rrule_type') in ('yearly', unicode('yearly')):
rel_date = relativedelta(years=data.get('count')+1)
end_date = data_date_deadline + rel_date
print "rel date : ",rel_date," + deadline : ",data_date_deadline, " = ",end_date
else:
end_date = data.get('end_date')
print "End date : ", end_date
return end_date
def _find_my_attendee(self, cr, uid, meeting_ids, context=None):
"""
Return the first attendee where the user connected has been invited from all the meeting_ids in parameters
"""
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
for meeting_id in meeting_ids:
for attendee in self.browse(cr,uid,meeting_id,context).attendee_ids:
if user.partner_id.id == attendee.partner_id.id:
return attendee
return False
def _get_display_time(self, cr, uid, meeting_id, context=None):
"""
Return date and time (from to from) based on duration with timezone in string :
eg.
1) if user add duration for 2 hours, return : August-23-2013 at ( 04-30 To 06-30) (Europe/Brussels)
2) if event all day ,return : AllDay, July-31-2013
"""
if context is None:
context = {}
tz = context.get('tz', pytz.timezone('UTC'))
meeting = self.browse(cr, uid, meeting_id, context=context)
date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
date_deadline = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context)
event_date = date.strftime('%B-%d-%Y')
display_time = date.strftime('%H-%M')
if meeting.allday:
time = _("AllDay , %s") % (event_date)
elif meeting.duration < 24:
duration = date + timedelta(hours= meeting.duration)
time = ("%s at ( %s To %s) (%s)") % (event_date, display_time, duration.strftime('%H-%M'), tz)
else :
time = ("%s at %s To\n %s at %s (%s)") % (event_date, display_time, date_deadline.strftime('%B-%d-%Y'), date_deadline.strftime('%H-%M'), tz)
return time
def _compute(self, cr, uid, ids, fields, arg, context=None):
res = {}
for meeting_id in ids:
res[meeting_id] = {}
attendee = self._find_my_attendee(cr, uid, [meeting_id], context)
for field in fields:
if field == 'is_attendee':
res[meeting_id][field] = True if attendee else False
elif field == 'attendee_status':
res[meeting_id][field] = attendee.state if attendee else 'needs-action'
elif field == 'display_time':
res[meeting_id][field] = self._get_display_time(cr, uid, meeting_id, context=context)
return res
def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
"""
Gets Recurrence rule string according to value type RECUR of iCalendar from the values given.
@return: dictionary of rrule value.
"""
result = {}
if not isinstance(ids, list):
ids = [ids]
for id in ids:
#read these fields as SUPERUSER because if the record is private a normal search could return False and raise an error
data = self.browse(cr, SUPERUSER_ID, id, context=context)
if data.interval < 0:
raise osv.except_osv(_('Warning!'), _('Interval cannot be negative.'))
if data.count <= 0:
raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
data = self.read(cr, uid, id, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list' ], context=context)
event = data['id']
if data['recurrency']:
result[event] = self.compute_rule_string(data)
else:
result[event] = ""
return result
def _rrule_write(self, obj, cr, uid, ids, field_name, field_value, args, context=None):
data = self._get_empty_rrule_data()
if field_value:
data['recurrency'] = True
for event in self.browse(cr, uid, ids, context=context):
rdate = rule_date or event.date #TO CHECK :/
update_data = self._parse_rrule(field_value, dict(data), rdate)
data.update(update_data)
self.write(cr, uid, ids, data, context=context)
return True
def _tz_get(self, cr, uid, context=None):
return [(x.lower(), x) for x in pytz.all_timezones]
_columns = {
'create_date': fields.datetime('Creation Date', readonly=True),
'write_date': fields.datetime('Write Date', readonly=True),
'state': fields.selection([('draft', 'Unconfirmed'), ('open', 'Confirmed')], string='Status', size=16, readonly=True, track_visibility='onchange'),
# Meeting fields
'name': fields.char('Meeting Subject', size=128, required=True, states={'done': [('readonly', True)]}),
'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
'attendee_status': fields.function(_compute, string='Attendee Status', type="selection", multi='attendee'),
'display_time': fields.function(_compute, string='Event Time', type="char", multi='attendee'),
# ---------------------
# OLD CALENDAR_EVENT
# ---------------------
'id': fields.integer('ID', readonly=True),
'sequence': fields.integer('Sequence'),
'date': fields.datetime('Date', states={'done': [('readonly', True)]}, required=True,),
'date_deadline': fields.datetime('End Date', states={'done': [('readonly', True)]}, required=True,),
'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
'description': fields.text('Description', states={'done': [('readonly', True)]}),
'class': fields.selection([('public', 'Public'), ('private', 'Private'), ('confidential', 'Public for Employees')], 'Privacy', states={'done': [('readonly', True)]}),
'location': fields.char('Location', size=264, help="Location of Event", states={'done': [('readonly', True)]}),
'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Show Time as', states={'done': [('readonly', True)]}),
#'state': fields.selection([('tentative', 'Uncertain'),('cancelled', 'Cancelled'),('confirmed', 'Confirmed'),],'Status', readonly=True, track_visibility='onchange'),
#FIELD FOR RECURRENCY
'exdate': fields.text('Exception Date/Times', help="This property defines the list of date/time exceptions for a recurring calendar component."),
'rrule': fields.function(_get_rulestring, type='char', size=124, fnct_inv=_rrule_write, store=True, string='Recurrent Rule'),
'rrule_type': fields.selection([('daily', 'Day(s)'),('weekly', 'Week(s)'),('monthly', 'Month(s)'),('yearly', 'Year(s)')], 'Recurrency', states={'done': [('readonly', True)]}, help="Let the event automatically repeat at that interval"),
'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
'recurrent_id': fields.integer('Recurrent ID'),
#'recurrent_id_date': fields.datetime('Recurrent ID date'),
#'recurrence_end_date': fields.function(_get_recurrence_end_date, type='datetime', store=True, string='Recurrence end date',priority=30),
'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'),
'end_type' : fields.selection([('count', 'Number of repetitions'), ('end_date','End date')], 'Recurrence Termination'),
'interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"),
'count': fields.integer('Repeat', help="Repeat x times"),
'mo': fields.boolean('Mon'),
'tu': fields.boolean('Tue'),
'we': fields.boolean('Wed'),
'th': fields.boolean('Thu'),
'fr': fields.boolean('Fri'),
'sa': fields.boolean('Sat'),
'su': fields.boolean('Sun'),
'month_by': fields.selection([('date', 'Date of month'),('day', 'Day of month')], 'Option'),
'day': fields.integer('Date of month'),
'week_list': fields.selection([('MO', 'Monday'),('TU', 'Tuesday'),('WE', 'Wednesday'),('TH', 'Thursday'),('FR', 'Friday'),('SA', 'Saturday'),('SU', 'Sunday')], 'Weekday'),
'byday': fields.selection([('1', 'First'),('2', 'Second'),('3', 'Third'),('4', 'Fourth'),('5', 'Fifth'),('-1', 'Last')], 'By day'),
'end_date': fields.date('Repeat Until'),
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'color_partner_id': fields.related('user_id','partner_id','id',type="int",string="colorize",store=False), #Color of creator
'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the event alarm information without removing it."),
'categ_ids': fields.many2many('crm.meeting.type', 'meeting_category_rel', 'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.many2many('calendar.attendee', 'crmmeeting_attendee_rel', 'crmmeeting_id', 'attendee_id', 'Attendees'),
'partner_ids': fields.many2many('res.partner', string='Attendees', states={'done': [('readonly', True)]}),
'alarm_ids': fields.many2many('calendar.alarm', string='Reminders'),
}
_defaults = {
'end_type': 'count',
'count': 1,
'rrule_type': False,
'state': 'draft',
'class': 'public',
'show_as': 'busy',
'month_by': 'date',
'interval': 1,
'active': 1,
'user_id': lambda self, cr, uid, ctx: uid,
'partner_ids': lambda self, cr, uid, ctx: [self.pool.get('res.users').browse(cr, uid, [uid],context=ctx)[0].partner_id.id]
}
def _check_closing_date(self, cr, uid, ids, context=None):
for event in self.browse(cr, uid, ids, context=context):
if event.date_deadline < event.date:
return False
return True
_constraints = [
(_check_closing_date, 'Error ! End date cannot be set before start date.', ['date_deadline']),
]
def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None):
"""Returns duration and/or end date based on values passed
@param ids: List of calendar event's IDs.
@param start_date: Starting date
@param duration: Duration between start date and end date
@param end_date: Ending Datee
"""
if context is None:
context = {}
value = {}
if not start_date:
return value
if not end_date and not duration:
duration = 1.00
value['duration'] = duration
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
if allday: # For all day event
duration = 24.0
value['duration'] = duration
# change start_date's time to 00:00:00 in the user's timezone
user = self.pool.get('res.users').browse(cr, uid, uid)
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone
start = start.replace(hour=0, minute=0, second=0) # remove time
start = start.astimezone(pytz.utc) # convert start back to utc
value['date'] = start.strftime("%Y-%m-%d %H:%M:%S")
if end_date and not duration:
end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
diff = end - start
duration = float(diff.days)* 24 + (float(diff.seconds) / 3600)
value['duration'] = round(duration, 2)
elif not end_date:
end = start + timedelta(hours=duration)
value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S")
elif end_date and duration and not allday:
# we have both, keep them synchronized:
# set duration based on end_date (arbitrary decision: this avoid
# getting dates like 06:31:48 instead of 06:32:00)
end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
diff = end - start
duration = float(diff.days)* 24 + (float(diff.seconds) / 3600)
value['duration'] = round(duration, 2)
return {'value': value}
def unlink_events(self, cr, uid, ids, context=None):
"""
This function deletes event which are linked with the event with recurrent_id
(Removes the events which refers to the same UID value)
"""
print "UNLINK EVENTS WITH", ids
if context is None:
context = {}
for event_id in ids:
r_ids = self.search(cr,uid,[('recurrent_id','=',event_id)],context=context)
self.unlink(cr, uid, r_ids, context=context)
return True
def new_invitation_token(self, cr, uid, record, partner_id):
db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid')
invitation_token = hashlib.sha256('%s-%s-%s-%s-%s' % (time.time(), db_uuid, record._name, record.id, partner_id)).hexdigest()
return invitation_token
def create_attendees(self, cr, uid, ids, context):
att_obj = self.pool.get('calendar.attendee')
user_obj = self.pool.get('res.users')
current_user = user_obj.browse(cr, uid, uid, context=context)
for event in self.browse(cr, uid, ids, context):
attendees = {}
for att in event.attendee_ids:
attendees[att.partner_id.id] = True
new_attendees = []
mail_to = ""
for partner in event.partner_ids:
if partner.id in attendees:
continue
access_token = self.new_invitation_token(cr, uid, event, partner.id)
att_id = self.pool.get('calendar.attendee').create(cr, uid, {
'partner_id': partner.id,
'user_id': partner.user_ids and partner.user_ids[0].id or False,
'ref': event.id,
'access_token': access_token,
'email': partner.email,
}, context=context)
if partner.email:
mail_to = mail_to + " " + partner.email
new_attendees.append(att_id)
self.write(cr, uid, [event.id], {'attendee_ids': [(4, att) for att in new_attendees]},context=context)
return True
def get_recurrent_ids(self, cr, uid, select, domain, limit=100, context=None):
"""Gives virtual event ids for recurring events based on value of Recurrence Rule
This method gives ids of dates that comes between start date and end date of calendar views
@param limit: The Number of Results to Return """
if not context:
context = {}
result = []
for data in self.read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'date', 'vtimezone'], context=context):
if not data['recurrency'] or not data['rrule']:
result.append(data['id'])
continue
# event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
# event_date = pytz.UTC.localize(event_date)
rdates = get_recurrent_dates(data['rrule'], data['date'], data['exdate'], data['vtimezone'], context=context)
for r_date in rdates:
# fix domain evaluation
# step 1: check date and replace expression by True or False, replace other expressions by True
# step 2: evaluation of & and |
# check if there are one False
pile = []
ok = True
for arg in domain:
if str(arg[0]) in (str('date'), str('date_deadline'), str('end_date')):
if (arg[1] == '='):
ok = r_date.strftime('%Y-%m-%d')==arg[2]
if (arg[1] == '>'):
ok = r_date.strftime('%Y-%m-%d')>arg[2]
if (arg[1] == '<'):
ok = r_date.strftime('%Y-%m-%d')<arg[2]
if (arg[1] == '>='):
ok = r_date.strftime('%Y-%m-%d')>=arg[2]
if (arg[1] == '<='):
ok = r_date.strftime('%Y-%m-%d')<=arg[2]
pile.append(ok)
elif str(arg) == str('&') or str(arg) == str('|'):
pile.append(arg)
else:
pile.append(True)
pile.reverse()
new_pile = []
for item in pile:
if not isinstance(item, basestring):
res = item
elif str(item) == str('&'):
first = new_pile.pop()
second = new_pile.pop()
res = first and second
elif str(item) == str('|'):
first = new_pile.pop()
second = new_pile.pop()
res = first or second
new_pile.append(res)
if [True for item in new_pile if not item]:
continue
# idval = real_id2base_calendar_id(data['id'], r_date.strftime("%Y-%m-%d %H:%M:%S"))
idval = '%d-%s' % (data['id'], r_date.strftime("%Y%m%d%H%M%S"))
result.append(idval)
if isinstance(select, (str, int, long)):
return ids and ids[0] or False
else:
ids = list(set(result))
return ids
def compute_rule_string(self, data):
"""
Compute rule string according to value type RECUR of iCalendar from the values given.
@param self: the object pointer
@param data: dictionary of freq and interval value
@return: string containing recurring rule (empty if no rule)
"""
def get_week_string(freq, data):
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: data.get(x) and x in weekdays, data))
#byday = map(lambda x: x.upper(),[data[day] for day in weekdays if data[day]])
if byday:
return ';BYDAY=' + ','.join(byday)
return ''
def get_month_string(freq, data):
if freq == 'monthly':
if data.get('month_by')=='date' and (data.get('day') < 1 or data.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select a proper day of the month."))
if data.get('month_by')=='day': #Eg : Second Monday of the month
return ';BYDAY=' + data.get('byday') + data.get('week_list')
elif data.get('month_by')=='date': #Eg : 16th of the month
return ';BYMONTHDAY=' + str(data.get('day'))
return ''
def get_end_date(data):
if data.get('end_date'):
data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959Z'
return (data.get('end_type') == 'count' and (';COUNT=' + str(data.get('count'))) or '') +\
((data.get('end_date_new') and data.get('end_type') == 'end_date' and (';UNTIL=' + data.get('end_date_new'))) or '')
freq = data.get('rrule_type', False) #day/week/month/year
res = ''
if freq:
interval_srting = data.get('interval') and (';INTERVAL=' + str(data.get('interval'))) or ''
res = 'FREQ=' + freq.upper() + get_week_string(freq, data) + interval_srting + get_end_date(data) + get_month_string(freq, data)
return res
def _get_empty_rrule_data(self):
return {
'byday' : False,
'recurrency' : False,
'end_date' : False,
'rrule_type' : False,
'month_by' : False,
'interval' : 0,
'count' : False,
'end_type' : False,
'mo' : False,
'tu' : False,
'we' : False,
'th' : False,
'fr' : False,
'sa' : False,
'su' : False,
'day' : False,
'week_list' : False
}
def _parse_rrule(self, rule, data, date_start):
day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
r = rrule.rrulestr(rule, dtstart=datetime.strptime(date_start, "%Y-%m-%d %H:%M:%S"))
if r._freq > 0 and r._freq < 4:
data['rrule_type'] = rrule_type[r._freq]
data['count'] = r._count
data['interval'] = r._interval
data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S")
#repeat weekly
if r._byweekday:
for i in xrange(0,7):
if i in r._byweekday:
data[day_list[i]] = True
data['rrule_type'] = 'weekly'
#repeat monthly by nweekday ((weekday, weeknumber), )
if r._bynweekday:
data['week_list'] = day_list[r._bynweekday[0][0]].upper()
data['byday'] = r._bynweekday[0][1]
data['month_by'] = 'day'
data['rrule_type'] = 'monthly'
if r._bymonthday:
data['day'] = r._bymonthday[0]
data['month_by'] = 'date'
data['rrule_type'] = 'monthly'
#repeat yearly but for openerp it's monthly, take same information as monthly but interval is 12 times
if r._bymonth:
data['interval'] = data['interval'] * 12
#FIXEME handle forever case
#end of recurrence
#in case of repeat for ever that we do not support right now
if not (data.get('count') or data.get('end_date')):
data['count'] = 100
if data.get('count'):
data['end_type'] = 'count'
else:
data['end_type'] = 'end_date'
return data
#def _get_data(self, cr, uid, id, context=None):
# return self.read(cr, uid, id,['date', 'date_deadline'])
# def need_to_update(self, event_id, vals):
# split_id = str(event_id).split("-")
# if len(split_id) < 2:
# return False
# else:
# date_start = vals.get('date', '')
# try:
# date_start = datetime.strptime(date_start, '%Y-%m-%d %H:%M:%S').strftime("%Y%m%d%H%M%S")
# return date_start == split_id[1]
# except Exception:
# return True
def message_get_subscription_data(self, cr, uid, ids, user_pid=None, context=None):
res = {}
for virtual_id in ids:
real_id = base_calendar_id2real_id(virtual_id)
result = super(crm_meeting, self).message_get_subscription_data(cr, uid, [real_id], user_pid=None, context=context)
res[virtual_id] = result[real_id]
return res
def onchange_partner_ids(self, cr, uid, ids, value, context=None):
""" The basic purpose of this method is to check that destination partners
effectively have email addresses. Otherwise a warning is thrown.
:param value: value format: [[6, 0, [3, 4]]]
"""
res = {'value': {}}
if not value or not value[0] or not value[0][0] == 6:
return
res.update(self.check_partners_email(cr, uid, value[0][2], context=context))
return res
def check_partners_email(self, cr, uid, partner_ids, context=None):
##TODO : REFACTOR !
""" Verify that selected partner_ids have an email_address defined.
Otherwise throw a warning. """
partner_wo_email_lst = []
for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context):
if not partner.email:
partner_wo_email_lst.append(partner)
if not partner_wo_email_lst:
return {}
warning_msg = _('The following contacts have no email address :')
for partner in partner_wo_email_lst:
warning_msg += '\n- %s' % (partner.name)
return {'warning': {
'title': _('Email addresses not found'),
'message': warning_msg,
}
}
# ----------------------------------------
# OpenChatter
# ----------------------------------------
# shows events of the day for this user
def _needaction_domain_get(self, cr, uid, context=None):
print 'IN _needaction_domain_get'
return [('end_date', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('date', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('user_id', '=', uid)]
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
if isinstance(thread_id, str):
thread_id = get_real_ids(thread_id)
if context.get('default_date'):
del context['default_date']
return super(crm_meeting, self).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, **kwargs)
def do_sendmail(self, cr, uid, ids, context=None):
for event in self.browse(cr, uid, ids, context):
attendees_dest = []
for partner in event.partner_ids:
att_id = self.pool.get('calendar.attendee').search(cr, uid,[('partner_id','=',partner.id),('ref','=',event.id)], context=context)
attendees_dest.append(att_id[0])
current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if current_user.email:
is_sent_mail = self.pool.get('calendar.attendee')._send_mail(cr, uid, attendees_dest, '', email_from = current_user.email, context=context)
if is_sent_mail:
self.message_post(cr, uid, event.id, body=_("An invitation email has been sent to attendee(s)"), context=context)
return;
def do_attendee_decline(self, cr, uid, ids, context=None):
attendee_pool = self.pool.get('calendar.attendee')
attendee = self._find_my_attendee(cr, uid, ids, context)
return attendee_pool.do_decline(cr, uid, [attendee.id], context=context)
def do_attendee_accept(self, cr, uid, ids, context=None):
attendee_pool = self.pool.get('calendar.attendee')
attendee = self._find_my_attendee(cr, uid, ids, context)
return attendee_pool.do_accept(cr, uid, [attendee.id], context=context)
def do_attendee_maybe(self, cr, uid, ids, context=None):
attendee_pool = self.pool.get('calendar.attendee')
attendee = self._find_my_attendee(cr, uid, ids, context)
return attendee_pool.do_tentative(cr, uid, [attendee.id], context=context)
def do_meeting_uncertain(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@param ids: List of Event IDs """
return self.write(cr, uid, ids, {'state': 'draft'}, context)
def do_meeting_confirm(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@param ids: List of Event IDs
@param context: A standard dictionary for contextual values
"""
return self.write(cr, uid, ids, {'state': 'open'}, context)
def get_attendee(self, cr, uid, meeting_id, context=None):
#Used for view in controller
invitation = {'meeting':{}, 'attendee': [], 'logo': ''}
attendee_pool = self.pool.get('calendar.attendee')
company_logo = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.logo
meeting = self.browse(cr, uid, int(meeting_id), context)
invitation['meeting'] = {
'event':meeting.name,
'where': meeting.location,
'when':meeting.display_time
}
invitation['logo'] = company_logo.replace('\n','\\n') if company_logo else ''
for attendee in meeting.attendee_ids:
invitation['attendee'].append({'name':attendee.cn,'status': attendee.state})
return invitation
def get_interval(self, cr, uid, ids, date, interval, context=None):
#Function used only in crm_meeting_data.xml for email template
date = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
if interval == 'day':
res = str(date.day)
elif interval == 'month':
res = date.strftime('%B') + " " + str(date.year)
elif interval == 'dayname':
res = date.strftime('%A')
elif interval == 'time':
res = date.strftime('%I:%M %p')
return res
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
print 'IN SEARCH',args
if context is None:
context={}
if context.get('mymeetings',False):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context).partner_id.id
args += ['|', ('partner_ids', 'in', [partner_id]), ('user_id', '=', uid)]
new_args = []
for arg in args:
new_arg = arg
if arg[0] in ('date', unicode('date')) and arg[1]==">=":
if context.get('virtual_id', True):
new_args += ['|','&',('recurrency','=',1),('end_date', arg[1], arg[2])]
# new_args += ['|','&',('recurrency','=',1),('date_deadline', arg[1], arg[2])]
elif arg[0] in ('date', unicode('date')):
if context.get('virtual_id', True):
new_args = ['|','&',('recurrency','=',1),('end_date', arg[1], arg[2])]
# new_args += ['|','&',('recurrency','=',1),('date_deadline', arg[1], arg[2])]
elif arg[0] == "id":
new_id = get_real_ids(arg[2])
new_arg = (arg[0], arg[1], new_id)
new_args.append(new_arg)
#offset, limit and count must be treated separately as we may need to deal with virtual ids
print 'AFTER SEARCH',new_args
res = super(crm_meeting,self).search(cr, uid, new_args, offset=0, limit=0, order=order, context=context, count=False)
if context.get('virtual_id', True):
res = self.get_recurrent_ids(cr, uid, res, args, limit, context=context)
if count:
return len(res)
elif limit:
return res[offset:offset+limit]
return res
def copy(self, cr, uid, id, default=None, context=None):
if context is None:
context = {}
default = default or {}
default['attendee_ids'] = False
res = super(crm_meeting, self).copy(cr, uid, base_calendar_id2real_id(id), default, context)
return res
def write(self, cr, uid, ids, values, context=None):
print "Write - ",values
def _only_changes_to_apply_on_real_ids(field_names):
''' return True if changes are only to be made on the real ids'''
for field in field_names:
if field not in ['message_follower_ids']:
return False
return True
context = context or {}
if isinstance(ids, (str, int, long)):
ids = [ids]
res = False
# Special write of complex IDS
for event_id in ids[:]:
if len(str(event_id).split('-')) == 1:
continue
ids.remove(event_id)
real_event_id = base_calendar_id2real_id(event_id)
# if we are setting the recurrency flag to False or if we are only changing fields that
# should be only updated on the real ID and not on the virtual (like message_follower_ids):
# then set real ids to be updated.
if not values.get('recurrency', True) or _only_changes_to_apply_on_real_ids(values.keys()):
ids.append(real_event_id)
continue
#if edit one instance of a reccurrent id
data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
'rrule', 'duration', 'exdate'])
if data.get('rrule'):
data.update(
values,
recurrent_id=real_event_id,
#recurrent_id_date=data.get('date'),
rrule_type=False,
rrule='',
recurrency=False,
)
#do not copy the id
if data.get('id'):
del(data['id'])
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
date_new = event_id.split('-')[1]
date_new = time.strftime("%Y%m%dT%H%M%SZ", \
time.strptime(date_new, "%Y%m%d%H%M%S"))
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
res = super(crm_meeting, self).write(cr, uid, [real_event_id], {'exdate': exdate})
context.update({'active_id': new_id, 'active_ids': [new_id]})
continue
res = super(crm_meeting, self).write(cr, uid, ids, values, context=context)
# set end_date for calendar searching
if values.get('recurrency', True) and values.get('end_type', 'count') in ('count', unicode('count')) and \
(values.get('rrule_type') or values.get('count') or values.get('date') or values.get('date_deadline')):
for data in self.read(cr, uid, ids, ['date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context):
end_date = self._get_recurrency_end_date(data, context=context)
super(crm_meeting, self).write(cr, uid, [data['id']], {'end_date': end_date}, context=context)
if values.get('partner_ids', False):
self.create_attendees(cr, uid, ids, context)
return res or True and False
def create(self, cr, uid, vals, context=None):
print "Create - ",vals
if context is None:
context = {}
if vals.get('duration', '') and vals.get('duration', '')==24 and not 'allday' in vals: #If from quick create
vals['allday'] = True
res = super(crm_meeting, self).create(cr, uid, vals, context)
#res = self.write(cr, uid, id_res,vals, context)
self.create_attendees(cr, uid, [res], context)
return res
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
print 'IN READ_GROUP'
if not context:
context = {}
if 'datesss' in groupby:
raise osv.except_osv(_('Warning!'), _('Group by date is not supported, use the calendar view instead.'))
virtual_id = context.get('virtual_id', True)
context.update({'virtual_id': False})
res = super(crm_meeting, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
for re in res:
#remove the count, since the value is not consistent with the result of the search when expand the group
for groupname in groupby:
if re.get(groupname + "_count"):
del re[groupname + "_count"]
re.get('__context', {}).update({'virtual_id' : virtual_id})
return res
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
print 'IN READ [',ids,']'
if context is None:
context = {}
fields2 = fields and fields[:] or None
EXTRAFIELDS = ('class','user_id','duration', 'date','rrule', 'vtimezone', 'exdate')
for f in EXTRAFIELDS:
if fields and (f not in fields):
fields2.append(f)
# FIXME This whole id mangling has to go!
if isinstance(ids, (str, int, long)):
select = [ids]
else:
select = ids
select = map(lambda x: (x, base_calendar_id2real_id(x)), select)
result = []
real_data = super(crm_meeting, self).read(cr, uid, [real_id for base_calendar_id, real_id in select], fields=fields2, context=context, load=load)
real_data = dict(zip([x['id'] for x in real_data], real_data))
for base_calendar_id, real_id in select:
res = real_data[real_id].copy()
res = real_data[real_id].copy()
ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
recurrent_dates = [d.strftime("%Y-%m-%d %H:%M:%S") for d in get_recurrent_dates(res['rrule'], res['date'], res['exdate'],res['vtimezone'], context=context)]
if not (ls[1] in recurrent_dates or ls[1] in res['exdate']): #when update a recurrent event
print 'will raise'
#NEED TO UPDATE ACTIVE ID ?
#NEED TO CONVERT EXDATE IN STR_DATE
# raise KeyError(
# 'Virtual id %r is not valid, event %r can '
# 'not produce it.' % (base_calendar_id, real_id))
res['date'] = ls[1]
res['date_deadline'] = ls[2]
res['id'] = base_calendar_id
result.append(res)
for r in result:
if r['user_id']:
user_id = type(r['user_id']) in (tuple,list) and r['user_id'][0] or r['user_id']
if user_id==uid:
continue
if r['class']=='private':
for f in r.keys():
if f not in ('id','date','date_deadline','duration','user_id','state','interval','count'):
if isinstance(r[f], list):
r[f] = []
else:
r[f] = False
if f=='name':
r[f] = _('Busy')
for r in result:
for k in EXTRAFIELDS:
if (k in r) and (fields and (k not in fields)):
del r[k]
if isinstance(ids, (str, int, long)):
return result and result[0] or False
return result
def unlink(self, cr, uid, ids, context=None):
if not isinstance(ids, list):
ids = [ids]
res = False
attendee_obj=self.pool.get('calendar.attendee')
for event_id in ids[:]:
if len(str(event_id).split('-')) == 1:
continue
real_event_id = base_calendar_id2real_id(event_id)
data = self.read(cr, uid, real_event_id, ['exdate'], context=context)
date_new = event_id.split('-')[1]
date_new = time.strftime("%Y%m%dT%H%M%S", \
time.strptime(date_new, "%Y%m%d%H%M%S"))
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
self.write(cr, uid, [real_event_id], {'exdate': exdate}, context=context)
ids.remove(event_id)
for event in self.browse(cr, uid, ids, context=context):
if event.attendee_ids:
attendee_obj.unlink(cr, uid, [x.id for x in event.attendee_ids], context=context)
res = super(crm_meeting, self).unlink(cr, uid, ids, context=context)
#self.pool.get('res.alarm').do_alarm_unlink(cr, uid, ids, self._name)
self.unlink_events(cr, uid, ids, context=context)
return res
class mail_message(osv.osv):
_inherit = "mail.message"
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
'''
convert the search on real ids in the case it was asked on virtual ids, then call super()
'''
for index in range(len(args)):
if args[index][0] == "res_id" and isinstance(args[index][2], str):
args[index][2] = get_real_ids(args[index][2])
return super(mail_message, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
def _find_allowed_model_wise(self, cr, uid, doc_model, doc_dict, context=None):
if doc_model == 'crm.meeting':
for virtual_id in self.pool[doc_model].get_recurrent_ids(cr, uid, doc_dict.keys(), [], context=context):
doc_dict.setdefault(virtual_id, doc_dict[get_real_ids(virtual_id)])
return super(mail_message, self)._find_allowed_model_wise(cr, uid, doc_model, doc_dict, context=context)
class ir_attachment(osv.osv):
_inherit = "ir.attachment"
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
'''
convert the search on real ids in the case it was asked on virtual ids, then call super()
'''
for index in range(len(args)):
if args[index][0] == "res_id" and isinstance(args[index][2], str):
args[index][2] = get_real_ids(args[index][2])
return super(ir_attachment, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
def write(self, cr, uid, ids, vals, context=None):
'''
when posting an attachment (new or not), convert the virtual ids in real ids.
'''
if isinstance(vals.get('res_id'), str):
vals['res_id'] = get_real_ids(vals.get('res_id'))
return super(ir_attachment, self).write(cr, uid, ids, vals, context=context)
class invite_wizard(osv.osv_memory):
_inherit = 'mail.wizard.invite'
def default_get(self, cr, uid, fields, context=None):
'''
in case someone clicked on 'invite others' wizard in the followers widget, transform virtual ids in real ids
'''
result = super(invite_wizard, self).default_get(cr, uid, fields, context=context)
if 'res_id' in result:
result['res_id'] = get_real_ids(result['res_id'])
return result

View File

@ -1,160 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
<!-- CASE CATEGORY(categ_id) -->
<record model="crm.meeting.type" id="categ_meet1">
<field name="name">Customer Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet2">
<field name="name">Internal Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet3">
<field name="name">Off-site Meeting</field>
</record>
<record model="crm.meeting.type" id="categ_meet4">
<field name="name">Open Discussion</field>
</record>
<record model="crm.meeting.type" id="categ_meet5">
<field name="name">Feedback Meeting</field>
</record>
<record model="res.request.link" id="request_link_meeting">
<field name="name">Meeting</field>
<field name="object">crm.meeting</field>
</record>
<record id="crm_email_template_meeting_invitation" model="email.template">
<field name="name">CRM Meeting Invitation</field>
<field name="email_from">${object.user_id.email or ''}</field>
<field name="subject">${object.name}</field>
<field name="email_to" >${ctx['att_obj'].email}</field>
<field name="model_id" ref="base_calendar.model_crm_meeting"/>
<field name="auto_delete" eval="True"/>
<field name="body_html"><![CDATA[
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>${object.name}</title>
</head>
<body>
<div style="border-radius: 2px; max-width: 1200px; height: auto;margin-left: auto;margin-right: auto;background-color:#f9f9f9;">
<div style="height:auto;text-align: center;font-size : 30px;color: #8A89BA;">
<strong>${object.name}</strong>
</div>
<div style="height: 50px;text-align: left;font-size : 14px;border-collapse: separate;margin-top:10px">
<strong style="margin-left:12px">Hello ${ctx['att_obj'].cn}</strong> ,<br/><p style="margin-left:12px">${object.organizer} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.</p>
</div>
<div style="height: auto;margin-left:12px;margin-top:30px;">
<table>
<tr>
<td>
<div style="border-top-left-radius:3px;border-top-right-radius:3px;font-size:12px;border-collapse:separate;text-align:center;font-weight:bold;color:#ffffff;width:130px;min-height: 18px;border-color:#ffffff;background:#8a89ba;padding-top: 4px;">${object.get_interval(object.date, 'dayname')}</div>
<div style="font-size:48px;min-height:auto;font-weight:bold;text-align:center;color: #5F5F5F;background-color: #E1E2F8;width: 130px;">
${object.get_interval(object.date,'day')}
</div>
<div style='font-size:12px;text-align:center;font-weight:bold;color:#ffffff;background-color:#8a89ba'>${object.get_interval(object.date, 'month')}</div>
<div style="border-collapse:separate;color:#8a89ba;text-align:center;width: 128px;font-size:12px;border-bottom-right-radius:3px;font-weight:bold;border:1px solid;border-bottom-left-radius:3px;">${object.get_interval(object.date, 'time')}</div>
</td>
<td>
<table cellspacing="0" cellpadding="0" border="0" style="margin-top: 15px; margin-left: 10px;font-size: 16px;">
% if object.location :
<tr style=" height: 30px;">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1" style="vertical-align:top;">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px" >
: ${object.location}
<span style= "color:#A9A9A9; ">(<a href="http://maps.google.com/maps?oi=map&q=${object.location}">View Map</a>)
</span>
</div>
</td>
</tr>
% endif
% if not object.location :
<tr style=" height: 30px;color:#909090">
<td>
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
Where
</div>
</td>
<td colspan="1">
<div style = "font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 14px;" >
: -
</div>
</td>
</tr>
% endif
% if object.description :
<tr style=" height:auto;">
<td style="vertical-align:top;">
<div style="height:auto; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: ${object.description or ''}
</div>
</td>
</tr>
% endif
% if not object.description :
<tr style=" height: 30px;color:#909090">
<td style="vertical-align:top;">
<div style="height: 25px; width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
What
</div>
</td>
<td colspan="3" style="vertical-align:text-top;">
<div style="font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
: -
</div>
</td>
</tr>
% endif
<tr style=" height: 30px;">
<td style="height: 25px;width: 120px; background : # CCCCCC; font-family: Lucica Grande', Ubuntu, Arial, Verdana, sans-serif;">
<div>
Attendees
</div>
</td>
<td colspan="3">
: <div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:grey;'></div>
<span style="margin-left:5px">You</span>
% for attendee in object.attendee_ids:
% if attendee.cn != ctx['att_obj'].cn:
<div style='display:inline-block; border-radius: 50%; width:10px; height:10px;background:${ctx['color'][attendee.state]};'></div>
<span style="margin-left:5px">${attendee.cn}</span>
% endif
% endfor
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div style="height: auto;width:300px; margin:0 auto;padding-top:20px;">
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#8A89BA;margin : 0 15px 0 0;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/accept?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Accept</a>
<a style="padding: 8px 30px 8px 30px;border-radius: 6px;border: 1px solid #CCCCCC;background:#808080;text-decoration: none;color:#FFFFFF;" href="${ctx['base_url']}/meeting_invitation/decline?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">Decline</a>
</div>
<div style="padding-top:10px;">
-- </br> Sent by ${object.user_id.name} from ${object.user_id.company_id.name}. View this meeting detail <a href="${ctx['base_url']}/meeting_invitation/view?db=${ctx['dbname']}&token=${ctx['att_obj'].access_token}&action=${ctx['action_id']}&id=${object.id}">directly in OpenERP.</a>
</div>
</div>
</body>
</html>
]]></field>
</record>
</data>
</openerp>

View File

@ -13,8 +13,7 @@
<field name="name"/>
</tree>
</field>
</record>
</record>
<record id="action_crm_meeting_type" model="ir.actions.act_window">
<field name="name">Meeting Types</field>
@ -23,34 +22,6 @@
<field name="view_id" ref="view_crm_meeting_type_tree"/>
</record>
<record id="view_test" model="ir.ui.view">
<field name="name">Friends View</field>
<field name="model">calendar.friendly</field>
<field name="arch" type="xml">
<tree string="Friends" editable="bottom" colors="red:active==True">
<field name="name"/>
<field name="color"/>
<field name="active"/>
</tree>
</field>
</record>
<record id="action_calendar_friend" model="ir.actions.act_window">
<field name="name">Calendar Friends</field>
<field name="res_model">calendar.friendly</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_test" />
</record>
<menuitem id="menu_calendar_configuration" name="Calendar" parent="base.menu_custom" groups="base.group_no_one"/>
<menuitem id="menu_crm_meeting_type" parent="menu_calendar_configuration" action="action_crm_meeting_type" groups="base.group_no_one"/>
<menuitem id="menu_calendar_friendly" parent="mail.mail_my_stuff" action="action_calendar_friend" />
<!-- CRM Meetings Form View -->
<record model="ir.ui.view" id="view_crm_meeting_form">
@ -58,13 +29,8 @@
<field name="model">crm.meeting</field>
<field name="arch" type="xml">
<form string="Meetings" version="7.0">
<header>
<button name="do_meeting_confirm" type="object" string="Confirm Meeting" attrs="{'invisible':['|',('is_attendee','=',False),('attendee_status','=','accepted')]}"/>
<!-- <button name="do_meeting_cancel" type="object" string="Cancel Meeting" attrs="{'invisible':['|',('is_attendee','=',False),('attendee_status','=','declined')]}"/> -->
<button name="do_meeting_uncertain" type="object" string="Uncertain Meeting" attrs="{'invisible':['|',('is_attendee','=',False),('attendee_status','=','tentative')]}"/>
<field name="state" invisible="True"/>
</header>
<sheet>
<field name="state" invisible="1"/>
<field name="is_attendee" invisible="1"/>
<field name="attendee_status" invisible="1"/>
<div class="oe_title">
@ -80,7 +46,7 @@
context="{'force_email':True}"
on_change="onchange_partner_ids(partner_ids)"
class="oe_inline"/>
<button name="do_sendmail" type="object" string="Send mail" attrs="{'invisible':['|',('partner_ids','=',False)]}" icon="terp-mail-message-new"/>
<button name="do_sendmail" type="object" string="Send mail(s)" attrs="{'invisible':[('partner_ids','=',False)]}" icon="terp-mail-message-new" class="oe_link"/>
</h2>
</div>
<notebook>
@ -103,7 +69,8 @@
</group>
<group>
<field name="categ_ids" widget="many2many_tags"/>
<field name="location"/>
<field name="alarm_ids" widget="many2many_tags" />
<field name="location"/>
</group>
</group>
@ -162,16 +129,13 @@
</group>
</group>
</page>
<page string="Reminders" groups="base.group_no_one">
<field name="alarm_ids" />
</page>
<page string="Invitations" groups="base.group_no_one">
<field name="attendee_ids" widget="one2many" >
<tree string="Invitation details" editable="top" >
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="email" string="Mail To"/>
<field name="state"/>
<field name="email"/>
<tree string="Invitation details" editable="top" create="false" delete="false">
<field name="partner_id" /> <!--on_change="onchange_partner_id(partner_id)"/>-->
<field name="state" />
<field name="email" widget="email"/>
<button name="do_attendee_maybe" states="needs-action,declined,accepted" string="Uncertain" type="object" icon="terp-crm" />
<button name="do_attendee_accept" string="Accept" states="needs-action,tentative,declined" type="object" icon="gtk-apply"/>
@ -196,25 +160,32 @@
<field name="model">crm.meeting</field>
<field name="arch" type="xml">
<tree string="Meetings" fonts="bold:message_unread==True">
<button name="do_run_stack_scheduler" string="RUN SCHEDULER" type="object" />
<field name="name" string="Subject"/>
<field name="date" string="Event Date"/>
<field name="user_id"/>
<field name="date"/>
<field name="location"/>
<field name="show_as"/>
<field name="class" string="Privacy"/>
<field name="state" invisible="True"/>
<field name="duration"/>
<field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<!-- CRM Meeting Calendar -->
<record model="ir.ui.view" id="view_crm_meeting_calendar">
<field name="name">CRM - Meetings Calendar</field>
<field name="model">crm.meeting</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<field name="arch" type="xml">
<calendar string="Meetings" date_start="date" date_stop="date_deadline" date_delay="duration"
display="[name] ([user_id])" color="color_partner_id" attendee="partner_ids">
display="[name] ([user_id])" color="color_partner_id" attendee="partner_ids"
color_is_attendee="True" use_contacts="True" quick_add="True">
<field name="name"/>
<field name="user_id"/>
<field name="color_partner_id"/>
@ -234,7 +205,6 @@
</record>
<!-- CRM Meeting Search View -->
<record id="view_crm_meeting_search" model="ir.ui.view">
<field name="name">CRM - Meetings Search</field>
<field name="model">crm.meeting</field>
@ -253,8 +223,7 @@
<group expand="0" string="Group By...">
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'show_as'}"/>
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
<filter string="Event Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Start Date of Event by Month"/>
</group>
</search>
@ -304,6 +273,15 @@
</record>
<menuitem name="Calendar" id="mail_menu_calendar" parent="mail.mail_my_stuff" sequence="10" action="action_crm_meeting"/>
<menuitem id="menu_calendar_configuration" name="Calendar" parent="base.menu_custom" groups="base.group_no_one"/>
<menuitem id="menu_crm_meeting_type" parent="menu_calendar_configuration" action="action_crm_meeting_type" groups="base.group_no_one"/>
<record id="action_crm_meeting_notify" model="ir.actions.act_window">
<field name="name">Meetings</field>
<field name="res_model">crm.meeting</field>
<field name="view_mode">form,calendar,tree,gantt</field>
<field name="view_id" ref="action_view_crm_meeting_form"/>
</record>
</data>
</openerp>

View File

@ -2,6 +2,80 @@ openerp.base_calendar = function(instance) {
var _t = instance.web._t;
var QWeb = instance.web.qweb;
instance.base_calendar = {}
instance.web.WebClient = instance.web.WebClient.extend({
getMyNotifBox: function(me) {
return $(me).closest(".ui-notify-message-style");
},
get_next_event: function() {
var self= this;
this.rpc("/calendar/NextNotify", {
type: "GET"
})
.then(
function(result) {
console.log(result);
_.each(result, function(res) {
setTimeout(function() {
if (!($.find(".eid_"+res.event_id)).length) {
res.title = "<span class='link2event eid_" + res.event_id + "'>" + res.title + "</span>";
res.message += "<br/><br/><button class='link2showed oe_highlight oe_form oe_button'><span>OK</span></button> \
<button class='link2event'>Details</button> \
<button class='link2recall ' >Snooze</button> ";
a = self.do_notify(res.title,res.message,true);
$(".link2event").on('click', function() {
self.getMyNotifBox(this).find('.ui-notify-close').trigger("click");
self.rpc("/web/action/load", {
action_id: "base_calendar.action_crm_meeting_notify",
}).then( function(r) {
r.res_id = res.event_id;
return self.action_manager.do_action(r);
});
});
a.element.find(".link2recall").on('click',function() {
self.getMyNotifBox(this).find('.ui-notify-close').trigger("click");
});
a.element.find(".link2showed").on('click',function() {
//alert('Mark event -' + res.event_id + 'as notified !')
self.rpc("/calendar/NextNotify", {
type: "UPDATE"
})
});
}
else if (self.getMyNotifBox($.find(".eid_"+res.event_id)).attr("style") !== ""){
self.getMyNotifBox($.find(".eid_"+res.event_id)).attr("style","");
}
else {
console.log("Already displayed !");
}
//send http as done
console.log("DONE EACH RESULT");
},res.timer * 100);
});
}
);
},
check_notifications: function() {
var self= this;
self.get_next_event();
setInterval(function(){
console.log("IN TIMER");
self.get_next_event();
}, 5 * 60 * 1000 );
},
show_application: function() {
this._super();
this.check_notifications();
}
});
instance.base_calendar.invitation = instance.web.Widget.extend({
@ -76,5 +150,7 @@ openerp.base_calendar = function(instance) {
new instance.base_calendar.invitation(null,db,action,id,view,attendee_data).appendTo($("body").addClass('openerp'));
});
}
};

View File

@ -10,6 +10,7 @@
<t t-set="i" t-value="i + 1"/>
</t>
</t>
<t t-name="invitation_view">
<div class="oe_right"><b><t t-esc="invitation['current_attendee'].cn"/> (<t t-esc="invitation['current_attendee'].email"/>)</b></div>
<div class="oe_left"><img class="cal_inline cal_image" t-attf-src="data:image/png;base64,#{invitation['logo']}"/><p class="cal_tag cal_inline">Calendar</p></div>

View File

@ -57,9 +57,13 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p
'report/available_holidays_view.xml',
'wizard/hr_holidays_summary_department_view.xml',
'wizard/hr_holidays_summary_employees_view.xml',
'board_hr_holidays_view.xml',
'board_hr_holidays_view.xml',
],
'demo': ['hr_holidays_demo.xml',],
'js': ['static/src/js/*.js'],
'qweb': [
'static/src/xml/*.xml',
],
'test': ['test/test_hr_holiday.yml',
'test/hr_holidays_report.yml',
],

View File

@ -143,7 +143,7 @@ class hr_holidays(osv.osv):
result[holiday.id] = True
return result
def _check_date(self, cr, uid, ids):
def _check_date(self, cr, uid, ids):
for holiday in self.browse(cr, uid, ids):
holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id)])
if holiday_ids:

View File

@ -40,7 +40,7 @@
<field name="name">Leave Request</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
<calendar string="Leave Request" color="employee_id" date_start="date_from" date_stop="date_to">
<calendar string="Leave Request" color="employee_id" date_start="date_from" date_stop="date_to" quick_add="True" quick_create_instance="instance.hr_holidays.QuickCreate">
<field name="employee_id"/>
<field name="holiday_status_id"/>
</calendar>

View File

@ -0,0 +1,82 @@
openerp.hr_holidays = function(instance) {
var _t = instance.web._t;
var QWeb = instance.web.qweb;
instance.hr_holidays = {}
/**
* Quick creation view.
*
* Triggers a single event "added" with a single parameter "name", which is the
* name entered by the user
*
* @class
* @type {*}
*/
instance.hr_holidays.QuickCreate = instance.web_calendar.QuickCreate.extend({
template: 'hr_holidays.QuickCreate',
/**
* close_btn: If true, the widget will display a "Close" button able to trigger
* a "close" event.
*/
init: function(parent, dataset, buttons, options, data_template) {
this._super(parent);
this.dataset = dataset;
this._buttons = buttons || false;
this.options = options;
//this.options.disable_quick_create = true;
// Can hold data pre-set from where you clicked on agenda
this.data_template = data_template || {};
},
start: function() {
var self=this;
this._super();
new instance.web.Model("hr.holidays.status").query(["name"]).filter([["active", "=",true]]).all().then(function(result) {
var holidays_status = {};
_.each(result, function(item) {
var filter_item = {
value: item.id,
label: item.name,
};
holidays_status[item.id] = filter_item;
});
var optionType = QWeb.render('hr_holidays.QuickCreate.select', { elements: holidays_status });
console.log(optionType);
$(".qc_type").html(optionType);
});
},
focus: function() {
this.$el.find('input').focus();
},
/**
* Gathers data from the quick create dialog a launch quick_create(data) method
*/
quick_add: function() {
var name = this.$el.find(".qc_name").val();
var type = this.$el.find(".qc_type").val();
if (/^\s*$/.test(name)) { return; }
this.quick_create({'name': name,'holiday_status_id':parseInt(type)});
},
slow_add: function() {
var name = this.$el.find(".qc_name").val();
var type = this.$el.find(".qc_type").val();
this.slow_create({'name': name,'holiday_status_id':parseInt(type)});
},
});
};

View File

@ -0,0 +1,21 @@
<template>
<div t-name="hr_holidays.QuickCreate" class="oe_calendar_quick_create openerp">
<input name='name' class="qc_name"/>
<select name='holiday_status_id' class="qc_type">
</select>
<t t-if="widget._buttons">
<div class="oe_calendar_quick_create_buttons">
<button class="oe_button oe_calendar_quick_create_add ">Add</button>
<button class="oe_button oe_link oe_calendar_quick_create_edit "><span>Edit</span></button>
</div>
</t>
</div>
<t t-name="hr_holidays.QuickCreate.select">
<div t-foreach="elements" class="oe_calendar_responsible" >
<option t-att-value="elements_value.value"> <t t-esc="elements_value.label" /> </option>
</div>
</t>
</template>

View File

@ -239,6 +239,7 @@ class mail_mail(osv.Model):
:return: True
"""
ir_mail_server = self.pool.get('ir.mail_server')
for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
try:
# handle attachments
@ -282,6 +283,7 @@ class mail_mail(osv.Model):
res = ir_mail_server.send_email(cr, uid, msg,
mail_server_id=mail.mail_server_id.id,
context=context)
if res:
mail.write({'state': 'sent', 'message_id': res})
mail_sent = True