commit
9adbbcab87
|
@ -160,5 +160,17 @@ class timesheet_invoice(osv.osv):
|
||||||
)""")
|
)""")
|
||||||
timesheet_invoice()
|
timesheet_invoice()
|
||||||
|
|
||||||
|
|
||||||
|
class res_partner(osv.osv):
|
||||||
|
""" Inherits partner and adds contract information in the partner form """
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'contract_ids': fields.one2many('account.analytic.account', \
|
||||||
|
'partner_id', 'Contracts'),
|
||||||
|
}
|
||||||
|
|
||||||
|
res_partner()
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
||||||
|
|
|
@ -29,30 +29,30 @@
|
||||||
<group colspan="8">
|
<group colspan="8">
|
||||||
<group colspan="4" height="450" width="600">
|
<group colspan="4" height="450" width="600">
|
||||||
<field name="charts"/>
|
<field name="charts"/>
|
||||||
<group colspan="4" attrs="{'invisible':[('charts','!=','configurable')]}">
|
<group colspan="4">
|
||||||
<field name="sale_tax" colspan="2"/>
|
<separator col="4" colspan="4" string="Configure Fiscal Year"/>
|
||||||
<field name="purchase_tax" colspan="2"/>
|
<field name="date_start" on_change="on_change_start_date(date_start)"/>
|
||||||
|
<field name="date_stop"/>
|
||||||
|
<field name="period" colspan="4"/>
|
||||||
</group>
|
</group>
|
||||||
<group colspan="4" attrs="{'invisible':[('charts','!=','configurable')]}">
|
<group colspan="4" attrs="{'invisible':[('charts','!=','configurable')]}">
|
||||||
<separator col="4" colspan="4" string="Bank and Cost Account"/>
|
<separator col="4" colspan="4" string="Bank and Cost Account"/>
|
||||||
<field colspan="4" mode="tree" name="bank_accounts_id" nolabel="1" widget="one2many_list">
|
<field colspan="4" mode="tree" name="bank_accounts_id" nolabel="1" widget="one2many_list">
|
||||||
<form string="Bank Information">
|
<form string="">
|
||||||
<field name="acc_name"/>
|
<field name="acc_name"/>
|
||||||
<field name="account_type"/>
|
<field name="account_type"/>
|
||||||
<field name="currency_id" widget="selection"/>
|
<field name="currency_id" widget="selection"/>
|
||||||
</form>
|
</form>
|
||||||
<tree editable="bottom" string="Bank Information">
|
<tree editable="bottom" string="">
|
||||||
<field name="acc_name"/>
|
<field name="acc_name"/>
|
||||||
<field name="account_type"/>
|
<field name="account_type"/>
|
||||||
<field name="currency_id" widget="selection"/>
|
<field name="currency_id" widget="selection"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</group>
|
</group>
|
||||||
<group colspan="4">
|
<group colspan="4" attrs="{'invisible':[('charts','!=','configurable')]}">
|
||||||
<separator col="4" colspan="4" string="Configure Fiscal Year"/>
|
<field name="sale_tax" colspan="2" on_change="on_change_tax(sale_tax)"/>
|
||||||
<field name="date_start" on_change="on_change_start_date(date_start)"/>
|
<field name="purchase_tax" colspan="2" />
|
||||||
<field name="date_stop"/>
|
|
||||||
<field name="period" colspan="4"/>
|
|
||||||
</group>
|
</group>
|
||||||
<group colspan="4" groups="base.group_extended">
|
<group colspan="4" groups="base.group_extended">
|
||||||
<separator col="4" colspan="4" string="Install Extra Account Module"/>
|
<separator col="4" colspan="4" string="Install Extra Account Module"/>
|
||||||
|
|
|
@ -478,6 +478,23 @@
|
||||||
<act_window domain="[('journal_id','=',active_id),('state','!=','draft'),('reconciled','=',False)]" id="act_account_journal_2_account_invoice_opened" name="Unpaid invoices" res_model="account.invoice" src_model="account.journal"/>
|
<act_window domain="[('journal_id','=',active_id),('state','!=','draft'),('reconciled','=',False)]" id="act_account_journal_2_account_invoice_opened" name="Unpaid invoices" res_model="account.invoice" src_model="account.journal"/>
|
||||||
|
|
||||||
<act_window domain="[('account_analytic_id', '=', active_id)]" id="act_account_analytic_account_2_account_invoice_line" name="Invoice lines" res_model="account.invoice.line" src_model="account.analytic.account"/>
|
<act_window domain="[('account_analytic_id', '=', active_id)]" id="act_account_analytic_account_2_account_invoice_line" name="Invoice lines" res_model="account.invoice.line" src_model="account.analytic.account"/>
|
||||||
|
|
||||||
|
<!-- Partners inherited form -->
|
||||||
|
|
||||||
|
<record id="view_invoice_partner_info_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.invoice.info.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<notebook position="inside">
|
||||||
|
<page string="Account Info">
|
||||||
|
<field name="invoice_ids" colspan="4" nolabel="1" context="{'group_by':'product_id'}"/>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -7,13 +7,13 @@ msgstr ""
|
||||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||||
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
|
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
|
||||||
"PO-Revision-Date: 2010-05-10 08:37+0000\n"
|
"PO-Revision-Date: 2010-05-13 14:03+0000\n"
|
||||||
"Last-Translator: eLBati - albatos.com <lorenzo.battistini@albatos.com>\n"
|
"Last-Translator: Carlo - didotech.com <Unknown>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2010-05-11 04:18+0000\n"
|
"X-Launchpad-Export-Date: 2010-05-14 04:55+0000\n"
|
||||||
"X-Generator: Launchpad (build Unknown)\n"
|
"X-Generator: Launchpad (build Unknown)\n"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
|
@ -2158,7 +2158,8 @@ msgid "Analytic Entry"
|
||||||
msgstr "Registrazione analitica"
|
msgstr "Registrazione analitica"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: view:res.company:0 field:res.company,overdue_msg:0
|
#: view:res.company:0
|
||||||
|
#: field:res.company,overdue_msg:0
|
||||||
msgid "Overdue Payments Message"
|
msgid "Overdue Payments Message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2231,7 +2232,7 @@ msgstr ""
|
||||||
#. module: account
|
#. module: account
|
||||||
#: rml:account.invoice:0
|
#: rml:account.invoice:0
|
||||||
msgid "VAT :"
|
msgid "VAT :"
|
||||||
msgstr ""
|
msgstr "IVA"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: wizard_field:account.general.ledger.report,account_selection,Account_list:0
|
#: wizard_field:account.general.ledger.report,account_selection,Account_list:0
|
||||||
|
@ -4475,7 +4476,7 @@ msgstr "Registrazioni contabili"
|
||||||
#. module: account
|
#. module: account
|
||||||
#: model:ir.actions.act_window,name:account.act_account_partner_account_move_unreconciled
|
#: model:ir.actions.act_window,name:account.act_account_partner_account_move_unreconciled
|
||||||
msgid "Receivables & Payables"
|
msgid "Receivables & Payables"
|
||||||
msgstr "Invassi & Pagamenti"
|
msgstr "Incassi & Pagamenti"
|
||||||
|
|
||||||
#. module: account
|
#. module: account
|
||||||
#: rml:account.general.ledger:0
|
#: rml:account.general.ledger:0
|
||||||
|
|
|
@ -44,7 +44,7 @@ class account_installer(osv.osv_memory):
|
||||||
sorted(((m.name, m.shortdesc)
|
sorted(((m.name, m.shortdesc)
|
||||||
for m in modules.browse(cr, uid, ids)),
|
for m in modules.browse(cr, uid, ids)),
|
||||||
key=itemgetter(1)))
|
key=itemgetter(1)))
|
||||||
charts.insert(0,('configurable','Configurable Chart of Account'))
|
charts.insert(0,('configurable','Generic Chart Of Account'))
|
||||||
return charts
|
return charts
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
@ -81,10 +81,13 @@ class account_installer(osv.osv_memory):
|
||||||
'period':lambda *a:'month',
|
'period':lambda *a:'month',
|
||||||
'sale_tax':lambda *a:0.0,
|
'sale_tax':lambda *a:0.0,
|
||||||
'purchase_tax':lambda *a:0.0,
|
'purchase_tax':lambda *a:0.0,
|
||||||
'charts':'configurable',
|
#'charts':'configurable',
|
||||||
'bank_accounts_id':_get_default_accounts
|
'bank_accounts_id':_get_default_accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def on_change_tax(self, cr, uid, id, tax):
|
||||||
|
return{'value':{'purchase_tax':tax}}
|
||||||
|
|
||||||
def on_change_start_date(self, cr, uid, id, start_date):
|
def on_change_start_date(self, cr, uid, id, start_date):
|
||||||
if start_date:
|
if start_date:
|
||||||
start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
||||||
|
|
|
@ -1496,4 +1496,15 @@ class account_invoice_tax(osv.osv):
|
||||||
return res
|
return res
|
||||||
account_invoice_tax()
|
account_invoice_tax()
|
||||||
|
|
||||||
|
|
||||||
|
class res_partner(osv.osv):
|
||||||
|
""" Inherits partner and adds invoice information in the partner form """
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'invoice_ids': fields.one2many('account.invoice.line', 'partner_id', 'Invoices'),
|
||||||
|
}
|
||||||
|
|
||||||
|
res_partner()
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -441,6 +441,21 @@
|
||||||
</graph>
|
</graph>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Partners inherited form -->
|
||||||
|
|
||||||
|
<record id="view_contract_partner_info_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.contract.info.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="invoice_ids" position="after">
|
||||||
|
<field name="contract_ids" colspan="4" nolabel="1" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -33,20 +33,105 @@ class base_action_rule(osv.osv):
|
||||||
|
|
||||||
_name = 'base.action.rule'
|
_name = 'base.action.rule'
|
||||||
_description = 'Action Rules'
|
_description = 'Action Rules'
|
||||||
|
|
||||||
|
def _state_get(self, cr, uid, context={}):
|
||||||
|
""" Get State
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
|
return self.state_get(cr, uid, context=context)
|
||||||
|
|
||||||
|
|
||||||
|
def state_get(self, cr, uid, context={}):
|
||||||
|
""" Get State
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
|
return [('', '')]
|
||||||
|
|
||||||
|
def priority_get(self, cr, uid, context={}):
|
||||||
|
""" Get Priority
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
|
return [('', '')]
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.many2one('ir.model', 'Model', required=True),
|
'name': fields.many2one('ir.model', 'Object', required=True),
|
||||||
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
|
'max_level': fields.integer('Max Level', help='Specifies maximum level.'),
|
||||||
'rule_lines': fields.one2many('base.action.rule.line','rule_id','Rule Lines'),
|
'create_date': fields.datetime('Create Date', readonly=1),
|
||||||
'create_date': fields.datetime('Create Date', readonly=1),
|
'active': fields.boolean('Active', help="If the active field is set to true,\
|
||||||
'active': fields.boolean('Active')
|
it will allow you to hide the rule without removing it."),
|
||||||
|
'sequence': fields.integer('Sequence', help="Gives the sequence order \
|
||||||
|
when displaying a list of rules."),
|
||||||
|
'trg_date_type': fields.selection([
|
||||||
|
('none', 'None'),
|
||||||
|
('create', 'Creation Date'),
|
||||||
|
('action_last', 'Last Action Date'),
|
||||||
|
('date', 'Date'),
|
||||||
|
('deadline', 'Deadline'),
|
||||||
|
], 'Trigger Date', size=16),
|
||||||
|
'trg_date_range': fields.integer('Delay after trigger date', \
|
||||||
|
help="Delay After Trigger Date,\
|
||||||
|
specifies you can put a negative number. If you need a delay before the \
|
||||||
|
trigger date, like sending a reminder 15 minutes before a meeting."),
|
||||||
|
'trg_date_range_type': fields.selection([('minutes', 'Minutes'), ('hour', 'Hours'), \
|
||||||
|
('day', 'Days'), ('month', 'Months')], 'Delay type'),
|
||||||
|
|
||||||
|
|
||||||
|
'trg_user_id': fields.many2one('res.users', 'Responsible'),
|
||||||
|
|
||||||
|
'trg_partner_id': fields.many2one('res.partner', 'Partner'),
|
||||||
|
'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'),
|
||||||
|
'trg_state_from': fields.selection(_state_get, 'State', size=16),
|
||||||
|
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
|
||||||
|
|
||||||
|
'act_method': fields.char('Call Object Method', size=64),
|
||||||
|
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
|
||||||
|
'act_state': fields.selection(_state_get, 'Set state to', size=16),
|
||||||
|
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="\
|
||||||
|
These people will receive a copy of the future communication between partner \
|
||||||
|
and users by email"),
|
||||||
|
'act_remind_partner': fields.boolean('Remind Partner', help="Check \
|
||||||
|
this if you want the rule to send a reminder by email to the partner."),
|
||||||
|
'act_remind_user': fields.boolean('Remind responsible', help="Check \
|
||||||
|
this if you want the rule to send a reminder by email to the user."),
|
||||||
|
'act_reply_to': fields.char('Reply-To', size=64),
|
||||||
|
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if you want that all documents attached to the object be attached to the reminder email sent."),
|
||||||
|
'act_mail_to_user': fields.boolean('Mail to responsible', help="Check\
|
||||||
|
this if you want the rule to send an email to the responsible person."),
|
||||||
|
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',
|
||||||
|
help="Check this if you want \
|
||||||
|
the rule to mark CC(mail to any other person defined in actions)."),
|
||||||
|
'act_mail_to_email': fields.char('Mail to these emails', size=128, \
|
||||||
|
help="Email-id of the persons whom mail is to be sent"),
|
||||||
|
'act_mail_body': fields.text('Mail body', help="Content of mail"),
|
||||||
|
'regex_name': fields.char('Regular Expression on Model Name', size=128),
|
||||||
|
'server_action_id': fields.many2one('ir.actions.server', 'Server Action', help="Describes the action name.\neg:on which object which action to be taken on basis of which condition"),
|
||||||
|
'filter_id':fields.many2one('ir.filters', 'Filter', required=False),
|
||||||
|
'domain':fields.char('Domain', size=124, required=False, readonly=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'active': lambda *a: True,
|
'active': lambda *a: True,
|
||||||
'max_level': lambda *a: 15,
|
'max_level': lambda *a: 15,
|
||||||
|
'trg_date_type': lambda *a: 'none',
|
||||||
|
'trg_date_range_type': lambda *a: 'day',
|
||||||
|
'act_mail_to_user': lambda *a: 0,
|
||||||
|
'act_remind_partner': lambda *a: 0,
|
||||||
|
'act_remind_user': lambda *a: 0,
|
||||||
|
'act_mail_to_watchers': lambda *a: 0,
|
||||||
|
'domain': lambda *a: '[]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_order = 'sequence'
|
||||||
|
|
||||||
def format_body(self, body):
|
def format_body(self, body):
|
||||||
""" Foramat Action rule's body
|
""" Foramat Action rule's body
|
||||||
@param self: The object pointer """
|
@param self: The object pointer """
|
||||||
|
@ -58,22 +143,22 @@ class base_action_rule(osv.osv):
|
||||||
@param self: The object pointer """
|
@param self: The object pointer """
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'object_id': obj.id,
|
'object_id': obj.id,
|
||||||
'object_subject': hasattr(obj, 'name') and obj.name or False,
|
'object_subject': hasattr(obj, 'name') and obj.name or False,
|
||||||
'object_date': hasattr(obj, 'date') and obj.date or False,
|
'object_date': hasattr(obj, 'date') and obj.date or False,
|
||||||
'object_description': hasattr(obj, 'description') and obj.description or False,
|
'object_description': hasattr(obj, 'description') and obj.description or False,
|
||||||
'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/',
|
'object_user': hasattr(obj, 'user_id') and (obj.user_id and obj.user_id.name) or '/',
|
||||||
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \
|
'object_user_email': hasattr(obj, 'user_id') and (obj.user_id and \
|
||||||
obj.user_id.address_id and obj.user_id.address_id.email) or '/',
|
obj.user_id.address_id and obj.user_id.address_id.email) or '/',
|
||||||
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and\
|
'object_user_phone': hasattr(obj, 'user_id') and (obj.user_id and\
|
||||||
obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
|
obj.user_id.address_id and obj.user_id.address_id.phone) or '/',
|
||||||
'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/',
|
'partner': hasattr(obj, 'partner_id') and (obj.partner_id and obj.partner_id.name) or '/',
|
||||||
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\
|
'partner_email': hasattr(obj, 'partner_address_id') and (obj.partner_address_id and\
|
||||||
obj.partner_address_id.email) or '/',
|
obj.partner_address_id.email) or '/',
|
||||||
}
|
}
|
||||||
return self.format_body(body % data)
|
return self.format_body(body % data)
|
||||||
|
|
||||||
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
|
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from', False), context={}):
|
||||||
""" send email
|
""" send email
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
|
@ -92,7 +177,7 @@ class base_action_rule(osv.osv):
|
||||||
emailfrom = tools.ustr(emailfrom)
|
emailfrom = tools.ustr(emailfrom)
|
||||||
reply_to = emailfrom
|
reply_to = emailfrom
|
||||||
if not emailfrom:
|
if not emailfrom:
|
||||||
raise osv.except_osv(_('Error!'),
|
raise osv.except_osv(_('Error!'),
|
||||||
_("No E-Mail ID Found for your Company address!"))
|
_("No E-Mail ID Found for your Company address!"))
|
||||||
return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
|
return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
|
||||||
|
|
||||||
|
@ -105,6 +190,10 @@ class base_action_rule(osv.osv):
|
||||||
@param context: A standard dictionary for contextual values """
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
|
if eval(action.domain):
|
||||||
|
obj_ids = obj._table.search(cr, uid, eval(action.domain), context=context)
|
||||||
|
if not obj.id in obj_ids:
|
||||||
|
ok = False
|
||||||
if hasattr(obj, 'user_id'):
|
if hasattr(obj, 'user_id'):
|
||||||
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
|
ok = ok and (not action.trg_user_id.id or action.trg_user_id.id==obj.user_id.id)
|
||||||
if hasattr(obj, 'partner_id'):
|
if hasattr(obj, 'partner_id'):
|
||||||
|
@ -122,10 +211,6 @@ class base_action_rule(osv.osv):
|
||||||
if state_to:
|
if state_to:
|
||||||
ok = ok and (not action.trg_state_to or action.trg_state_to==state_to)
|
ok = ok and (not action.trg_state_to or action.trg_state_to==state_to)
|
||||||
|
|
||||||
if hasattr(obj, 'priority'):
|
|
||||||
ok = ok and (not action.trg_priority_from or action.trg_priority_from>=obj.priority)
|
|
||||||
ok = ok and (not action.trg_priority_to or action.trg_priority_to<=obj.priority)
|
|
||||||
|
|
||||||
reg_name = action.regex_name
|
reg_name = action.regex_name
|
||||||
result_name = True
|
result_name = True
|
||||||
if reg_name:
|
if reg_name:
|
||||||
|
@ -147,7 +232,7 @@ class base_action_rule(osv.osv):
|
||||||
@param context: A standard dictionary for contextual values """
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
if action.server_action_id:
|
if action.server_action_id:
|
||||||
context.update({'active_id':obj.id,'active_ids':[obj.id]})
|
context.update({'active_id':obj.id, 'active_ids':[obj.id]})
|
||||||
self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context)
|
self.pool.get('ir.actions.server').run(cr, uid, [action.server_action_id.id], context)
|
||||||
write = {}
|
write = {}
|
||||||
if hasattr(obj, 'user_id') and action.act_user_id:
|
if hasattr(obj, 'user_id') and action.act_user_id:
|
||||||
|
@ -163,10 +248,6 @@ class base_action_rule(osv.osv):
|
||||||
obj.categ_id = action.act_categ_id
|
obj.categ_id = action.act_categ_id
|
||||||
write['categ_id'] = action.act_categ_id.id
|
write['categ_id'] = action.act_categ_id.id
|
||||||
|
|
||||||
if hasattr(obj, 'priority') and action.act_priority:
|
|
||||||
obj.priority = action.act_priority
|
|
||||||
write['priority'] = action.act_priority
|
|
||||||
|
|
||||||
model_obj.write(cr, uid, [obj.id], write, context)
|
model_obj.write(cr, uid, [obj.id], write, context)
|
||||||
|
|
||||||
if hasattr(model_obj, 'remind_user') and action.act_remind_user:
|
if hasattr(model_obj, 'remind_user') and action.act_remind_user:
|
||||||
|
@ -201,175 +282,58 @@ class base_action_rule(osv.osv):
|
||||||
|
|
||||||
if not scrit:
|
if not scrit:
|
||||||
scrit = []
|
scrit = []
|
||||||
rule_line_obj = self.pool.get('base.action.rule.line')
|
cr.execute("select id from base_action_rule order by sequence")
|
||||||
for rule in self.browse(cr, uid, ids):
|
rule_ids = map(lambda x: x[0], cr.fetchall())
|
||||||
level = rule.max_level
|
for action in self.browse(cr, uid, rule_ids):
|
||||||
|
level = action.max_level
|
||||||
if not level:
|
if not level:
|
||||||
break
|
break
|
||||||
newactions = []
|
model_obj = self.pool.get(action.name.model)
|
||||||
scrit += [('rule_id','=',rule.id)]
|
|
||||||
line_ids = rule_line_obj.search(cr, uid, scrit)
|
|
||||||
actions = rule_line_obj.browse(cr, uid, line_ids, context=context)
|
|
||||||
model_obj = self.pool.get(rule.name.model)
|
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
for action in actions:
|
ok = self.do_check(cr, uid, action, obj, context=context)
|
||||||
ok = self.do_check(cr, uid, action, obj, context=context)
|
if not ok:
|
||||||
if not ok:
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
base = False
|
base = False
|
||||||
if hasattr(obj, 'create_date') and action.trg_date_type=='create':
|
if hasattr(obj, 'create_date') and action.trg_date_type=='create':
|
||||||
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
|
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
|
||||||
elif hasattr(obj, 'create_date') and action.trg_date_type=='action_last':
|
elif hasattr(obj, 'create_date') and action.trg_date_type=='action_last':
|
||||||
if hasattr(obj, 'date_action_last') and obj.date_action_last:
|
if hasattr(obj, 'date_action_last') and obj.date_action_last:
|
||||||
base = mx.DateTime.strptime(obj.date_action_last, '%Y-%m-%d %H:%M:%S')
|
base = mx.DateTime.strptime(obj.date_action_last, '%Y-%m-%d %H:%M:%S')
|
||||||
else:
|
|
||||||
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
|
|
||||||
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' \
|
|
||||||
and obj.date_deadline:
|
|
||||||
base = mx.DateTime.strptime(obj.date_deadline, '%Y-%m-%d %H:%M:%S')
|
|
||||||
elif hasattr(obj, 'date') and action.trg_date_type=='date' and obj.date:
|
|
||||||
base = mx.DateTime.strptime(obj.date, '%Y-%m-%d %H:%M:%S')
|
|
||||||
if base:
|
|
||||||
fnct = {
|
|
||||||
'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval),
|
|
||||||
'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval),
|
|
||||||
'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval),
|
|
||||||
'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval),
|
|
||||||
}
|
|
||||||
d = base + fnct[action.trg_date_range_type](action.trg_date_range)
|
|
||||||
dt = d.strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
ok = False
|
|
||||||
if hasattr(obj, 'date_action_last') and hasattr(obj, 'date_action_next'):
|
|
||||||
ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \
|
|
||||||
((not obj.date_action_next) or \
|
|
||||||
(dt >= obj.date_action_next and \
|
|
||||||
obj.date_action_last < obj.date_action_next))
|
|
||||||
if not ok:
|
|
||||||
if not obj.date_action_next or dt < obj.date_action_next:
|
|
||||||
obj.date_action_next = dt
|
|
||||||
model_obj.write(cr, uid, [obj.id], {'date_action_next': dt}, context)
|
|
||||||
else:
|
else:
|
||||||
ok = action.trg_date_type=='none'
|
base = mx.DateTime.strptime(obj.create_date[:19], '%Y-%m-%d %H:%M:%S')
|
||||||
|
elif hasattr(obj, 'date_deadline') and action.trg_date_type=='deadline' \
|
||||||
|
and obj.date_deadline:
|
||||||
|
base = mx.DateTime.strptime(obj.date_deadline, '%Y-%m-%d %H:%M:%S')
|
||||||
|
elif hasattr(obj, 'date') and action.trg_date_type=='date' and obj.date:
|
||||||
|
base = mx.DateTime.strptime(obj.date, '%Y-%m-%d %H:%M:%S')
|
||||||
|
if base:
|
||||||
|
fnct = {
|
||||||
|
'minutes': lambda interval: mx.DateTime.RelativeDateTime(minutes=interval),
|
||||||
|
'day': lambda interval: mx.DateTime.RelativeDateTime(days=interval),
|
||||||
|
'hour': lambda interval: mx.DateTime.RelativeDateTime(hours=interval),
|
||||||
|
'month': lambda interval: mx.DateTime.RelativeDateTime(months=interval),
|
||||||
|
}
|
||||||
|
d = base + fnct[action.trg_date_range_type](action.trg_date_range)
|
||||||
|
dt = d.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
ok = False
|
||||||
|
if hasattr(obj, 'date_action_last') and hasattr(obj, 'date_action_next'):
|
||||||
|
ok = (dt <= time.strftime('%Y-%m-%d %H:%M:%S')) and \
|
||||||
|
((not obj.date_action_next) or \
|
||||||
|
(dt >= obj.date_action_next and \
|
||||||
|
obj.date_action_last < obj.date_action_next))
|
||||||
|
if not ok:
|
||||||
|
if not obj.date_action_next or dt < obj.date_action_next:
|
||||||
|
obj.date_action_next = dt
|
||||||
|
model_obj.write(cr, uid, [obj.id], {'date_action_next': dt}, context)
|
||||||
|
else:
|
||||||
|
ok = action.trg_date_type=='none'
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
self.do_action(cr, uid, action, model_obj, obj, context)
|
self.do_action(cr, uid, action, model_obj, obj, context)
|
||||||
break
|
break
|
||||||
level -= 1
|
level -= 1
|
||||||
return True
|
return True
|
||||||
base_action_rule()
|
|
||||||
|
|
||||||
class base_action_rule_line(osv.osv):
|
|
||||||
_name = 'base.action.rule.line'
|
|
||||||
_description = 'Action Rule Lines'
|
|
||||||
|
|
||||||
def _state_get(self, cr, uid, context={}):
|
|
||||||
""" Get State
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
return self.state_get(cr, uid, context=context)
|
|
||||||
def _priority_get(self, cr, uid, context={}):
|
|
||||||
""" Get Priority
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
return self.priority_get(cr, uid, context=context)
|
|
||||||
|
|
||||||
def state_get(self, cr, uid, context={}):
|
|
||||||
""" Get State
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
return [('','')]
|
|
||||||
def priority_get(self, cr, uid, context={}):
|
|
||||||
""" Get Priority
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
return [('','')]
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'name': fields.char('Rule Name',size=64, required=True),
|
|
||||||
'rule_id': fields.many2one('base.action.rule','Rule'),
|
|
||||||
'active': fields.boolean('Active', help="If the active field is set to true,\
|
|
||||||
it will allow you to hide the rule without removing it."),
|
|
||||||
'sequence': fields.integer('Sequence', help="Gives the sequence order when\
|
|
||||||
displaying a list of rules."),
|
|
||||||
|
|
||||||
'trg_date_type': fields.selection([
|
|
||||||
('none','None'),
|
|
||||||
('create','Creation Date'),
|
|
||||||
('action_last','Last Action Date'),
|
|
||||||
('date','Date'),
|
|
||||||
('deadline', 'Deadline'),
|
|
||||||
], 'Trigger Date', size=16),
|
|
||||||
'trg_date_range': fields.integer('Delay after trigger date',help="Delay After Trigger Date,\
|
|
||||||
specifies you can put a negative number " \
|
|
||||||
"if you need a delay before the trigger date, like sending a reminder 15 minutes before a meeting."),
|
|
||||||
'trg_date_range_type': fields.selection([('minutes', 'Minutes'),('hour','Hours'),\
|
|
||||||
('day','Days'),('month','Months')], 'Delay type'),
|
|
||||||
|
|
||||||
|
|
||||||
'trg_user_id': fields.many2one('res.users', 'Responsible'),
|
|
||||||
|
|
||||||
'trg_partner_id': fields.many2one('res.partner', 'Partner'),
|
|
||||||
'trg_partner_categ_id': fields.many2one('res.partner.category', 'Partner Category'),
|
|
||||||
'trg_state_from': fields.selection(_state_get, 'State', size=16),
|
|
||||||
'trg_state_to': fields.selection(_state_get, 'Button Pressed', size=16),
|
|
||||||
'trg_priority_from': fields.selection(_priority_get, 'Minimum Priority'),
|
|
||||||
'trg_priority_to': fields.selection(_priority_get, 'Maximum Priority'),
|
|
||||||
|
|
||||||
'act_method': fields.char('Call Object Method', size=64),
|
|
||||||
'act_user_id': fields.many2one('res.users', 'Set responsible to'),
|
|
||||||
'act_state': fields.selection(_state_get, 'Set state to', size=16),
|
|
||||||
'act_priority': fields.selection(_priority_get, 'Set priority to'),
|
|
||||||
'act_email_cc': fields.char('Add watchers (Cc)', size=250, help="These people\
|
|
||||||
will receive a copy of the future communication between partner and users by email"),
|
|
||||||
|
|
||||||
'act_remind_partner': fields.boolean('Remind Partner', help="Check this if\
|
|
||||||
you want the rule to send a reminder by email to the partner."),
|
|
||||||
'act_remind_user': fields.boolean('Remind responsible', help="Check this if \
|
|
||||||
you want the rule to send a reminder by email to the user."),
|
|
||||||
'act_reply_to': fields.char('Reply-To', size=64),
|
|
||||||
'act_remind_attach': fields.boolean('Remind with attachment', help="Check this if\
|
|
||||||
you want that all documents attached to the object be attached \
|
|
||||||
to the reminder email sent."),
|
|
||||||
|
|
||||||
'act_mail_to_user': fields.boolean('Mail to responsible',help="Check this if \
|
|
||||||
you want the rule to send an email to the responsible person."),
|
|
||||||
'act_mail_to_watchers': fields.boolean('Mail to watchers (CC)',help="Check this\
|
|
||||||
if you want the rule to mark CC(mail to any other person\
|
|
||||||
defined in actions)."),
|
|
||||||
'act_mail_to_email': fields.char('Mail to these emails', size=128,help="Email-id \
|
|
||||||
of the persons whom mail is to be sent"),
|
|
||||||
'act_mail_body': fields.text('Mail body',help="Content of mail"),
|
|
||||||
'regex_name': fields.char('Regular Expression on Model Name', size=128),
|
|
||||||
'server_action_id': fields.many2one('ir.actions.server','Server Action',help="Describes the\
|
|
||||||
action name." \
|
|
||||||
"eg:on which object which action to be taken on basis of which condition"),
|
|
||||||
}
|
|
||||||
|
|
||||||
_defaults = {
|
|
||||||
'active': lambda *a: 1,
|
|
||||||
'trg_date_type': lambda *a: 'none',
|
|
||||||
'trg_date_range_type': lambda *a: 'day',
|
|
||||||
'act_mail_to_user': lambda *a: 0,
|
|
||||||
'act_remind_partner': lambda *a: 0,
|
|
||||||
'act_remind_user': lambda *a: 0,
|
|
||||||
'act_mail_to_watchers': lambda *a: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
_order = 'sequence'
|
|
||||||
|
|
||||||
|
|
||||||
def _check_mail(self, cr, uid, ids, context=None):
|
def _check_mail(self, cr, uid, ids, context=None):
|
||||||
""" Check Mail
|
""" Check Mail
|
||||||
|
@ -390,9 +354,9 @@ class base_action_rule_line(osv.osv):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_constraints = [
|
_constraints = [
|
||||||
(_check_mail, 'Error: The mail is not well formated', ['act_mail_body']),
|
(_check_mail, 'Error: The mail is not well formated', ['act_mail_body']),
|
||||||
]
|
]
|
||||||
|
|
||||||
base_action_rule_line()
|
base_action_rule()
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -16,52 +16,9 @@
|
||||||
<form string="Action Rule">
|
<form string="Action Rule">
|
||||||
<field name="name" select="1"/>
|
<field name="name" select="1"/>
|
||||||
<field name="active"/>
|
<field name="active"/>
|
||||||
|
<field name="sequence"/>
|
||||||
<field name="max_level" />
|
<field name="max_level" />
|
||||||
<separator colspan="4" string="Rule lines"/>
|
<field name="domain" colspan="4"/>
|
||||||
<field name="rule_lines" colspan="4" nolabel="1"/>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Action Rule Tree View -->
|
|
||||||
|
|
||||||
<record id="view_base_action_rule_tree" model="ir.ui.view">
|
|
||||||
<field name="name">base.action.rule.tree</field>
|
|
||||||
<field name="model">base.action.rule</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Action Rule">
|
|
||||||
<field name="name" colspan="4"/>
|
|
||||||
<field name="max_level"/>
|
|
||||||
<field name="rule_lines" colspan="4" nolabel="1"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Action Rule Action -->
|
|
||||||
|
|
||||||
<record id="base_action_rule_act" model="ir.actions.act_window">
|
|
||||||
<field name="name">Action Rules</field>
|
|
||||||
<field name="res_model">base.action.rule</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="view_id" ref="view_base_action_rule_tree"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem id="menu_base_action_rule_form"
|
|
||||||
parent="menu_base_action_rule" action="base_action_rule_act" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Action Rule Lines Form View
|
|
||||||
-->
|
|
||||||
<record id="view_base_action_rule_line_form" model="ir.ui.view">
|
|
||||||
<field name="name">base.action.rule.line.form</field>
|
|
||||||
<field name="model">base.action.rule.line</field>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Action Rule Line">
|
|
||||||
<field name="name" select="1"/>
|
|
||||||
<field name="active"/>
|
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Conditions">
|
<page string="Conditions">
|
||||||
<group col="2" colspan="2" name="model">
|
<group col="2" colspan="2" name="model">
|
||||||
|
@ -79,45 +36,46 @@
|
||||||
<field name="trg_state_from"/>
|
<field name="trg_state_from"/>
|
||||||
<field name="trg_state_to"/>
|
<field name="trg_state_to"/>
|
||||||
</group>
|
</group>
|
||||||
<group col="2" colspan="2">
|
|
||||||
<separator colspan="4" string="Conditions on Priority Range"/>
|
|
||||||
<field name="trg_priority_from"/>
|
|
||||||
<field name="trg_priority_to"/>
|
|
||||||
</group>
|
|
||||||
<group col="2" colspan="2">
|
<group col="2" colspan="2">
|
||||||
<separator colspan="4" string="Conditions on Timing"/>
|
<separator colspan="4" string="Conditions on Timing"/>
|
||||||
<field name="trg_date_type"/>
|
<field name="trg_date_type"/>
|
||||||
<label align="1.0" string="Delay After Trigger Date:"/>
|
<group col="3" colspan="2" attrs="{'invisible': [('trg_date_type', '=', 'none')]}">
|
||||||
<group col="2" colspan="1">
|
<field name="trg_date_range" string="Delay After Trigger Date"/>
|
||||||
<field name="trg_date_range" nolabel="1"/>
|
|
||||||
<field name="trg_date_range_type" nolabel="1"/>
|
<field name="trg_date_range_type" nolabel="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<separator colspan="4" string="Note"/>
|
<separator colspan="4" string="Note"/>
|
||||||
<label align="0.0" string="The rule use a AND operator. The model must match all non empty fields so that the rule execute the action described in the 'Actions' tab." colspan="4"/>
|
<label align="0.0" colspan="4" width="900"
|
||||||
|
string="The rule use a AND operator. The model must match all non empty fields so that the rule execute the action described in the 'Actions' tab." />
|
||||||
</page>
|
</page>
|
||||||
<page string="Actions">
|
<page string="Actions">
|
||||||
<separator colspan="4" string="Fields to Change"/>
|
<separator colspan="4" string="Fields to Change"/>
|
||||||
<field name="act_user_id"/>
|
<field name="act_user_id"/>
|
||||||
<field name="act_state"/>
|
<field name="act_state"/>
|
||||||
<field name="act_priority"/>
|
|
||||||
<separator colspan="4" string="E-Mail Reminders (includes the content of the object)"/>
|
|
||||||
<field name="act_remind_partner"/>
|
|
||||||
<field name="act_remind_attach"/>
|
|
||||||
<field name="act_remind_user"/>
|
|
||||||
<group col="2" colspan="2" attrs="{'invisible': [('act_remind_user','=',False)]}">
|
|
||||||
<field name="act_reply_to" attrs="{'required':[('act_remind_user','=',True)]}"/>
|
|
||||||
</group>
|
|
||||||
<field colspan="4" name="act_email_cc"/>
|
|
||||||
<separator colspan="4" string="Server Action to be Triggered"/>
|
<separator colspan="4" string="Server Action to be Triggered"/>
|
||||||
<field name="server_action_id"/>
|
<field name="server_action_id"/>
|
||||||
|
<field name="filter_id"/>
|
||||||
</page>
|
</page>
|
||||||
<page string="E-Mail Actions">
|
<page string="E-Mail Actions">
|
||||||
<separator colspan="4" string="Template of Email to Send"/>
|
<!-- <group col="4" colspan="2">-->
|
||||||
<field name="act_mail_to_user"/>
|
<separator colspan="4" string="Template of Email to Send"/>
|
||||||
<field name="act_mail_to_watchers"/>
|
<field name="act_mail_to_watchers"/>
|
||||||
<field colspan="4" name="act_mail_to_email"/>
|
<field name="act_mail_to_user"/>
|
||||||
<field colspan="4" name="act_mail_body" attrs="{'required':[('act_remind_user','=',True)]}"/>
|
<field colspan="4" name="act_mail_to_email"/>
|
||||||
|
<!-- </group>-->
|
||||||
|
<!-- <group col="4" colspan="2">-->
|
||||||
|
<separator colspan="4" string="E-Mail Reminders (includes the content of the object)"/>
|
||||||
|
<field name="act_remind_partner"/>
|
||||||
|
<field name="act_remind_attach"/>
|
||||||
|
<field name="act_remind_user"/>
|
||||||
|
<group col="2" colspan="2" attrs="{'invisible': [('act_remind_user','=',False)]}">
|
||||||
|
<field name="act_reply_to" attrs="{'required':[('act_remind_user','=',True)]}"/>
|
||||||
|
</group>
|
||||||
|
<field colspan="4" name="act_email_cc"/>
|
||||||
|
<!-- </group>-->
|
||||||
|
<separator colspan="4" string="Mail Body"/>
|
||||||
|
<field colspan="4" name="act_mail_body" height="250"
|
||||||
|
nolabel="1" attrs="{'required':[('act_remind_user','=',True)]}" />
|
||||||
<separator colspan="4" string="Special Keywords to Be Used in The Body"/>
|
<separator colspan="4" string="Special Keywords to Be Used in The Body"/>
|
||||||
<label align="0.0" string="%%(object_id)s = Object ID" colspan="2"/>
|
<label align="0.0" string="%%(object_id)s = Object ID" colspan="2"/>
|
||||||
<label align="0.0" string="%%(object_subject)s = Object subject" colspan="2"/>
|
<label align="0.0" string="%%(object_subject)s = Object subject" colspan="2"/>
|
||||||
|
@ -128,34 +86,41 @@
|
||||||
<label align="0.0" string="%%(object_user)s = Responsible name" colspan="2"/>
|
<label align="0.0" string="%%(object_user)s = Responsible name" colspan="2"/>
|
||||||
<label align="0.0" string="%%(object_user_email)s = Responsible email" colspan="2"/>
|
<label align="0.0" string="%%(object_user_email)s = Responsible email" colspan="2"/>
|
||||||
<label align="0.0" string="%%(object_user_phone)s = Responsible phone" colspan="2"/>
|
<label align="0.0" string="%%(object_user_phone)s = Responsible phone" colspan="2"/>
|
||||||
<label align="0.0" string="%% = The '%%' Character" colspan="2"/>
|
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Action Rule Lines Tree View -->
|
<!-- Action Rule Tree View -->
|
||||||
|
|
||||||
<record id="view_base_action_rule_line_tree" model="ir.ui.view">
|
<record id="view_base_action_rule_tree" model="ir.ui.view">
|
||||||
<field name="name">base.action.rule.line.tree</field>
|
<field name="name">base.action.rule.tree</field>
|
||||||
<field name="model">base.action.rule.line</field>
|
<field name="model">base.action.rule</field>
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Action Rule Lines">
|
<tree string="Action Rule">
|
||||||
<field name="name"/>
|
<field name="name" colspan="4"/>
|
||||||
<field name="active"/>
|
<field name="sequence"/>
|
||||||
|
<field name="max_level"/>
|
||||||
|
<field name="domain"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Action Rule Action -->
|
||||||
|
|
||||||
|
<record id="base_action_rule_act" model="ir.actions.act_window">
|
||||||
|
<field name="name">Action Rules</field>
|
||||||
|
<field name="res_model">base.action.rule</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_id" ref="view_base_action_rule_tree"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<act_window
|
<menuitem id="menu_base_action_rule_form"
|
||||||
domain="[('rule_id', '=', active_id)]"
|
parent="menu_base_action_rule" action="base_action_rule_act" />
|
||||||
id="act_rule_lines_open"
|
|
||||||
name="Open Rule Lines"
|
|
||||||
context="{'rule_id': active_id}"
|
|
||||||
res_model="base.action.rule.line"
|
|
||||||
src_model="base.action.rule"/>
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
"access_base_action_rule","base.action.rule","model_base_action_rule",,1,1,1,1
|
"access_base_action_rule","base.action.rule","model_base_action_rule",,1,1,1,1
|
||||||
"access_base_action_rule_line","base.action.rule.line","model_base_action_rule_line",,1,1,1,1
|
|
||||||
|
|
|
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, date
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from dateutil import rrule
|
from dateutil import rrule
|
||||||
from osv import fields, osv
|
from osv import fields, osv
|
||||||
|
@ -36,7 +36,7 @@ months = {
|
||||||
10: "October", 11: "November", 12: "December"
|
10: "October", 11: "November", 12: "December"
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_recurrent_dates(rrulestring, exdate, startdate=None):
|
def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
|
||||||
"""
|
"""
|
||||||
Get recurrent dates based on Rule string considering exdate and start date
|
Get recurrent dates based on Rule string considering exdate and start date
|
||||||
@param rrulestring: Rulestring
|
@param rrulestring: Rulestring
|
||||||
|
@ -56,6 +56,8 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None):
|
||||||
for date in exdate:
|
for date in exdate:
|
||||||
datetime_obj = todate(date)
|
datetime_obj = todate(date)
|
||||||
rset1._exdate.append(datetime_obj)
|
rset1._exdate.append(datetime_obj)
|
||||||
|
if exrule:
|
||||||
|
rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
|
||||||
re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
|
re_dates = map(lambda x:x.strftime('%Y-%m-%d %H:%M:%S'), rset1._iter())
|
||||||
return re_dates
|
return re_dates
|
||||||
|
|
||||||
|
@ -176,28 +178,12 @@ html_invitation = """
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr valign="top">
|
|
||||||
<td><b>Are you coming?</b></td>
|
|
||||||
<td><b>:</b></td>
|
|
||||||
<td colspan="3">
|
|
||||||
<UL>
|
|
||||||
<LI>YES</LI>
|
|
||||||
<LI>NO</LI>
|
|
||||||
<LI>MAYBE</LI>
|
|
||||||
</UL>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<table border="0" cellspacing="10" cellpadding="0" width="100%%"
|
<table border="0" cellspacing="10" cellpadding="0" width="100%%"
|
||||||
style="font-family: Arial, Sans-serif; font-size: 14">
|
style="font-family: Arial, Sans-serif; font-size: 14">
|
||||||
<tr>
|
|
||||||
<td width="100%%"><b>Note:</b> If you are interested please reply this
|
|
||||||
mail and keep only your response from options <i>YES, NO</i>
|
|
||||||
and <i>MAYBE</i>.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td width="100%%">From:</td>
|
<td width="100%%">From:</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -257,22 +243,22 @@ class calendar_attendee(osv.osv):
|
||||||
attdata.sent_by_uid.address_id.email)
|
attdata.sent_by_uid.address_id.email)
|
||||||
if name == 'cn':
|
if name == 'cn':
|
||||||
if attdata.user_id:
|
if attdata.user_id:
|
||||||
result[id][name] = self._get_address(attdata.user_id.name, attdata.email)
|
result[id][name] = attdata.user_id.name
|
||||||
elif attdata.partner_address_id:
|
elif attdata.partner_address_id:
|
||||||
result[id][name] = self._get_address(attdata.partner_id.name, attdata.email)
|
result[id][name] = attdata.partner_address_id.name or attdata.partner_id.name
|
||||||
else:
|
else:
|
||||||
result[id][name] = self._get_address(None, attdata.email)
|
result[id][name] = attdata.email or ''
|
||||||
if name == 'delegated_to':
|
if name == 'delegated_to':
|
||||||
todata = []
|
todata = []
|
||||||
for parent in attdata.parent_ids:
|
for child in attdata.child_ids:
|
||||||
if parent.email:
|
if child.email:
|
||||||
todata.append('MAILTO:' + parent.email)
|
todata.append('MAILTO:' + child.email)
|
||||||
result[id][name] = ', '.join(todata)
|
result[id][name] = ', '.join(todata)
|
||||||
if name == 'delegated_from':
|
if name == 'delegated_from':
|
||||||
fromdata = []
|
fromdata = []
|
||||||
for child in attdata.child_ids:
|
for parent in attdata.parent_ids:
|
||||||
if child.email:
|
if parent.email:
|
||||||
fromdata.append('MAILTO:' + child.email)
|
fromdata.append('MAILTO:' + parent.email)
|
||||||
result[id][name] = ', '.join(fromdata)
|
result[id][name] = ', '.join(fromdata)
|
||||||
if name == 'event_date':
|
if name == 'event_date':
|
||||||
if attdata.ref:
|
if attdata.ref:
|
||||||
|
@ -383,49 +369,72 @@ property or property parameter."),
|
||||||
'state': lambda *x: 'needs-action',
|
'state': lambda *x: 'needs-action',
|
||||||
}
|
}
|
||||||
|
|
||||||
response_re = re.compile("Are you coming\?.*\n*.*(YES|NO|MAYBE).*", re.UNICODE)
|
def get_ics_file(self, cr, uid, event_obj, context=None):
|
||||||
|
"""
|
||||||
def msg_new(self, cr, uid, msg):
|
Returns iCalendar file for the event invitation
|
||||||
"""
|
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
"""
|
|
||||||
return False
|
|
||||||
|
|
||||||
def msg_act_get(self, msg):
|
|
||||||
"""
|
|
||||||
Get Message.
|
|
||||||
@param self: The object pointer
|
|
||||||
@return: dictionary of actions which contain state field value.
|
|
||||||
"""
|
|
||||||
|
|
||||||
mailgate_obj = self.pool.get('mail.gateway')
|
|
||||||
body = mailgate_obj.msg_body_get(msg)
|
|
||||||
actions = {}
|
|
||||||
res = self.response_re.findall(body['body'])
|
|
||||||
if res:
|
|
||||||
actions['state'] = res[0]
|
|
||||||
return actions
|
|
||||||
|
|
||||||
def msg_update(self, cr, uid, ids, msg, data={}, default_act='None'):
|
|
||||||
"""
|
|
||||||
Update msg state which may be accepted.declined.tentative.
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param ids: List of calendar attendee’s IDs.
|
@param event_obj: Event object (browse record)
|
||||||
@param context: A standard dictionary for contextual values
|
@param context: A standard dictionary for contextual values
|
||||||
@return: True
|
@return: .ics file content
|
||||||
"""
|
"""
|
||||||
msg_actions = self.msg_act_get(msg)
|
res = None
|
||||||
if msg_actions.get('state'):
|
def ics_datetime(idate, short=False):
|
||||||
if msg_actions['state'] in ['YES', 'NO', 'MAYBE']:
|
if short:
|
||||||
mapping = {'YES': 'accepted', 'NO': 'declined', 'MAYBE': 'tentative'}
|
return date.fromtimestamp(time.mktime(time.strptime(idate, '%Y-%m-%d')))
|
||||||
status = mapping[msg_actions['state']]
|
else:
|
||||||
print 'Got response for invitation id: %s as %s' % (ids, status)
|
return datetime.strptime(idate, '%Y-%m-%d %H:%M:%S')
|
||||||
self.write(cr, uid, ids, {'state': status})
|
try:
|
||||||
return True
|
import vobject
|
||||||
|
except ImportError:
|
||||||
|
return res
|
||||||
|
cal = vobject.iCalendar()
|
||||||
|
event = cal.add('vevent')
|
||||||
|
event.add('created').value = ics_datetime(time.strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
event.add('dtstart').value = ics_datetime(event_obj.date)
|
||||||
|
event.add('dtend').value = ics_datetime(event_obj.date_deadline)
|
||||||
|
event.add('summary').value = event_obj.name
|
||||||
|
if event_obj.description:
|
||||||
|
event.add('description').value = event_obj.description
|
||||||
|
if event_obj.location:
|
||||||
|
event.add('location').value = event_obj.location
|
||||||
|
if event_obj.rrule:
|
||||||
|
event.add('rrule').value = event_obj.rrule
|
||||||
|
|
||||||
|
if event_obj.alarm_id:
|
||||||
|
# computes alarm data
|
||||||
|
valarm = event.add('valarm')
|
||||||
|
alarm_object = self.pool.get('res.alarm')
|
||||||
|
alarm_data = alarm_object.read(cr, uid, event_obj.alarm_id.id, context=context)
|
||||||
|
# Compute trigger data
|
||||||
|
interval = alarm_data['trigger_interval']
|
||||||
|
occurs = alarm_data['trigger_occurs']
|
||||||
|
duration = (occurs == 'after' and alarm_data['trigger_duration']) \
|
||||||
|
or -(alarm_data['trigger_duration'])
|
||||||
|
related = alarm_data['trigger_related']
|
||||||
|
trigger = valarm.add('TRIGGER')
|
||||||
|
trigger.params['related'] = [related.upper()]
|
||||||
|
if interval == 'days':
|
||||||
|
delta = timedelta(days=duration)
|
||||||
|
if interval == 'hours':
|
||||||
|
delta = timedelta(hours=duration)
|
||||||
|
if interval == 'minutes':
|
||||||
|
delta = timedelta(minutes=duration)
|
||||||
|
trigger.value = delta
|
||||||
|
|
||||||
|
# Compute other details
|
||||||
|
valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP'
|
||||||
|
|
||||||
|
for attendee in event_obj.attendee_ids:
|
||||||
|
attendee_add = event.add('attendee')
|
||||||
|
attendee_add.params['CUTYPE'] = [str(attendee.cutype)]
|
||||||
|
attendee_add.params['ROLE'] = [str(attendee.role)]
|
||||||
|
attendee_add.params['RSVP'] = [str(attendee.rsvp)]
|
||||||
|
attendee_add.value = 'MAILTO:' + attendee.email
|
||||||
|
res = cal.serialize()
|
||||||
|
return res
|
||||||
|
|
||||||
def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context={}):
|
def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context={}):
|
||||||
"""
|
"""
|
||||||
Send mail for calendar attendee.
|
Send mail for calendar attendee.
|
||||||
|
@ -443,7 +452,7 @@ property or property parameter."),
|
||||||
res_obj = att.ref
|
res_obj = att.ref
|
||||||
sub = '[%s Invitation][%d] %s' % (company, att.id, res_obj.name)
|
sub = '[%s Invitation][%d] %s' % (company, att.id, res_obj.name)
|
||||||
att_infos = []
|
att_infos = []
|
||||||
other_invitaion_ids = self.search(cr, uid, [('ref', '=', att.ref._name + ',' + str(att.ref.id))])
|
other_invitaion_ids = self.search(cr, uid, [('ref', '=', res_obj._name + ',' + str(res_obj.id))])
|
||||||
for att2 in self.browse(cr, uid, other_invitaion_ids):
|
for att2 in self.browse(cr, uid, other_invitaion_ids):
|
||||||
att_infos.append(((att2.user_id and att2.user_id.name) or \
|
att_infos.append(((att2.user_id and att2.user_id.name) or \
|
||||||
(att2.partner_id and att2.partner_id.name) or \
|
(att2.partner_id and att2.partner_id.name) or \
|
||||||
|
@ -459,15 +468,17 @@ property or property parameter."),
|
||||||
'company': company
|
'company': company
|
||||||
}
|
}
|
||||||
body = html_invitation % body_vals
|
body = html_invitation % body_vals
|
||||||
|
attach = self.get_ics_file(cr, uid, res_obj, context=context)
|
||||||
if mail_to and email_from:
|
if mail_to and email_from:
|
||||||
tools.email_send(
|
tools.email_send(
|
||||||
email_from,
|
email_from,
|
||||||
mail_to,
|
mail_to,
|
||||||
sub,
|
sub,
|
||||||
body,
|
body,
|
||||||
subtype='html',
|
attach=attach and [('invitation.ics', attach)] or None,
|
||||||
reply_to=email_from
|
subtype='html',
|
||||||
)
|
reply_to=email_from
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
|
def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
|
||||||
|
@ -569,7 +580,7 @@ class res_alarm(osv.osv):
|
||||||
are both optional, but if one occurs, so MUST the other"""),
|
are both optional, but if one occurs, so MUST the other"""),
|
||||||
'repeat': fields.integer('Repeat'),
|
'repeat': fields.integer('Repeat'),
|
||||||
'active': fields.boolean('Active', help="If the active field is set to \
|
'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.")
|
true, it will allow you to hide the event alarm information without removing it.")
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'trigger_interval': lambda *x: 'minutes',
|
'trigger_interval': lambda *x: 'minutes',
|
||||||
|
@ -659,41 +670,41 @@ class calendar_alarm(osv.osv):
|
||||||
__attribute__ = {}
|
__attribute__ = {}
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'),
|
'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'),
|
||||||
'name': fields.char('Summary', size=124, help="""Contains the text to be \
|
'name': fields.char('Summary', size=124, help="""Contains the text to be \
|
||||||
used as the message subject for email \
|
used as the message subject for email \
|
||||||
or contains the text to be used for display"""),
|
or contains the text to be used for display"""),
|
||||||
'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
|
'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
|
||||||
('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
|
('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
|
||||||
required=True, help="Defines the action to be invoked when an alarm is triggered"),
|
required=True, help="Defines the action to be invoked when an alarm is triggered"),
|
||||||
'description': fields.text('Description', help='Provides a more complete \
|
'description': fields.text('Description', help='Provides a more complete \
|
||||||
description of the calendar component, than that \
|
description of the calendar component, than that \
|
||||||
provided by the "SUMMARY" property'),
|
provided by the "SUMMARY" property'),
|
||||||
'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
|
'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
|
||||||
'alarm_id', 'attendee_id', 'Attendees', readonly=True),
|
'alarm_id', 'attendee_id', 'Attendees', readonly=True),
|
||||||
'attach': fields.binary('Attachment', help="""* Points to a sound resource,\
|
'attach': fields.binary('Attachment', help="""* Points to a sound resource,\
|
||||||
which is rendered when the alarm is triggered for audio,
|
which is rendered when the alarm is triggered for audio,
|
||||||
* File which is intended to be sent as message attachments for email,
|
* File which is intended to be sent as message attachments for email,
|
||||||
* Points to a procedure resource, which is invoked when\
|
* Points to a procedure resource, which is invoked when\
|
||||||
the alarm is triggered for procedure."""),
|
the alarm is triggered for procedure."""),
|
||||||
'res_id': fields.integer('Resource ID'),
|
'res_id': fields.integer('Resource ID'),
|
||||||
'model_id': fields.many2one('ir.model', 'Model'),
|
'model_id': fields.many2one('ir.model', 'Model'),
|
||||||
'user_id': fields.many2one('res.users', 'Owner'),
|
'user_id': fields.many2one('res.users', 'Owner'),
|
||||||
'event_date': fields.datetime('Event Date'),
|
'event_date': fields.datetime('Event Date'),
|
||||||
'event_end_date': fields.datetime('Event End Date'),
|
'event_end_date': fields.datetime('Event End Date'),
|
||||||
'trigger_date': fields.datetime('Trigger Date', readonly="True"),
|
'trigger_date': fields.datetime('Trigger Date', readonly="True"),
|
||||||
'state':fields.selection([('draft', 'Draft'),
|
'state':fields.selection([
|
||||||
('run', 'Run'),
|
('draft', 'Draft'),
|
||||||
('stop', 'Stop'),
|
('run', 'Run'),
|
||||||
('done', 'Done'),
|
('stop', 'Stop'),
|
||||||
], 'State', select=True, readonly=True),
|
('done', 'Done'),
|
||||||
}
|
], 'State', select=True, readonly=True),
|
||||||
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'action': lambda *x: 'email',
|
'action': lambda *x: 'email',
|
||||||
'state': lambda *x: 'run',
|
'state': lambda *x: 'run',
|
||||||
}
|
}
|
||||||
|
|
||||||
def create(self, cr, uid, vals, context={}):
|
def create(self, cr, uid, vals, context={}):
|
||||||
"""
|
"""
|
||||||
create new record.
|
create new record.
|
||||||
|
@ -983,7 +994,7 @@ class calendar_event(osv.osv):
|
||||||
'exdate': fields.text('Exception Date/Times', help="This property \
|
'exdate': fields.text('Exception Date/Times', help="This property \
|
||||||
defines the list of date/time exceptions for arecurring calendar component."),
|
defines the list of date/time exceptions for arecurring calendar component."),
|
||||||
'exrule': fields.char('Exception Rule', size=352, help="defines a \
|
'exrule': fields.char('Exception Rule', size=352, help="defines a \
|
||||||
rule or repeating pattern for anexception to a recurrence set"),
|
rule or repeating pattern for an exception to a recurrence set"),
|
||||||
'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, \
|
'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, \
|
||||||
string='Recurrent Rule', store=True, \
|
string='Recurrent Rule', store=True, \
|
||||||
fnct_inv=_set_rrulestring, help='Defines a\
|
fnct_inv=_set_rrulestring, help='Defines a\
|
||||||
|
@ -1031,7 +1042,9 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
|
||||||
'end_date': fields.date('Repeat Until'),
|
'end_date': fields.date('Repeat Until'),
|
||||||
'attendee_ids': fields.many2many('calendar.attendee', 'event_attendee_rel',\
|
'attendee_ids': fields.many2many('calendar.attendee', 'event_attendee_rel',\
|
||||||
'event_id', 'attendee_id', 'Attendees'),
|
'event_id', 'attendee_id', 'Attendees'),
|
||||||
'allday': fields.boolean('All Day')
|
'allday': fields.boolean('All Day'),
|
||||||
|
'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.")
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
@ -1040,6 +1053,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
|
||||||
'freq': lambda *x: 'None',
|
'freq': lambda *x: 'None',
|
||||||
'select1': lambda *x: 'date',
|
'select1': lambda *x: 'date',
|
||||||
'interval': lambda *x: 1,
|
'interval': lambda *x: 1,
|
||||||
|
'active': lambda *x: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
def open_event(self, cr, uid, ids, context=None):
|
def open_event(self, cr, uid, ids, context=None):
|
||||||
|
@ -1164,8 +1178,10 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
|
||||||
if count > limit:
|
if count > limit:
|
||||||
break
|
break
|
||||||
event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
|
event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
|
||||||
if start_date and start_date <= event_date:
|
# To check: If the start date is replace by event date .. the event date will be changed by that of calendar code
|
||||||
start_date = event_date
|
# if start_date and start_date <= event_date:
|
||||||
|
# start_date = event_date
|
||||||
|
start_date = event_date
|
||||||
if not data['rrule']:
|
if not data['rrule']:
|
||||||
if start_date and (event_date < start_date):
|
if start_date and (event_date < start_date):
|
||||||
continue
|
continue
|
||||||
|
@ -1213,7 +1229,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
|
||||||
if isinstance(select, (str, int, long)):
|
if isinstance(select, (str, int, long)):
|
||||||
return ids and ids[0] or False
|
return ids and ids[0] or False
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
def compute_rule_string(self, cr, uid, datas, context=None, *args):
|
def compute_rule_string(self, cr, uid, datas, context=None, *args):
|
||||||
"""
|
"""
|
||||||
Compute rule string.
|
Compute rule string.
|
||||||
|
@ -1332,6 +1348,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
|
||||||
event_id = base_calendar_id2real_id(event_id)
|
event_id = base_calendar_id2real_id(event_id)
|
||||||
if not event_id in new_ids:
|
if not event_id in new_ids:
|
||||||
new_ids.append(event_id)
|
new_ids.append(event_id)
|
||||||
|
|
||||||
res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
|
res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
|
||||||
if vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'):
|
if vals.has_key('alarm_id') or vals.has_key('base_calendar_alarm_id'):
|
||||||
alarm_obj = self.pool.get('res.alarm')
|
alarm_obj = self.pool.get('res.alarm')
|
||||||
|
|
|
@ -8,8 +8,13 @@
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Invitation details">
|
<form string="Invitation details">
|
||||||
<field name="email" string="Invitation To"/>
|
<group col="6" colspan="4">
|
||||||
<field name="sent_by_uid" string="Invitation From" />
|
<field name="email" string="Invitation To"/>
|
||||||
|
<field name="cutype" string="Invitation type" />
|
||||||
|
<field name="rsvp" />
|
||||||
|
<field name="role" string="Role" />
|
||||||
|
<field name="sent_by_uid" string="Invitation From" />
|
||||||
|
</group>
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Invitation">
|
<page string="Invitation">
|
||||||
<separator string="Invitation Detail" colspan="4" />
|
<separator string="Invitation Detail" colspan="4" />
|
||||||
|
@ -20,9 +25,6 @@
|
||||||
string="Partner Contact" />
|
string="Partner Contact" />
|
||||||
<field name="partner_id"
|
<field name="partner_id"
|
||||||
string="Partner" readonly="1" />
|
string="Partner" readonly="1" />
|
||||||
<field name="role" string="Role" />
|
|
||||||
<field name="cutype" string="Invitation type" />
|
|
||||||
<field name="rsvp" />
|
|
||||||
</group>
|
</group>
|
||||||
<separator string="Event Detail" colspan="4" />
|
<separator string="Event Detail" colspan="4" />
|
||||||
<group colspan="4" col="4">
|
<group colspan="4" col="4">
|
||||||
|
@ -181,7 +183,7 @@
|
||||||
<field name="model">calendar.event</field>
|
<field name="model">calendar.event</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Meetings">
|
<form string="Events">
|
||||||
<group col="6" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="name" select="1" string="Summary"
|
<field name="name" select="1" string="Summary"
|
||||||
colspan="4" />
|
colspan="4" />
|
||||||
|
@ -190,7 +192,7 @@
|
||||||
<field name="date" string="Start Date" required="1" select="1"
|
<field name="date" string="Start Date" required="1" select="1"
|
||||||
on_change="onchange_dates(date,duration,False,allday)" />
|
on_change="onchange_dates(date,duration,False,allday)" />
|
||||||
<field name="duration" widget="float_time"
|
<field name="duration" widget="float_time"
|
||||||
on_change="onchange_dates(date,duration,False,allday)" attrs="{'readonly': [('allday', '=', True)]}"/>
|
on_change="onchange_dates(date,duration,False,allday)" attrs="{'invisible': [('allday', '=', True)]}"/>
|
||||||
<field name="date_deadline" string="End Date" required="1"
|
<field name="date_deadline" string="End Date" required="1"
|
||||||
on_change="onchange_dates(date,False,date_deadline)" />
|
on_change="onchange_dates(date,False,date_deadline)" />
|
||||||
<field name="location" />
|
<field name="location" />
|
||||||
|
@ -257,7 +259,6 @@
|
||||||
<field name="user_id" string="Responsible User" />
|
<field name="user_id" string="Responsible User" />
|
||||||
<field name="show_as" string="Show time as"/>
|
<field name="show_as" string="Show time as"/>
|
||||||
<field name="class" string="Privacy"/>
|
<field name="class" string="Privacy"/>
|
||||||
<field name="rrule" groups="base.group_extended" />
|
|
||||||
<field name="recurrent_id" invisible="1" />
|
<field name="recurrent_id" invisible="1" />
|
||||||
<field name="recurrent_uid" invisible="1" />
|
<field name="recurrent_uid" invisible="1" />
|
||||||
</group>
|
</group>
|
||||||
|
@ -333,6 +334,7 @@
|
||||||
<field name="location" />
|
<field name="location" />
|
||||||
<field name="show_as" />
|
<field name="show_as" />
|
||||||
<field name="class" string="Privacy" />
|
<field name="class" string="Privacy" />
|
||||||
|
<field name="user_id" invisible="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -353,6 +355,44 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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="type">search</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Search Events">
|
||||||
|
<group col="12" colspan="4">
|
||||||
|
<filter icon="terp-partner" string="My Events"
|
||||||
|
domain="[('user_id','=',uid)]"
|
||||||
|
help="My Events" />
|
||||||
|
<separator orientation="vertical"/>
|
||||||
|
<field name="name" select="1" string="Summary"/>
|
||||||
|
<field name="location" select="1" string="Location"/>
|
||||||
|
<separator orientation="vertical"/>
|
||||||
|
<field name="user_id" select="1" widget="selection"/>
|
||||||
|
<separator orientation="vertical"/>
|
||||||
|
<field name="show_as" select="1"/>
|
||||||
|
<field name="class" select="1"/>
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="0" string="Group By..." colspan="16">
|
||||||
|
<filter string="Date" icon="terp-project"
|
||||||
|
domain="[]" context="{'group_by':'date'}" />
|
||||||
|
<filter string="Availability" icon="terp-project"
|
||||||
|
domain="[]" context="{'group_by':'show_as'}" />
|
||||||
|
<filter string="Privacy" icon="terp-crm"
|
||||||
|
domain="[]" context="{'group_by':'class'}" />
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="Responsible" icon="terp-crm" domain="[]"
|
||||||
|
context="{'group_by':'user_id'}" />
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<!-- Event action -->
|
<!-- Event action -->
|
||||||
|
|
||||||
<record id="action_view_event" model="ir.actions.act_window">
|
<record id="action_view_event" model="ir.actions.act_window">
|
||||||
|
@ -361,6 +401,7 @@
|
||||||
<field name="res_model">calendar.event</field>
|
<field name="res_model">calendar.event</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form,calendar</field>
|
<field name="view_mode">tree,form,calendar</field>
|
||||||
|
<field name="search_view_id" ref="view_calendar_event_filter"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Event menu -->
|
<!-- Event menu -->
|
||||||
|
|
|
@ -44,7 +44,7 @@ class base_calendar_invite_attendee(osv.osv_memory):
|
||||||
'contact_ids': fields.many2many('res.partner.address', 'invite_contact_rel',
|
'contact_ids': fields.many2many('res.partner.address', 'invite_contact_rel',
|
||||||
'invite_id', 'contact_id', 'Contacts'),
|
'invite_id', 'contact_id', 'Contacts'),
|
||||||
'send_mail': fields.boolean('Send mail?', help='Check this if you want to \
|
'send_mail': fields.boolean('Send mail?', help='Check this if you want to \
|
||||||
send an Email to Invited Person')
|
send an Email to Invited Person')
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
@ -66,20 +66,19 @@ class base_calendar_invite_attendee(osv.osv_memory):
|
||||||
|
|
||||||
model = False
|
model = False
|
||||||
model_field = False
|
model_field = False
|
||||||
|
|
||||||
context_id = context and context.get('active_id', False) or False
|
context_id = context and context.get('active_id', False) or False
|
||||||
if not context or not context.get('model'):
|
if not context or not context.get('model'):
|
||||||
return {}
|
return {}
|
||||||
else:
|
else:
|
||||||
model = context.get('model')
|
model = context.get('model')
|
||||||
|
|
||||||
model_field = context.get('attendee_field', False)
|
model_field = context.get('attendee_field', False)
|
||||||
|
obj = self.pool.get(model)
|
||||||
|
res_obj = obj.browse(cr, uid, context_id)
|
||||||
|
att_obj = self.pool.get('calendar.attendee')
|
||||||
|
|
||||||
for datas in self.read(cr, uid, ids, context=context):
|
for datas in self.read(cr, uid, ids, context=context):
|
||||||
|
|
||||||
obj = self.pool.get(model)
|
|
||||||
res_obj = obj.browse(cr, uid, context_id)
|
|
||||||
type = datas.get('type')
|
type = datas.get('type')
|
||||||
att_obj = self.pool.get('calendar.attendee')
|
|
||||||
vals = []
|
vals = []
|
||||||
mail_to = []
|
mail_to = []
|
||||||
attendees = []
|
attendees = []
|
||||||
|
@ -123,15 +122,13 @@ class base_calendar_invite_attendee(osv.osv_memory):
|
||||||
if contact.email:
|
if contact.email:
|
||||||
mail_to.append(contact.email)
|
mail_to.append(contact.email)
|
||||||
|
|
||||||
att = att_obj.browse(cr, uid, context_id)
|
|
||||||
|
|
||||||
for att_val in vals:
|
for att_val in vals:
|
||||||
if model == 'calendar.attendee':
|
if model == 'calendar.attendee':
|
||||||
if ref:
|
att = att_obj.browse(cr, uid, context_id)
|
||||||
att_val.update({
|
att_val.update({
|
||||||
'parent_ids': [(4, att.id)],
|
'parent_ids': [(4, att.id)],
|
||||||
'ref': att.ref._name + ',' +str(att.ref.id)
|
'ref': att.ref._name + ',' +str(att.ref.id)
|
||||||
})
|
})
|
||||||
attendees.append(att_obj.create(cr, uid, att_val))
|
attendees.append(att_obj.create(cr, uid, att_val))
|
||||||
if model_field:
|
if model_field:
|
||||||
for attendee in attendees:
|
for attendee in attendees:
|
||||||
|
|
|
@ -17,33 +17,34 @@
|
||||||
<attribute name='string'>Configure Your Company</attribute>
|
<attribute name='string'>Configure Your Company</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||||
<attribute name='rowspan'>23</attribute>
|
<attribute name='rowspan'>25</attribute>
|
||||||
<attribute name='string'></attribute>
|
<attribute name='string'></attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<group string="res_config_contents" position="replace">
|
<group string="res_config_contents" position="replace">
|
||||||
<group colspan="5">
|
<group colspan="5">
|
||||||
<group colspan="2">
|
<group colspan="2">
|
||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="name" colspan="4" required="True"/>
|
<field name="name" required="True"/><field name="account_no"/>
|
||||||
<newline/>
|
<field name="currency" widget="selection"/><field name="street"/>
|
||||||
<field name="street"/>
|
|
||||||
<field name="street2"/>
|
|
||||||
<field name="zip"/>
|
<field name="zip"/>
|
||||||
|
<field name="street2"/>
|
||||||
<field name="city"/>
|
<field name="city"/>
|
||||||
<field name="country_id"/>
|
<field name="country_id"/>
|
||||||
<field name="state_id"/>
|
<field name="state_id"/>
|
||||||
<field name="email"/>
|
<field name="email"/>
|
||||||
<field name="phone"/>
|
<field name="phone"/>
|
||||||
<field name="currency" widget="selection"/>
|
<field name="website"/>
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group colspan="2" groups="base.group_extended">
|
||||||
<separator string="Report Information" colspan="4"/>
|
<separator string="Report Information" colspan="4"/>
|
||||||
<field name="rml_header1" colspan="4"/>
|
<field name="rml_header1" colspan="5"/>
|
||||||
<field name="rml_footer1" colspan="4"/>
|
<field name="rml_footer1" colspan="5"/>
|
||||||
<field name="rml_footer2" colspan="4"/>
|
<field name="rml_footer2" colspan="5"/>
|
||||||
|
|
||||||
<separator colspan="4"
|
<separator colspan="4"
|
||||||
string="Your Logo - Use a size of about 450x150 pixels."/>
|
string="Your Logo - Use a size of about 450x150 pixels."/>
|
||||||
<field colspan="4" name="logo" widget="image"
|
<field colspan="5" name="logo" widget="image"
|
||||||
nolabel="1"/>
|
nolabel="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|
|
@ -70,7 +70,8 @@ class base_gtkcontactform(osv.osv_memory):
|
||||||
'training':fields.boolean('OpenERP Training Program'),
|
'training':fields.boolean('OpenERP Training Program'),
|
||||||
'other':fields.boolean('Other'),
|
'other':fields.boolean('Other'),
|
||||||
'ebook':fields.boolean('ebook'),
|
'ebook':fields.boolean('ebook'),
|
||||||
'updates':fields.boolean('updates'),
|
'updates':fields.boolean('Updates'),
|
||||||
|
'contact_me':fields.boolean('Contact Me'),
|
||||||
}
|
}
|
||||||
def execute(self, cr, uid, ids, context=None):
|
def execute(self, cr, uid, ids, context=None):
|
||||||
company_id = self.pool.get('base.setup.company').search(cr, uid, [])
|
company_id = self.pool.get('base.setup.company').search(cr, uid, [])
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<attribute name="string">Information about you and the company you are related too. The Basic information related to the company has already been collected and some more information required by you to fill.</attribute>
|
<attribute name="string">Information about you and the company you are related too. The Basic information related to the company has already been collected and some more information required by you to fill.</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr='//separator[@string="title"]' position='attributes'>
|
<xpath expr='//separator[@string="title"]' position='attributes'>
|
||||||
<attribute name='string'>Do You Need Them ?</attribute>
|
<attribute name='string'>Would you like more information or documentation ?</attribute>
|
||||||
<attribute name='colspan'>4</attribute>
|
<attribute name='colspan'>4</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
<xpath expr='//separator[@string="vsep"]' position='attributes'>
|
||||||
|
@ -24,6 +24,8 @@
|
||||||
<group string="res_config_contents" position="replace">
|
<group string="res_config_contents" position="replace">
|
||||||
<group colspan="4" height="450" width="600">
|
<group colspan="4" height="450" width="600">
|
||||||
<group colspan="4">
|
<group colspan="4">
|
||||||
|
<field name="contact_me" nolabel="1"/>
|
||||||
|
<label align="0.0" string="I accept to be contacted by OpenERP." colspan="3"/>
|
||||||
<field name="ebook" nolabel="1"/>
|
<field name="ebook" nolabel="1"/>
|
||||||
<label align="0.0" string="I want to receive the Open ERP ebook (PDF) by email." colspan="3"/>
|
<label align="0.0" string="I want to receive the Open ERP ebook (PDF) by email." colspan="3"/>
|
||||||
<field name="updates" nolabel="1"/>
|
<field name="updates" nolabel="1"/>
|
||||||
|
@ -71,7 +73,7 @@
|
||||||
<record id="base_setup_contact_todo" model="ir.actions.todo">
|
<record id="base_setup_contact_todo" model="ir.actions.todo">
|
||||||
<field name="action_id" ref="action_base_contact"/>
|
<field name="action_id" ref="action_base_contact"/>
|
||||||
<field name="sequence">1</field>
|
<field name="sequence">1</field>
|
||||||
<field name="restart">onskip</field>
|
<field name="restart">never</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -73,6 +73,7 @@ class base_setup_installer(osv.osv_memory):
|
||||||
'report_designer':fields.boolean('Advanced Reporting',
|
'report_designer':fields.boolean('Advanced Reporting',
|
||||||
help="Lets you install various tools to simplify and enhance "
|
help="Lets you install various tools to simplify and enhance "
|
||||||
"OpenERP's report creation."),
|
"OpenERP's report creation."),
|
||||||
|
'thunderbird' :fields.boolean('Thunderbird'),
|
||||||
# Vertical modules
|
# Vertical modules
|
||||||
'profile_association':fields.boolean('Associations',
|
'profile_association':fields.boolean('Associations',
|
||||||
help="Installs a preselected set of OpenERP "
|
help="Installs a preselected set of OpenERP "
|
||||||
|
|
|
@ -86,8 +86,8 @@ class base_setup_company(osv.osv_memory):
|
||||||
'street2':fields.char('Street 2', size=128),
|
'street2':fields.char('Street 2', size=128),
|
||||||
'zip':fields.char('Zip Code', size=24),
|
'zip':fields.char('Zip Code', size=24),
|
||||||
'city':fields.char('City', size=128),
|
'city':fields.char('City', size=128),
|
||||||
'state_id':fields.selection(_get_all_states, 'States'),
|
'state_id':fields.selection(_get_all_states, 'State'),
|
||||||
'country_id':fields.selection(_get_all_countries, 'Countries'),
|
'country_id':fields.selection(_get_all_countries, 'Country'),
|
||||||
'email':fields.char('E-mail', size=64),
|
'email':fields.char('E-mail', size=64),
|
||||||
'phone':fields.char('Phone', size=64),
|
'phone':fields.char('Phone', size=64),
|
||||||
'currency':fields.many2one('res.currency', 'Currency', required=True),
|
'currency':fields.many2one('res.currency', 'Currency', required=True),
|
||||||
|
@ -104,6 +104,8 @@ Web: http://openerp.com - Fax: +32.81.73.35.01 - Fortis Bank: 126-2013269-07''')
|
||||||
We suggest you to put bank information here:
|
We suggest you to put bank information here:
|
||||||
IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701'''),
|
IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701'''),
|
||||||
'logo':fields.binary('Logo'),
|
'logo':fields.binary('Logo'),
|
||||||
|
'account_no':fields.char('Account No', size=64),
|
||||||
|
'website': fields.char('Web', size=64),
|
||||||
}
|
}
|
||||||
|
|
||||||
def execute(self, cr, uid, ids, context=None):
|
def execute(self, cr, uid, ids, context=None):
|
||||||
|
@ -120,10 +122,12 @@ IBAN: BE74 1262 0121 6907 - SWIFT: CPDF BE71 - VAT: BE0477.472.701'''),
|
||||||
'rml_footer2':payload.rml_footer2,
|
'rml_footer2':payload.rml_footer2,
|
||||||
'logo':payload.logo,
|
'logo':payload.logo,
|
||||||
'currency_id':payload.currency.id,
|
'currency_id':payload.currency.id,
|
||||||
|
'account_no':payload.account_no,
|
||||||
})
|
})
|
||||||
|
|
||||||
company.partner_id.write({
|
company.partner_id.write({
|
||||||
'name':payload.name,
|
'name':payload.name,
|
||||||
|
'website':payload.website,
|
||||||
})
|
})
|
||||||
|
|
||||||
address_data = {
|
address_data = {
|
||||||
|
|
|
@ -7,13 +7,13 @@ msgstr ""
|
||||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||||
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
|
"POT-Creation-Date: 2009-08-28 16:01+0000\n"
|
||||||
"PO-Revision-Date: 2009-12-29 16:54+0000\n"
|
"PO-Revision-Date: 2010-05-13 13:58+0000\n"
|
||||||
"Last-Translator: Carlo Vettore <Unknown>\n"
|
"Last-Translator: eLBati - albatos.com <lorenzo.battistini@albatos.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2010-04-17 04:03+0000\n"
|
"X-Launchpad-Export-Date: 2010-05-14 04:55+0000\n"
|
||||||
"X-Generator: Launchpad (build Unknown)\n"
|
"X-Generator: Launchpad (build Unknown)\n"
|
||||||
|
|
||||||
#. module: base_vat
|
#. module: base_vat
|
||||||
|
@ -31,9 +31,9 @@ msgstr "XML non valido per Visualizzazione Architettura!"
|
||||||
#. module: base_vat
|
#. module: base_vat
|
||||||
#: field:res.partner,vat_subjected:0
|
#: field:res.partner,vat_subjected:0
|
||||||
msgid "VAT Legal Statement"
|
msgid "VAT Legal Statement"
|
||||||
msgstr "Numero di Partita IVA"
|
msgstr "Soggetto a IVA"
|
||||||
|
|
||||||
#. module: base_vat
|
#. module: base_vat
|
||||||
#: model:ir.module.module,shortdesc:base_vat.module_meta_information
|
#: model:ir.module.module,shortdesc:base_vat.module_meta_information
|
||||||
msgid "VAT"
|
msgid "VAT"
|
||||||
msgstr "P.IVA"
|
msgstr "Partita IVA"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_crm_opportunity_categ_graph">
|
<record model="ir.ui.view" id="view_crm_opportunity_categ_graph">
|
||||||
<field name="name">report.crm.opportunity.categ.graph</field>
|
<field name="name">report.crm.opportunity.categ.graph</field>
|
||||||
<field name="model">crm.opportunity.report</field>
|
<field name="model">crm.lead.report</field>
|
||||||
<field name="type">graph</field>
|
<field name="type">graph</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<graph string="Opportunity by Sales Team and Categories" type="bar" orientation="horizontal">
|
<graph string="Opportunity by Sales Team and Categories" type="bar" orientation="horizontal">
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<act_window name="Opportunities By Categories"
|
<act_window name="Opportunities By Categories"
|
||||||
domain="[('state','!=','done'),('state','!=','cancel')]"
|
domain="[('state','!=','done'),('state','!=','cancel')]"
|
||||||
res_model="crm.opportunity.report"
|
res_model="crm.lead.report"
|
||||||
view_type="form"
|
view_type="form"
|
||||||
view_mode="graph,tree,form"
|
view_mode="graph,tree,form"
|
||||||
view_id="view_crm_opportunity_categ_graph"
|
view_id="view_crm_opportunity_categ_graph"
|
||||||
|
|
|
@ -1,88 +1,77 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
<record model="board.note.type" id="note_crm_type">
|
<record model="board.note.type" id="note_crm_type">
|
||||||
<field name="name">CRM Configuration</field>
|
<field name="name">CRM Configuration</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="ir.ui.view" id="view_crm_opportunity_user_graph1">
|
<record model="ir.ui.view" id="view_crm_opportunity_user_graph1">
|
||||||
<field name="name">crm.opportunity.user.graph1</field>
|
<field name="name">crm.opportunity.user.graph1</field>
|
||||||
<field name="model">crm.opportunity.report</field>
|
<field name="model">crm.lead.report</field>
|
||||||
<field name="type">graph</field>
|
<field name="type">graph</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<graph string="Opportunities by User and Sales Team" type="pie">
|
<graph string="Opportunities by User and Section" type="pie">
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="nbr" operator="+"/>
|
<field name="nbr" operator="+"/>
|
||||||
</graph>
|
</graph>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="ir.actions.act_window" id="act_my_leads">
|
<record model="ir.actions.act_window" id="act_my_leads">
|
||||||
<field name="res_model">crm.lead</field>
|
<field name="res_model">crm.lead</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="view_id" ref="crm.crm_case_tree_view_leads"/>
|
<field name="view_id" ref="crm.crm_case_tree_view_leads"/>
|
||||||
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="ir.actions.act_window" id="act_my_meetings">
|
<record model="ir.actions.act_window" id="act_my_meetings">
|
||||||
<field name="res_model">crm.meeting</field>
|
<field name="res_model">crm.meeting</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="view_id" ref="crm.crm_case_tree_view_meet"/>
|
<field name="view_id" ref="crm.crm_case_tree_view_meet"/>
|
||||||
|
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="act_my_leads_stage">
|
||||||
|
<field name="res_model">crm.lead.report</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">graph,tree,form</field>
|
||||||
|
<field name="view_id" ref="view_crm_lead_stage_graph1"/>
|
||||||
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
||||||
</record>
|
</record>
|
||||||
<act_window name="My Cases"
|
|
||||||
domain="[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]"
|
|
||||||
res_model="crm.case"
|
|
||||||
view_type="form"
|
|
||||||
view_mode="tree,form"
|
|
||||||
id="act_my_cases"/>
|
|
||||||
<record model="ir.actions.act_window" id="act_my_leads_stage">
|
|
||||||
<field name="res_model">crm.lead.report</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">graph,tree,form</field>
|
|
||||||
<field name="view_id" ref="view_crm_lead_stage_graph1"/>
|
|
||||||
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="act_sales_pipeline">
|
<record model="ir.actions.act_window" id="act_sales_pipeline">
|
||||||
<field name="res_model">crm.opportunity.report</field>
|
<field name="res_model">crm.lead.report</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">graph,tree,form</field>
|
<field name="view_mode">graph,tree,form</field>
|
||||||
<field name="view_id" ref="view_crm_opportunity_user_graph1"/>
|
<field name="view_id" ref="view_crm_opportunity_user_graph1"/>
|
||||||
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel')]</field>
|
<field name="domain">[('user_id','=',uid),('state','!=','pending'),('state','!=','cancel'),('type', '=', 'opportunity')]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="board_crm_form">
|
<record model="ir.ui.view" id="board_crm_form">
|
||||||
<field name="name">CRM - Dashboard Form</field>
|
<field name="name">CRM - Dashboard Form</field>
|
||||||
<field name="model">board.board</field>
|
<field name="model">board.board</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Connecting Dashboard">
|
<form string="Connecting Dashboard">
|
||||||
<hpaned>
|
<hpaned>
|
||||||
|
|
||||||
<child1>
|
<child1>
|
||||||
|
|
||||||
<action
|
<action
|
||||||
name="%(act_my_leads)d"
|
name="%(act_my_leads)d"
|
||||||
colspan="4"
|
colspan="4"
|
||||||
height="150"
|
height="150"
|
||||||
width="510"/>
|
width="510"/>
|
||||||
|
|
||||||
<action
|
<action
|
||||||
string="My Meetings"
|
string="My Meetings"
|
||||||
name="%(act_my_meetings)d"
|
name="%(act_my_meetings)d"
|
||||||
height="150"
|
height="150"
|
||||||
colspan="4"/>
|
colspan="4"/>
|
||||||
|
|
||||||
<action
|
</child1>
|
||||||
string="My Cases"
|
|
||||||
name="%(act_my_cases)d"
|
|
||||||
colspan="4"
|
|
||||||
height="200"/>
|
|
||||||
|
|
||||||
</child1>
|
<child2>
|
||||||
|
<action
|
||||||
<child2>
|
|
||||||
<action
|
|
||||||
string="My Leads By Stage"
|
string="My Leads By Stage"
|
||||||
name="%(act_my_leads_stage)d"
|
name="%(act_my_leads_stage)d"
|
||||||
colspan="4"/>
|
colspan="4"/>
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
Buffering HTTP Server
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from utils import VERSION, AUTHOR
|
|
||||||
__version__ = VERSION
|
|
||||||
__author__ = AUTHOR
|
|
||||||
|
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
|
||||||
import os
|
|
||||||
|
|
||||||
class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
|
|
||||||
"""
|
|
||||||
Buffering HTTP Request Handler
|
|
||||||
|
|
||||||
This class is an extension to the BaseHTTPRequestHandler
|
|
||||||
class which buffers the whole output and sends it at once
|
|
||||||
after the processing if the request is finished.
|
|
||||||
|
|
||||||
This makes it possible to work together with some clients
|
|
||||||
which otherwise would break (e.g. cadaver)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _init_buffer(self):
|
|
||||||
"""initialize the buffer.
|
|
||||||
|
|
||||||
If you override the handle() method remember to call
|
|
||||||
this (see below)
|
|
||||||
"""
|
|
||||||
self.__buffer = ""
|
|
||||||
self.__outfp = os.tmpfile()
|
|
||||||
|
|
||||||
def _append(self,s):
|
|
||||||
""" append a string to the buffer """
|
|
||||||
self.__buffer = self.__buffer+s
|
|
||||||
|
|
||||||
def _flush(self):
|
|
||||||
""" flush the buffer to wfile """
|
|
||||||
self.wfile.write(self.__buffer)
|
|
||||||
self.__outfp.write(self.__buffer)
|
|
||||||
self.__outfp.flush()
|
|
||||||
self.wfile.flush()
|
|
||||||
self.__buffer = ""
|
|
||||||
|
|
||||||
def handle(self):
|
|
||||||
""" Handle a HTTP request """
|
|
||||||
self._init_buffer()
|
|
||||||
BaseHTTPRequestHandler.handle(self)
|
|
||||||
self._flush()
|
|
||||||
|
|
||||||
def send_header(self, keyword, value):
|
|
||||||
"""Send a MIME header."""
|
|
||||||
if self.request_version != 'HTTP/0.9':
|
|
||||||
self._append("%s: %s\r\n" % (keyword, value))
|
|
||||||
|
|
||||||
def end_headers(self):
|
|
||||||
"""Send the blank line ending the MIME headers."""
|
|
||||||
if self.request_version != 'HTTP/0.9':
|
|
||||||
self._append("\r\n")
|
|
||||||
|
|
||||||
def send_response(self, code, message=None):
|
|
||||||
self.log_request(code)
|
|
||||||
|
|
||||||
if message is None:
|
|
||||||
if self.responses.has_key(code):
|
|
||||||
message = self.responses[code][0]
|
|
||||||
else:
|
|
||||||
message = ''
|
|
||||||
|
|
||||||
if self.request_version != 'HTTP/0.9':
|
|
||||||
self._append("%s %s %s\r\n" %
|
|
||||||
(self.protocol_version, str(code), message))
|
|
||||||
|
|
||||||
self.send_header('Server', self.version_string())
|
|
||||||
self.send_header('Connection', 'close')
|
|
||||||
self.send_header('Date', self.date_time_string())
|
|
||||||
|
|
||||||
protocol_version="HTTP/1.1"
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,381 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
Python WebDAV Server.
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
This module builds on AuthServer by implementing the standard DAV
|
|
||||||
methods.
|
|
||||||
|
|
||||||
Subclass this class and specify an IFACE_CLASS. See example.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
DEBUG = None
|
|
||||||
|
|
||||||
from utils import VERSION, AUTHOR
|
|
||||||
__version__ = VERSION
|
|
||||||
__author__ = AUTHOR
|
|
||||||
|
|
||||||
from propfind import PROPFIND
|
|
||||||
from delete import DELETE
|
|
||||||
from davcopy import COPY
|
|
||||||
from davmove import MOVE
|
|
||||||
|
|
||||||
from string import atoi, split
|
|
||||||
from status import STATUS_CODES
|
|
||||||
from errors import *
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import socket
|
|
||||||
import string
|
|
||||||
import posixpath
|
|
||||||
import base64
|
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
import BaseHTTPServer
|
|
||||||
|
|
||||||
class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
||||||
"""Simple DAV request handler with
|
|
||||||
|
|
||||||
- GET
|
|
||||||
- HEAD
|
|
||||||
- PUT
|
|
||||||
- OPTIONS
|
|
||||||
- PROPFIND
|
|
||||||
- PROPPATCH
|
|
||||||
- MKCOL
|
|
||||||
|
|
||||||
It uses the resource/collection classes for serving and
|
|
||||||
storing content.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
server_version = "DAV/" + __version__
|
|
||||||
protocol_version = 'HTTP/1.1'
|
|
||||||
|
|
||||||
### utility functions
|
|
||||||
def _log(self, message):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _append(self, s):
|
|
||||||
""" write the string to wfile """
|
|
||||||
self.wfile.write(s)
|
|
||||||
|
|
||||||
def send_body(self, DATA, code, msg, desc, ctype='application/octet-stream', headers=None):
|
|
||||||
""" send a body in one part """
|
|
||||||
|
|
||||||
if not headers:
|
|
||||||
headers = {}
|
|
||||||
self.send_response(code, message=msg)
|
|
||||||
self.send_header("Connection", "keep-alive")
|
|
||||||
self.send_header("Accept-Ranges", "bytes")
|
|
||||||
|
|
||||||
for a, v in headers.items():
|
|
||||||
self.send_header(a, v)
|
|
||||||
|
|
||||||
if DATA:
|
|
||||||
self.send_header("Content-Length", str(len(DATA)))
|
|
||||||
self.send_header("Content-Type", ctype)
|
|
||||||
else:
|
|
||||||
self.send_header("Content-Length", "0")
|
|
||||||
|
|
||||||
self.end_headers()
|
|
||||||
if DATA:
|
|
||||||
self._append(DATA)
|
|
||||||
|
|
||||||
def send_body_chunks(self, DATA, code, msg, desc, ctype='text/xml; encoding="utf-8"'):
|
|
||||||
""" send a body in chunks """
|
|
||||||
|
|
||||||
self.responses[207]=(msg, desc)
|
|
||||||
self.send_response(code, message=msg)
|
|
||||||
self.send_header("Content-type", ctype)
|
|
||||||
self.send_header("Connection", "keep-alive")
|
|
||||||
self.send_header("Transfer-Encoding", "chunked")
|
|
||||||
self.end_headers()
|
|
||||||
self._append(hex(len(DATA))[2:]+"\r\n")
|
|
||||||
self._append(DATA)
|
|
||||||
self._append("\r\n")
|
|
||||||
self._append("0\r\n")
|
|
||||||
self._append("\r\n")
|
|
||||||
|
|
||||||
### HTTP METHODS
|
|
||||||
|
|
||||||
def do_OPTIONS(self):
|
|
||||||
"""return the list of capabilities """
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
|
|
||||||
self.send_header("Content-Type", "text/plain")
|
|
||||||
self.send_header("Connection", "keep-alive")
|
|
||||||
self.send_header("DAV", "1")
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
def do_PROPFIND(self):
|
|
||||||
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
# read the body
|
|
||||||
body = None
|
|
||||||
if self.headers.has_key("Content-Length"):
|
|
||||||
l = self.headers['Content-Length']
|
|
||||||
body = self.rfile.read(atoi(l))
|
|
||||||
alt_body = """<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<propfind xmlns="DAV:"><prop>
|
|
||||||
<getcontentlength xmlns="DAV:"/>
|
|
||||||
<getlastmodified xmlns="DAV:"/>
|
|
||||||
<getcreationdate xmlns="DAV:"/>
|
|
||||||
<checked-in xmlns="DAV:"/>
|
|
||||||
<executable xmlns="http://apache.org/dav/props/"/>
|
|
||||||
<displayname xmlns="DAV:"/>
|
|
||||||
<resourcetype xmlns="DAV:"/>
|
|
||||||
<checked-out xmlns="DAV:"/>
|
|
||||||
</prop></propfind>"""
|
|
||||||
#self.wfile.write(body)
|
|
||||||
|
|
||||||
# which Depth?
|
|
||||||
if self.headers.has_key('Depth'):
|
|
||||||
d = self.headers['Depth']
|
|
||||||
else:
|
|
||||||
d = "infinity"
|
|
||||||
|
|
||||||
uri = self.geturi()
|
|
||||||
pf = PROPFIND(uri, dc, d)
|
|
||||||
|
|
||||||
if body:
|
|
||||||
pf.read_propfind(body)
|
|
||||||
|
|
||||||
try:
|
|
||||||
DATA = pf.createResponse()
|
|
||||||
DATA = DATA+"\n"
|
|
||||||
# print "Data:", DATA
|
|
||||||
except DAV_NotFound, (ec, dd):
|
|
||||||
return self.send_notFound(dd, uri)
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
return self.send_error(ec, dd)
|
|
||||||
|
|
||||||
self.send_body_chunks(DATA, 207, "Multi-Status", "Multiple responses")
|
|
||||||
|
|
||||||
def geturi(self):
|
|
||||||
buri = self.IFACE_CLASS.baseuri
|
|
||||||
if buri[-1] == '/':
|
|
||||||
return urllib.unquote(buri[:-1]+self.path)
|
|
||||||
else:
|
|
||||||
return urllib.unquote(buri+self.path)
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
"""Serve a GET request."""
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
uri = self.geturi()
|
|
||||||
|
|
||||||
# get the last modified date
|
|
||||||
try:
|
|
||||||
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
|
|
||||||
except:
|
|
||||||
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
|
|
||||||
headers = {"Last-Modified":lm , "Connection": "keep-alive"}
|
|
||||||
|
|
||||||
# get the content type
|
|
||||||
try:
|
|
||||||
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
|
|
||||||
except:
|
|
||||||
ct = "application/octet-stream"
|
|
||||||
|
|
||||||
# get the data
|
|
||||||
try:
|
|
||||||
data = dc.get_data(uri)
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
return
|
|
||||||
|
|
||||||
# send the data
|
|
||||||
self.send_body(data, 200, "OK", "OK", ct, headers)
|
|
||||||
|
|
||||||
def do_HEAD(self):
|
|
||||||
""" Send a HEAD response """
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
uri = self.geturi()
|
|
||||||
|
|
||||||
# get the last modified date
|
|
||||||
try:
|
|
||||||
lm = dc.get_prop(uri, "DAV:", "getlastmodified")
|
|
||||||
except:
|
|
||||||
lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
|
|
||||||
|
|
||||||
headers = {"Last-Modified":lm, "Connection": "keep-alive"}
|
|
||||||
|
|
||||||
# get the content type
|
|
||||||
try:
|
|
||||||
ct = dc.get_prop(uri, "DAV:", "getcontenttype")
|
|
||||||
except:
|
|
||||||
ct = "application/octet-stream"
|
|
||||||
|
|
||||||
try:
|
|
||||||
data = dc.get_data(uri)
|
|
||||||
headers["Content-Length"] = str(len(data))
|
|
||||||
except DAV_NotFound:
|
|
||||||
self.send_body(None, 404, "Not Found", "")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.send_body(None, 200, "OK", "OK", ct, headers)
|
|
||||||
|
|
||||||
def do_POST(self):
|
|
||||||
self.send_error(404, "File not found")
|
|
||||||
|
|
||||||
def do_MKCOL(self):
|
|
||||||
""" create a new collection """
|
|
||||||
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
uri = self.geturi()
|
|
||||||
try:
|
|
||||||
res = dc.mkcol(uri)
|
|
||||||
if res:
|
|
||||||
self.send_body(None, 201, "Created", '')
|
|
||||||
else:
|
|
||||||
self.send_body(None, 415, "Cannot create", '')
|
|
||||||
#self.send_header("Connection", "keep-alive")
|
|
||||||
# Todo: some content, too
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_body(None, int(ec), dd, dd)
|
|
||||||
|
|
||||||
def do_DELETE(self):
|
|
||||||
""" delete an resource """
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
uri = self.geturi()
|
|
||||||
dl = DELETE(uri, dc)
|
|
||||||
if dc.is_collection(uri):
|
|
||||||
res = dl.delcol()
|
|
||||||
else:
|
|
||||||
res = dl.delone()
|
|
||||||
|
|
||||||
if res:
|
|
||||||
self.send_status(207, body=res)
|
|
||||||
else:
|
|
||||||
self.send_status(204)
|
|
||||||
|
|
||||||
def do_PUT(self):
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
|
|
||||||
# read the body
|
|
||||||
body = None
|
|
||||||
if self.headers.has_key("Content-Length"):
|
|
||||||
l = self.headers['Content-Length']
|
|
||||||
body = self.rfile.read(atoi(l))
|
|
||||||
uri = self.geturi()
|
|
||||||
|
|
||||||
ct = None
|
|
||||||
if self.headers.has_key("Content-Type"):
|
|
||||||
ct = self.headers['Content-Type']
|
|
||||||
try:
|
|
||||||
dc.put(uri, body, ct)
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
return
|
|
||||||
self.send_status(201)
|
|
||||||
|
|
||||||
def do_COPY(self):
|
|
||||||
""" copy one resource to another """
|
|
||||||
try:
|
|
||||||
self.copymove(COPY)
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
|
|
||||||
def do_MOVE(self):
|
|
||||||
""" move one resource to another """
|
|
||||||
try:
|
|
||||||
self.copymove(MOVE)
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
|
|
||||||
def copymove(self, CLASS):
|
|
||||||
""" common method for copying or moving objects """
|
|
||||||
dc = self.IFACE_CLASS
|
|
||||||
|
|
||||||
# get the source URI
|
|
||||||
source_uri = self.geturi()
|
|
||||||
|
|
||||||
# get the destination URI
|
|
||||||
dest_uri = self.headers['Destination']
|
|
||||||
dest_uri = urllib.unquote(dest_uri)
|
|
||||||
|
|
||||||
# Overwrite?
|
|
||||||
overwrite = 1
|
|
||||||
result_code = 204
|
|
||||||
if self.headers.has_key("Overwrite"):
|
|
||||||
if self.headers['Overwrite']=="F":
|
|
||||||
overwrite=None
|
|
||||||
result_code=201
|
|
||||||
|
|
||||||
# instanciate ACTION class
|
|
||||||
cp = CLASS(dc, source_uri, dest_uri, overwrite)
|
|
||||||
|
|
||||||
# Depth?
|
|
||||||
d = "infinity"
|
|
||||||
if self.headers.has_key("Depth"):
|
|
||||||
d = self.headers['Depth']
|
|
||||||
|
|
||||||
if d!="0" and d!="infinity":
|
|
||||||
self.send_status(400)
|
|
||||||
return
|
|
||||||
|
|
||||||
if d=="0":
|
|
||||||
res = cp.single_action()
|
|
||||||
self.send_status(res)
|
|
||||||
return
|
|
||||||
|
|
||||||
# now it only can be "infinity" but we nevertheless check for a collection
|
|
||||||
if dc.is_collection(source_uri):
|
|
||||||
try:
|
|
||||||
res = cp.tree_action()
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
res = cp.single_action()
|
|
||||||
except DAV_Error, (ec, dd):
|
|
||||||
self.send_status(ec)
|
|
||||||
return
|
|
||||||
|
|
||||||
if res:
|
|
||||||
self.send_body_chunks(res, 207, STATUS_CODES[207], STATUS_CODES[207],
|
|
||||||
ctype='text/xml; charset="utf-8"')
|
|
||||||
else:
|
|
||||||
self.send_status(result_code)
|
|
||||||
|
|
||||||
def get_userinfo(self, user, pw):
|
|
||||||
""" Dummy method which lets all users in """
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def send_status(self, code=200, mediatype='text/xml; charset="utf-8"', \
|
|
||||||
msg=None, body=None):
|
|
||||||
|
|
||||||
if not msg: msg = STATUS_CODES[code]
|
|
||||||
self.send_body(body, code, STATUS_CODES[code], msg, mediatype)
|
|
||||||
|
|
||||||
def send_notFound(self, descr, uri):
|
|
||||||
body = """<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<D:response xmlns:D="DAV:">
|
|
||||||
<D:href>%s</D:href>
|
|
||||||
<D:error/>
|
|
||||||
<D:responsedescription>%s</D:responsedescription>
|
|
||||||
</D:response>
|
|
||||||
"""
|
|
||||||
return self.send_status(404, descr, body=body % (uri, descr))
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,20 +0,0 @@
|
||||||
"""
|
|
||||||
python davserver
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
constants definition
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# definition for resourcetype
|
|
||||||
COLLECTION=1
|
|
||||||
OBJECT=None
|
|
||||||
DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
|
|
||||||
|
|
||||||
|
|
||||||
# Request classes in propfind
|
|
||||||
|
|
||||||
RT_ALLPROP=1
|
|
||||||
RT_PROPNAME=2
|
|
||||||
RT_PROP=3
|
|
|
@ -1,219 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
davcmd.py
|
|
||||||
---------
|
|
||||||
|
|
||||||
containts commands like copy, move, delete for normal
|
|
||||||
resources and collections
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from string import split,replace,joinfields
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
from utils import create_treelist, is_prefix
|
|
||||||
from errors import *
|
|
||||||
|
|
||||||
def deltree(dc, uri, exclude={}):
|
|
||||||
""" delete a tree of resources
|
|
||||||
|
|
||||||
dc -- dataclass to use
|
|
||||||
uri -- root uri to delete
|
|
||||||
exclude -- an optional list of uri:error_code pairs which should not
|
|
||||||
be deleted.
|
|
||||||
|
|
||||||
returns dict of uri:error_code tuples from which
|
|
||||||
another method can create a multistatus xml element.
|
|
||||||
|
|
||||||
Also note that we only know Depth=infinity thus we don't have
|
|
||||||
to test for it.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
tlist = create_treelist(dc,uri)
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for i in range(len(tlist),0,-1):
|
|
||||||
problem_uris = result.keys()
|
|
||||||
element = tlist[i-1]
|
|
||||||
|
|
||||||
# test here, if an element is a prefix of an uri which
|
|
||||||
# generated an error before.
|
|
||||||
# note that we walk here from childs to parents, thus
|
|
||||||
# we cannot delete a parent if a child made a problem.
|
|
||||||
# (see example in 8.6.2.1)
|
|
||||||
ok = 1
|
|
||||||
for p in problem_uris:
|
|
||||||
if is_prefix(element,p):
|
|
||||||
ok = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if not ok: continue
|
|
||||||
|
|
||||||
# here we test for the exclude list which is the other way round!
|
|
||||||
for p in exclude.keys():
|
|
||||||
if is_prefix(p,element):
|
|
||||||
ok = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if not ok: continue
|
|
||||||
|
|
||||||
# now delete stuff
|
|
||||||
try:
|
|
||||||
delone(dc,element)
|
|
||||||
except DAV_Error, (ec,dd):
|
|
||||||
result[element] = ec
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def delone(dc, uri):
|
|
||||||
""" delete a single object """
|
|
||||||
if dc.is_collection(uri):
|
|
||||||
dc.rmcol(uri) # should be empty
|
|
||||||
else:
|
|
||||||
dc.rm(uri)
|
|
||||||
|
|
||||||
###
|
|
||||||
### COPY
|
|
||||||
###
|
|
||||||
|
|
||||||
# helper function
|
|
||||||
|
|
||||||
def copy(dc, src, dst):
|
|
||||||
""" only copy the element
|
|
||||||
|
|
||||||
This is just a helper method factored out from copy and
|
|
||||||
copytree. It will not handle the overwrite or depth header.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# destination should have been deleted before
|
|
||||||
if dc.exists(dst): raise DAV_Error, 412
|
|
||||||
|
|
||||||
# source should exist also
|
|
||||||
if not dc.exists(src): raise DAV_NotFound
|
|
||||||
|
|
||||||
if dc.is_collection(src):
|
|
||||||
dc.copycol(src,dst) # an exception will be passed thru
|
|
||||||
else:
|
|
||||||
dc.copy(src,dst) # an exception will be passed thru
|
|
||||||
|
|
||||||
|
|
||||||
# the main functions
|
|
||||||
|
|
||||||
def copyone(dc, src, dst, overwrite=None):
|
|
||||||
""" copy one resource to a new destination """
|
|
||||||
|
|
||||||
if overwrite and dc.exists(dst):
|
|
||||||
delres = deltree(dc,dst)
|
|
||||||
else:
|
|
||||||
delres = {}
|
|
||||||
|
|
||||||
# if we cannot delete everything, then do not copy!
|
|
||||||
if delres: return delres
|
|
||||||
|
|
||||||
try:
|
|
||||||
copy(dc,src,dst) # pass thru exceptions
|
|
||||||
except DAV_Error, (ec,dd):
|
|
||||||
return ec
|
|
||||||
|
|
||||||
def copytree(dc, src, dst, overwrite=None):
|
|
||||||
""" copy a tree of resources to another location
|
|
||||||
|
|
||||||
dc -- dataclass to use
|
|
||||||
src -- src uri from where to copy
|
|
||||||
dst -- dst uri
|
|
||||||
overwrite -- if 1 then delete dst uri before
|
|
||||||
|
|
||||||
returns dict of uri:error_code tuples from which
|
|
||||||
another method can create a multistatus xml element.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# first delete the destination resource
|
|
||||||
if overwrite and dc.exists(dst):
|
|
||||||
delres = deltree(dc,dst)
|
|
||||||
else:
|
|
||||||
delres = {}
|
|
||||||
|
|
||||||
# if we cannot delete everything, then do not copy!
|
|
||||||
if delres: return delres
|
|
||||||
|
|
||||||
# get the tree we have to copy
|
|
||||||
tlist = create_treelist(dc,src)
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
# prepare destination URIs (get the prefix)
|
|
||||||
dpath = urlparse.urlparse(dst)[2]
|
|
||||||
|
|
||||||
for element in tlist:
|
|
||||||
problem_uris = result.keys()
|
|
||||||
|
|
||||||
# now URIs get longer and longer thus we have
|
|
||||||
# to test if we had a parent URI which we were not
|
|
||||||
# able to copy in problem_uris which is the prefix
|
|
||||||
# of the actual element. If it is, then we cannot
|
|
||||||
# copy this as well but do not generate another error.
|
|
||||||
ok = 1
|
|
||||||
for p in problem_uris:
|
|
||||||
if is_prefix(p,element):
|
|
||||||
ok = None
|
|
||||||
break
|
|
||||||
|
|
||||||
if not ok: continue
|
|
||||||
|
|
||||||
# now create the destination URI which corresponds to
|
|
||||||
# the actual source URI. -> actual_dst
|
|
||||||
# ("subtract" the base src from the URI and prepend the
|
|
||||||
# dst prefix to it.)
|
|
||||||
esrc = replace(element,src,"")
|
|
||||||
actual_dst = dpath+esrc
|
|
||||||
|
|
||||||
# now copy stuff
|
|
||||||
try:
|
|
||||||
copy(dc,element,actual_dst)
|
|
||||||
except DAV_Error, (ec,dd):
|
|
||||||
result[element] = ec
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
### MOVE
|
|
||||||
###
|
|
||||||
|
|
||||||
|
|
||||||
def moveone(dc, src, dst, overwrite=None):
|
|
||||||
""" move a single resource
|
|
||||||
|
|
||||||
This is done by first copying it and then deleting
|
|
||||||
the original.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# first copy it
|
|
||||||
copyone(dc,src,dst,overwrite)
|
|
||||||
|
|
||||||
# then delete it
|
|
||||||
dc.rm(src)
|
|
||||||
|
|
||||||
def movetree(dc, src, dst, overwrite=None):
|
|
||||||
""" move a collection
|
|
||||||
|
|
||||||
This is done by first copying it and then deleting
|
|
||||||
the original.
|
|
||||||
|
|
||||||
PROBLEM: if something did not copy then we have a problem
|
|
||||||
when deleting as the original might get deleted!
|
|
||||||
"""
|
|
||||||
|
|
||||||
# first copy it
|
|
||||||
res = copytree(dc,src,dst,overwrite)
|
|
||||||
|
|
||||||
# then delete it
|
|
||||||
res = deltree(dc,src,exclude=res)
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,134 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
python davserver
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from xml.dom import ext
|
|
||||||
from xml.dom.Document import Document
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
import utils
|
|
||||||
from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
|
|
||||||
from errors import *
|
|
||||||
from utils import create_treelist, quote_uri, gen_estring
|
|
||||||
|
|
||||||
class COPY:
|
|
||||||
""" copy resources and eventually create multistatus responses
|
|
||||||
|
|
||||||
This module implements the COPY class which is responsible for
|
|
||||||
copying resources. Usually the normal copy work is done in the
|
|
||||||
interface class. This class only creates error messages if error
|
|
||||||
occur.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
|
|
||||||
self.__dataclass = dataclass
|
|
||||||
self.__src = src_uri
|
|
||||||
self.__dst = dst_uri
|
|
||||||
self.__overwrite = overwrite
|
|
||||||
|
|
||||||
|
|
||||||
def single_action(self):
|
|
||||||
""" copy a normal resources.
|
|
||||||
|
|
||||||
We try to copy it and return the result code.
|
|
||||||
This is for Depth==0
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
dc = self.__dataclass
|
|
||||||
base = self.__src
|
|
||||||
|
|
||||||
### some basic tests
|
|
||||||
# test if dest exists and overwrite is false
|
|
||||||
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
|
|
||||||
# test if src and dst are the same
|
|
||||||
# (we assume that both uris are on the same server!)
|
|
||||||
ps = urlparse.urlparse(self.__src)[2]
|
|
||||||
pd = urlparse.urlparse(self.__dst)[2]
|
|
||||||
if ps==pd: raise DAV_Error, 403
|
|
||||||
|
|
||||||
return dc.copyone(self.__src,self.__dst,self.__overwrite)
|
|
||||||
|
|
||||||
#return copyone(dc,self.__src,self.__dst,self.__overwrite)
|
|
||||||
|
|
||||||
def tree_action(self):
|
|
||||||
""" copy a tree of resources (a collection)
|
|
||||||
|
|
||||||
Here we return a multistatus xml element.
|
|
||||||
|
|
||||||
"""
|
|
||||||
dc = self.__dataclass
|
|
||||||
base = self.__src
|
|
||||||
|
|
||||||
### some basic tests
|
|
||||||
# test if dest exists and overwrite is false
|
|
||||||
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
|
|
||||||
# test if src and dst are the same
|
|
||||||
# (we assume that both uris are on the same server!)
|
|
||||||
ps = urlparse.urlparse(self.__src)[2]
|
|
||||||
pd = urlparse.urlparse(self.__dst)[2]
|
|
||||||
if ps==pd: raise DAV_Error, 403
|
|
||||||
|
|
||||||
|
|
||||||
result = dc.copytree(self.__src,self.__dst,self.__overwrite)
|
|
||||||
#result=copytree(dc,self.__src,self.__dst,self.__overwrite)
|
|
||||||
|
|
||||||
if not result: return None
|
|
||||||
|
|
||||||
###
|
|
||||||
### create the multistatus XML element
|
|
||||||
### (this is also the same as in delete.py.
|
|
||||||
### we might make a common method out of it)
|
|
||||||
###
|
|
||||||
|
|
||||||
doc = Document(None)
|
|
||||||
ms = doc.createElement("D:multistatus")
|
|
||||||
ms.setAttribute("xmlns:D","DAV:")
|
|
||||||
doc.appendChild(ms)
|
|
||||||
|
|
||||||
for el,ec in result.items():
|
|
||||||
re = doc.createElement("D:response")
|
|
||||||
hr = doc.createElement("D:href")
|
|
||||||
st = doc.createElement("D:status")
|
|
||||||
huri = doc.createTextNode(quote_uri(el))
|
|
||||||
t = doc.createTextNode(gen_estring(ec))
|
|
||||||
st.appendChild(t)
|
|
||||||
hr.appendChild(huri)
|
|
||||||
re.appendChild(hr)
|
|
||||||
re.appendChild(st)
|
|
||||||
ms.appendChild(re)
|
|
||||||
|
|
||||||
sfile = StringIO()
|
|
||||||
ext.PrettyPrint(doc,stream = sfile)
|
|
||||||
s = sfile.getvalue()
|
|
||||||
sfile.close()
|
|
||||||
return s
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,103 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
python davserver
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
import utils
|
|
||||||
from constants import COLLECTION, OBJECT, DAV_PROPS
|
|
||||||
from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
|
|
||||||
from errors import *
|
|
||||||
from utils import create_treelist, quote_uri, gen_estring, make_xmlresponse
|
|
||||||
from davcmd import moveone, movetree
|
|
||||||
|
|
||||||
class MOVE:
|
|
||||||
""" move resources and eventually create multistatus responses
|
|
||||||
|
|
||||||
This module implements the MOVE class which is responsible for
|
|
||||||
moving resources.
|
|
||||||
|
|
||||||
MOVE is implemented by a COPY followed by a DELETE of the old
|
|
||||||
resource.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, dataclass, src_uri, dst_uri, overwrite):
|
|
||||||
self.__dataclass = dataclass
|
|
||||||
self.__src = src_uri
|
|
||||||
self.__dst = dst_uri
|
|
||||||
self.__overwrite = overwrite
|
|
||||||
|
|
||||||
|
|
||||||
def single_action(self):
|
|
||||||
""" move a normal resources.
|
|
||||||
|
|
||||||
We try to move it and return the result code.
|
|
||||||
This is for Depth==0
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
dc = self.__dataclass
|
|
||||||
base = self.__src
|
|
||||||
|
|
||||||
### some basic tests
|
|
||||||
# test if dest exists and overwrite is false
|
|
||||||
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
|
|
||||||
# test if src and dst are the same
|
|
||||||
# (we assume that both uris are on the same server!)
|
|
||||||
ps = urlparse.urlparse(self.__src)[2]
|
|
||||||
pd = urlparse.urlparse(self.__dst)[2]
|
|
||||||
if ps==pd: raise DAV_Error, 403
|
|
||||||
|
|
||||||
return dc.moveone(self.__src,self.__dst,self.__overwrite)
|
|
||||||
|
|
||||||
def tree_action(self):
|
|
||||||
""" move a tree of resources (a collection)
|
|
||||||
|
|
||||||
Here we return a multistatus xml element.
|
|
||||||
|
|
||||||
"""
|
|
||||||
dc = self.__dataclass
|
|
||||||
base = self.__src
|
|
||||||
|
|
||||||
### some basic tests
|
|
||||||
# test if dest exists and overwrite is false
|
|
||||||
if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
|
|
||||||
# test if src and dst are the same
|
|
||||||
# (we assume that both uris are on the same server!)
|
|
||||||
ps = urlparse.urlparse(self.__src)[2]
|
|
||||||
pd = urlparse.urlparse(self.__dst)[2]
|
|
||||||
if ps==pd: raise DAV_Error, 403
|
|
||||||
|
|
||||||
result = dc.movetree(self.__src,self.__dst,self.__overwrite)
|
|
||||||
if not result: return None
|
|
||||||
|
|
||||||
# create the multistatus XML element
|
|
||||||
return make_xmlresponse(result)
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,64 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
python davserver
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import string
|
|
||||||
import urllib
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
from status import STATUS_CODES
|
|
||||||
from utils import gen_estring, quote_uri, make_xmlresponse
|
|
||||||
from davcmd import deltree
|
|
||||||
|
|
||||||
class DELETE:
|
|
||||||
|
|
||||||
def __init__(self, uri, dataclass):
|
|
||||||
self.__dataclass = dataclass
|
|
||||||
self.__uri = uri
|
|
||||||
|
|
||||||
def delcol(self):
|
|
||||||
""" delete a collection """
|
|
||||||
|
|
||||||
dc = self.__dataclass
|
|
||||||
result = dc.deltree(self.__uri)
|
|
||||||
|
|
||||||
if not len(result.items()):
|
|
||||||
return None # everything ok
|
|
||||||
|
|
||||||
# create the result element
|
|
||||||
return make_xmlresponse(result)
|
|
||||||
|
|
||||||
def delone(self):
|
|
||||||
""" delete a resource """
|
|
||||||
|
|
||||||
dc = self.__dataclass
|
|
||||||
result = dc.delone(self.__uri)
|
|
||||||
|
|
||||||
if not result: return None
|
|
||||||
if not len(result.items()):
|
|
||||||
return None # everything ok
|
|
||||||
|
|
||||||
# create the result element
|
|
||||||
return make_xmlresponse(result)
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,57 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
"""
|
|
||||||
|
|
||||||
Exceptions for the DAVserver implementation
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
class DAV_Error(Exception):
|
|
||||||
""" in general we can have the following arguments:
|
|
||||||
|
|
||||||
1. the error code
|
|
||||||
2. the error result element, e.g. a <multistatus> element
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
if len(args)==1:
|
|
||||||
self.args = (args[0],"")
|
|
||||||
else:
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
class DAV_Secret(DAV_Error):
|
|
||||||
""" the user is not allowed to know anything about it
|
|
||||||
|
|
||||||
returning this for a property value means to exclude it
|
|
||||||
from the response xml element.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
DAV_Error.__init__(self,0)
|
|
||||||
pass
|
|
||||||
|
|
||||||
class DAV_NotFound(DAV_Error):
|
|
||||||
""" a requested property was not found for a resource """
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
if len(args):
|
|
||||||
if isinstance(args[0], list):
|
|
||||||
stre = "Path %s not found!"%('/'.join(args[0]))
|
|
||||||
else:
|
|
||||||
stre = args[0]
|
|
||||||
DAV_Error.__init__(self,404,stre)
|
|
||||||
else:
|
|
||||||
DAV_Error.__init__(self,404)
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
class DAV_Forbidden(DAV_Error):
|
|
||||||
""" a method on a resource is not allowed """
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
if len(args):
|
|
||||||
DAV_Error.__init__(self,403,args[0])
|
|
||||||
else:
|
|
||||||
DAV_Error.__init__(self,403)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,264 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
basic interface class
|
|
||||||
|
|
||||||
use this for subclassing when writing your own interface
|
|
||||||
class.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from errors import *
|
|
||||||
|
|
||||||
import time
|
|
||||||
from string import lower
|
|
||||||
|
|
||||||
class dav_interface:
|
|
||||||
""" interface class for implementing DAV servers """
|
|
||||||
|
|
||||||
### defined properties (modify this but let the DAV stuff there!)
|
|
||||||
### the format is namespace: [list of properties]
|
|
||||||
|
|
||||||
PROPS={"DAV:" : ('creationdate',
|
|
||||||
'displayname',
|
|
||||||
'getcontentlanguage',
|
|
||||||
'getcontentlength',
|
|
||||||
'getcontenttype',
|
|
||||||
'getetag',
|
|
||||||
'getlastmodified',
|
|
||||||
'lockdiscovery',
|
|
||||||
'resourcetype',
|
|
||||||
'source',
|
|
||||||
'supportedlock'),
|
|
||||||
"NS2" : ("p1","p2")
|
|
||||||
}
|
|
||||||
|
|
||||||
# here we define which methods handle which namespace
|
|
||||||
# the first item is the namespace URI and the second one
|
|
||||||
# the method prefix
|
|
||||||
# e.g. for DAV:getcontenttype we call dav_getcontenttype()
|
|
||||||
M_NS = {"DAV:" : "_get_dav",
|
|
||||||
"NS2" : "ns2" }
|
|
||||||
|
|
||||||
def get_propnames(self, uri):
|
|
||||||
""" return the property names allowed for the given URI
|
|
||||||
|
|
||||||
In this method we simply return the above defined properties
|
|
||||||
assuming that they are valid for any resource.
|
|
||||||
You can override this in order to return a different set
|
|
||||||
of property names for each resource.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return self.PROPS
|
|
||||||
|
|
||||||
def get_prop2(self, uri, ns, pname):
|
|
||||||
""" return the value of a property
|
|
||||||
"""
|
|
||||||
if lower(ns)=="dav:": return self.get_dav(uri,pname)
|
|
||||||
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
def get_prop(self, uri, ns, propname):
|
|
||||||
""" return the value of a given property
|
|
||||||
|
|
||||||
uri -- uri of the object to get the property of
|
|
||||||
ns -- namespace of the property
|
|
||||||
pname -- name of the property
|
|
||||||
"""
|
|
||||||
if self.M_NS.has_key(ns):
|
|
||||||
prefix = self.M_NS[ns]
|
|
||||||
else:
|
|
||||||
print "No namespace:",ns, "( for prop:", propname,")"
|
|
||||||
raise DAV_NotFound
|
|
||||||
mname = prefix+"_"+propname
|
|
||||||
if not hasattr(self,mname):
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
try:
|
|
||||||
m = getattr(self,mname)
|
|
||||||
r = m(uri)
|
|
||||||
return r
|
|
||||||
except AttributeError, e:
|
|
||||||
print 'Property %s not supported' % propname
|
|
||||||
print "Exception:", e
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
###
|
|
||||||
### DATA methods (for GET and PUT)
|
|
||||||
###
|
|
||||||
|
|
||||||
def get_data(self, uri):
|
|
||||||
""" return the content of an object
|
|
||||||
|
|
||||||
return data or raise an exception
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
def put(self, uri, data):
|
|
||||||
""" write an object to the repository
|
|
||||||
|
|
||||||
return a result code or raise an exception
|
|
||||||
"""
|
|
||||||
|
|
||||||
raise DAV_Forbidden
|
|
||||||
|
|
||||||
###
|
|
||||||
### Methods for DAV properties
|
|
||||||
###
|
|
||||||
|
|
||||||
def _get_dav_creationdate(self, uri):
|
|
||||||
""" return the creationdate of a resource """
|
|
||||||
d = self.get_creationdate(uri)
|
|
||||||
# format it
|
|
||||||
if isinstance(d, int) or isinstance(d, float):
|
|
||||||
d = time.localtimetime(d)
|
|
||||||
return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d)
|
|
||||||
|
|
||||||
def _get_dav_getlastmodified(self, uri):
|
|
||||||
""" return the last modified date of a resource """
|
|
||||||
d = self.get_lastmodified(uri)
|
|
||||||
if isinstance(d, int) or isinstance(d, float):
|
|
||||||
d = time.localtime(d)
|
|
||||||
# format it
|
|
||||||
return time.asctime(d)
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
### OVERRIDE THESE!
|
|
||||||
###
|
|
||||||
|
|
||||||
def get_creationdate(self, uri):
|
|
||||||
""" return the creationdate of the resource """
|
|
||||||
return time.time()
|
|
||||||
|
|
||||||
def get_lastmodified(self, uri):
|
|
||||||
""" return the last modification date of the resource """
|
|
||||||
return time.time()
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
### COPY MOVE DELETE
|
|
||||||
###
|
|
||||||
|
|
||||||
### methods for deleting a resource
|
|
||||||
|
|
||||||
def rmcol(self, uri):
|
|
||||||
""" delete a collection
|
|
||||||
|
|
||||||
This should not delete any children! This is automatically done
|
|
||||||
before by the DELETE class in DAV/delete.py
|
|
||||||
|
|
||||||
return a success code or raise an exception
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
def rm(self, uri):
|
|
||||||
""" delete a single resource
|
|
||||||
|
|
||||||
return a success code or raise an exception
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise DAV_NotFound
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
COPY/MOVE HANDLER
|
|
||||||
|
|
||||||
These handler are called when a COPY or MOVE method is invoked by
|
|
||||||
a client. In the default implementation it works as follows:
|
|
||||||
|
|
||||||
- the davserver receives a COPY/MOVE method
|
|
||||||
- the davcopy or davmove module will be loaded and the corresponding
|
|
||||||
class will be initialized
|
|
||||||
- this class parses the query and decides which method of the interface class
|
|
||||||
to call:
|
|
||||||
|
|
||||||
copyone for a single resource to copy
|
|
||||||
copytree for a tree to copy (collection)
|
|
||||||
(the same goes for move of course).
|
|
||||||
|
|
||||||
- the interface class has now two options:
|
|
||||||
1. to handle the action directly (e.g. cp or mv on filesystems)
|
|
||||||
2. to let it handle via the copy/move methods in davcmd.
|
|
||||||
|
|
||||||
ad 1) The first approach can be used when we know that no error can
|
|
||||||
happen inside a tree or when the action can exactly tell which
|
|
||||||
element made which error. We have to collect these and return
|
|
||||||
it in a dict of the form {uri: error_code, ...}
|
|
||||||
|
|
||||||
ad 2) The copytree/movetree/... methods of davcmd.py will do the recursion
|
|
||||||
themselves and call for each resource the copy/move method of the
|
|
||||||
interface class. Thus method will then only act on a single resource.
|
|
||||||
(Thus a copycol on a normal unix filesystem actually only needs to do
|
|
||||||
an mkdir as the content will be copied by the davcmd.py function.
|
|
||||||
The davcmd.py method will also automatically collect all errors and
|
|
||||||
return the dictionary described above.
|
|
||||||
When you use 2) you also have to implement the copy() and copycol()
|
|
||||||
methods in your interface class. See the example for details.
|
|
||||||
|
|
||||||
To decide which approach is the best you have to decide if your application
|
|
||||||
is able to generate errors inside a tree. E.g. a function which completely
|
|
||||||
fails on a tree if one of the tree's childs fail is not what we need. Then
|
|
||||||
2) would be your way of doing it.
|
|
||||||
Actually usually 2) is the better solution and should only be replaced by
|
|
||||||
1) if you really need it.
|
|
||||||
|
|
||||||
The remaining question is if we should do the same for the DELETE method.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
### MOVE handlers
|
|
||||||
|
|
||||||
def moveone(self, src, dst, overwrite):
|
|
||||||
""" move one resource with Depth=0 """
|
|
||||||
return moveone(self, src, dst, overwrite)
|
|
||||||
|
|
||||||
def movetree(self, src, dst, overwrite):
|
|
||||||
""" move a collection with Depth=infinity """
|
|
||||||
return movetree(self, src, dst, overwrite)
|
|
||||||
|
|
||||||
### COPY handlers
|
|
||||||
|
|
||||||
def copyone(self, src, dst, overwrite):
|
|
||||||
""" copy one resource with Depth=0 """
|
|
||||||
return copyone(self, src, dst, overwrite)
|
|
||||||
|
|
||||||
def copytree(self, src, dst, overwrite):
|
|
||||||
""" copy a collection with Depth=infinity """
|
|
||||||
return copytree(self, src, dst, overwrite)
|
|
||||||
|
|
||||||
|
|
||||||
### low level copy methods (you only need these for method 2)
|
|
||||||
def copy(self, src, dst):
|
|
||||||
""" copy a resource with depth==0
|
|
||||||
|
|
||||||
You don't need to bother about overwrite or not.
|
|
||||||
This has been done already.
|
|
||||||
|
|
||||||
return a success code or raise an exception if something fails
|
|
||||||
"""
|
|
||||||
return 201
|
|
||||||
|
|
||||||
|
|
||||||
def copycol(self, src, dst):
|
|
||||||
""" copy a resource with depth==infinity
|
|
||||||
|
|
||||||
You don't need to bother about overwrite or not.
|
|
||||||
This has been done already.
|
|
||||||
|
|
||||||
return a success code or raise an exception if something fails
|
|
||||||
"""
|
|
||||||
return 201
|
|
||||||
|
|
||||||
### some utility functions you need to implement
|
|
||||||
|
|
||||||
def exists(self, uri):
|
|
||||||
""" return 1 or None depending on if a resource exists """
|
|
||||||
return None # no
|
|
||||||
|
|
||||||
def is_collection(self, uri):
|
|
||||||
""" return 1 or None depending on if a resource is a collection """
|
|
||||||
return None # no
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,373 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
python davserver
|
|
||||||
Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from xml.dom import ext
|
|
||||||
from xml.dom.Document import Document
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import string
|
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
import utils
|
|
||||||
from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
|
|
||||||
from errors import *
|
|
||||||
|
|
||||||
def utf8str(st):
|
|
||||||
if isinstance(st,unicode):
|
|
||||||
return st.encode('utf8')
|
|
||||||
else:
|
|
||||||
return str(st)
|
|
||||||
|
|
||||||
class PROPFIND:
|
|
||||||
""" parse a propfind xml element and extract props
|
|
||||||
|
|
||||||
It will set the following instance vars:
|
|
||||||
|
|
||||||
request_class: ALLPROP | PROPNAME | PROP
|
|
||||||
proplist: list of properties
|
|
||||||
nsmap: map of namespaces
|
|
||||||
|
|
||||||
The list of properties will contain tuples of the form
|
|
||||||
(element name, ns_prefix, ns_uri)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, uri, dataclass, depth):
|
|
||||||
self.request_type = None
|
|
||||||
self.nsmap = {}
|
|
||||||
self.proplist = {}
|
|
||||||
self.default_ns = None
|
|
||||||
self.__dataclass = dataclass
|
|
||||||
self.__depth = str(depth)
|
|
||||||
self.__uri = uri
|
|
||||||
self.use_full_urls = True
|
|
||||||
self.__has_body = None # did we parse a body?
|
|
||||||
|
|
||||||
def read_propfind(self, xml_doc):
|
|
||||||
self.request_type,self.proplist,self.namespaces = utils.parse_propfind(xml_doc)
|
|
||||||
|
|
||||||
# a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype
|
|
||||||
# but we also have to return the http://groupdav.org/:resourcetype property!
|
|
||||||
if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']:
|
|
||||||
if not self.proplist.has_key('http://groupdav.org/'):
|
|
||||||
self.proplist['http://groupdav.org/'] = []
|
|
||||||
self.proplist['http://groupdav.org/'].append('resourcetype')
|
|
||||||
if 'DAV:' in self.namespaces: #TMP
|
|
||||||
self.namespaces.append('http://groupdav.org/')
|
|
||||||
|
|
||||||
def createResponse(self):
|
|
||||||
""" create the multistatus response
|
|
||||||
|
|
||||||
This will be delegated to the specific method
|
|
||||||
depending on which request (allprop, propname, prop)
|
|
||||||
was found.
|
|
||||||
|
|
||||||
If we get a PROPNAME then we simply return the list with empty
|
|
||||||
values which we get from the interface class
|
|
||||||
|
|
||||||
If we get an ALLPROP we first get the list of properties and then
|
|
||||||
we do the same as with a PROP method.
|
|
||||||
|
|
||||||
If the uri doesn't exist, return an xml response with a 404 status
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not self.__dataclass.exists(self.__uri):
|
|
||||||
raise DAV_NotFound("Path %s doesn't exist" % self.__uri)
|
|
||||||
|
|
||||||
if self.request_type==RT_ALLPROP:
|
|
||||||
return self.create_allprop()
|
|
||||||
|
|
||||||
if self.request_type==RT_PROPNAME:
|
|
||||||
return self.create_propname()
|
|
||||||
|
|
||||||
if self.request_type==RT_PROP:
|
|
||||||
return self.create_prop()
|
|
||||||
|
|
||||||
# no body means ALLPROP!
|
|
||||||
return self.create_allprop()
|
|
||||||
|
|
||||||
def create_propname(self):
|
|
||||||
""" create a multistatus response for the prop names """
|
|
||||||
|
|
||||||
dc = self.__dataclass
|
|
||||||
# create the document generator
|
|
||||||
doc = Document(None)
|
|
||||||
ms = doc.createElement("D:multistatus")
|
|
||||||
ms.setAttribute("xmlns:D","DAV:")
|
|
||||||
doc.appendChild(ms)
|
|
||||||
|
|
||||||
if self.__depth=="0":
|
|
||||||
pnames = dc.get_propnames(self.__uri)
|
|
||||||
re = self.mk_propname_response(self.__uri,pnames,doc)
|
|
||||||
ms.appendChild(re)
|
|
||||||
|
|
||||||
elif self.__depth=="1":
|
|
||||||
pnames = dc.get_propnames(self.__uri)
|
|
||||||
re = self.mk_propname_response(self.__uri,pnames,doc)
|
|
||||||
ms.appendChild(re)
|
|
||||||
|
|
||||||
for newuri in dc.get_childs(self.__uri):
|
|
||||||
pnames = dc.get_propnames(newuri)
|
|
||||||
re = self.mk_propname_response(newuri,pnames,doc)
|
|
||||||
ms.appendChild(re)
|
|
||||||
# *** depth=="infinity"
|
|
||||||
|
|
||||||
sfile = StringIO()
|
|
||||||
ext.PrettyPrint(doc,stream = sfile)
|
|
||||||
s = sfile.getvalue()
|
|
||||||
sfile.close()
|
|
||||||
return s
|
|
||||||
|
|
||||||
def create_allprop(self):
|
|
||||||
""" return a list of all properties """
|
|
||||||
self.proplist = {}
|
|
||||||
self.namespaces = []
|
|
||||||
for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
|
|
||||||
self.proplist[ns] = plist
|
|
||||||
self.namespaces.append(ns)
|
|
||||||
|
|
||||||
return self.create_prop()
|
|
||||||
|
|
||||||
def create_prop(self):
|
|
||||||
""" handle a <prop> request
|
|
||||||
|
|
||||||
This will
|
|
||||||
|
|
||||||
1. set up the <multistatus>-Framework
|
|
||||||
|
|
||||||
2. read the property values for each URI
|
|
||||||
(which is dependant on the Depth header)
|
|
||||||
This is done by the get_propvalues() method.
|
|
||||||
|
|
||||||
3. For each URI call the append_result() method
|
|
||||||
to append the actual <result>-Tag to the result
|
|
||||||
document.
|
|
||||||
|
|
||||||
We differ between "good" properties, which have been
|
|
||||||
assigned a value by the interface class and "bad"
|
|
||||||
properties, which resulted in an error, either 404
|
|
||||||
(Not Found) or 403 (Forbidden).
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# create the document generator
|
|
||||||
doc = Document(None)
|
|
||||||
ms = doc.createElement("D:multistatus")
|
|
||||||
ms.setAttribute("xmlns:D","DAV:")
|
|
||||||
doc.appendChild(ms)
|
|
||||||
|
|
||||||
if self.__depth=="0":
|
|
||||||
gp,bp = self.get_propvalues(self.__uri)
|
|
||||||
res = self.mk_prop_response(self.__uri,gp,bp,doc)
|
|
||||||
ms.appendChild(res)
|
|
||||||
|
|
||||||
elif self.__depth=="1":
|
|
||||||
gp,bp = self.get_propvalues(self.__uri)
|
|
||||||
res = self.mk_prop_response(self.__uri,gp,bp,doc)
|
|
||||||
ms.appendChild(res)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for newuri in self.__dataclass.get_childs(self.__uri):
|
|
||||||
gp,bp = self.get_propvalues(newuri)
|
|
||||||
res = self.mk_prop_response(newuri,gp,bp,doc)
|
|
||||||
ms.appendChild(res)
|
|
||||||
except DAV_NotFound:
|
|
||||||
# If no children, never mind.
|
|
||||||
pass
|
|
||||||
|
|
||||||
sfile = StringIO()
|
|
||||||
ext.PrettyPrint(doc,stream = sfile)
|
|
||||||
s = sfile.getvalue()
|
|
||||||
sfile.close()
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def mk_propname_response(self,uri,propnames,doc):
|
|
||||||
""" make a new <prop> result element for a PROPNAME request
|
|
||||||
|
|
||||||
This will simply format the propnames list.
|
|
||||||
propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
|
|
||||||
|
|
||||||
"""
|
|
||||||
re = doc.createElement("D:response")
|
|
||||||
|
|
||||||
# write href information
|
|
||||||
href = doc.createElement("D:href")
|
|
||||||
if self.use_full_urls:
|
|
||||||
huri = doc.createTextNode(uri)
|
|
||||||
else:
|
|
||||||
uparts = urlparse.urlparse(uri)
|
|
||||||
fileloc = uparts[2]
|
|
||||||
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
|
|
||||||
href.appendChild(huri)
|
|
||||||
re.appendChild(href)
|
|
||||||
|
|
||||||
ps = doc.createElement("D:propstat")
|
|
||||||
nsnum = 0
|
|
||||||
|
|
||||||
for ns,plist in propnames.items():
|
|
||||||
# write prop element
|
|
||||||
pr = doc.createElement("D:prop")
|
|
||||||
nsp = "ns"+str(nsnum)
|
|
||||||
pr.setAttribute("xmlns:"+nsp,ns)
|
|
||||||
nsnum = nsnum+1
|
|
||||||
|
|
||||||
# write propertynames
|
|
||||||
for p in plist:
|
|
||||||
pe = doc.createElement(nsp+":"+p)
|
|
||||||
pr.appendChild(pe)
|
|
||||||
|
|
||||||
ps.appendChild(pr)
|
|
||||||
|
|
||||||
re.appendChild(ps)
|
|
||||||
|
|
||||||
return re
|
|
||||||
|
|
||||||
def mk_prop_response(self,uri,good_props,bad_props,doc):
|
|
||||||
""" make a new <prop> result element
|
|
||||||
|
|
||||||
We differ between the good props and the bad ones for
|
|
||||||
each generating an extra <propstat>-Node (for each error
|
|
||||||
one, that means).
|
|
||||||
|
|
||||||
"""
|
|
||||||
re = doc.createElement("D:response")
|
|
||||||
# append namespaces to response
|
|
||||||
nsnum = 0
|
|
||||||
for nsname in self.namespaces:
|
|
||||||
re.setAttribute("xmlns:ns"+str(nsnum),nsname)
|
|
||||||
nsnum = nsnum+1
|
|
||||||
|
|
||||||
# write href information
|
|
||||||
href = doc.createElement("D:href")
|
|
||||||
if self.use_full_urls:
|
|
||||||
huri = doc.createTextNode(uri)
|
|
||||||
else:
|
|
||||||
uparts = urlparse.urlparse(uri)
|
|
||||||
fileloc = uparts[2]
|
|
||||||
huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
|
|
||||||
href.appendChild(huri)
|
|
||||||
re.appendChild(href)
|
|
||||||
|
|
||||||
# write good properties
|
|
||||||
if good_props and len(good_props.items()):
|
|
||||||
ps = doc.createElement("D:propstat")
|
|
||||||
|
|
||||||
gp = doc.createElement("D:prop")
|
|
||||||
for ns in good_props.keys():
|
|
||||||
ns_prefix="ns"+str(self.namespaces.index(ns))+":"
|
|
||||||
for p,v in good_props[ns].items():
|
|
||||||
pe = doc.createElement(ns_prefix+str(p))
|
|
||||||
if v == None:
|
|
||||||
pass
|
|
||||||
elif ns=='DAV:' and p=="resourcetype":
|
|
||||||
if v == 1:
|
|
||||||
ve=doc.createElement("D:collection")
|
|
||||||
pe.appendChild(ve)
|
|
||||||
elif isinstance(v,tuple) and v[1] == ns:
|
|
||||||
ve=doc.createElement(ns_prefix+v[0])
|
|
||||||
pe.appendChild(ve)
|
|
||||||
else:
|
|
||||||
ve = doc.createTextNode(utf8str(v))
|
|
||||||
pe.appendChild(ve)
|
|
||||||
|
|
||||||
gp.appendChild(pe)
|
|
||||||
if gp.hasChildNodes():
|
|
||||||
re.appendChild(ps)
|
|
||||||
ps.appendChild(gp)
|
|
||||||
s = doc.createElement("D:status")
|
|
||||||
t = doc.createTextNode("HTTP/1.1 200 OK")
|
|
||||||
s.appendChild(t)
|
|
||||||
ps.appendChild(s)
|
|
||||||
re.appendChild(ps)
|
|
||||||
|
|
||||||
# now write the errors!
|
|
||||||
if len(bad_props.items()):
|
|
||||||
|
|
||||||
# write a propstat for each error code
|
|
||||||
for ecode in bad_props.keys():
|
|
||||||
ps = doc.createElement("D:propstat")
|
|
||||||
re.appendChild(ps)
|
|
||||||
bp = doc.createElement("D:prop")
|
|
||||||
ps.appendChild(bp)
|
|
||||||
|
|
||||||
for ns in bad_props[ecode].keys():
|
|
||||||
ns_prefix = "ns"+str(self.namespaces.index(ns))+":"
|
|
||||||
|
|
||||||
for p in bad_props[ecode][ns]:
|
|
||||||
pe = doc.createElement(ns_prefix+str(p))
|
|
||||||
bp.appendChild(pe)
|
|
||||||
|
|
||||||
s = doc.createElement("D:status")
|
|
||||||
t = doc.createTextNode(utils.gen_estring(ecode))
|
|
||||||
s.appendChild(t)
|
|
||||||
ps.appendChild(s)
|
|
||||||
re.appendChild(ps)
|
|
||||||
|
|
||||||
# return the new response element
|
|
||||||
return re
|
|
||||||
|
|
||||||
def get_propvalues(self,uri):
|
|
||||||
""" create lists of property values for an URI
|
|
||||||
|
|
||||||
We create two lists for an URI: the properties for
|
|
||||||
which we found a value and the ones for which we
|
|
||||||
only got an error, either because they haven't been
|
|
||||||
found or the user is not allowed to read them.
|
|
||||||
|
|
||||||
"""
|
|
||||||
good_props = {}
|
|
||||||
bad_props = {}
|
|
||||||
|
|
||||||
for (ns,plist) in self.proplist.items():
|
|
||||||
good_props[ns] = {}
|
|
||||||
bad_props = {}
|
|
||||||
ec = 0
|
|
||||||
for prop in plist:
|
|
||||||
try:
|
|
||||||
ec = 0
|
|
||||||
r = self.__dataclass.get_prop(uri,ns,prop)
|
|
||||||
good_props[ns][prop] = r
|
|
||||||
except DAV_Error, error_code:
|
|
||||||
ec = error_code[0]
|
|
||||||
|
|
||||||
# ignore props with error_code if 0 (invisible)
|
|
||||||
if ec==0: continue
|
|
||||||
|
|
||||||
if bad_props.has_key(ec):
|
|
||||||
if bad_props[ec].has_key(ns):
|
|
||||||
bad_props[ec][ns].append(prop)
|
|
||||||
else:
|
|
||||||
bad_props[ec][ns] = [prop]
|
|
||||||
else:
|
|
||||||
bad_props[ec] = {ns:[prop]}
|
|
||||||
|
|
||||||
return good_props, bad_props
|
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,31 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
status codes for DAV services
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
STATUS_CODES={
|
|
||||||
102: "Processing",
|
|
||||||
200: "Ok",
|
|
||||||
201: "Created",
|
|
||||||
204: "No Content",
|
|
||||||
207: "Multi-Status",
|
|
||||||
201: "Created",
|
|
||||||
400: "Bad Request",
|
|
||||||
403: "Forbidden",
|
|
||||||
404: "Not Found",
|
|
||||||
405: "Method Not Allowed",
|
|
||||||
409: "Conflict",
|
|
||||||
412: "Precondition failed",
|
|
||||||
423: "Locked",
|
|
||||||
415: "Unsupported Media Type",
|
|
||||||
507: "Insufficient Storage",
|
|
||||||
422: "Unprocessable Entity",
|
|
||||||
423: "Locked",
|
|
||||||
424: "Failed Dependency",
|
|
||||||
502: "Bad Gateway",
|
|
||||||
507: "Insufficient Storage",
|
|
||||||
999: "Some error in Create Method please check the data of create method"
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
UTILITIES
|
|
||||||
|
|
||||||
- parse a propfind request body into a list of props
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from xml.dom import ext
|
|
||||||
from xml.dom.Document import Document
|
|
||||||
from xml.dom.ext.reader import PyExpat
|
|
||||||
from xml.dom import Node
|
|
||||||
from xml.dom import NodeIterator, NodeFilter
|
|
||||||
|
|
||||||
from string import lower, split, atoi, joinfields
|
|
||||||
import urlparse
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
|
|
||||||
from status import STATUS_CODES
|
|
||||||
|
|
||||||
VERSION = '0.6'
|
|
||||||
AUTHOR = 'Simon Pamies <s.pamies@banality.de>'
|
|
||||||
|
|
||||||
|
|
||||||
def gen_estring(ecode):
|
|
||||||
""" generate an error string from the given code """
|
|
||||||
ec = atoi(str(ecode))
|
|
||||||
if STATUS_CODES.has_key(ec):
|
|
||||||
return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
|
|
||||||
else:
|
|
||||||
return "HTTP/1.1 %s" %(ec)
|
|
||||||
|
|
||||||
def parse_propfind(xml_doc):
|
|
||||||
""" parse an propfind xml file and return a list of props
|
|
||||||
|
|
||||||
returns:
|
|
||||||
|
|
||||||
request_type -- ALLPROP, PROPNAME, PROP
|
|
||||||
proplist -- list of properties found
|
|
||||||
namespaces -- list of namespaces found
|
|
||||||
|
|
||||||
"""
|
|
||||||
doc = PyExpat.Reader().fromString(xml_doc)
|
|
||||||
snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
|
|
||||||
|
|
||||||
request_type = None
|
|
||||||
props = {}
|
|
||||||
namespaces = []
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
curr_elem = snit.nextNode()
|
|
||||||
if not curr_elem: break
|
|
||||||
ename=fname = lower(curr_elem.nodeName)
|
|
||||||
if ":" in fname:
|
|
||||||
ename = split(fname,":")[1]
|
|
||||||
if ename=="prop": request_type = RT_PROP; continue
|
|
||||||
if ename=="propfind": continue
|
|
||||||
if ename=="allprop": request_type = RT_ALLPROP; continue
|
|
||||||
if ename=="propname": request_type = RT_PROPNAME; continue
|
|
||||||
|
|
||||||
# rest should be names of attributes
|
|
||||||
|
|
||||||
ns = curr_elem.namespaceURI
|
|
||||||
if props.has_key(ns):
|
|
||||||
props[ns].append(ename)
|
|
||||||
else:
|
|
||||||
props[ns] = [ename]
|
|
||||||
namespaces.append(ns)
|
|
||||||
|
|
||||||
return request_type,props,namespaces
|
|
||||||
|
|
||||||
|
|
||||||
def create_treelist(dataclass, uri):
|
|
||||||
""" create a list of resources out of a tree
|
|
||||||
|
|
||||||
This function is used for the COPY, MOVE and DELETE methods
|
|
||||||
|
|
||||||
uri - the root of the subtree to flatten
|
|
||||||
|
|
||||||
It will return the flattened tree as list
|
|
||||||
|
|
||||||
"""
|
|
||||||
queue = [uri]
|
|
||||||
list = [uri]
|
|
||||||
while len(queue):
|
|
||||||
element = queue[-1]
|
|
||||||
if dataclass.is_collection(element):
|
|
||||||
childs = dataclass.get_childs(element)
|
|
||||||
else:
|
|
||||||
childs = []
|
|
||||||
if len(childs):
|
|
||||||
list = list+childs
|
|
||||||
# update queue
|
|
||||||
del queue[-1]
|
|
||||||
if len(childs):
|
|
||||||
queue = queue+childs
|
|
||||||
return list
|
|
||||||
|
|
||||||
def is_prefix(uri1, uri2):
|
|
||||||
""" returns 1 of uri1 is a prefix of uri2 """
|
|
||||||
if uri2[:len(uri1)]==uri1:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def quote_uri(uri):
|
|
||||||
""" quote an URL but not the protocol part """
|
|
||||||
import urlparse
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
up = urlparse.urlparse(uri)
|
|
||||||
np = urllib.quote(up[2])
|
|
||||||
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
|
|
||||||
|
|
||||||
def get_uriparentpath(uri):
|
|
||||||
""" extract the uri path and remove the last element """
|
|
||||||
up = urlparse.urlparse(uri)
|
|
||||||
return joinfields(split(up[2], "/")[:-1], "/")
|
|
||||||
|
|
||||||
def get_urifilename(uri):
|
|
||||||
""" extract the uri path and return the last element """
|
|
||||||
up = urlparse.urlparse(uri)
|
|
||||||
return split(up[2], "/")[-1]
|
|
||||||
|
|
||||||
def get_parenturi(uri):
|
|
||||||
""" return the parent of the given resource"""
|
|
||||||
up = urlparse.urlparse(uri)
|
|
||||||
np = joinfields(split(up[2], "/")[:-1], "/")
|
|
||||||
return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
|
|
||||||
|
|
||||||
### XML utilities
|
|
||||||
|
|
||||||
def make_xmlresponse(result):
|
|
||||||
""" construct a response from a dict of uri:error_code elements """
|
|
||||||
doc = Document(None)
|
|
||||||
ms = doc.createElement("D:multistatus")
|
|
||||||
ms.setAttribute("xmlns:D", "DAV:")
|
|
||||||
doc.appendChild(ms)
|
|
||||||
|
|
||||||
for el, ec in result.items():
|
|
||||||
re = doc.createElement("D:response")
|
|
||||||
hr = doc.createElement("D:href")
|
|
||||||
st = doc.createElement("D:status")
|
|
||||||
huri = doc.createTextNode(quote_uri(el))
|
|
||||||
t = doc.createTextNode(gen_estring(ec))
|
|
||||||
st.appendChild(t)
|
|
||||||
hr.appendChild(huri)
|
|
||||||
re.appendChild(hr)
|
|
||||||
re.appendChild(st)
|
|
||||||
ms.appendChild(re)
|
|
||||||
|
|
||||||
sfile = StringIO()
|
|
||||||
ext.PrettyPrint(doc, stream = sfile)
|
|
||||||
s = sfile.getvalue()
|
|
||||||
sfile.close()
|
|
||||||
return s
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -19,11 +19,9 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
import calendar
|
||||||
|
import calendar_collection
|
||||||
import caldav
|
import caldav
|
||||||
import caldav_cache
|
|
||||||
import caldav_fs
|
|
||||||
import caldav_node
|
|
||||||
import webdav_server
|
|
||||||
import wizard
|
import wizard
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"version" : "1.0",
|
"version" : "1.0",
|
||||||
"depends" : [
|
"depends" : [
|
||||||
"base",
|
"base",
|
||||||
|
"document_webdav",
|
||||||
],
|
],
|
||||||
'description': """
|
'description': """
|
||||||
This module Contains basic functionality for caldav system like:
|
This module Contains basic functionality for caldav system like:
|
||||||
|
@ -34,7 +35,7 @@
|
||||||
- Provides iCal Import/Export functionality
|
- Provides iCal Import/Export functionality
|
||||||
|
|
||||||
To access OpenERP Calendar using caldav to remote site use the URL like:
|
To access OpenERP Calendar using caldav to remote site use the URL like:
|
||||||
http://HOSTNAME:PORT/calendar/DATABASE_NAME/CALENDAR_NAME.ics
|
http://HOSTNAME:PORT/webdav/DATABASE_NAME/Calendars/CALENDAR_NAME.ics
|
||||||
|
|
||||||
Where,
|
Where,
|
||||||
HOSTNAME: Host on which OpenERP server(With webdav) is running
|
HOSTNAME: Host on which OpenERP server(With webdav) is running
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,62 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
import heapq
|
|
||||||
|
|
||||||
def memoize(maxsize):
|
|
||||||
"""decorator to 'memoize' a function - caching its results"""
|
|
||||||
def decorating_function(f):
|
|
||||||
cache = {} # map from key to value
|
|
||||||
heap = [] # list of keys, in LRU heap
|
|
||||||
cursize = 0 # because len() is slow
|
|
||||||
def wrapper(*args):
|
|
||||||
key = repr(args)
|
|
||||||
# performance crap
|
|
||||||
_cache = cache
|
|
||||||
_heap = heap
|
|
||||||
_heappop = heapq.heappop
|
|
||||||
_heappush = heapq.heappush
|
|
||||||
_time = time.time
|
|
||||||
_cursize = cursize
|
|
||||||
_maxsize = maxsize
|
|
||||||
if not _cache.has_key(key):
|
|
||||||
if _cursize == _maxsize:
|
|
||||||
# pop oldest element
|
|
||||||
(_, oldkey) = _heappop(_heap)
|
|
||||||
_cache.pop(oldkey)
|
|
||||||
else:
|
|
||||||
_cursize += 1
|
|
||||||
# insert this element
|
|
||||||
_cache[key] = f(*args)
|
|
||||||
_heappush(_heap, (_time(), key))
|
|
||||||
wrapper.misses += 1
|
|
||||||
else:
|
|
||||||
wrapper.hits += 1
|
|
||||||
return cache[key]
|
|
||||||
wrapper.__doc__ = f.__doc__
|
|
||||||
wrapper.__name__ = f.__name__
|
|
||||||
wrapper.hits = wrapper.misses = 0
|
|
||||||
return wrapper
|
|
||||||
return decorating_function
|
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -0,0 +1,380 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record model="document.directory" id="document.dir_calendars">
|
||||||
|
<field name="name">Calendars</field>
|
||||||
|
<field name="calendar_collection">True</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Event attributes-->
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_comment">
|
||||||
|
<field name="name">comment</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_uid">
|
||||||
|
<field name="name">uid</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_seq">
|
||||||
|
<field name="name">seq</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_recurrence-id">
|
||||||
|
<field name="name">recurrence-id</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_transp">
|
||||||
|
<field name="name">transp</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_attendee">
|
||||||
|
<field name="name">attendee</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_related">
|
||||||
|
<field name="name">related</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_rrule">
|
||||||
|
<field name="name">rrule</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_dtend">
|
||||||
|
<field name="name">dtend</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_valarm">
|
||||||
|
<field name="name">valarm</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_vtimezone">
|
||||||
|
<field name="name">vtimezone</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_priority">
|
||||||
|
<field name="name">priority</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_location">
|
||||||
|
<field name="name">location</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_exrule">
|
||||||
|
<field name="name">exrule</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_resources">
|
||||||
|
<field name="name">resources</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_rstatus">
|
||||||
|
<field name="name">rstatus</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_status">
|
||||||
|
<field name="name">status</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_exdate">
|
||||||
|
<field name="name">exdate</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_dtstamp">
|
||||||
|
<field name="name">dtstamp</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_description">
|
||||||
|
<field name="name">description</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_rdate">
|
||||||
|
<field name="name">rdate</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_dtstart">
|
||||||
|
<field name="name">dtstart</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_class">
|
||||||
|
<field name="name">class</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_x-openobject-model">
|
||||||
|
<field name="name">x-openobject-model</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_created">
|
||||||
|
<field name="name">created</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_url">
|
||||||
|
<field name="name">url</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_summary">
|
||||||
|
<field name="name">summary</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_event_contact">
|
||||||
|
<field name="name">contact</field>
|
||||||
|
<field name="type">vevent</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Todo attributes-->
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_status">
|
||||||
|
<field name="name">status</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_comment">
|
||||||
|
<field name="name">comment</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_attendee">
|
||||||
|
<field name="name">attendee</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_valarm">
|
||||||
|
<field name="name">valarm</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_description">
|
||||||
|
<field name="name">description</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_seq">
|
||||||
|
<field name="name">seq</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_vtimezone">
|
||||||
|
<field name="name">vtimezone</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_url">
|
||||||
|
<field name="name">url</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_completed">
|
||||||
|
<field name="name">completed</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_percent">
|
||||||
|
<field name="name">percent</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_due">
|
||||||
|
<field name="name">due</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_summary">
|
||||||
|
<field name="name">summary</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_priority">
|
||||||
|
<field name="name">priority</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_exdate">
|
||||||
|
<field name="name">exdate</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_location">
|
||||||
|
<field name="name">location</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_exrule">
|
||||||
|
<field name="name">exrule</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_duration">
|
||||||
|
<field name="name">duration</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_organizer">
|
||||||
|
<field name="name">organizer</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_dtstart">
|
||||||
|
<field name="name">dtstart</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_rrule">
|
||||||
|
<field name="name">rrule</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_class">
|
||||||
|
<field name="name">class</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_todo_uid">
|
||||||
|
<field name="name">uid</field>
|
||||||
|
<field name="type">vtodo</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Attendee's attributes-->
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_cn">
|
||||||
|
<field name="name">cn</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_sent-by">
|
||||||
|
<field name="name">sent-by</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_language">
|
||||||
|
<field name="name">language</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_delegated-from">
|
||||||
|
<field name="name">delegated-from</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_member">
|
||||||
|
<field name="name">member</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_cutype">
|
||||||
|
<field name="name">cutype</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_role">
|
||||||
|
<field name="name">role</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_partstat">
|
||||||
|
<field name="name">partstat</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_delegated-to">
|
||||||
|
<field name="name">delegated-to</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_dir">
|
||||||
|
<field name="name">dir</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_attendee_rsvp">
|
||||||
|
<field name="name">rsvp</field>
|
||||||
|
<field name="type">attendee</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Alarm attributes-->
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_attendee">
|
||||||
|
<field name="name">attendee</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_trigger_duration">
|
||||||
|
<field name="name">trigger_duration</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_description">
|
||||||
|
<field name="name">description</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_attach">
|
||||||
|
<field name="name">attach</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_trigger_occurs">
|
||||||
|
<field name="name">trigger_occurs</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_trigger_interval">
|
||||||
|
<field name="name">trigger_interval</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_summary">
|
||||||
|
<field name="name">summary</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_duration">
|
||||||
|
<field name="name">duration</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_repeat">
|
||||||
|
<field name="name">repeat</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_action">
|
||||||
|
<field name="name">action</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="basic.calendar.attributes" id="field_alarm_trigger_related">
|
||||||
|
<field name="name">trigger_related</field>
|
||||||
|
<field name="type">alarm</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -1,334 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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 pooler
|
|
||||||
|
|
||||||
import base64
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from string import joinfields, split, lower
|
|
||||||
|
|
||||||
from service import security
|
|
||||||
|
|
||||||
import netsvc
|
|
||||||
import urlparse
|
|
||||||
|
|
||||||
from DAV.constants import COLLECTION, OBJECT
|
|
||||||
from DAV.errors import *
|
|
||||||
from DAV.iface import *
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
|
|
||||||
from caldav_cache import memoize
|
|
||||||
from tools import misc
|
|
||||||
CACHE_SIZE=20000
|
|
||||||
|
|
||||||
|
|
||||||
class tinydav_handler(dav_interface):
|
|
||||||
"""
|
|
||||||
This class models a Tiny ERP interface for the DAV server
|
|
||||||
"""
|
|
||||||
PROPS={'DAV:': dav_interface.PROPS['DAV:'], }
|
|
||||||
|
|
||||||
M_NS={ "DAV:": dav_interface.M_NS['DAV:'], }
|
|
||||||
|
|
||||||
def __init__(self, parent, verbose=False):
|
|
||||||
self.db_name = False
|
|
||||||
self.parent = parent
|
|
||||||
self.baseuri = parent.baseuri
|
|
||||||
|
|
||||||
def get_propnames(self, uri):
|
|
||||||
props = self.PROPS
|
|
||||||
self.parent.log_message('get propnames: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
return props
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if node:
|
|
||||||
props.update(node.get_dav_props(cr))
|
|
||||||
cr.close()
|
|
||||||
return props
|
|
||||||
|
|
||||||
def get_prop(self,uri,ns,propname):
|
|
||||||
""" return the value of a given property
|
|
||||||
|
|
||||||
uri -- uri of the object to get the property of
|
|
||||||
ns -- namespace of the property
|
|
||||||
pname -- name of the property
|
|
||||||
"""
|
|
||||||
if self.M_NS.has_key(ns):
|
|
||||||
return dav_interface.get_prop(self, uri, ns, propname)
|
|
||||||
|
|
||||||
if uri[-1]=='/':uri = uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
raise DAV_NotFound
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
cr.close()
|
|
||||||
raise DAV_NotFound
|
|
||||||
res = node.get_dav_eprop(cr, ns, propname)
|
|
||||||
cr.close()
|
|
||||||
return res
|
|
||||||
|
|
||||||
def urijoin(self,*ajoin):
|
|
||||||
""" Return the base URI of this request, or even join it with the
|
|
||||||
ajoin path elements
|
|
||||||
"""
|
|
||||||
return self.baseuri+ '/'.join(ajoin)
|
|
||||||
|
|
||||||
def uri2local(self, uri):
|
|
||||||
uparts = urlparse.urlparse(uri)
|
|
||||||
reluri = uparts[2]
|
|
||||||
if reluri and reluri[-1]=="/":
|
|
||||||
reluri = reluri[:-1]
|
|
||||||
return reluri
|
|
||||||
|
|
||||||
#
|
|
||||||
# pos: -1 to get the parent of the uri
|
|
||||||
#
|
|
||||||
def get_cr(self, uri):
|
|
||||||
pdb = self.parent.auth_proxy.last_auth
|
|
||||||
reluri = self.uri2local(uri)
|
|
||||||
try:
|
|
||||||
dbname = reluri.split('/')[2]
|
|
||||||
except:
|
|
||||||
dbname = False
|
|
||||||
if not dbname:
|
|
||||||
return None, None, None, False, None
|
|
||||||
if not pdb and dbname:
|
|
||||||
# if dbname was in our uri, we should have authenticated
|
|
||||||
# against that.
|
|
||||||
raise Exception("Programming error")
|
|
||||||
assert pdb == dbname, " %s != %s" %(pdb, dbname)
|
|
||||||
user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb]
|
|
||||||
db,pool = pooler.get_db_and_pool(dbname)
|
|
||||||
cr = db.cursor()
|
|
||||||
uri2 = reluri.split('/')[3:]
|
|
||||||
return cr, uid, pool, dbname, uri2
|
|
||||||
|
|
||||||
def uri2object(self, cr, uid, pool, uri):
|
|
||||||
if not uid:
|
|
||||||
return None
|
|
||||||
return pool.get('basic.calendar').get_calendar_object(cr, uid, uri)
|
|
||||||
|
|
||||||
def get_data(self, uri):
|
|
||||||
self.parent.log_message('GET: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
try:
|
|
||||||
if not dbname:
|
|
||||||
raise DAV_Error, 409
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
try:
|
|
||||||
datas = node.get_data(cr, uid)
|
|
||||||
except TypeError, e:
|
|
||||||
import traceback
|
|
||||||
self.parent.log_error("GET typeError: %s", str(e))
|
|
||||||
self.parent.log_message("Exc: %s", traceback.format_exc())
|
|
||||||
raise DAV_Forbidden
|
|
||||||
except IndexError, e :
|
|
||||||
self.parent.log_error("GET IndexError: %s", str(e))
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
except Exception, e:
|
|
||||||
import traceback
|
|
||||||
self.parent.log_error("GET exception: %s", str(e))
|
|
||||||
self.parent.log_message("Exc: %s", traceback.format_exc())
|
|
||||||
raise DAV_Error, 409
|
|
||||||
return datas
|
|
||||||
finally:
|
|
||||||
cr.close()
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def _get_dav_resourcetype(self, uri):
|
|
||||||
""" return type of object """
|
|
||||||
self.parent.log_message('get RT: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
try:
|
|
||||||
if not dbname:
|
|
||||||
return COLLECTION
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
return OBJECT
|
|
||||||
finally:
|
|
||||||
cr.close()
|
|
||||||
|
|
||||||
def _get_dav_displayname(self, uri):
|
|
||||||
self.parent.log_message('get DN: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
return COLLECTION
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
cr.close()
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
cr.close()
|
|
||||||
return node.displayname
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def _get_dav_getcontentlength(self, uri):
|
|
||||||
""" return the content length of an object """
|
|
||||||
self.parent.log_message('get length: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
result = 0
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
return '0'
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
cr.close()
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
result = node.content_length or 0
|
|
||||||
cr.close()
|
|
||||||
return str(result)
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def _get_dav_getetag(self, uri):
|
|
||||||
""" return the ETag of an object """
|
|
||||||
self.parent.log_message('get etag: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
result = 0
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
return '0'
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
cr.close()
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
result = node.get_etag(cr)
|
|
||||||
cr.close()
|
|
||||||
return str(result)
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def get_lastmodified(self, uri):
|
|
||||||
""" return the last modified date of the object """
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
today = time.time()
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
try:
|
|
||||||
if not dbname:
|
|
||||||
return today
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
if node.write_date:
|
|
||||||
return time.mktime(time.strptime(node.write_date, '%Y-%m-%d %H:%M:%S'))
|
|
||||||
else:
|
|
||||||
return today
|
|
||||||
finally:
|
|
||||||
cr.close()
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def get_creationdate(self, uri):
|
|
||||||
""" return the last modified date of the object """
|
|
||||||
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
try:
|
|
||||||
if not dbname:
|
|
||||||
raise DAV_Error, 409
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
if node.create_date:
|
|
||||||
result = time.strptime(node.create_date, '%Y-%m-%d %H:%M:%S')
|
|
||||||
else:
|
|
||||||
result = time.gmtime()
|
|
||||||
return result
|
|
||||||
finally:
|
|
||||||
cr.close()
|
|
||||||
|
|
||||||
@memoize(CACHE_SIZE)
|
|
||||||
def _get_dav_getcontenttype(self, uri):
|
|
||||||
self.parent.log_message('get contenttype: %s' % uri)
|
|
||||||
if uri[-1]=='/':uri=uri[:-1]
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
try:
|
|
||||||
if not dbname:
|
|
||||||
return 'httpd/unix-directory'
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if not node:
|
|
||||||
raise DAV_NotFound(uri2)
|
|
||||||
result = node.mimetype
|
|
||||||
return result
|
|
||||||
#raise DAV_NotFound, 'Could not find %s' % path
|
|
||||||
finally:
|
|
||||||
cr.close()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def put(self, uri, data, content_type=None):
|
|
||||||
""" put the object into the filesystem """
|
|
||||||
self.parent.log_message('Putting %s (%d), %s'%(misc.ustr(uri), len(data), content_type))
|
|
||||||
parent='/'.join(uri.split('/')[:-1])
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
raise DAV_Forbidden
|
|
||||||
try:
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2[:])
|
|
||||||
except:
|
|
||||||
node = False
|
|
||||||
|
|
||||||
if not node:
|
|
||||||
raise DAV_Forbidden
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
node.set_data(cr, uid, data)
|
|
||||||
except Exception, e:
|
|
||||||
import traceback
|
|
||||||
self.parent.log_error("Cannot save :%s", str(e))
|
|
||||||
self.parent.log_message("Exc: %s", traceback.format_exc())
|
|
||||||
raise DAV_Forbidden
|
|
||||||
|
|
||||||
cr.commit()
|
|
||||||
cr.close()
|
|
||||||
return 201
|
|
||||||
|
|
||||||
def exists(self, uri):
|
|
||||||
""" test if a resource exists """
|
|
||||||
result = False
|
|
||||||
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
|
|
||||||
if not dbname:
|
|
||||||
cr.close()
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
node = self.uri2object(cr, uid, pool, uri2)
|
|
||||||
if node:
|
|
||||||
result = True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
cr.close()
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -25,31 +25,104 @@ import pooler
|
||||||
import tools
|
import tools
|
||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
|
from document import nodes
|
||||||
|
import StringIO
|
||||||
|
|
||||||
|
class node_database(nodes.node_database):
|
||||||
|
def _child_get(self, cr, name=False, parent_id=False, domain=None):
|
||||||
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
if not domain:
|
||||||
|
domain = []
|
||||||
|
domain2 = domain + [('calendar_collection','=', False)]
|
||||||
|
res = super(node_database, self)._child_get(cr, name=name, parent_id=parent_id, domain=domain2)
|
||||||
|
where = [('parent_id','=',parent_id)]
|
||||||
|
domain2 = domain + [('calendar_collection','=', True)]
|
||||||
|
if name:
|
||||||
|
where.append(('name','=',name))
|
||||||
|
if domain2:
|
||||||
|
where += domain2
|
||||||
|
|
||||||
|
where2 = where + [('type', '=', 'directory')]
|
||||||
|
ids = dirobj.search(cr, uid, where2, context=ctx)
|
||||||
|
for dirr in dirobj.browse(cr,uid,ids,context=ctx):
|
||||||
|
res.append(node_calendar_collection(dirr.name,self,self.context,dirr))
|
||||||
|
return res
|
||||||
|
|
||||||
|
class node_calendar_collection(nodes.node_dir):
|
||||||
|
PROPS = {
|
||||||
|
"http://calendarserver.org/ns/" : ('getctag'),
|
||||||
|
}
|
||||||
|
M_NS = {
|
||||||
|
"http://calendarserver.org/ns/" : '_get_dav',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class node_calendar(object):
|
def get_dav_props(self, cr):
|
||||||
def __init__(self, path, context, calendar):
|
return self.PROPS
|
||||||
self.path = path
|
|
||||||
self.context = context
|
|
||||||
self.calendar_id = calendar.id
|
|
||||||
self.mimetype = 'ics'
|
|
||||||
self.create_date = calendar.create_date
|
|
||||||
self.write_date = calendar.write_date or calendar.create_date
|
|
||||||
self.content_length = 0
|
|
||||||
self.displayname = calendar.name
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_data(self, cr, uid):
|
def get_dav_eprop(self,cr, ns, propname):
|
||||||
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
|
if self.M_NS.has_key(ns):
|
||||||
return calendar_obj.export_cal(cr, uid, [self.calendar_id])
|
prefix = self.M_NS[ns]
|
||||||
|
else:
|
||||||
|
print "No namespace:",ns, "( for prop:", propname,")"
|
||||||
|
return None
|
||||||
|
|
||||||
def get_data_len(self, cr):
|
mname = prefix + "_" + propname
|
||||||
return self.content_length
|
|
||||||
|
|
||||||
def set_data(self, cr, uid, data):
|
if not hasattr(self, mname):
|
||||||
calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
|
return None
|
||||||
return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
m = getattr(self, mname)
|
||||||
|
r = m(cr)
|
||||||
|
return r
|
||||||
|
except AttributeError, e:
|
||||||
|
print 'Property %s not supported' % propname
|
||||||
|
print "Exception:", e
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _file_get(self,cr, nodename=False):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _child_get(self, cr, name=False, parent_id=False, domain=None):
|
||||||
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
where = [('collection_id','=',self.dir_id)]
|
||||||
|
ext = False
|
||||||
|
if name:
|
||||||
|
res = name.split('.ics')
|
||||||
|
if len(res) > 1:
|
||||||
|
name = res[0]
|
||||||
|
ext = '.ics'
|
||||||
|
if name:
|
||||||
|
where.append(('name','=',name))
|
||||||
|
if not domain:
|
||||||
|
domain = []
|
||||||
|
where = where + domain
|
||||||
|
fil_obj = dirobj.pool.get('basic.calendar')
|
||||||
|
ids = fil_obj.search(cr,uid,where,context=ctx)
|
||||||
|
res = []
|
||||||
|
for calender in fil_obj.browse(cr, uid, ids, context=ctx):
|
||||||
|
if not ext:
|
||||||
|
res.append(node_calendar(calender.name, self, self.context, calender))
|
||||||
|
else:
|
||||||
|
res.append(res_node_calendar(name, self, self.context, calender))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _get_dav_owner(self, cr):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_etag(self, cr):
|
def get_etag(self, cr):
|
||||||
""" Get a tag, unique per object + modification.
|
""" Get a tag, unique per object + modification.
|
||||||
|
|
||||||
|
@ -64,24 +137,385 @@ class node_calendar(object):
|
||||||
return str(wtime)
|
return str(wtime)
|
||||||
|
|
||||||
def _get_ttag(self, cr):
|
def _get_ttag(self, cr):
|
||||||
return 'calendar-%d' % self.calendar_id
|
return 'calendar collection-%d' % self.dir_id
|
||||||
|
|
||||||
|
def _get_dav_getctag(self, cr):
|
||||||
|
result = self.get_etag(cr)
|
||||||
|
return str(result)
|
||||||
|
|
||||||
|
|
||||||
|
class node_calendar(nodes.node_class):
|
||||||
|
our_type = 'collection'
|
||||||
|
PROPS = {
|
||||||
|
"http://calendarserver.org/ns/" : ('getctag'),
|
||||||
|
"urn:ietf:params:xml:ns:caldav" : (
|
||||||
|
'calendar-description',
|
||||||
|
'calendar-data',
|
||||||
|
'calendar-home-set',
|
||||||
|
'calendar-user-address-set',
|
||||||
|
'schedule-inbox-URL',
|
||||||
|
'schedule-outbox-URL',)}
|
||||||
|
M_NS = {
|
||||||
|
"DAV:" : '_get_dav',
|
||||||
|
"http://calendarserver.org/ns/" : '_get_dav',
|
||||||
|
"urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
|
||||||
|
|
||||||
|
def __init__(self,path, parent, context, calendar):
|
||||||
|
super(node_calendar,self).__init__(path, parent,context)
|
||||||
|
self.calendar_id = calendar.id
|
||||||
|
self.mimetype = 'application/x-directory'
|
||||||
|
self.create_date = calendar.create_date
|
||||||
|
self.write_date = calendar.write_date or calendar.create_date
|
||||||
|
self.content_length = 0
|
||||||
|
self.displayname = calendar.name
|
||||||
|
self.cal_type = calendar.type
|
||||||
|
|
||||||
|
def _get_dav_getctag(self, cr):
|
||||||
|
result = self._get_ttag(cr) + ':' + str(time.time())
|
||||||
|
return str(result)
|
||||||
|
|
||||||
|
def match_dav_eprop(self, cr, match, ns, prop):
|
||||||
|
if ns == "DAV:" and prop == "getetag":
|
||||||
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
tem, dav_time = tuple(match.split(':'))
|
||||||
|
model, res_id = tuple(tem.split('_'))
|
||||||
|
model_obj = dirobj.pool.get(model)
|
||||||
|
model = model_obj.browse(cr, uid, res_id, context=ctx)
|
||||||
|
write_time = model.write_date or model.create_date
|
||||||
|
wtime = time.mktime(time.strptime(write_time,'%Y-%m-%d %H:%M:%S'))
|
||||||
|
if float(dav_time) == float(wtime):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
res = super(node_calendar, self).match_dav_eprop(cr, match, ns, prop)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def get_domain(self, cr, filters):
|
||||||
|
res = []
|
||||||
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
calendar_obj = dirobj.pool.get('basic.calendar')
|
||||||
|
if not filters:
|
||||||
|
return res
|
||||||
|
if filters.localName == 'calendar-query':
|
||||||
|
res = []
|
||||||
|
for filter_child in filters.childNodes:
|
||||||
|
if filter_child.nodeType == filter_child.TEXT_NODE:
|
||||||
|
continue
|
||||||
|
if filter_child.localName == 'filter':
|
||||||
|
for vcalendar_filter in filter_child.childNodes:
|
||||||
|
if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:
|
||||||
|
continue
|
||||||
|
if vcalendar_filter.localName == 'comp-filter':
|
||||||
|
if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
|
||||||
|
for vevent_filter in vcalendar_filter.childNodes:
|
||||||
|
if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
|
||||||
|
continue
|
||||||
|
if vevent_filter.localName == 'comp-filter':
|
||||||
|
if vevent_filter.getAttribute('name') == 'VEVENT':
|
||||||
|
res = [('type','=','vevent')]
|
||||||
|
if vevent_filter.getAttribute('name') == 'VTODO':
|
||||||
|
res = [('type','=','vtodo')]
|
||||||
|
return res
|
||||||
|
elif filters.localName == 'calendar-multiget':
|
||||||
|
names = []
|
||||||
|
for filter_child in filters.childNodes:
|
||||||
|
if filter_child.nodeType == filter_child.TEXT_NODE:
|
||||||
|
continue
|
||||||
|
if filter_child.localName == 'href':
|
||||||
|
if not filter_child.firstChild:
|
||||||
|
continue
|
||||||
|
uri = filter_child.firstChild.data
|
||||||
|
caluri = uri.split('/')
|
||||||
|
if len(caluri):
|
||||||
|
caluri = caluri[-2]
|
||||||
|
if caluri not in names : names.append(caluri)
|
||||||
|
res = [('name','in',names)]
|
||||||
|
return res
|
||||||
|
return res
|
||||||
|
|
||||||
|
def children(self, cr, domain=None):
|
||||||
|
return self._child_get(cr, domain=domain)
|
||||||
|
|
||||||
|
def child(self,cr, name, domain=None):
|
||||||
|
res = self._child_get(cr, name, domain=domain)
|
||||||
|
if res:
|
||||||
|
return res[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Calendar(osv.osv):
|
def _child_get(self, cr, name=False, parent_id=False, domain=None):
|
||||||
_inherit = 'basic.calendar'
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
where = []
|
||||||
|
if name:
|
||||||
|
where.append(('id','=',int(name)))
|
||||||
|
if not domain:
|
||||||
|
domain = []
|
||||||
|
#for opr1, opt, opr2 in domain:
|
||||||
|
# if opr1 == 'type' and opr2 != self.cal_type:
|
||||||
|
# return []
|
||||||
|
|
||||||
def get_calendar_object(self, cr, uid, uri, context=None):
|
fil_obj = dirobj.pool.get('basic.calendar')
|
||||||
if not uri:
|
ids = fil_obj.search(cr, uid, domain)
|
||||||
|
res = []
|
||||||
|
if self.calendar_id in ids:
|
||||||
|
res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_dav_props(self, cr):
|
||||||
|
return self.PROPS
|
||||||
|
|
||||||
|
def get_dav_eprop(self,cr, ns, propname):
|
||||||
|
if self.M_NS.has_key(ns):
|
||||||
|
prefix = self.M_NS[ns]
|
||||||
|
else:
|
||||||
|
print "No namespace:",ns, "( for prop:", propname,")"
|
||||||
return None
|
return None
|
||||||
if len(uri) > 1:
|
propname = propname.replace('-','_')
|
||||||
|
mname = prefix + "_" + propname
|
||||||
|
if not hasattr(self, mname):
|
||||||
return None
|
return None
|
||||||
name, file_type = tuple(uri[0].split('.'))
|
|
||||||
res = self.name_search(cr, uid, name)
|
|
||||||
if not res:
|
|
||||||
return None
|
|
||||||
calendar_id, calendar_name = res[0]
|
|
||||||
calendar = self.browse(cr, uid, calendar_id)
|
|
||||||
return node_calendar(uri, context, calendar)
|
|
||||||
Calendar()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
|
try:
|
||||||
|
m = getattr(self, mname)
|
||||||
|
r = m(cr)
|
||||||
|
return r
|
||||||
|
except AttributeError, e:
|
||||||
|
print 'Property %s not supported' % propname
|
||||||
|
print "Exception:", e
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_child(self,cr,path,data):
|
||||||
|
""" API function to create a child file object and node
|
||||||
|
Return the node_* created
|
||||||
|
"""
|
||||||
|
return self.set_data(cr, data)
|
||||||
|
|
||||||
|
|
||||||
|
def set_data(self, cr, data, fil_obj = None):
|
||||||
|
uid = self.context.uid
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
|
||||||
|
|
||||||
|
def get_data_len(self, cr, fil_obj = None):
|
||||||
|
return self.content_length
|
||||||
|
|
||||||
|
|
||||||
|
def _get_ttag(self,cr):
|
||||||
|
return 'calendar-%d' % (self.calendar_id,)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_etag(self, cr):
|
||||||
|
""" Get a tag, unique per object + modification.
|
||||||
|
|
||||||
|
see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
|
||||||
|
return self._get_ttag(cr) + ':' + self._get_wtag(cr)
|
||||||
|
|
||||||
|
def _get_wtag(self, cr):
|
||||||
|
""" Return the modification time as a unique, compact string """
|
||||||
|
if self.write_date:
|
||||||
|
wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
|
||||||
|
else: wtime = time.time()
|
||||||
|
return str(wtime)
|
||||||
|
|
||||||
|
def rmcol(self, cr):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class res_node_calendar(nodes.node_class):
|
||||||
|
our_type = 'file'
|
||||||
|
PROPS = {
|
||||||
|
"http://calendarserver.org/ns/" : ('getctag'),
|
||||||
|
"urn:ietf:params:xml:ns:caldav" : (
|
||||||
|
'calendar-description',
|
||||||
|
'calendar-data',
|
||||||
|
'calendar-home-set',
|
||||||
|
'calendar-user-address-set',
|
||||||
|
'schedule-inbox-URL',
|
||||||
|
'schedule-outbox-URL',)}
|
||||||
|
M_NS = {
|
||||||
|
"http://calendarserver.org/ns/" : '_get_dav',
|
||||||
|
"urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
|
||||||
|
|
||||||
|
def __init__(self,path, parent, context, res_obj, res_model=None, res_id=None):
|
||||||
|
super(res_node_calendar,self).__init__(path, parent, context)
|
||||||
|
self.mimetype = 'text/calendar'
|
||||||
|
self.create_date = parent.create_date
|
||||||
|
self.write_date = parent.write_date or parent.create_date
|
||||||
|
self.calendar_id = hasattr(parent, 'calendar_id') and parent.calendar_id or False
|
||||||
|
if res_obj:
|
||||||
|
if not self.calendar_id: self.calendar_id = res_obj.id
|
||||||
|
self.create_date = res_obj.create_date
|
||||||
|
self.write_date = res_obj.write_date or res_obj.create_date
|
||||||
|
self.displayname = res_obj.name
|
||||||
|
|
||||||
|
self.content_length = 0
|
||||||
|
|
||||||
|
self.model = res_model
|
||||||
|
self.res_id = res_id
|
||||||
|
|
||||||
|
def open(self, cr, mode=False):
|
||||||
|
uid = self.context.uid
|
||||||
|
if self.type in ('collection','database'):
|
||||||
|
return False
|
||||||
|
s = StringIO.StringIO(self.get_data(cr))
|
||||||
|
s.name = self
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_dav_props(self, cr):
|
||||||
|
return self.PROPS
|
||||||
|
|
||||||
|
def get_dav_eprop(self,cr, ns, propname):
|
||||||
|
if self.M_NS.has_key(ns):
|
||||||
|
prefix = self.M_NS[ns]
|
||||||
|
else:
|
||||||
|
print "No namespace:",ns, "( for prop:", propname,")"
|
||||||
|
return None
|
||||||
|
propname = propname.replace('-','_')
|
||||||
|
mname = prefix + "_" + propname
|
||||||
|
if not hasattr(self, mname):
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
m = getattr(self, mname)
|
||||||
|
r = m(cr)
|
||||||
|
return r
|
||||||
|
except AttributeError, e:
|
||||||
|
print 'Property %s not supported' % propname
|
||||||
|
print "Exception:", e
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(self, cr, fil_obj = None):
|
||||||
|
uid = self.context.uid
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
context = self.context.context.copy()
|
||||||
|
context.update({'model': self.model, 'res_id':self.res_id})
|
||||||
|
res = calendar_obj.export_cal(cr, uid, [self.calendar_id], context=context)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_data_len(self, cr, fil_obj = None):
|
||||||
|
return self.content_length
|
||||||
|
|
||||||
|
def set_data(self, cr, data, fil_obj = None):
|
||||||
|
uid = self.context.uid
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
|
||||||
|
|
||||||
|
def _get_ttag(self,cr):
|
||||||
|
res = False
|
||||||
|
if self.model and self.res_id:
|
||||||
|
res = '%s_%d' % (self.model, self.res_id)
|
||||||
|
elif self.calendar_id:
|
||||||
|
res = '%d' % (self.calendar_id)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _get_caldav_calendar_data(self, cr):
|
||||||
|
return self.get_data(cr)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_caldav_calendar_description(self, cr):
|
||||||
|
uid = self.context.uid
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
|
||||||
|
return calendar.description
|
||||||
|
|
||||||
|
|
||||||
|
def _get_caldav_calendar_home_set(self, cr):
|
||||||
|
import xml.dom.minidom
|
||||||
|
import urllib
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
|
||||||
|
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
|
||||||
|
huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
|
||||||
|
href = doc.documentElement
|
||||||
|
href.tagName = 'D:href'
|
||||||
|
href.appendChild(huri)
|
||||||
|
return href
|
||||||
|
|
||||||
|
def _get_caldav_calendar_user_address_set(self, cr):
|
||||||
|
import xml.dom.minidom
|
||||||
|
dirobj = self.context._dirobj
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
user_obj = self.context._dirobj.pool.get('res.users')
|
||||||
|
user = user_obj.browse(cr, uid, uid, context=ctx)
|
||||||
|
doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
|
||||||
|
href = doc.documentElement
|
||||||
|
href.tagName = 'D:href'
|
||||||
|
huri = doc.createTextNode('MAILTO:' + user.email)
|
||||||
|
href.appendChild(huri)
|
||||||
|
return href
|
||||||
|
|
||||||
|
|
||||||
|
def _get_caldav_schedule_inbox_URL(self, cr):
|
||||||
|
import xml.dom.minidom
|
||||||
|
import urllib
|
||||||
|
uid = self.context.uid
|
||||||
|
ctx = self.context.context.copy()
|
||||||
|
ctx.update(self.dctx)
|
||||||
|
calendar_obj = self.context._dirobj.pool.get('basic.calendar')
|
||||||
|
calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
|
||||||
|
res = '%s/%s' %(calendar.name, calendar.collection_id.name)
|
||||||
|
doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
|
||||||
|
href = doc.documentElement
|
||||||
|
href.tagName = 'D:href'
|
||||||
|
huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
|
||||||
|
href.appendChild(huri)
|
||||||
|
return href
|
||||||
|
|
||||||
|
|
||||||
|
def rm(self, cr):
|
||||||
|
uid = self.context.uid
|
||||||
|
res = False
|
||||||
|
if self.type in ('collection','database'):
|
||||||
|
return False
|
||||||
|
if self.model and self.res_id:
|
||||||
|
document_obj = self.context._dirobj.pool.get(self.model)
|
||||||
|
if document_obj:
|
||||||
|
res = False
|
||||||
|
#res = document_obj.unlink(cr, uid, [self.res_id]) #TOFIX
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _get_caldav_schedule_outbox_URL(self, cr):
|
||||||
|
return self._get_caldav_schedule_inbox_URL(cr)
|
||||||
|
|
||||||
|
|
||||||
|
def get_etag(self, cr):
|
||||||
|
""" Get a tag, unique per object + modification.
|
||||||
|
|
||||||
|
see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
|
||||||
|
return self._get_ttag(cr) + ':' + self._get_wtag(cr)
|
||||||
|
|
||||||
|
def _get_wtag(self, cr):
|
||||||
|
""" Return the modification time as a unique, compact string """
|
||||||
|
if self.write_date:
|
||||||
|
wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
|
||||||
|
else: wtime = time.time()
|
||||||
|
return str(wtime)
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
|
||||||
|
|
|
@ -1,61 +1,151 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_calendar_collection_form">
|
||||||
|
<field name="name">Calendar Collections : Form</field>
|
||||||
|
<field name="model">document.directory</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Calendar Collections">
|
||||||
|
<field name="name" select="1" colspan="4"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="parent_id"/>
|
||||||
|
<field name="calendar_ids" colspan="4" nolabel="1"/>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="view_calendar_collection_tree">
|
||||||
|
<field name="name">Calendar Collections : Tree</field>
|
||||||
|
<field name="model">document.directory</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Calendar Collections" toolbar="1">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="create_date"/>
|
||||||
|
<field name="write_date"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="view_calendar_form" model="ir.ui.view">
|
<record model="ir.actions.act_window" id="action_calendar_collection_form">
|
||||||
<field name="name">Basic Calendar</field>
|
<field name="name">Calendar Collections</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">document.directory</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="domain">[('calendar_collection','=',True)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="document.action_document_directory_form">
|
||||||
|
<field name="domain">[('calendar_collection','=',False)]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_dir_view1" model="ir.actions.act_window.view">
|
||||||
|
<field eval="10" name="sequence"/>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="view_id" ref="view_calendar_collection_tree"/>
|
||||||
|
<field name="act_window_id" ref="action_calendar_collection_form"/>
|
||||||
|
</record>
|
||||||
|
<record id="action_dir_view2" model="ir.actions.act_window.view">
|
||||||
|
<field eval="20" name="sequence"/>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="view_calendar_collection_form"/>
|
||||||
|
<field name="act_window_id" ref="action_calendar_collection_form"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_calendar"
|
||||||
|
name="Calendar"
|
||||||
|
parent="base.menu_document_configuration"/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
action="action_calendar_collection_form"
|
||||||
|
id="menu_calendar_collection"
|
||||||
|
parent="menu_calendar"/>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="view_caldav_form">
|
||||||
|
<field name="name">Calendar : Form</field>
|
||||||
<field name="model">basic.calendar</field>
|
<field name="model">basic.calendar</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Basic Calendar">
|
<form string="Calendar">
|
||||||
<field name="name" required="1" select="1" />
|
<field name="name"/>
|
||||||
<field name="active" select="1" />
|
<field name="type"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="collection_id" required="1"/>
|
||||||
<field name="line_ids" mode="form,tree" colspan="4" nolabel="1">
|
<field name="line_ids" mode="form,tree" colspan="4" nolabel="1">
|
||||||
<form string="Calendar Lines">
|
<form string="Calendar Lines">
|
||||||
<field name="name" required="1" select="1" />
|
<field name="name" required="1" select="1" />
|
||||||
<field name="object_id" required="1" select="1" />
|
<field name="object_id" required="1" select="1" />
|
||||||
<field name="domain" select="1" />
|
<field name="domain" select="1" />
|
||||||
<field name="mapping_ids" select="1" colspan="4" nolabel="1">
|
<field name="mapping_ids" select="1" colspan="4" nolabel="1">
|
||||||
<tree string="Attributes Mapping" editable="bottom">
|
<tree string="Attributes Mapping" editable="bottom">
|
||||||
<field name="name" required="1" domain="[('type', '=', parent.name)]"/>
|
<field name="name" required="1" domain="[('type', '=', parent.name)]"/>
|
||||||
<field name="fn" select="1" />
|
<field name="fn" select="1" />
|
||||||
<field name="field_id" select="1" required="1" domain="[('model_id', '=', parent.object_id)]" />
|
<field name="field_id" select="1" required="1" domain="[('model_id', '=', parent.object_id)]" />
|
||||||
<field name="expr" />
|
<field name="expr" />
|
||||||
</tree>
|
</tree>
|
||||||
<form string="Attributes Mapping">
|
<form string="Attributes Mapping">
|
||||||
<field name="name" select="1" required="1" domain="[('type', '=', parent.name)]"/>
|
<field name="name" select="1" required="1" domain="[('type', '=', parent.name)]"/>
|
||||||
<field name="field_id" select="1" domain="[('model_id', '=', parent.object_id)]"
|
<field name="field_id" select="1" domain="[('model_id', '=', parent.object_id)]"
|
||||||
required="1" />
|
required="1" />
|
||||||
<field name="fn" select="1" required="1" />
|
<field name="fn" select="1" required="1" />
|
||||||
<field name="expr" />
|
<field name="expr" />
|
||||||
<separator string="Value Mapping" colspan="4" />
|
<separator string="Value Mapping" colspan="4" />
|
||||||
<field name="mapping" select="1" colspan="4" nolabel="1" />
|
<field name="mapping" select="1" colspan="4" nolabel="1" />
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</form>
|
</form>
|
||||||
<tree string="Attributes Mapping" editable="bottom">
|
<tree string="Calendar Lines" editable="bottom">
|
||||||
<field name="name" select="1" />
|
<field name="name" select="1" />
|
||||||
<field name="object_id" select="1" />
|
<field name="object_id" select="1" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_view_calendar" model="ir.actions.act_window">
|
|
||||||
<field name="name">Calendar</field>
|
|
||||||
<field name="type">ir.actions.act_window</field>
|
|
||||||
<field name="res_model">basic.calendar</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem id="base.menu_calendar_configuration" name="Calendar"
|
<record model="ir.ui.view" id="view_caldav_tree">
|
||||||
parent="base.menu_base_config" sequence="10" />
|
<field name="name">Calendar : Tree</field>
|
||||||
|
<field name="model">basic.calendar</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Calendars" toolbar="1">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="create_date"/>
|
||||||
|
<field name="write_date"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<menuitem id="menu_calendar"
|
<record model="ir.actions.act_window" id="action_caldav_form">
|
||||||
name="Calendar" parent="base.menu_calendar_configuration"
|
<field name="name">Calendars</field>
|
||||||
sequence="5" action="action_view_calendar" />
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">basic.calendar</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
<record id="action_caldav_view1" model="ir.actions.act_window.view">
|
||||||
|
<field eval="10" name="sequence"/>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="view_id" ref="view_caldav_tree"/>
|
||||||
|
<field name="act_window_id" ref="action_caldav_form"/>
|
||||||
|
</record>
|
||||||
|
<record id="action_caldav_view2" model="ir.actions.act_window.view">
|
||||||
|
<field eval="20" name="sequence"/>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="view_id" ref="view_caldav_form"/>
|
||||||
|
<field name="act_window_id" ref="action_caldav_form"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
action="action_caldav_form"
|
||||||
|
id="menu_caldav_directories"
|
||||||
|
parent="menu_calendar"/>
|
||||||
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,80 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
from osv import osv, fields
|
||||||
|
from tools.translate import _
|
||||||
|
import caldav_node
|
||||||
|
|
||||||
|
class calendar_collection(osv.osv):
|
||||||
|
_inherit = 'document.directory'
|
||||||
|
_columns = {
|
||||||
|
'calendar_collection' : fields.boolean('Calendar Collection'),
|
||||||
|
'calendar_ids': fields.one2many('basic.calendar', 'collection_id', 'Calendars'),
|
||||||
|
}
|
||||||
|
_default = {
|
||||||
|
'calendar_collection' : False,
|
||||||
|
}
|
||||||
|
def _get_root_calendar_directory(self, cr, uid, context=None):
|
||||||
|
objid = self.pool.get('ir.model.data')
|
||||||
|
try:
|
||||||
|
mid = objid._get_id(cr, uid, 'document', 'dir_calendars')
|
||||||
|
if not mid:
|
||||||
|
return False
|
||||||
|
root_id = objid.read(cr, uid, mid, ['res_id'])['res_id']
|
||||||
|
root_cal_dir = self.browse(cr, uid, root_id, context=context)
|
||||||
|
return root_cal_dir.name
|
||||||
|
except Exception, e:
|
||||||
|
import netsvc
|
||||||
|
logger = netsvc.Logger()
|
||||||
|
logger.notifyChannel("document", netsvc.LOG_WARNING, 'Cannot set root directory for Calendars:'+ str(e))
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _locate_child(self, cr, uid, root_id, uri,nparent, ncontext):
|
||||||
|
""" try to locate the node in uri,
|
||||||
|
Return a tuple (node_dir, remaining_path)
|
||||||
|
"""
|
||||||
|
return (caldav_node.node_database(context=ncontext), uri)
|
||||||
|
|
||||||
|
def get_description(self, cr, uid, ids, context=None):
|
||||||
|
#TODO : return description of all calendars
|
||||||
|
if not context:
|
||||||
|
context = {}
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_schedule_inbox_URL(self, cr, uid, ids, context=None):
|
||||||
|
calendar_obj = self.pool.get('basic.calendar')
|
||||||
|
|
||||||
|
calendar_ids = calendar_obj.search(cr, uid, [
|
||||||
|
('user_id', '=', uid), ('collection_id', 'in', ids)
|
||||||
|
], limit=1, context=context)
|
||||||
|
|
||||||
|
root_cal_dir = self._get_root_calendar_directory(cr, uid, context=context)
|
||||||
|
if not calendar_ids:
|
||||||
|
return root_cal_dir
|
||||||
|
calendar_id = calendar_ids[0]
|
||||||
|
calendar = calendar_obj.browse(cr, uid, calendar_id,
|
||||||
|
context=context)
|
||||||
|
return '%s/%s' %(root_cal_dir, calendar.name)
|
||||||
|
|
||||||
|
def get_schedule_outbox_URL(self, cr, uid, ids, context=None):
|
||||||
|
return self.get_schedule_inbox_URL(cr, uid, ids, context=context)
|
||||||
|
|
||||||
|
calendar_collection()
|
|
@ -1,5 +1,4 @@
|
||||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||||
"access_basic_calendar_all","basic.calendar","model_basic_calendar","base.group_user",1,1,1,1
|
|
||||||
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event","base.group_user",1,1,1,0
|
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event","base.group_user",1,1,1,0
|
||||||
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee","base.group_user",1,1,1,0
|
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee","base.group_user",1,1,1,0
|
||||||
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo","base.group_user",1,1,1,0
|
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo","base.group_user",1,1,1,0
|
||||||
|
|
|
|
@ -1,78 +0,0 @@
|
||||||
# -*- encoding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
|
|
||||||
#
|
|
||||||
# WARNING: This program as such is intended to be used by professional
|
|
||||||
# programmers who take the whole responsability of assessing all potential
|
|
||||||
# consequences resulting from its eventual inadequacies and bugs
|
|
||||||
# End users who are looking for a ready-to-use solution with commercial
|
|
||||||
# garantees and support are strongly adviced to contract a Free Software
|
|
||||||
# Service Company
|
|
||||||
#
|
|
||||||
# This program is Free Software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License
|
|
||||||
# as published by the Free Software Foundation; either version 2
|
|
||||||
# of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
import netsvc
|
|
||||||
from caldav_fs import tinydav_handler
|
|
||||||
from tools.config import config
|
|
||||||
from DAV.WebDAVServer import DAVRequestHandler
|
|
||||||
from service.websrv_lib import HTTPDir,FixSendError
|
|
||||||
|
|
||||||
|
|
||||||
class DAVHandler(FixSendError, DAVRequestHandler):
|
|
||||||
verbose = False
|
|
||||||
|
|
||||||
def get_userinfo(self, user, pw):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _log(self, message):
|
|
||||||
netsvc.Logger().notifyChannel("webdav", netsvc.LOG_DEBUG, message)
|
|
||||||
|
|
||||||
def handle(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def finish(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
davpath = '/calendar/'
|
|
||||||
self.baseuri = "http://%s:%d%s"% (self.server.server_name, self.server.server_port, davpath)
|
|
||||||
self.IFACE_CLASS = tinydav_handler(self)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)
|
|
||||||
|
|
||||||
def log_error(self, format, *args):
|
|
||||||
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_WARNING, format % args)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from service.http_server import reg_http_service, OpenERPAuthProvider
|
|
||||||
davpath = '/calendar/'
|
|
||||||
handler = DAVHandler
|
|
||||||
handler.verbose = config.get_misc('webdav', 'verbose', True)
|
|
||||||
handler.debug = config.get_misc('webdav', 'debug', True)
|
|
||||||
reg_http_service(HTTPDir(davpath, DAVHandler, OpenERPAuthProvider()))
|
|
||||||
netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% davpath)
|
|
||||||
except Exception, e:
|
|
||||||
logger = netsvc.Logger()
|
|
||||||
logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<field name="target">new</field>
|
<field name="target">new</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<act_window id="action_calendar_event_import_values"
|
<act_window id="action_calendar_event_import_values"
|
||||||
key2="client_action_multi" name="Import .ics File"
|
key2="client_action_multi" name="Import .ics File"
|
||||||
res_model="calendar.event.import" src_model="basic.calendar"
|
res_model="calendar.event.import" src_model="basic.calendar"
|
||||||
view_mode="form" target="new" view_type="form" />
|
view_mode="form" target="new" view_type="form" />
|
||||||
|
|
|
@ -52,4 +52,4 @@
|
||||||
view_mode="form" target="new" view_type="form" />
|
view_mode="form" target="new" view_type="form" />
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -24,13 +24,12 @@ import crm_mailgate
|
||||||
import crm_action_rule
|
import crm_action_rule
|
||||||
import crm_segmentation
|
import crm_segmentation
|
||||||
import crm_meeting
|
import crm_meeting
|
||||||
import crm_opportunity
|
|
||||||
import crm_lead
|
import crm_lead
|
||||||
import crm_phonecall
|
import crm_opportunity
|
||||||
import crm_claim
|
|
||||||
import crm_fundraising
|
|
||||||
import crm_helpdesk
|
|
||||||
|
|
||||||
|
import crm_phonecall
|
||||||
|
|
||||||
|
import crm_installer
|
||||||
import report
|
import report
|
||||||
import wizard
|
import wizard
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,12 @@
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Customer & Supplier Relationship Management',
|
'name': 'Customer & Supplier Relationship Management',
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Generic Modules/CRM & SRM',
|
'category': 'Generic Modules/CRM & SRM',
|
||||||
'description': """The generic Open ERP Customer Relationship Management
|
'description': """The generic Open ERP Customer Relationship Management
|
||||||
system enables a group of people to intelligently and efficiently manage
|
system enables a group of people to intelligently and efficiently manage
|
||||||
leads, opportunities, claims, meeting, phonecall etc.
|
leads, opportunities, meeting, phonecall etc.
|
||||||
It manages key tasks such as communication, identification, prioritization,
|
It manages key tasks such as communication, identification, prioritization,
|
||||||
assignment, resolution and notification.
|
assignment, resolution and notification.
|
||||||
|
|
||||||
|
@ -41,86 +41,78 @@ appropriate staff, and making sure all future correspondence gets to the right
|
||||||
place.
|
place.
|
||||||
|
|
||||||
The CRM module has a email gateway for the synchronisation interface
|
The CRM module has a email gateway for the synchronisation interface
|
||||||
between mails and Open ERP.""",
|
between mails and Open ERP.""",
|
||||||
'author': 'Tiny',
|
'author': 'Tiny',
|
||||||
'website': 'http://www.openerp.com',
|
'website': 'http://www.openerp.com',
|
||||||
'depends': [
|
'depends': [
|
||||||
'base',
|
'base',
|
||||||
'base_action_rule',
|
'base_action_rule',
|
||||||
'process',
|
'process',
|
||||||
'mail_gateway',
|
'mail_gateway',
|
||||||
'base_calendar',
|
'base_calendar',
|
||||||
'resource',
|
'resource',
|
||||||
],
|
],
|
||||||
'init_xml': [
|
'init_xml': [
|
||||||
'crm_data.xml',
|
'crm_data.xml',
|
||||||
'crm_meeting_data.xml',
|
'crm_meeting_data.xml',
|
||||||
'crm_claims_data.xml',
|
'crm_lead_data.xml',
|
||||||
'crm_fund_data.xml',
|
'crm_meeting_data.xml',
|
||||||
'crm_helpdesk_data.xml',
|
'crm_opportunity_data.xml',
|
||||||
'crm_lead_data.xml',
|
'crm_phonecall_data.xml',
|
||||||
'crm_meeting_data.xml',
|
],
|
||||||
'crm_opportunity_data.xml',
|
|
||||||
'crm_phonecall_data.xml',
|
|
||||||
],
|
|
||||||
|
|
||||||
'update_xml': [
|
'update_xml': [
|
||||||
'wizard/crm_lead_to_partner_view.xml',
|
'wizard/crm_lead_to_partner_view.xml',
|
||||||
'wizard/crm_lead_to_opportunity_view.xml',
|
'wizard/crm_lead_to_opportunity_view.xml',
|
||||||
|
|
||||||
'wizard/crm_phonecall_to_phonecall_view.xml',
|
'wizard/crm_phonecall_to_phonecall_view.xml',
|
||||||
'wizard/crm_phonecall_to_partner_view.xml',
|
'wizard/crm_phonecall_to_partner_view.xml',
|
||||||
'wizard/crm_phonecall_to_opportunity_view.xml',
|
'wizard/crm_phonecall_to_opportunity_view.xml',
|
||||||
|
|
||||||
'wizard/crm_opportunity_to_phonecall_view.xml',
|
'wizard/crm_opportunity_to_phonecall_view.xml',
|
||||||
'wizard/crm_partner_to_opportunity_view.xml',
|
'wizard/crm_partner_to_opportunity_view.xml',
|
||||||
|
|
||||||
'wizard/crm_forward_to_partner_view.xml',
|
'wizard/crm_forward_to_partner_view.xml',
|
||||||
'wizard/crm_send_email_view.xml',
|
'wizard/crm_send_email_view.xml',
|
||||||
'wizard/crm_email_add_cc_view.xml',
|
'crm_view.xml',
|
||||||
'crm_view.xml',
|
|
||||||
|
|
||||||
'crm_action_rule_view.xml',
|
'crm_action_rule_view.xml',
|
||||||
'crm_lead_view.xml',
|
'crm_lead_view.xml',
|
||||||
'crm_lead_menu.xml',
|
'crm_lead_menu.xml',
|
||||||
'crm_meeting_view.xml',
|
|
||||||
'crm_meeting_menu.xml',
|
'crm_meeting_view.xml',
|
||||||
'crm_phonecall_view.xml',
|
'crm_meeting_menu.xml',
|
||||||
'crm_phonecall_menu.xml',
|
|
||||||
'crm_opportunity_view.xml',
|
'crm_phonecall_view.xml',
|
||||||
'crm_opportunity_menu.xml',
|
'crm_phonecall_menu.xml',
|
||||||
'crm_fund_view.xml',
|
|
||||||
'crm_fund_menu.xml',
|
'crm_opportunity_view.xml',
|
||||||
'crm_claims_view.xml',
|
'crm_opportunity_menu.xml',
|
||||||
'crm_claims_menu.xml',
|
|
||||||
|
'security/crm_security.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
|
||||||
'crm_helpdesk_view.xml',
|
'report/crm_lead_report_view.xml',
|
||||||
'crm_helpdesk_menu.xml',
|
'report/crm_phonecall_report_view.xml',
|
||||||
|
|
||||||
'security/crm_security.xml',
|
'process/crm_configuration_process.xml',
|
||||||
'security/ir.model.access.csv',
|
'crm_installer_view.xml'
|
||||||
|
],
|
||||||
'report/crm_report_view.xml',
|
|
||||||
'report/crm_claim_report_view.xml',
|
|
||||||
'report/crm_lead_report_view.xml',
|
|
||||||
'report/crm_fundraising_report_view.xml',
|
|
||||||
'report/crm_opportunity_report_view.xml' ,
|
|
||||||
'report/crm_phonecall_report_view.xml',
|
|
||||||
|
|
||||||
'process/crm_configuration_process.xml',
|
|
||||||
],
|
|
||||||
'demo_xml': [
|
'demo_xml': [
|
||||||
'crm_demo.xml',
|
'crm_demo.xml',
|
||||||
'crm_claims_demo.xml',
|
'crm_lead_demo.xml',
|
||||||
'crm_fund_demo.xml',
|
'crm_meeting_demo.xml',
|
||||||
'crm_helpdesk_demo.xml',
|
'crm_opportunity_demo.xml',
|
||||||
'crm_lead_demo.xml',
|
|
||||||
'crm_meeting_demo.xml',
|
|
||||||
'crm_opportunity_demo.xml',
|
|
||||||
'crm_phonecall_demo.xml'
|
'crm_phonecall_demo.xml'
|
||||||
],
|
],
|
||||||
'installable': True,
|
'test': [
|
||||||
'active': False,
|
'test/test_crm_lead.yml',
|
||||||
'certificate': '0079056041421',
|
'test/test_crm_meeting.yml',
|
||||||
|
'test/test_crm_opportunity.yml',
|
||||||
|
'test/test_crm_phonecall.yml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'active': False,
|
||||||
|
'certificate': '0079056041421',
|
||||||
}
|
}
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -45,6 +45,273 @@ AVAILABLE_PRIORITIES = [
|
||||||
('5', 'Lowest'),
|
('5', 'Lowest'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class crm_case(object):
|
||||||
|
"""A simple python class to be used for common functions """
|
||||||
|
|
||||||
|
def _get_default_partner_address(self, cr, uid, context):
|
||||||
|
"""Gives id of default address for current user
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
"""
|
||||||
|
if not context.get('portal', False):
|
||||||
|
return False
|
||||||
|
return self.pool.get('res.users').browse(cr, uid, uid, context).address_id.id
|
||||||
|
|
||||||
|
def _get_default_partner(self, cr, uid, context):
|
||||||
|
"""Gives id of partner for current user
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
"""
|
||||||
|
if not context.get('portal', False):
|
||||||
|
return False
|
||||||
|
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||||
|
if not user.address_id:
|
||||||
|
return False
|
||||||
|
return user.address_id.partner_id.id
|
||||||
|
|
||||||
|
def _get_default_email(self, cr, uid, context):
|
||||||
|
"""Gives default email address for current user
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
"""
|
||||||
|
if not context.get('portal', False):
|
||||||
|
return False
|
||||||
|
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
||||||
|
if not user.address_id:
|
||||||
|
return False
|
||||||
|
return user.address_id.email
|
||||||
|
|
||||||
|
def _get_default_user(self, cr, uid, context):
|
||||||
|
"""Gives current user id
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
"""
|
||||||
|
if context.get('portal', False):
|
||||||
|
return False
|
||||||
|
return uid
|
||||||
|
|
||||||
|
def _get_section(self, cr, uid, context):
|
||||||
|
"""Gives section id for current User
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param context: A standard dictionary for contextual values
|
||||||
|
"""
|
||||||
|
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||||
|
return user.context_section_id.id or False
|
||||||
|
|
||||||
|
def stage_next(self, cr, uid, ids, context=None):
|
||||||
|
"""This function computes next stage for case from its current stage
|
||||||
|
using available stage for that case type
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case IDs
|
||||||
|
@param context: A standard dictionary for contextual values"""
|
||||||
|
if not context:
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
s = self.get_stage_dict(cr, uid, ids, context=context)
|
||||||
|
for case in self.browse(cr, uid, ids, context):
|
||||||
|
section = (case.section_id.id or False)
|
||||||
|
if section in s:
|
||||||
|
st = case.stage_id.id or False
|
||||||
|
if st in s[section]:
|
||||||
|
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_stage_dict(self, cr, uid, ids, context=None):
|
||||||
|
"""This function gives dictionary for stage according to stage levels
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case IDs
|
||||||
|
@param context: A standard dictionary for contextual values"""
|
||||||
|
if not context:
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
sid = self.pool.get('crm.case.stage').search(cr, uid, \
|
||||||
|
[('object_id.model', '=', self._name)], context=context)
|
||||||
|
s = {}
|
||||||
|
previous = {}
|
||||||
|
for stage in self.pool.get('crm.case.stage').browse(cr, uid, sid, context=context):
|
||||||
|
section = stage.section_id.id or False
|
||||||
|
s.setdefault(section, {})
|
||||||
|
s[section][previous.get(section, False)] = stage.id
|
||||||
|
previous[section] = stage.id
|
||||||
|
return s
|
||||||
|
|
||||||
|
def stage_previous(self, cr, uid, ids, context=None):
|
||||||
|
"""This function computes previous stage for case from its current stage
|
||||||
|
using available stage for that case type
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case IDs
|
||||||
|
@param context: A standard dictionary for contextual values"""
|
||||||
|
if not context:
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
s = self.get_stage_dict(cr, uid, ids, context=context)
|
||||||
|
for case in self.browse(cr, uid, ids, context):
|
||||||
|
section = (case.section_id.id or False)
|
||||||
|
|
||||||
|
if section in s:
|
||||||
|
st = case.stage_id.id or False
|
||||||
|
s[section] = dict([(v, k) for (k, v) in s[section].iteritems()])
|
||||||
|
if st in s[section]:
|
||||||
|
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
|
||||||
|
return True
|
||||||
|
|
||||||
|
def onchange_partner_id(self, cr, uid, ids, part, email=False):
|
||||||
|
"""This function returns value of partner address based on partner
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case IDs
|
||||||
|
@param part: Partner's id
|
||||||
|
@email: Partner's email ID
|
||||||
|
"""
|
||||||
|
if not part:
|
||||||
|
return {'value': {'partner_address_id': False,
|
||||||
|
'email_from': False,
|
||||||
|
}}
|
||||||
|
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
|
||||||
|
data = {'partner_address_id': addr['contact']}
|
||||||
|
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
|
||||||
|
return {'value': data}
|
||||||
|
|
||||||
|
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
|
||||||
|
"""This function returns value of partner email based on Partner Address
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case IDs
|
||||||
|
@param add: Id of Partner's address
|
||||||
|
@email: Partner's email ID
|
||||||
|
"""
|
||||||
|
if not add:
|
||||||
|
return {'value': {'email_from': False}}
|
||||||
|
address = self.pool.get('res.partner.address').browse(cr, uid, add)
|
||||||
|
return {'value': {'email_from': address.email}}
|
||||||
|
|
||||||
|
def case_open(self, cr, uid, ids, *args):
|
||||||
|
"""Opens Case
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
self._history(cr, uid, cases, _('Open'))
|
||||||
|
for case in cases:
|
||||||
|
data = {'state': 'open', 'active': True}
|
||||||
|
if not case.user_id:
|
||||||
|
data['user_id'] = uid
|
||||||
|
self.write(cr, uid, case.id, data)
|
||||||
|
self._action(cr, uid, cases, 'open')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def case_close(self, cr, uid, ids, *args):
|
||||||
|
"""Closes Case
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
cases[0].state # to fill the browse record cache
|
||||||
|
self._history(cr, uid, cases, _('Close'))
|
||||||
|
self.write(cr, uid, ids, {'state': 'done',
|
||||||
|
'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
})
|
||||||
|
#
|
||||||
|
# We use the cache of cases to keep the old case state
|
||||||
|
#
|
||||||
|
self._action(cr, uid, cases, 'done')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def case_escalate(self, cr, uid, ids, *args):
|
||||||
|
"""Escalates case to top level
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
for case in cases:
|
||||||
|
data = {'active': True, 'user_id': False}
|
||||||
|
|
||||||
|
if case.section_id.parent_id:
|
||||||
|
data['section_id'] = case.section_id.parent_id.id
|
||||||
|
if case.section_id.parent_id.user_id:
|
||||||
|
data['user_id'] = case.section_id.parent_id.user_id.id
|
||||||
|
else:
|
||||||
|
raise osv.except_osv(_('Error !'), _('You can not escalate this case.\nYou are already at the top level.'))
|
||||||
|
self.write(cr, uid, [case.id], data)
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
self._history(cr, uid, cases, _('Escalate'))
|
||||||
|
self._action(cr, uid, cases, 'escalate')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def case_cancel(self, cr, uid, ids, *args):
|
||||||
|
"""Cancels Case
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
cases[0].state # to fill the browse record cache
|
||||||
|
self._history(cr, uid, cases, _('Cancel'))
|
||||||
|
self.write(cr, uid, ids, {'state': 'cancel',
|
||||||
|
'active': True})
|
||||||
|
self._action(cr, uid, cases, 'cancel')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def case_pending(self, cr, uid, ids, *args):
|
||||||
|
"""Marks case as pending
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
cases[0].state # to fill the browse record cache
|
||||||
|
self._history(cr, uid, cases, _('Pending'))
|
||||||
|
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
|
||||||
|
self._action(cr, uid, cases, 'pending')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def case_reset(self, cr, uid, ids, *args):
|
||||||
|
"""Resets case as draft
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
cases = self.browse(cr, uid, ids)
|
||||||
|
cases[0].state # to fill the browse record cache
|
||||||
|
self._history(cr, uid, cases, _('Draft'))
|
||||||
|
self.write(cr, uid, ids, {'state': 'draft', 'active': True})
|
||||||
|
self._action(cr, uid, cases, 'draft')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class crm_case_section(osv.osv):
|
class crm_case_section(osv.osv):
|
||||||
"""Sales Team"""
|
"""Sales Team"""
|
||||||
|
|
||||||
|
@ -59,6 +326,7 @@ class crm_case_section(osv.osv):
|
||||||
true, it will allow you to hide the sales team without removing it."),
|
true, it will allow you to hide the sales team without removing it."),
|
||||||
'allow_unlink': fields.boolean('Allow Delete', help="Allows to delete non draft cases"),
|
'allow_unlink': fields.boolean('Allow Delete', help="Allows to delete non draft cases"),
|
||||||
'user_id': fields.many2one('res.users', 'Responsible User'),
|
'user_id': fields.many2one('res.users', 'Responsible User'),
|
||||||
|
'member_ids':fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'),
|
||||||
'reply_to': fields.char('Reply-To', size=64, help="The email address put \
|
'reply_to': fields.char('Reply-To', size=64, help="The email address put \
|
||||||
in the 'Reply-To' of all emails sent by Open ERP about cases in this sales team"),
|
in the 'Reply-To' of all emails sent by Open ERP about cases in this sales team"),
|
||||||
'parent_id': fields.many2one('crm.case.section', 'Parent Team'),
|
'parent_id': fields.many2one('crm.case.section', 'Parent Team'),
|
||||||
|
@ -78,15 +346,16 @@ class crm_case_section(osv.osv):
|
||||||
]
|
]
|
||||||
|
|
||||||
def _check_recursion(self, cr, uid, ids):
|
def _check_recursion(self, cr, uid, ids):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Checks for recursion level for sections
|
Checks for recursion level for sales team
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param ids: List of section ids
|
@param ids: List of Sales team ids
|
||||||
"""
|
"""
|
||||||
level = 100
|
level = 100
|
||||||
|
|
||||||
while len(ids):
|
while len(ids):
|
||||||
cr.execute('select distinct parent_id from crm_case_section where id =ANY(%s)', (ids,))
|
cr.execute('select distinct parent_id from crm_case_section where id =ANY(%s)', (ids,))
|
||||||
ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
||||||
|
@ -97,7 +366,7 @@ class crm_case_section(osv.osv):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_constraints = [
|
_constraints = [
|
||||||
(_check_recursion, 'Error ! You cannot create recursive sections.', ['parent_id'])
|
(_check_recursion, 'Error ! You cannot create recursive Sales team.', ['parent_id'])
|
||||||
]
|
]
|
||||||
|
|
||||||
def name_get(self, cr, uid, ids, context=None):
|
def name_get(self, cr, uid, ids, context=None):
|
||||||
|
@ -105,7 +374,7 @@ class crm_case_section(osv.osv):
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param ids: List of section ids
|
@param ids: List of sales team ids
|
||||||
"""
|
"""
|
||||||
if not context:
|
if not context:
|
||||||
context = {}
|
context = {}
|
||||||
|
@ -136,8 +405,8 @@ class crm_case_categ(osv.osv):
|
||||||
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||||
'object_id': fields.many2one('ir.model', 'Object Name'),
|
'object_id': fields.many2one('ir.model', 'Object Name'),
|
||||||
}
|
}
|
||||||
def _find_object_id(self, cr, uid, context=None):
|
|
||||||
|
|
||||||
|
def _find_object_id(self, cr, uid, context=None):
|
||||||
"""Finds id for case object
|
"""Finds id for case object
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
|
@ -151,8 +420,8 @@ class crm_case_categ(osv.osv):
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'object_id' : _find_object_id
|
'object_id' : _find_object_id
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
crm_case_categ()
|
crm_case_categ()
|
||||||
|
|
||||||
|
|
||||||
|
@ -238,501 +507,6 @@ def _links_get(self, cr, uid, context=None):
|
||||||
res = obj.read(cr, uid, ids, ['object', 'name'], context)
|
res = obj.read(cr, uid, ids, ['object', 'name'], context)
|
||||||
return [(r['object'], r['name']) for r in res]
|
return [(r['object'], r['name']) for r in res]
|
||||||
|
|
||||||
class crm_case(osv.osv):
|
|
||||||
""" CRM Case """
|
|
||||||
|
|
||||||
_name = "crm.case"
|
|
||||||
_description = "Case"
|
|
||||||
|
|
||||||
def _email_last(self, cursor, user, ids, name, arg, context=None):
|
|
||||||
"""Return last email from History
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case’s IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
res = {}
|
|
||||||
for case in self.browse(cursor, user, ids):
|
|
||||||
if case.history_line:
|
|
||||||
res[case.id] = case.history_line[0].description
|
|
||||||
else:
|
|
||||||
res[case.id] = False
|
|
||||||
return res
|
|
||||||
|
|
||||||
def copy(self, cr, uid, id, default=None, context=None):
|
|
||||||
"""Overrides orm copy method
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case’s IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
if not default:
|
|
||||||
default = {}
|
|
||||||
default.update({'state': 'draft', 'id': False})
|
|
||||||
return super(crm_case, self).copy(cr, uid, id, default, context)
|
|
||||||
|
|
||||||
def _get_log_ids(self, cr, uid, ids, field_names, arg, context=None):
|
|
||||||
"""Gets id for case log from history of particular case
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of Case IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
history_obj = False
|
|
||||||
model_obj = self.pool.get('ir.model')
|
|
||||||
|
|
||||||
if 'history_line' in field_names:
|
|
||||||
history_obj = self.pool.get('crm.case.history')
|
|
||||||
name = 'history_line'
|
|
||||||
|
|
||||||
if 'log_ids' in field_names:
|
|
||||||
history_obj = self.pool.get('crm.case.log')
|
|
||||||
name = 'log_ids'
|
|
||||||
|
|
||||||
if not history_obj:
|
|
||||||
return result
|
|
||||||
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
model_ids = model_obj.search(cr, uid, [('model', '=', case._name)])
|
|
||||||
history_ids = history_obj.search(cr, uid, [('model_id', '=', model_ids[0]), \
|
|
||||||
('res_id', '=', case.id)])
|
|
||||||
if history_ids:
|
|
||||||
result[case.id] = {name: history_ids}
|
|
||||||
else:
|
|
||||||
result[case.id] = {name: []}
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'id': fields.integer('ID', readonly=True),
|
|
||||||
'name': fields.char('Description', size=1024, required=True),
|
|
||||||
'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the case without removing it."),
|
|
||||||
'description': fields.text('Description'),
|
|
||||||
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
|
|
||||||
select=True, help='Sales team to which Case belongs to.\
|
|
||||||
Define Responsible user and Email account for mail gateway.'),
|
|
||||||
'email_from': fields.char('Email', size=128, help="These people will receive email."),
|
|
||||||
'email_cc': fields.text('Watchers Emails', size=252 , help="These people\
|
|
||||||
will receive a copy of the future" \
|
|
||||||
" communication between partner and users by email"),
|
|
||||||
'probability': fields.float('Probability'),
|
|
||||||
'email_last': fields.function(_email_last, method=True,
|
|
||||||
string='Latest E-Mail', type='text'),
|
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner'),
|
|
||||||
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
|
|
||||||
domain="[('partner_id','=',partner_id)]"),
|
|
||||||
'create_date': fields.datetime('Creation Date' , readonly=True),
|
|
||||||
'write_date': fields.datetime('Update Date' , readonly=True),
|
|
||||||
'date_deadline': fields.date('Deadline'),
|
|
||||||
'user_id': fields.many2one('res.users', 'Responsible'),
|
|
||||||
'history_line': fields.function(_get_log_ids, method=True, type='one2many', \
|
|
||||||
multi="history_line", relation="crm.case.history", string="Communication"),
|
|
||||||
'log_ids': fields.function(_get_log_ids, method=True, type='one2many', \
|
|
||||||
multi="log_ids", relation="crm.case.log", string="Logs History"),
|
|
||||||
'stage_id': fields.many2one ('crm.case.stage', 'Stage', \
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.opportunity')]"),
|
|
||||||
'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True,
|
|
||||||
help='The state is set to \'Draft\', when a case is created.\
|
|
||||||
\nIf the case is in progress the state is set to \'Open\'.\
|
|
||||||
\nWhen the case is over, the state is set to \'Done\'.\
|
|
||||||
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
|
|
||||||
'company_id': fields.many2one('res.company', 'Company'),
|
|
||||||
}
|
|
||||||
def _get_default_partner_address(self, cr, uid, context):
|
|
||||||
"""Gives id of default address for current user
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context.get('portal', False):
|
|
||||||
return False
|
|
||||||
return self.pool.get('res.users').browse(cr, uid, uid, context).address_id.id
|
|
||||||
|
|
||||||
def _get_default_partner(self, cr, uid, context):
|
|
||||||
"""Gives id of partner for current user
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context.get('portal', False):
|
|
||||||
return False
|
|
||||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
|
||||||
if not user.address_id:
|
|
||||||
return False
|
|
||||||
return user.address_id.partner_id.id
|
|
||||||
|
|
||||||
def _get_default_email(self, cr, uid, context):
|
|
||||||
"""Gives default email address for current user
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context.get('portal', False):
|
|
||||||
return False
|
|
||||||
user = self.pool.get('res.users').browse(cr, uid, uid, context)
|
|
||||||
if not user.address_id:
|
|
||||||
return False
|
|
||||||
return user.address_id.email
|
|
||||||
|
|
||||||
def _get_default_user(self, cr, uid, context):
|
|
||||||
"""Gives current user id
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if context.get('portal', False):
|
|
||||||
return False
|
|
||||||
return uid
|
|
||||||
|
|
||||||
def _get_section(self, cr, uid, context):
|
|
||||||
"""Gives section id for current User
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
||||||
return user.context_section_id.id or False
|
|
||||||
|
|
||||||
_defaults = {
|
|
||||||
'active': lambda *a: 1,
|
|
||||||
'user_id': _get_default_user,
|
|
||||||
'partner_id': _get_default_partner,
|
|
||||||
'partner_address_id': _get_default_partner_address,
|
|
||||||
'email_from': _get_default_email,
|
|
||||||
'state': lambda *a: 'draft',
|
|
||||||
'section_id': _get_section,
|
|
||||||
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
|
|
||||||
}
|
|
||||||
|
|
||||||
_order = 'date_deadline desc, create_date desc,id desc'
|
|
||||||
|
|
||||||
def unlink(self, cr, uid, ids, context=None):
|
|
||||||
"""Overrides orm unlink method
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
hist_obj = self.pool.get('crm.case.history')
|
|
||||||
log_obj = self.pool.get('crm.case.log')
|
|
||||||
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
if (not case.section_id.allow_unlink) and (case.state <> 'draft'):
|
|
||||||
raise osv.except_osv(_('Warning !'),
|
|
||||||
_('You can not delete this case. You should better cancel it.'))
|
|
||||||
|
|
||||||
# Also removing history and logs
|
|
||||||
history_ids = map(lambda x: x.id, case.history_line)
|
|
||||||
log_ids = map(lambda x: x.id, case.log_ids)
|
|
||||||
hist_obj.unlink(cr, uid, history_ids, context=context)
|
|
||||||
log_obj.unlink(cr, uid, log_ids, context=context)
|
|
||||||
return super(crm_case, self).unlink(cr, uid, ids, context)
|
|
||||||
|
|
||||||
def stage_next(self, cr, uid, ids, context=None):
|
|
||||||
"""This function computes next stage for case from its current stage
|
|
||||||
using available stage for that case type
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param context: A standard dictionary for contextual values"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
s = self.get_stage_dict(cr, uid, ids, context=context)
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
section = (case.section_id.id or False)
|
|
||||||
if section in s:
|
|
||||||
st = case.stage_id.id or False
|
|
||||||
if st in s[section]:
|
|
||||||
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_stage_dict(self, cr, uid, ids, context=None):
|
|
||||||
"""This function gives dictionary for stage according to stage levels
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param context: A standard dictionary for contextual values"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
sid = self.pool.get('crm.case.stage').search(cr, uid, \
|
|
||||||
[('object_id.model', '=', self._name)], context=context)
|
|
||||||
s = {}
|
|
||||||
previous = {}
|
|
||||||
for stage in self.pool.get('crm.case.stage').browse(cr, uid, sid, context=context):
|
|
||||||
section = stage.section_id.id or False
|
|
||||||
s.setdefault(section, {})
|
|
||||||
s[section][previous.get(section, False)] = stage.id
|
|
||||||
previous[section] = stage.id
|
|
||||||
return s
|
|
||||||
|
|
||||||
def stage_previous(self, cr, uid, ids, context=None):
|
|
||||||
"""This function computes previous stage for case from its current stage
|
|
||||||
using available stage for that case type
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param context: A standard dictionary for contextual values"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
s = self.get_stage_dict(cr, uid, ids, context=context)
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
section = (case.section_id.id or False)
|
|
||||||
|
|
||||||
if section in s:
|
|
||||||
st = case.stage_id.id or False
|
|
||||||
s[section] = dict([(v, k) for (k, v) in s[section].iteritems()])
|
|
||||||
if st in s[section]:
|
|
||||||
self.write(cr, uid, [case.id], {'stage_id': s[section][st]})
|
|
||||||
return True
|
|
||||||
|
|
||||||
def __history(self, cr, uid, cases, keyword, history=False, email=False, details=None, email_from=False, message_id=False, context={}):
|
|
||||||
"""
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param cases: a browse record list
|
|
||||||
@param keyword: Case action keyword e.g.: If case is closed "Close" keyword is used
|
|
||||||
@param history: Value True/False, If True it makes entry in case History otherwise in Case Log
|
|
||||||
@param email: Email address if any
|
|
||||||
@param details: Details of case history if any
|
|
||||||
@param context: A standard dictionary for contextual values"""
|
|
||||||
if not context:
|
|
||||||
context = {}
|
|
||||||
|
|
||||||
# The mailgate sends the ids of the cases and not the object list
|
|
||||||
if all(isinstance(case_id, (int, long)) for case_id in cases) and context.get('model'):
|
|
||||||
cases = self.pool.get(context['model']).browse(cr, uid, cases, context=context)
|
|
||||||
|
|
||||||
model_obj = self.pool.get('ir.model')
|
|
||||||
obj = self.pool.get('crm.case.log')
|
|
||||||
for case in cases:
|
|
||||||
model_ids = model_obj.search(cr, uid, [('model', '=', case._name)])
|
|
||||||
data = {
|
|
||||||
'name': keyword,
|
|
||||||
'user_id': uid,
|
|
||||||
'date': time.strftime('%Y-%m-%d %H:%M:%S'),
|
|
||||||
'model_id' : model_ids and model_ids[0] or False,
|
|
||||||
'res_id': case.id,
|
|
||||||
'section_id': case.section_id.id,
|
|
||||||
'message_id':message_id
|
|
||||||
}
|
|
||||||
|
|
||||||
if history:
|
|
||||||
obj = self.pool.get('crm.case.history')
|
|
||||||
data['description'] = details or case.description
|
|
||||||
data['email_to'] = email or \
|
|
||||||
(case.section_id and case.section_id.reply_to) or \
|
|
||||||
(case.user_id and case.user_id.address_id and \
|
|
||||||
case.user_id.address_id.email) or tools.config.get('email_from', False)
|
|
||||||
data['email_from'] = email_from or \
|
|
||||||
(case.section_id and case.section_id.reply_to) or \
|
|
||||||
(case.user_id and case.user_id.address_id and \
|
|
||||||
case.user_id.address_id.email) or tools.config.get('email_from', False)
|
|
||||||
res = obj.create(cr, uid, data, context)
|
|
||||||
return True
|
|
||||||
|
|
||||||
_history = __history
|
|
||||||
history = __history
|
|
||||||
|
|
||||||
def create(self, cr, uid, *args, **argv):
|
|
||||||
"""Overrides orm create method
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
@param **argv: Dictionay of Keyword Params
|
|
||||||
"""
|
|
||||||
res = super(crm_case, self).create(cr, uid, *args, **argv)
|
|
||||||
cases = self.browse(cr, uid, [res])
|
|
||||||
cases[0].state # to fill the browse record cache
|
|
||||||
self._action(cr, uid, cases, 'draft')
|
|
||||||
return res
|
|
||||||
|
|
||||||
def add_reply(self, cursor, user, ids, context=None):
|
|
||||||
"""This function finds last email and gives its description value for reply mail
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cursor: the current row, from the database cursor,
|
|
||||||
@param user: the current user’s ID for security checks
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
for case in self.browse(cursor, user, ids, context=context):
|
|
||||||
if case.email_last:
|
|
||||||
description = case.email_last
|
|
||||||
self.write(cursor, user, case.id, {
|
|
||||||
'description': '> ' + description.replace('\n', '\n> '),
|
|
||||||
}, context=context)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def onchange_partner_id(self, cr, uid, ids, part, email=False):
|
|
||||||
"""This function returns value of partner address based on partner
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param part: Partner's id
|
|
||||||
@email: Partner's email ID
|
|
||||||
"""
|
|
||||||
if not part:
|
|
||||||
return {'value': {'partner_address_id': False,
|
|
||||||
'email_from': False,
|
|
||||||
}}
|
|
||||||
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
|
|
||||||
data = {'partner_address_id': addr['contact']}
|
|
||||||
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
|
|
||||||
return {'value': data}
|
|
||||||
|
|
||||||
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
|
|
||||||
"""This function returns value of partner email based on Partner Address
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case IDs
|
|
||||||
@param add: Id of Partner's address
|
|
||||||
@email: Partner's email ID
|
|
||||||
"""
|
|
||||||
if not add:
|
|
||||||
return {'value': {'email_from': False}}
|
|
||||||
address = self.pool.get('res.partner.address').browse(cr, uid, add)
|
|
||||||
return {'value': {'email_from': address.email}}
|
|
||||||
|
|
||||||
def case_close(self, cr, uid, ids, *args):
|
|
||||||
"""Closes Case
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
cases[0].state # to fill the browse record cache
|
|
||||||
self.__history(cr, uid, cases, _('Close'))
|
|
||||||
self.write(cr, uid, ids, {'state': 'done',
|
|
||||||
'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'),
|
|
||||||
'probability' : 100.0})
|
|
||||||
#
|
|
||||||
# We use the cache of cases to keep the old case state
|
|
||||||
#
|
|
||||||
self._action(cr, uid, cases, 'done')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def case_escalate(self, cr, uid, ids, *args):
|
|
||||||
"""Escalates case to top level
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
for case in cases:
|
|
||||||
data = {'active': True, 'user_id': False}
|
|
||||||
|
|
||||||
if case.section_id.parent_id:
|
|
||||||
data['section_id'] = case.section_id.parent_id.id
|
|
||||||
if case.section_id.parent_id.user_id:
|
|
||||||
data['user_id'] = case.section_id.parent_id.user_id.id
|
|
||||||
else:
|
|
||||||
raise osv.except_osv(_('Error !'), _('You can not escalate this case.\nYou are already at the top level.'))
|
|
||||||
self.write(cr, uid, [case.id], data)
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
self.__history(cr, uid, cases, _('Escalate'))
|
|
||||||
self._action(cr, uid, cases, 'escalate')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def case_open(self, cr, uid, ids, *args):
|
|
||||||
"""Opens Case
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
self.__history(cr, uid, cases, _('Open'))
|
|
||||||
|
|
||||||
for case in cases:
|
|
||||||
data = {'state': 'open', 'active': True}
|
|
||||||
if not case.user_id:
|
|
||||||
data['user_id'] = uid
|
|
||||||
self.write(cr, uid, case.id, data)
|
|
||||||
self._action(cr, uid, cases, 'open')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def case_cancel(self, cr, uid, ids, *args):
|
|
||||||
"""Cancels Case
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
cases[0].state # to fill the browse record cache
|
|
||||||
self.__history(cr, uid, cases, _('Cancel'))
|
|
||||||
self.write(cr, uid, ids, {'state': 'cancel',
|
|
||||||
'active': True,
|
|
||||||
'probability' : 0.0})
|
|
||||||
self._action(cr, uid, cases, 'cancel')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def case_pending(self, cr, uid, ids, *args):
|
|
||||||
"""Marks case as pending
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
cases[0].state # to fill the browse record cache
|
|
||||||
self.__history(cr, uid, cases, _('Pending'))
|
|
||||||
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
|
|
||||||
self._action(cr, uid, cases, 'pending')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def case_reset(self, cr, uid, ids, *args):
|
|
||||||
"""Resets case as draft
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case Ids
|
|
||||||
@param *args: Tuple Value for additional Params
|
|
||||||
"""
|
|
||||||
cases = self.browse(cr, uid, ids)
|
|
||||||
cases[0].state # to fill the browse record cache
|
|
||||||
self.__history(cr, uid, cases, _('Draft'))
|
|
||||||
self.write(cr, uid, ids, {'state': 'draft', 'active': True})
|
|
||||||
self._action(cr, uid, cases, 'draft')
|
|
||||||
return True
|
|
||||||
|
|
||||||
crm_case()
|
|
||||||
|
|
||||||
|
|
||||||
class crm_case_log(osv.osv):
|
class crm_case_log(osv.osv):
|
||||||
""" Case Communication History """
|
""" Case Communication History """
|
||||||
|
@ -743,7 +517,7 @@ class crm_case_log(osv.osv):
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Status', size=64),
|
'name': fields.char('Status', size=64),
|
||||||
'date': fields.datetime('Date'),
|
'date': fields.datetime('Date'),
|
||||||
'section_id': fields.many2one('crm.case.section', 'Section'),
|
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||||
'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
|
'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
|
||||||
'model_id': fields.many2one('ir.model', "Model"),
|
'model_id': fields.many2one('ir.model', "Model"),
|
||||||
'res_id': fields.integer('Resource ID'),
|
'res_id': fields.integer('Resource ID'),
|
||||||
|
|
|
@ -37,7 +37,7 @@ import crm
|
||||||
class case(osv.osv):
|
class case(osv.osv):
|
||||||
""" Case """
|
""" Case """
|
||||||
|
|
||||||
_inherit = 'crm.case'
|
_inherit = 'mailgate.thread'
|
||||||
_description = 'case'
|
_description = 'case'
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
@ -89,7 +89,7 @@ class case(osv.osv):
|
||||||
attach_to_send = None
|
attach_to_send = None
|
||||||
|
|
||||||
if attach:
|
if attach:
|
||||||
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', 'crm.case'), ('res_id', '=', case.id)])
|
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', 'mailgate.thread'), ('res_id', '=', case.id)])
|
||||||
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname','datas'])
|
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname','datas'])
|
||||||
attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
|
attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
|
||||||
|
|
||||||
|
@ -152,6 +152,18 @@ class base_action_rule(osv.osv):
|
||||||
""" Base Action Rule """
|
""" Base Action Rule """
|
||||||
_inherit = 'base.action.rule'
|
_inherit = 'base.action.rule'
|
||||||
_description = 'Action Rules'
|
_description = 'Action Rules'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'trg_section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
||||||
|
'trg_max_history': fields.integer('Maximum Communication History'),
|
||||||
|
'trg_categ_id': fields.many2one('crm.case.categ', 'Category'),
|
||||||
|
'regex_history' : fields.char('Regular Expression on Case History', size=128),
|
||||||
|
'act_section_id': fields.many2one('crm.case.section', 'Set Team to'),
|
||||||
|
'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'),
|
||||||
|
'act_mail_to_partner': fields.boolean('Mail to partner',help="Check \
|
||||||
|
this if you want the rule to send an email to the partner."),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
|
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
|
||||||
body = self.format_mail(obj, body)
|
body = self.format_mail(obj, body)
|
||||||
|
@ -183,16 +195,18 @@ class base_action_rule(osv.osv):
|
||||||
ok = ok and (not action.trg_section_id or action.trg_section_id.id==obj.section_id.id)
|
ok = ok and (not action.trg_section_id or action.trg_section_id.id==obj.section_id.id)
|
||||||
if hasattr(obj, 'categ_id'):
|
if hasattr(obj, 'categ_id'):
|
||||||
ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==obj.categ_id.id)
|
ok = ok and (not action.trg_categ_id or action.trg_categ_id.id==obj.categ_id.id)
|
||||||
if hasattr(obj, 'history_line'):
|
|
||||||
ok = ok and (not action.trg_max_history or action.trg_max_history<=(len(obj.history_line)+1))
|
# TODO: history_line is removed
|
||||||
reg_history = action.regex_history
|
# if hasattr(obj, 'history_line'):
|
||||||
result_history = True
|
# ok = ok and (not action.trg_max_history or action.trg_max_history<=(len(obj.history_line)+1))
|
||||||
if reg_history:
|
# reg_history = action.regex_history
|
||||||
ptrn = re.compile(str(reg_history))
|
# result_history = True
|
||||||
if obj.history_line:
|
# if reg_history:
|
||||||
_result = ptrn.search(str(obj.history_line[0].description))
|
# ptrn = re.compile(str(reg_history))
|
||||||
if not _result:
|
# if obj.history_line:
|
||||||
result_history = False
|
# _result = ptrn.search(str(obj.history_line[0].description))
|
||||||
|
# if not _result:
|
||||||
|
# result_history = False
|
||||||
regex_h = not reg_history or result_history
|
regex_h = not reg_history or result_history
|
||||||
ok = ok and regex_h
|
ok = ok and regex_h
|
||||||
return ok
|
return ok
|
||||||
|
@ -207,7 +221,7 @@ class base_action_rule(osv.osv):
|
||||||
res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)
|
res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)
|
||||||
write = {}
|
write = {}
|
||||||
|
|
||||||
if hasattr(action, act_section_id) and action.act_section_id:
|
if hasattr(action, 'act_section_id') and action.act_section_id:
|
||||||
obj.section_id = action.act_section_id
|
obj.section_id = action.act_section_id
|
||||||
write['section_id'] = action.act_section_id.id
|
write['section_id'] = action.act_section_id.id
|
||||||
|
|
||||||
|
@ -231,13 +245,6 @@ class base_action_rule(osv.osv):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
base_action_rule()
|
|
||||||
|
|
||||||
class base_action_rule_line(osv.osv):
|
|
||||||
""" Base Action Rule Line """
|
|
||||||
_inherit = 'base.action.rule.line'
|
|
||||||
_description = 'Base Action Rule Line'
|
|
||||||
|
|
||||||
def state_get(self, cr, uid, context={}):
|
def state_get(self, cr, uid, context={}):
|
||||||
|
|
||||||
"""@param self: The object pointer
|
"""@param self: The object pointer
|
||||||
|
@ -245,7 +252,7 @@ class base_action_rule_line(osv.osv):
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param context: A standard dictionary for contextual values """
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
res = super(base_action_rule_line, self).state_get(cr, uid, context=context)
|
res = super(base_action_rule, self).state_get(cr, uid, context=context)
|
||||||
return res + [('escalate','Escalate')] + crm.AVAILABLE_STATES
|
return res + [('escalate','Escalate')] + crm.AVAILABLE_STATES
|
||||||
|
|
||||||
def priority_get(self, cr, uid, context={}):
|
def priority_get(self, cr, uid, context={}):
|
||||||
|
@ -255,18 +262,10 @@ class base_action_rule_line(osv.osv):
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param context: A standard dictionary for contextual values """
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
res = super(base_action_rule_line, self).priority_get(cr, uid, context=context)
|
res = super(base_action_rule, self).priority_get(cr, uid, context=context)
|
||||||
return res + crm.AVAILABLE_PRIORITIES
|
return res + crm.AVAILABLE_PRIORITIES
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'trg_section_id': fields.many2one('crm.case.section', 'Sales Team'),
|
|
||||||
'trg_max_history': fields.integer('Maximum Communication History'),
|
|
||||||
'trg_categ_id': fields.many2one('crm.case.categ', 'Category'),
|
|
||||||
'regex_history' : fields.char('Regular Expression on Case History', size=128),
|
|
||||||
'act_section_id': fields.many2one('crm.case.section', 'Set Team to'),
|
|
||||||
'act_categ_id': fields.many2one('crm.case.categ', 'Set Category to'),
|
|
||||||
'act_mail_to_partner': fields.boolean('Mail to partner',help="Check this \
|
|
||||||
if you want the rule to send an email to the partner."),
|
|
||||||
}
|
|
||||||
|
|
||||||
base_action_rule_line()
|
base_action_rule()
|
||||||
|
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
<data>
|
<data>
|
||||||
<!-- Case rules -->
|
<!-- Case rules -->
|
||||||
<record id="view_base_action_rule_line_form1" model="ir.ui.view">
|
<record id="view_base_action_rule_line_form1" model="ir.ui.view">
|
||||||
<field name="name">base.action.rule.line.form.inherit</field>
|
<field name="name">base.action.rule.form.inherit</field>
|
||||||
<field name="model">base.action.rule.line</field>
|
<field name="model">base.action.rule</field>
|
||||||
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_line_form"/>
|
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<group name="partner" position="after">
|
<group name="partner" position="after">
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_base_action_rule_line_form2" model="ir.ui.view">
|
<record id="view_base_action_rule_line_form2" model="ir.ui.view">
|
||||||
<field name="name">base.action.rule.line.form2.inherit</field>
|
<field name="name">base.action.rule.form2.inherit</field>
|
||||||
<field name="model">base.action.rule.line</field>
|
<field name="model">base.action.rule</field>
|
||||||
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_line_form"/>
|
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<group name="partner" position="after">
|
<group name="partner" position="after">
|
||||||
|
@ -35,17 +35,29 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_base_action_rule_line_form3" model="ir.ui.view">
|
<record id="view_base_action_rule_line_form3" model="ir.ui.view">
|
||||||
<field name="name">base.action.rule.line.form3.inherit</field>
|
<field name="name">base.action.rule.form3.inherit</field>
|
||||||
<field name="model">base.action.rule.line</field>
|
<field name="model">base.action.rule</field>
|
||||||
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_line_form"/>
|
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="act_user_id" position="after">
|
<field name="act_user_id" position="after">
|
||||||
<field name="act_section_id"/>
|
<field name="act_section_id"/>
|
||||||
<field name="act_categ_id"/>
|
<field name="act_categ_id"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_base_action_rule_line_form4" model="ir.ui.view">
|
||||||
|
<field name="name">base.action.rule.form4.inherit</field>
|
||||||
|
<field name="model">base.action.rule</field>
|
||||||
|
<field name="inherit_id" ref="base_action_rule.view_base_action_rule_form"/>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="act_mail_to_watchers" position="before">
|
||||||
<field name="act_mail_to_partner"/>
|
<field name="act_mail_to_partner"/>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from osv import fields, osv
|
|
||||||
import crm
|
|
||||||
|
|
||||||
class crm_claim(osv.osv):
|
|
||||||
"""
|
|
||||||
Crm claim
|
|
||||||
"""
|
|
||||||
_name = "crm.claim"
|
|
||||||
_description = "Claim Cases"
|
|
||||||
_order = "id desc"
|
|
||||||
_inherit = 'crm.case'
|
|
||||||
_columns = {
|
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
|
||||||
'date': fields.datetime('Date'),
|
|
||||||
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
|
|
||||||
'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128),
|
|
||||||
'canal_id': fields.many2one('res.partner.canal', 'Channel',\
|
|
||||||
help="The channels represent the different communication\
|
|
||||||
modes available with the customer." \
|
|
||||||
" With each commercial opportunity, you can indicate the\
|
|
||||||
canall which is this opportunity source."),
|
|
||||||
'planned_revenue': fields.float('Planned Revenue'),
|
|
||||||
'planned_cost': fields.float('Planned Costs'),
|
|
||||||
'som': fields.many2one('res.partner.som', 'State of Mind', \
|
|
||||||
help="The minds states allow to define a value scale which represents" \
|
|
||||||
"the partner mentality in relation to our services.The scale has" \
|
|
||||||
"to be created with a factor for each level from 0 (Very dissatisfied) \
|
|
||||||
to 10 (Extremely satisfied)."),
|
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]"),
|
|
||||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
|
||||||
'type_id': fields.many2one('crm.case.resource.type', 'Claim Type',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]"),
|
|
||||||
|
|
||||||
'partner_name': fields.char("Employee's Name", size=64),
|
|
||||||
'partner_mobile': fields.char('Mobile', size=32),
|
|
||||||
'partner_phone': fields.char('Phone', size=32),
|
|
||||||
'stage_id': fields.many2one ('crm.case.stage', 'Stage',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]"),
|
|
||||||
'probability': fields.float('Probability (%)'),
|
|
||||||
}
|
|
||||||
|
|
||||||
_defaults = {
|
|
||||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
|
||||||
}
|
|
||||||
|
|
||||||
crm_claim()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -2,11 +2,6 @@
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<record id="req_link_case" model="res.request.link">
|
|
||||||
<field name="name">Case</field>
|
|
||||||
<field name="object">crm.case</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="event_type_case_create" model="res.partner.event.type">
|
<record id="event_type_case_create" model="res.partner.event.type">
|
||||||
<field name="name">CRM: Create Case</field>
|
<field name="name">CRM: Create Case</field>
|
||||||
<field name="key">crm_case_draft</field>
|
<field name="key">crm_case_draft</field>
|
||||||
|
|
|
@ -1,296 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- Fund Raising Categories Form View -->
|
|
||||||
|
|
||||||
<record id="crm_fund_categ_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Fundraising Categories</field>
|
|
||||||
<field name="res_model">crm.case.categ</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_id" ref="crm.crm_case_categ_tree-view"/>
|
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.fundraising')]</field>
|
|
||||||
<field name="context">{'object_id':'crm.fundraising'}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem action="crm_fund_categ_action"
|
|
||||||
id="menu_crm_case_fundraising-act"
|
|
||||||
groups="base.group_extended"
|
|
||||||
parent="crm.menu_crm_case_categ" />
|
|
||||||
|
|
||||||
<!-- Fund Stage Form View -->
|
|
||||||
|
|
||||||
<record id="crm_fundraising_stage_act" model="ir.actions.act_window">
|
|
||||||
<field name="name">Fundraising Stages</field>
|
|
||||||
<field name="res_model">crm.case.stage</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_id" ref="crm.crm_case_stage_tree"/>
|
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.fundraising')]</field>
|
|
||||||
<field name="context">{'object_id':'crm.fundraising'}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem action="crm_fundraising_stage_act"
|
|
||||||
groups="base.group_extended"
|
|
||||||
id="menu_crm_fundraising_stage_act"
|
|
||||||
parent="crm.menu_crm_case_stage" />
|
|
||||||
|
|
||||||
<!-- Fund Resource Type Form View -->
|
|
||||||
|
|
||||||
<record id="crm_fundraising_resource_act" model="ir.actions.act_window">
|
|
||||||
<field name="name">Fundraising Resource Type</field>
|
|
||||||
<field name="res_model">crm.case.resource.type</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_id" ref="crm.crm_case_resource_type_tree"/>
|
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.fundraising')]</field>
|
|
||||||
<field name="context">{'object_id':'crm.fundraising'}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem action="crm_fundraising_resource_act"
|
|
||||||
groups="base.group_extended"
|
|
||||||
id="menu_crm_fundraising_resource_act"
|
|
||||||
parent="crm.menu_crm_case_resource_type" />
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Fund Raising Tree View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_tree_view_fund">
|
|
||||||
<field name="name">CRM - Funds Tree</field>
|
|
||||||
<field name="model">crm.fundraising</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Funds Tree"
|
|
||||||
colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')">
|
|
||||||
<field name="id" />
|
|
||||||
<field name="name" string="Fund Description" />
|
|
||||||
<field name="categ_id" />
|
|
||||||
<field name="type_id" string="Payment Mode" />
|
|
||||||
<field name="planned_cost" string="Amount" />
|
|
||||||
<field name="probability" />
|
|
||||||
<field name="user_id" />
|
|
||||||
<field name="state" />
|
|
||||||
<button name="case_close" string="Done"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
icon="gtk-close" />
|
|
||||||
<button name="case_open" string="Open"
|
|
||||||
states="draft,pending" type="object"
|
|
||||||
icon="gtk-go-forward" />
|
|
||||||
<button name="case_cancel" string="Cancel"
|
|
||||||
states="draft,open,pending" type="object"
|
|
||||||
icon="gtk-cancel" />
|
|
||||||
<button name="case_pending" string="Pending"
|
|
||||||
states="draft,open" type="object" icon="gtk-media-pause" />
|
|
||||||
<button name="case_escalate" string="Escalate"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
groups="base.group_extended"
|
|
||||||
icon="gtk-go-up" />
|
|
||||||
<button name="case_reset" string="Reset to Draft"
|
|
||||||
states="done,cancel" type="object" icon="gtk-convert" />
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fund Raising Form View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_form_view_fund">
|
|
||||||
<field name="name">CRM - Funds Form</field>
|
|
||||||
<field name="model">crm.fundraising</field>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Funds Form">
|
|
||||||
<group colspan="4" col="4">
|
|
||||||
<field name="name" select="1" string="Name"/>
|
|
||||||
<field name="type_id" string="Payment Mode" select="1" widget="selection"
|
|
||||||
domain="[('object_id.model', '=', 'crm.fundraising')]" />
|
|
||||||
</group>
|
|
||||||
<notebook colspan="4">
|
|
||||||
<page string="Funds">
|
|
||||||
<separator colspan="4" string="Communication"/>
|
|
||||||
<group colspan="4" col="4">
|
|
||||||
<field name="partner_id" select="1"
|
|
||||||
on_change="onchange_partner_id(partner_id, email_from)"
|
|
||||||
colspan="2" />
|
|
||||||
<field name="partner_address_id"
|
|
||||||
string="Contact"
|
|
||||||
on_change="onchange_partner_address_id(partner_address_id, email_from)"
|
|
||||||
colspan="1" />
|
|
||||||
<field name="email_from" colspan="2"/>
|
|
||||||
</group>
|
|
||||||
<separator colspan="4" string="Estimates"/>
|
|
||||||
<field name="planned_cost"/>
|
|
||||||
<field name="planned_revenue"/>
|
|
||||||
<field name="probability"/>
|
|
||||||
<separator colspan="4" string="Categorization"/>
|
|
||||||
<group colspan="4" col="6">
|
|
||||||
<field name="user_id" string="Responsible"/>
|
|
||||||
<field name="section_id" colspan="1" widget="selection"/>
|
|
||||||
<field name="categ_id" select="1"
|
|
||||||
widget="selection"
|
|
||||||
domain="[('object_id.model', '=', 'crm.fundraising')]" />
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="duration"/>
|
|
||||||
</group>
|
|
||||||
<separator colspan="4" string="Notes"/>
|
|
||||||
<field name="description" nolabel="1" colspan="4"/>
|
|
||||||
<separator colspan="4"/>
|
|
||||||
<group col="8" colspan="4">
|
|
||||||
<field name="state"/>
|
|
||||||
<button name="case_close" string="Done"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
icon="gtk-close" />
|
|
||||||
<button name="case_open" string="Open"
|
|
||||||
states="draft,pending" type="object"
|
|
||||||
icon="gtk-go-forward" />
|
|
||||||
<button name="case_cancel" string="Cancel"
|
|
||||||
states="draft,open,pending" type="object"
|
|
||||||
icon="gtk-cancel" />
|
|
||||||
<button name="case_pending" string="Pending"
|
|
||||||
states="draft,open" type="object"
|
|
||||||
icon="gtk-media-pause" />
|
|
||||||
<button name="case_escalate" string="Escalate"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
groups="base.group_extended"
|
|
||||||
icon="gtk-go-up" />
|
|
||||||
<button name="case_reset"
|
|
||||||
string="Reset to Draft" states="done,cancel"
|
|
||||||
type="object" icon="gtk-convert" />
|
|
||||||
</group>
|
|
||||||
</page>
|
|
||||||
<page string="History" groups="base.group_extended">
|
|
||||||
<field name="id" select="1"/>
|
|
||||||
<field name="active"/>
|
|
||||||
<field name="priority" string="Priority"/>
|
|
||||||
<newline/>
|
|
||||||
<field name="canal_id"/>
|
|
||||||
<field name="som"/>
|
|
||||||
<separator colspan="4" string="Dates"/>
|
|
||||||
<field name="create_date"/>
|
|
||||||
<field name="date_closed"/>
|
|
||||||
<separator colspan="4" string="References"/>
|
|
||||||
<field name="ref" colspan="4"/>
|
|
||||||
<field name="ref2" colspan="4"/>
|
|
||||||
|
|
||||||
<field name="log_ids" nolabel="1" colspan="4">
|
|
||||||
<form string="Actions">
|
|
||||||
<separator string="Action Information" colspan="4"/>
|
|
||||||
<field name="name" colspan="4"/>
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="user_id"/>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
<page string="Emails" groups="base.group_extended">
|
|
||||||
<group colspan="4">
|
|
||||||
<field colspan="4" name="email_cc" string="CC"/>
|
|
||||||
</group>
|
|
||||||
<field name="history_line" colspan="4" nolabel="1" mode="form,tree">
|
|
||||||
<form string="Communication history">
|
|
||||||
<group col="7" colspan="4">
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="email_to"/>
|
|
||||||
<field name="email_from"/>
|
|
||||||
<button
|
|
||||||
string="Add a CC"
|
|
||||||
name="%(crm.action_view_crm_email_add_cc_wizard)d"
|
|
||||||
icon="gtk-add" type="action"/>
|
|
||||||
</group>
|
|
||||||
<newline/>
|
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
|
||||||
<button colspan="4"
|
|
||||||
string="Reply to Last Email"
|
|
||||||
name="%(action_crm_send_mail)d"
|
|
||||||
context="{'mail':'reply', 'model': 'crm.fundraising'}"
|
|
||||||
icon="gtk-undo" type="action" />
|
|
||||||
</form>
|
|
||||||
<tree string="Communication history">
|
|
||||||
<field name="description"/>
|
|
||||||
<field name="email_to"/>
|
|
||||||
<field name="date"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<button colspan="4" string="Send New Email"
|
|
||||||
name="%(action_crm_send_mail)d"
|
|
||||||
context="{'mail':'new', 'model': 'crm.fundraising'}"
|
|
||||||
icon="gtk-go-forward" type="action" />
|
|
||||||
</page>
|
|
||||||
</notebook>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fund Raising Calendar View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_calendar_view_fund">
|
|
||||||
<field name="name">CRM - Funds Calendar</field>
|
|
||||||
<field name="model">crm.fundraising</field>
|
|
||||||
<field name="type">calendar</field>
|
|
||||||
<field name="priority" eval="2"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<calendar string="Funds" date_start="date" color="user_id" date_delay="duration">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="partner_id"/>
|
|
||||||
</calendar>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fund Raising Graph View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_graph_view_fund">
|
|
||||||
<field name="name">CRM - Funds Graph</field>
|
|
||||||
<field name="model">crm.fundraising</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph string="Funds by Categories" type="bar" orientation="horizontal">
|
|
||||||
<field name="categ_id"/>
|
|
||||||
<field name="planned_cost" operator="+"/>
|
|
||||||
<field name="state" group="True"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fund Raising Search View -->
|
|
||||||
|
|
||||||
<record id="view_crm_case_fund_filter" model="ir.ui.view">
|
|
||||||
<field name="name">CRM - Funds Search</field>
|
|
||||||
<field name="model">crm.fundraising</field>
|
|
||||||
<field name="type">search</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<search string="Search Funds">
|
|
||||||
<group col='6' colspan='4'>
|
|
||||||
<filter icon="terp-partner" string="My Funds"
|
|
||||||
default="1" domain="[('user_id','=',uid)]"
|
|
||||||
separator="1"
|
|
||||||
help="Funds Related to Current User"
|
|
||||||
/>
|
|
||||||
<separator orientation="vertical" />
|
|
||||||
<filter icon="gtk-new" string="Current Funds"
|
|
||||||
domain="[('state','in',('draft', 'open'))]"
|
|
||||||
help="Current Funds" />
|
|
||||||
<filter icon="gtk-yes" string="Open Funds"
|
|
||||||
domain="[('state','=','open')]"
|
|
||||||
help="Open Funds" />
|
|
||||||
<filter icon="gtk-media-pause"
|
|
||||||
string="Pending Funds"
|
|
||||||
domain="[('state','=','pending')]"
|
|
||||||
help="Pending Funds" />
|
|
||||||
<separator orientation="vertical" />
|
|
||||||
<field name="state" select="1" />
|
|
||||||
<field name="name" select='1'
|
|
||||||
string="Fund Description" />
|
|
||||||
<field name="user_id" select="1"
|
|
||||||
widget="selection">
|
|
||||||
<filter icon="terp-partner"
|
|
||||||
domain="[('user_id','=', False)]"
|
|
||||||
help="Unassigned" />
|
|
||||||
</field>
|
|
||||||
<field name="section_id" select="1" widget="selection" string="Sales Team">
|
|
||||||
<filter icon="terp-crm"
|
|
||||||
domain="[('section_id','=',context.get('section_id',False))]"
|
|
||||||
help="My section" />
|
|
||||||
</field>
|
|
||||||
</group>
|
|
||||||
</search>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -1,68 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from osv import fields
|
|
||||||
from osv import orm
|
|
||||||
from osv import osv
|
|
||||||
import crm
|
|
||||||
|
|
||||||
class crm_helpdesk(osv.osv):
|
|
||||||
""" Helpdesk Cases """
|
|
||||||
|
|
||||||
_name = "crm.helpdesk"
|
|
||||||
_description = "Helpdesk Cases"
|
|
||||||
_order = "id desc"
|
|
||||||
_inherit = 'crm.case'
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
|
||||||
'date': fields.datetime('Date'),
|
|
||||||
'ref' : fields.reference('Reference', selection=crm._links_get, size=128),
|
|
||||||
'ref2' : fields.reference('Reference 2', selection=crm._links_get, size=128),
|
|
||||||
'canal_id': fields.many2one('res.partner.canal', 'Channel', \
|
|
||||||
help="The channels represent the different communication \
|
|
||||||
modes available with the customer." \
|
|
||||||
" With each commercial opportunity, you can indicate\
|
|
||||||
the canall which is this opportunity source."),
|
|
||||||
'planned_revenue': fields.float('Planned Revenue'),
|
|
||||||
'planned_cost': fields.float('Planned Costs'),
|
|
||||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
|
||||||
'probability': fields.float('Probability (%)'),
|
|
||||||
'som': fields.many2one('res.partner.som', 'State of Mind', \
|
|
||||||
help="The minds states allow to define a value scale which represents" \
|
|
||||||
"the partner mentality in relation to our services.The scale has" \
|
|
||||||
"to be created with a factor for each level from 0 \
|
|
||||||
(Very dissatisfied) to 10 (Extremely satisfied)."),
|
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.helpdesk')]"),
|
|
||||||
'duration': fields.float('Duration'),
|
|
||||||
}
|
|
||||||
|
|
||||||
_defaults = {
|
|
||||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
|
|
||||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
|
||||||
}
|
|
||||||
|
|
||||||
crm_helpdesk()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
||||||
|
|
|
@ -1,249 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- Helpdesk Support Categories Form View -->
|
|
||||||
|
|
||||||
<record id="crm_helpdesk_categ_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Helpdesk Categories</field>
|
|
||||||
<field name="res_model">crm.case.categ</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_id" ref="crm.crm_case_categ_tree-view"/>
|
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.helpdesk')]</field>
|
|
||||||
<field name="context">{'object_id':'crm.helpdesk'}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem action="crm_helpdesk_categ_action"
|
|
||||||
id="menu_crm_case_helpdesk-act" parent="crm.menu_crm_case_categ" />
|
|
||||||
|
|
||||||
<!-- Helpdesk Support Form View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_form_view_helpdesk">
|
|
||||||
<field name="name">CRM - Helpdesk Support Form</field>
|
|
||||||
<field name="model">crm.helpdesk</field>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Helpdesk Support">
|
|
||||||
<group colspan="4" col="4">
|
|
||||||
<field colspan="4" name="name" select="1"/>
|
|
||||||
<field name="date" select="1"/>
|
|
||||||
<field name="date_deadline"/>
|
|
||||||
</group>
|
|
||||||
<notebook colspan="4">
|
|
||||||
<page string="General">
|
|
||||||
<separator colspan="4" string="Communication"/>
|
|
||||||
<group col="7" colspan="4">
|
|
||||||
<field name="partner_id"
|
|
||||||
on_change="onchange_partner_id(partner_id, email_from)"
|
|
||||||
select="1" />
|
|
||||||
<field name="partner_address_id"
|
|
||||||
on_change="onchange_partner_address_id(partner_address_id, email_from)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<field name="email_from" />
|
|
||||||
<button name="remind_partner"
|
|
||||||
states="open,pending"
|
|
||||||
string="Send Reminder" type="object"
|
|
||||||
icon="gtk-go-forward" />
|
|
||||||
|
|
||||||
</group>
|
|
||||||
<separator colspan="4" string="Categorization"/>
|
|
||||||
<group col="7" colspan="4">
|
|
||||||
<field name="priority"/>
|
|
||||||
<field name="section_id" widget="selection"/>
|
|
||||||
<field name="user_id" select="1"/>
|
|
||||||
<button name="remind_user"
|
|
||||||
states="open,pending"
|
|
||||||
string="Send Reminder" type="object"
|
|
||||||
icon="gtk-go-forward" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<separator colspan="4" string="Notes"/>
|
|
||||||
<field name="description" colspan="4"
|
|
||||||
nolabel="1" widget="text_tag" readonly="1" />
|
|
||||||
<separator colspan="4"/>
|
|
||||||
<group col="8" colspan="4">
|
|
||||||
<field name="state" select="1"/>
|
|
||||||
<button name="case_close"
|
|
||||||
states="open,draft,pending" string="Close"
|
|
||||||
type="object" icon="gtk-close" />
|
|
||||||
<button name="case_open"
|
|
||||||
states="draft,pending" string="Open"
|
|
||||||
type="object" icon="gtk-go-forward" />
|
|
||||||
<button name="case_cancel"
|
|
||||||
states="draft,open,pending" string="Cancel"
|
|
||||||
type="object" icon="gtk-cancel" />
|
|
||||||
<button name="case_pending"
|
|
||||||
states="draft,open" string="Pending"
|
|
||||||
type="object" icon="gtk-media-pause" />
|
|
||||||
<button name="case_escalate"
|
|
||||||
states="open,draft,pending"
|
|
||||||
string="Escalate" type="object"
|
|
||||||
icon="gtk-go-up" />
|
|
||||||
<button name="case_reset"
|
|
||||||
states="done,cancel"
|
|
||||||
string="Reset to Draft" type="object"
|
|
||||||
icon="gtk-convert" />
|
|
||||||
</group>
|
|
||||||
</page>
|
|
||||||
<page string="History" groups="base.group_extended">
|
|
||||||
<field name="id" select="1"/>
|
|
||||||
<field name="active"/>
|
|
||||||
<field name="categ_id"/>
|
|
||||||
<field name="som"/>
|
|
||||||
<separator colspan="4" string="Dates"/>
|
|
||||||
<field name="create_date"/>
|
|
||||||
<field name="date_closed"/>
|
|
||||||
<separator colspan="4" string="Estimates"/>
|
|
||||||
<field name="planned_cost"/>
|
|
||||||
<separator colspan="4" string="References"/>
|
|
||||||
<field colspan="4" name="ref"/>
|
|
||||||
<field colspan="4" name="ref2"/>
|
|
||||||
<field colspan="4" name="log_ids" nolabel="1">
|
|
||||||
<form string="Actions">
|
|
||||||
<separator colspan="4" string="Action Information"/>
|
|
||||||
<field colspan="4" name="name"/>
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="user_id"/>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</page>
|
|
||||||
<page string="Emails" groups="base.group_extended">
|
|
||||||
<group colspan="4">
|
|
||||||
<field colspan="4" name="email_cc" string="CC"/>
|
|
||||||
</group>
|
|
||||||
<field name="history_line" colspan="4" nolabel="1" mode="form,tree">
|
|
||||||
<form string="Communication history">
|
|
||||||
<group col="7" colspan="4">
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="email_to"/>
|
|
||||||
<field name="email_from"/>
|
|
||||||
<button
|
|
||||||
string="Add a CC"
|
|
||||||
name="%(crm.action_view_crm_email_add_cc_wizard)d"
|
|
||||||
icon="gtk-add" type="action"/>
|
|
||||||
</group>
|
|
||||||
<newline/>
|
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
|
||||||
<button colspan="4"
|
|
||||||
string="Reply to Last Email"
|
|
||||||
name="%(action_crm_send_mail)d"
|
|
||||||
context="{'mail':'reply', 'model': 'crm.helpdesk'}"
|
|
||||||
icon="gtk-undo" type="action" />
|
|
||||||
</form>
|
|
||||||
<tree string="Communication history">
|
|
||||||
<field name="description"/>
|
|
||||||
<field name="email_to"/>
|
|
||||||
<field name="date"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
<button colspan="4" string="Send New Email"
|
|
||||||
name="%(action_crm_send_mail)d"
|
|
||||||
context="{'mail':'new', 'model': 'crm.helpdesk'}"
|
|
||||||
icon="gtk-go-forward" type="action" />
|
|
||||||
</page>
|
|
||||||
</notebook>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Helpdesk Support Tree View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_tree_view_helpdesk">
|
|
||||||
<field name="name">CRM - Helpdesk Support Tree</field>
|
|
||||||
<field name="model">crm.helpdesk</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Helpdesk Support Tree"
|
|
||||||
colors="black:state=='open';blue:state=='pending';grey:state in ('cancel','close')">
|
|
||||||
<field name="id"/>
|
|
||||||
<field name="name" string="Query Description" />
|
|
||||||
<field name="partner_id" string="Partner"/>
|
|
||||||
<field name="date" string="Date"/>
|
|
||||||
<field name="date_deadline"/>
|
|
||||||
<field name="user_id"/>
|
|
||||||
<field name="section_id"/>
|
|
||||||
<field name="state"/>
|
|
||||||
<button name="case_close" string="Done"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
icon="gtk-close" />
|
|
||||||
<button name="case_open" string="Open"
|
|
||||||
states="draft,pending" type="object"
|
|
||||||
icon="gtk-go-forward" />
|
|
||||||
<button name="case_cancel" string="Cancel"
|
|
||||||
states="draft,open,pending" type="object"
|
|
||||||
icon="gtk-cancel" />
|
|
||||||
<button name="case_pending" string="Pending"
|
|
||||||
states="draft,open" type="object"
|
|
||||||
icon="gtk-media-pause" />
|
|
||||||
<button name="case_escalate" string="Escalate"
|
|
||||||
groups="base.group_extended"
|
|
||||||
states="open,draft,pending" type="object"
|
|
||||||
icon="gtk-go-up" />
|
|
||||||
<button name="case_reset" string="Reset to Draft"
|
|
||||||
states="done,cancel" type="object" icon="gtk-convert" />
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Helpdesk Support Calendar View -->
|
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_helpdesk_calendar_view">
|
|
||||||
<field name="name">CRM - Helpdesk Support Calendar</field>
|
|
||||||
<field name="model">crm.helpdesk</field>
|
|
||||||
<field name="type">calendar</field>
|
|
||||||
<field name="priority" eval="2"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<calendar string="Helpdesk Supports" date_start="date" color="user_id" date_delay="duration">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="partner_id"/>
|
|
||||||
<field name="categ_id"/>
|
|
||||||
</calendar>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Helpdesk Support Search View -->
|
|
||||||
|
|
||||||
<record id="view_crm_case_helpdesk_filter" model="ir.ui.view">
|
|
||||||
<field name="name">CRM - Helpdesk Search</field>
|
|
||||||
<field name="model">crm.helpdesk</field>
|
|
||||||
<field name="type">search</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<search string="Search Helpdesk">
|
|
||||||
<filter icon="gtk-home" string="Today"
|
|
||||||
separator="1"
|
|
||||||
domain="[('date::date','=',time.strftime('%%Y-%%m-%%d'))]"
|
|
||||||
help="Todays's Helpdesk Requests"
|
|
||||||
/>
|
|
||||||
<filter icon="gtk-media-rewind"
|
|
||||||
string="7 Days" separator="1"
|
|
||||||
help="Helpdesk requests during last 7 days"
|
|
||||||
domain="[('date','<', time.strftime('%%Y-%%m-%%d')), ('date','>=',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"
|
|
||||||
/>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<field name="name" select='1' string="Query"/>
|
|
||||||
<field name="partner_id" />
|
|
||||||
<field name="user_id" select="1" widget="selection">
|
|
||||||
<filter icon="terp-partner"
|
|
||||||
domain="[('user_id','=',uid)]"
|
|
||||||
help="My Helpdesk Requests" default="1" />
|
|
||||||
</field>
|
|
||||||
<field name="section_id" select="1" widget="selection" string="Sales Team">
|
|
||||||
<filter icon="terp-crm"
|
|
||||||
domain="[('section_id','=',context.get('section_id',False))]"
|
|
||||||
help="My section" />
|
|
||||||
</field>
|
|
||||||
<newline/>
|
|
||||||
<group expand="1" string="Group By..." colspan="12" col="20">
|
|
||||||
<filter string="Customer" icon="terp-crm"
|
|
||||||
domain="[]" context="{'group_by':'partner_id'}" />
|
|
||||||
<filter string="State" icon="terp-crm" domain="[]"
|
|
||||||
context="{'group_by':'state'}" />
|
|
||||||
<filter string="Priority" icon="terp-crm"
|
|
||||||
domain="[]" context="{'group_by':'priority'}" />
|
|
||||||
</group>
|
|
||||||
</search>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
from operator import itemgetter
|
||||||
|
from osv import fields, osv
|
||||||
|
import netsvc
|
||||||
|
import os
|
||||||
|
import pooler
|
||||||
|
import tools
|
||||||
|
|
||||||
|
class crm_installer(osv.osv_memory):
|
||||||
|
_name = 'crm.installer'
|
||||||
|
_inherit = 'res.config.installer'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'name': fields.char('Name', size=64),
|
||||||
|
'crm_helpdesk': fields.boolean('Helpdesk', help="Manages an Helpdesk service."),
|
||||||
|
'crm_fundraising': fields.boolean('Fundraising', help="This may help associations in their fund raising process and tracking."),
|
||||||
|
'crm_claim': fields.boolean('Claims', help="Manages the supplier and customers claims, including your corrective or preventive actions."),
|
||||||
|
'crm_caldav': fields.boolean('Calendar Synchronizing', help="Help you to synchronize the meetings with other calender clients(e.g.: Sunbird)."),
|
||||||
|
'sale_crm': fields.boolean('Create Quotation from Opportunity', help="This module relates sale to opportunity cases in the CRM."),
|
||||||
|
'fetchmail': fields.boolean('Fetch Emails', help="Fetchmail Server."),
|
||||||
|
'thunderbird': fields.boolean('Thunderbird', help="Thunderbird Interface."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||||
|
res = super(crm_installer, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
|
||||||
|
#Checking sale module is installed or not
|
||||||
|
cr.execute("SELECT * from ir_module_module where state='installed' and name = 'sale'")
|
||||||
|
count = cr.fetchall()
|
||||||
|
if count:
|
||||||
|
doc = etree.XML(res['arch'])
|
||||||
|
nodes = doc.xpath("//field[@name='sale_crm']")
|
||||||
|
for node in nodes:
|
||||||
|
node.set('invisible', '0')
|
||||||
|
res['arch'] = etree.tostring(doc)
|
||||||
|
return res
|
||||||
|
|
||||||
|
crm_installer()
|
||||||
|
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,60 @@
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<record id="view_crm_installer" model="ir.ui.view">
|
||||||
|
<field name="name">crm.installer.view</field>
|
||||||
|
<field name="model">crm.installer</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.res_config_installer"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<data>
|
||||||
|
<form position="attributes">
|
||||||
|
<attribute name="string">CRM Modules Installation</attribute>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<separator string="title" position="attributes">
|
||||||
|
<attribute name="string">Configure Your CRM System</attribute>
|
||||||
|
</separator>
|
||||||
|
<xpath expr="//label[@string='description']"
|
||||||
|
position="attributes">
|
||||||
|
<attribute name="string">You can enhance OpenERP's basic CRM support with a few additional OpenERP applications</attribute>
|
||||||
|
</xpath>
|
||||||
|
<group colspan="8">
|
||||||
|
<group colspan="2" col="2">
|
||||||
|
<separator string="Synchronization" colspan="4" />
|
||||||
|
<field name="crm_caldav" />
|
||||||
|
<field name="fetchmail" />
|
||||||
|
</group>
|
||||||
|
<group colspan="2" col="2">
|
||||||
|
<separator string="Plugins" colspan="4" />
|
||||||
|
<field name="thunderbird" />
|
||||||
|
<label string="*Outlook*" />
|
||||||
|
<!-- TODO <field name="outlook" />-->
|
||||||
|
</group>
|
||||||
|
<group colspan="4" col="4" groups="base.group_extended" name="other">
|
||||||
|
<separator string="Additional features" colspan="4" />
|
||||||
|
<field name="crm_claim" />
|
||||||
|
<field name="crm_helpdesk" />
|
||||||
|
<field name="crm_fundraising" />
|
||||||
|
<field name="sale_crm" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</data>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_crm_installer" model="ir.actions.act_window">
|
||||||
|
<field name="name">CRM Modules Installation</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">crm.installer</field>
|
||||||
|
<field name="view_id" ref="view_crm_installer"/>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="crm_installer_todo" model="ir.actions.todo">
|
||||||
|
<field name="action_id" ref="action_crm_installer"/>
|
||||||
|
<field name="sequence">3</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -26,27 +26,15 @@ import math
|
||||||
import time
|
import time
|
||||||
import mx.DateTime
|
import mx.DateTime
|
||||||
from tools.translate import _
|
from tools.translate import _
|
||||||
|
from crm import crm_case
|
||||||
|
|
||||||
class crm_lead(osv.osv):
|
class crm_lead(osv.osv, crm_case):
|
||||||
""" CRM Lead Case """
|
""" CRM Lead Case """
|
||||||
|
|
||||||
_name = "crm.lead"
|
_name = "crm.lead"
|
||||||
_description = "Leads Cases"
|
_description = "Leads Cases"
|
||||||
_order = "priority, id desc"
|
_order = "priority, id desc"
|
||||||
_inherit = ['res.partner.address', 'crm.case']
|
_inherit = ['res.partner.address', 'mailgate.thread']
|
||||||
|
|
||||||
def case_open(self, cr, uid, ids, *args):
|
|
||||||
"""
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of case's Ids
|
|
||||||
@param *args: Give Tuple Value
|
|
||||||
"""
|
|
||||||
|
|
||||||
res = super(crm_lead, self).case_open(cr, uid, ids, *args)
|
|
||||||
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _compute_day(self, cr, uid, ids, fields, args, context={}):
|
def _compute_day(self, cr, uid, ids, fields, args, context={}):
|
||||||
"""
|
"""
|
||||||
|
@ -85,8 +73,8 @@ class crm_lead(osv.osv):
|
||||||
resource_id = resource_ids[0]
|
resource_id = resource_ids[0]
|
||||||
|
|
||||||
duration = float(ans.days)
|
duration = float(ans.days)
|
||||||
if lead.section_id.resource_calendar_id:
|
if lead.section_id and lead.section_id.resource_calendar_id:
|
||||||
duration = float(ans.days) * 24
|
duration = float(ans.days) * 24
|
||||||
new_dates = cal_obj.interval_get(cr,
|
new_dates = cal_obj.interval_get(cr,
|
||||||
uid,
|
uid,
|
||||||
lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False,
|
lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False,
|
||||||
|
@ -106,22 +94,36 @@ class crm_lead(osv.osv):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
# From crm.case
|
||||||
|
'email_from': fields.char('Email', size=128, help="These people will receive email."),
|
||||||
|
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
|
||||||
|
select=True, help='Sales team to which Case belongs to.\
|
||||||
|
Define Responsible user and Email account for mail gateway.'),
|
||||||
|
'create_date': fields.datetime('Creation Date' , readonly=True),
|
||||||
|
'email_cc': fields.text('Watchers Emails', size=252 , help="These \
|
||||||
|
people will receive a copy of the future communication between partner \
|
||||||
|
and users by email"),
|
||||||
|
'description': fields.text('Description'),
|
||||||
|
'write_date': fields.datetime('Update Date' , readonly=True),
|
||||||
|
|
||||||
|
# Lead fields
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Lead Source', \
|
'categ_id': fields.many2one('crm.case.categ', 'Lead Source', \
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
('object_id.model', '=', 'crm.opportunity')]"),
|
('object_id.model', '=', 'crm.lead')]"),
|
||||||
'type_id': fields.many2one('crm.case.resource.type', 'Lead Type', \
|
'type_id': fields.many2one('crm.case.resource.type', 'Lead Type', \
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
('object_id.model', '=', 'crm.lead')]"),
|
('object_id.model', '=', 'crm.lead')]"),
|
||||||
'partner_name': fields.char("Company Name", size=64),
|
'partner_name': fields.char("Partner Name", size=64),
|
||||||
|
'type':fields.selection([
|
||||||
|
('lead','Lead'),
|
||||||
|
('opportunity','Opportunity'),
|
||||||
|
|
||||||
|
],'Type', help="Type is used to separate Leads and Opportunities"),
|
||||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
'date_closed': fields.datetime('Closed', readonly=True),
|
||||||
'stage_id': fields.many2one('crm.case.stage', 'Stage', \
|
'stage_id': fields.many2one('crm.case.stage', 'Stage', \
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
('object_id.model', '=', 'crm.lead')]"),
|
('object_id.model', '=', 'crm.lead')]"),
|
||||||
'opportunity_id': fields.many2one('crm.opportunity', 'Opportunity'),
|
|
||||||
|
|
||||||
'user_id': fields.many2one('res.users', 'Salesman'),
|
'user_id': fields.many2one('res.users', 'Salesman'),
|
||||||
'referred': fields.char('Referred By', size=64),
|
'referred': fields.char('Referred By', size=64),
|
||||||
'date_open': fields.datetime('Opened', readonly=True),
|
'date_open': fields.datetime('Opened', readonly=True),
|
||||||
|
@ -129,14 +131,47 @@ class crm_lead(osv.osv):
|
||||||
method=True, multi='day_open', type="float", store=True),
|
method=True, multi='day_open', type="float", store=True),
|
||||||
'day_close': fields.function(_compute_day, string='Days to Close', \
|
'day_close': fields.function(_compute_day, string='Days to Close', \
|
||||||
method=True, multi='day_close', type="float", store=True),
|
method=True, multi='day_close', type="float", store=True),
|
||||||
'function_name': fields.char('Function', size=64),
|
'function_name': fields.char('Function', size=64),
|
||||||
|
'state': fields.selection(crm.AVAILABLE_STATES, 'State', size=16, readonly=True,
|
||||||
|
help='The state is set to \'Draft\', when a case is created.\
|
||||||
|
\nIf the case is in progress the state is set to \'Open\'.\
|
||||||
|
\nWhen the case is over, the state is set to \'Done\'.\
|
||||||
|
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
'active': lambda *a: 1,
|
||||||
|
'user_id': crm_case._get_default_user,
|
||||||
|
'email_from': crm_case._get_default_email,
|
||||||
|
'state': lambda *a: 'draft',
|
||||||
|
'section_id': crm_case._get_section,
|
||||||
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
|
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
|
||||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def case_open(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides cancel for crm_case for setting Open Date
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case's Ids
|
||||||
|
@param *args: Give Tuple Value
|
||||||
|
"""
|
||||||
|
res = super(crm_lead, self).case_open(cr, uid, ids, *args)
|
||||||
|
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def case_close(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides close for crm_case for setting close date
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
res = super(crm_lead, self).case_close(cr, uid, ids, args)
|
||||||
|
self.write(cr, uid, ids, {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
return res
|
||||||
|
|
||||||
def convert_opportunity(self, cr, uid, ids, context=None):
|
def convert_opportunity(self, cr, uid, ids, context=None):
|
||||||
""" Precomputation for converting lead to opportunity
|
""" Precomputation for converting lead to opportunity
|
||||||
|
@ -158,7 +193,6 @@ class crm_lead(osv.osv):
|
||||||
if data_id:
|
if data_id:
|
||||||
view_id = data_obj.browse(cr, uid, data_id, context=context).res_id
|
view_id = data_obj.browse(cr, uid, data_id, context=context).res_id
|
||||||
for case in self.browse(cr, uid, ids):
|
for case in self.browse(cr, uid, ids):
|
||||||
context.update({'opportunity_id': case.id})
|
|
||||||
context.update({'active_id': case.id})
|
context.update({'active_id': case.id})
|
||||||
if not case.partner_id:
|
if not case.partner_id:
|
||||||
data_id = data_obj._get_id(cr, uid, 'crm', 'view_crm_lead2opportunity_partner')
|
data_id = data_obj._get_id(cr, uid, 'crm', 'view_crm_lead2opportunity_partner')
|
||||||
|
|
|
@ -9,72 +9,77 @@
|
||||||
<record id="crm_case_itisatelesalescampaign0" model="crm.lead">
|
<record id="crm_case_itisatelesalescampaign0" model="crm.lead">
|
||||||
<field name="type_id" ref="crm.type_lead1"/>
|
<field name="type_id" ref="crm.type_lead1"/>
|
||||||
<field eval="'3'" name="priority"/>
|
<field eval="'3'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Luc Latour'" name="name"/>
|
<field eval="'Luc Latour'" name="partner_name"/>
|
||||||
<field eval="'open'" name="state"/>
|
<field eval="'open'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(726) 782-0636'" name="mobile"/>
|
<field eval="'(726) 782-0636'" name="mobile"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor6"/>
|
<field name="categ_id" ref="crm.categ_oppor6"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||||
<field eval="'The Oil Company'" name="partner_name"/>
|
<field eval="'The Oil Company'" name="name"/>
|
||||||
<field eval="'(769) 703-274'" name="phone"/>
|
<field eval="'(769) 703-274'" name="phone"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_electonicgoodsdealer0" model="crm.lead">
|
<record id="crm_case_electonicgoodsdealer0" model="crm.lead">
|
||||||
<field name="type_id" ref="crm.type_lead7"/>
|
<field name="type_id" ref="crm.type_lead7"/>
|
||||||
<field eval="'4'" name="priority"/>
|
<field eval="'4'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Marc Dufour'" name="name"/>
|
<field eval="'Marc Dufour'" name="partner_name"/>
|
||||||
<field eval="'open'" name="state"/>
|
<field eval="'open'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(392) 895-7917'" name="mobile"/>
|
<field eval="'(392) 895-7917'" name="mobile"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor2"/>
|
<field name="categ_id" ref="crm.categ_oppor2"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||||
<field eval="'Le Club SARL'" name="partner_name"/>
|
<field eval="'Le Club SARL'" name="name"/>
|
||||||
<field eval="'(956) 293-2595'" name="phone"/>
|
<field eval="'(956) 293-2595'" name="phone"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_developingwebapplications0" model="crm.lead">
|
<record id="crm_case_developingwebapplications0" model="crm.lead">
|
||||||
<field name="type_id" ref="crm.type_lead5"/>
|
<field name="type_id" ref="crm.type_lead5"/>
|
||||||
<field eval="'2'" name="priority"/>
|
<field eval="'2'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_demo"/>
|
<field name="user_id" ref="base.user_demo"/>
|
||||||
<field eval="'Fabien Pinckaers'" name="name"/>
|
<field eval="'Fabien Pinckaers'" name="partner_name"/>
|
||||||
<field eval="'done'" name="state"/>
|
<field eval="'done'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(820) 167-3208'" name="mobile"/>
|
<field eval="'(820) 167-3208'" name="mobile"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor4"/>
|
<field name="categ_id" ref="crm.categ_oppor4"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||||
<field eval="'TheKompany'" name="partner_name"/>
|
<field eval="'TheKompany'" name="name"/>
|
||||||
<field eval="'(079) 681-2139'" name="phone"/>
|
<field eval="'(079) 681-2139'" name="phone"/>
|
||||||
<field eval="'contact@tecsas.fr'" name="email_from"/>
|
<field eval="'contact@tecsas.fr'" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_qrecorp0" model="crm.lead">
|
<record id="crm_case_qrecorp0" model="crm.lead">
|
||||||
<field name="type_id" ref="crm.type_lead8"/>
|
<field name="type_id" ref="crm.type_lead8"/>
|
||||||
<field eval="'3'" name="priority"/>
|
<field eval="'3'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Henry Mc Coy'" name="name"/>
|
<field eval="'Henry Mc Coy'" name="partner_name"/>
|
||||||
<field eval="'draft'" name="state"/>
|
<field eval="'draft'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(077) 582-4035'" name="mobile"/>
|
<field eval="'(077) 582-4035'" name="mobile"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor1"/>
|
<field name="categ_id" ref="crm.categ_oppor1"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||||
<field eval="'The Gas Company'" name="partner_name"/>
|
<field eval="'The Gas Company'" name="name"/>
|
||||||
<field eval="'(514) 698-4118'" name="phone"/>
|
<field eval="'(514) 698-4118'" name="phone"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_itdeveloper0" model="crm.lead">
|
<record id="crm_case_itdeveloper0" model="crm.lead">
|
||||||
<field name="type_id" ref="crm.type_lead4"/>
|
<field name="type_id" ref="crm.type_lead4"/>
|
||||||
<field eval="'3'" name="priority"/>
|
<field eval="'3'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Carrie Helle'" name="name"/>
|
<field eval="'Carrie Helle'" name="partner_name"/>
|
||||||
<field eval="'pending'" name="state"/>
|
<field eval="'pending'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(333) 715-1450'" name="mobile"/>
|
<field eval="'(333) 715-1450'" name="mobile"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor6"/>
|
<field name="categ_id" ref="crm.categ_oppor6"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||||
<field eval="'Stonage IT'" name="partner_name"/>
|
<field eval="'Stonage IT'" name="name"/>
|
||||||
<field eval="'(855) 924-4364'" name="phone"/>
|
<field eval="'(855) 924-4364'" name="phone"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_mgroperations0" model="crm.lead">
|
<record id="crm_case_mgroperations0" model="crm.lead">
|
||||||
|
@ -83,14 +88,15 @@
|
||||||
<field name="type_id" ref="crm.type_lead3"/>
|
<field name="type_id" ref="crm.type_lead3"/>
|
||||||
<field name="partner_id" ref="base.res_partner_9"/>
|
<field name="partner_id" ref="base.res_partner_9"/>
|
||||||
<field eval="'3'" name="priority"/>
|
<field eval="'3'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Tina Pinero'" name="name"/>
|
<field eval="'Tina Pinero'" name="partner_name"/>
|
||||||
<field eval="'cancel'" name="state"/>
|
<field eval="'cancel'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(468) 017-2684'" name="mobile"/>
|
<field eval="'(468) 017-2684'" name="mobile"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor8"/>
|
<field name="categ_id" ref="crm.categ_oppor8"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead6"/>
|
<field name="stage_id" ref="crm.stage_lead6"/>
|
||||||
<field eval="'Opensides SPRL'" name="partner_name"/>
|
<field eval="'Opensides SPRL'" name="name"/>
|
||||||
<field eval="'(373) 907-1009'" name="phone"/>
|
<field eval="'(373) 907-1009'" name="phone"/>
|
||||||
<field eval="'info@opensides.be'" name="email_from"/>
|
<field eval="'info@opensides.be'" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -98,14 +104,15 @@
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="type_id" ref="crm.type_lead8"/>
|
<field name="type_id" ref="crm.type_lead8"/>
|
||||||
<field eval="'3'" name="priority"/>
|
<field eval="'3'" name="priority"/>
|
||||||
|
<field name="type">lead</field>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field eval="'Wendi Baltz'" name="name"/>
|
<field eval="'Wendi Baltz'" name="partner_name"/>
|
||||||
<field eval="'done'" name="state"/>
|
<field eval="'done'" name="state"/>
|
||||||
<field name="section_id" ref="crm.section_sales_department"/>
|
<field name="section_id" ref="crm.section_sales_department"/>
|
||||||
<field eval="'(463) 014-1208'" name="mobile"/>
|
<field eval="'(463) 014-1208'" name="mobile"/>
|
||||||
<field name="categ_id" ref="crm.categ_oppor4"/>
|
<field name="categ_id" ref="crm.categ_oppor4"/>
|
||||||
<field name="stage_id" ref="crm.stage_lead4"/>
|
<field name="stage_id" ref="crm.stage_lead4"/>
|
||||||
<field eval="'Gardner Group'" name="partner_name"/>
|
<field eval="'Gardner Group'" name="name"/>
|
||||||
<field eval="'(282) 603-7489'" name="phone"/>
|
<field eval="'(282) 603-7489'" name="phone"/>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -6,15 +6,19 @@
|
||||||
<field name="name">Leads</field>
|
<field name="name">Leads</field>
|
||||||
<field name="res_model">crm.lead</field>
|
<field name="res_model">crm.lead</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="domain">['|', ('type','=','lead'), ('type','=',False)]</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
||||||
<field name="search_view_id" ref="crm.view_crm_case_leads_filter"/>
|
<field name="search_view_id" ref="crm.view_crm_case_leads_filter"/>
|
||||||
|
<field name="context">{'search_default_current':1}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_leads_all">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_leads_all">
|
||||||
<field name="sequence" eval="1"/>
|
<field name="sequence" eval="1"/>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
<field name="view_id" ref="crm_case_tree_view_leads"/>
|
||||||
<field name="act_window_id" ref="crm_case_category_act_leads_all"/>
|
<field name="act_window_id" ref="crm_case_category_act_leads_all"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_leads_all">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_leads_all">
|
||||||
<field name="sequence" eval="3"/>
|
<field name="sequence" eval="3"/>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
|
@ -24,6 +28,8 @@
|
||||||
|
|
||||||
<menuitem id="base.menu_sales" name="Sales"
|
<menuitem id="base.menu_sales" name="Sales"
|
||||||
parent="base.menu_base_partner" sequence="1" />
|
parent="base.menu_base_partner" sequence="1" />
|
||||||
|
|
||||||
|
|
||||||
<menuitem parent="base.menu_sales" name="Leads"
|
<menuitem parent="base.menu_sales" name="Leads"
|
||||||
groups="base.group_extended"
|
groups="base.group_extended"
|
||||||
id="menu_crm_case_categ0_act_leads"
|
id="menu_crm_case_categ0_act_leads"
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Leads Form">
|
<form string="Leads Form">
|
||||||
<group colspan="4" col="7">
|
<group colspan="4" col="7">
|
||||||
<field name="name" required="1"/>
|
<field name="name" required="1" string="Name"/>
|
||||||
<field name="priority"/>
|
<field name="priority"/>
|
||||||
<field name="date_deadline"/>
|
<field name="date_deadline"/>
|
||||||
<button
|
<button
|
||||||
|
@ -48,8 +48,7 @@
|
||||||
string="Convert"
|
string="Convert"
|
||||||
help="Convert to Opportunity"
|
help="Convert to Opportunity"
|
||||||
icon="gtk-index"
|
icon="gtk-index"
|
||||||
type="object"
|
type="object"/>
|
||||||
attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
|
|
||||||
<newline />
|
<newline />
|
||||||
<field name="section_id" colspan="1"
|
<field name="section_id" colspan="1"
|
||||||
widget="selection" />
|
widget="selection" />
|
||||||
|
@ -69,7 +68,7 @@
|
||||||
<page string="Lead">
|
<page string="Lead">
|
||||||
<group colspan="2" col="4">
|
<group colspan="2" col="4">
|
||||||
<separator string="Contact" colspan="4" col="4"/>
|
<separator string="Contact" colspan="4" col="4"/>
|
||||||
<field name="partner_name" colspan="4"/>
|
<field name="partner_name" string="Partner Name" colspan="4"/>
|
||||||
<newline/>
|
<newline/>
|
||||||
<field domain="[('domain', '=', 'contact')]" name="title"/>
|
<field domain="[('domain', '=', 'contact')]" name="title"/>
|
||||||
<field name="function_name" />
|
<field name="function_name" />
|
||||||
|
@ -97,7 +96,6 @@
|
||||||
icon="gtk-index" type="action"
|
icon="gtk-index" type="action"
|
||||||
string="Create"
|
string="Create"
|
||||||
attrs="{'invisible':[('partner_id','!=',False)]}"/>
|
attrs="{'invisible':[('partner_id','!=',False)]}"/>
|
||||||
<field name="opportunity_id"/>
|
|
||||||
</group>
|
</group>
|
||||||
<separator colspan="4" string="Notes"/>
|
<separator colspan="4" string="Notes"/>
|
||||||
<field name="description" nolabel="1" colspan="4"/>
|
<field name="description" nolabel="1" colspan="4"/>
|
||||||
|
@ -133,7 +131,7 @@
|
||||||
widget="selection" colspan="2" />
|
widget="selection" colspan="2" />
|
||||||
<field name="categ_id"
|
<field name="categ_id"
|
||||||
widget="selection"
|
widget="selection"
|
||||||
domain="[('object_id.model', '=', 'crm.opportunity')]" />
|
domain="[('object_id.model', '=', 'crm.lead')]" />
|
||||||
<field name="type_id" string="Campaign" select="1"/>
|
<field name="type_id" string="Campaign" select="1"/>
|
||||||
<field name="referred"/>
|
<field name="referred"/>
|
||||||
</group>
|
</group>
|
||||||
|
@ -154,16 +152,13 @@
|
||||||
<group colspan="4">
|
<group colspan="4">
|
||||||
<field colspan="4" name="email_cc" string="CC"/>
|
<field colspan="4" name="email_cc" string="CC"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="history_line" colspan="4" nolabel="1" mode="form,tree">
|
<!-- TODO-->
|
||||||
|
<field name="message_ids" colspan="4" nolabel="1" mode="form,tree">
|
||||||
<form string="Communication history">
|
<form string="Communication history">
|
||||||
<group col="7" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<field name="email_to"/>
|
<field name="email_to"/>
|
||||||
<field name="email_from"/>
|
<field name="email_from"/>
|
||||||
<button
|
|
||||||
string="Add a CC"
|
|
||||||
name="%(crm.action_view_crm_email_add_cc_wizard)d"
|
|
||||||
icon="gtk-add" type="action"/>
|
|
||||||
</group>
|
</group>
|
||||||
<newline/>
|
<newline/>
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
<field name="description" colspan="4" nolabel="1"/>
|
||||||
|
@ -180,7 +175,7 @@
|
||||||
<field name="email_to"/>
|
<field name="email_to"/>
|
||||||
<field name="description"/>
|
<field name="description"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
<button colspan="2" string="Send New Email"
|
<button colspan="2" string="Send New Email"
|
||||||
name="%(action_crm_send_mail)d"
|
name="%(action_crm_send_mail)d"
|
||||||
context="{'mail':'new', 'model': 'crm.lead'}"
|
context="{'mail':'new', 'model': 'crm.lead'}"
|
||||||
|
@ -271,7 +266,7 @@
|
||||||
<search string="Search Leads">
|
<search string="Search Leads">
|
||||||
<filter icon="terp-project"
|
<filter icon="terp-project"
|
||||||
string="Current"
|
string="Current"
|
||||||
default="1"
|
default="1" name="current"
|
||||||
domain="[('state','in',('draft','open'))]"/>
|
domain="[('state','in',('draft','open'))]"/>
|
||||||
<filter icon="terp-project"
|
<filter icon="terp-project"
|
||||||
string="Open"
|
string="Open"
|
||||||
|
@ -314,7 +309,7 @@
|
||||||
help="Show Sales Team"/>
|
help="Show Sales Team"/>
|
||||||
</field>
|
</field>
|
||||||
<newline/>
|
<newline/>
|
||||||
<group expand="1" string="Group By..." colspan="14">
|
<group expand="0" string="Group By..." colspan="14">
|
||||||
<filter string="Stage" icon="terp-crm" domain="[]" context="{'group_by':'stage_id'}"/>
|
<filter string="Stage" icon="terp-crm" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||||
<filter string="State" icon="terp-crm" domain="[]" context="{'group_by':'state'}"/>
|
<filter string="State" icon="terp-crm" domain="[]" context="{'group_by':'state'}"/>
|
||||||
<filter string="Source" icon="terp-crm" domain="[]" context="{'group_by':'categ_id'}"/>
|
<filter string="Source" icon="terp-crm" domain="[]" context="{'group_by':'categ_id'}"/>
|
||||||
|
|
|
@ -38,8 +38,8 @@ from tools import command_re
|
||||||
class crm_cases(osv.osv):
|
class crm_cases(osv.osv):
|
||||||
""" crm cases """
|
""" crm cases """
|
||||||
|
|
||||||
_name = "crm.case"
|
_name = "mailgate.thread"
|
||||||
_inherit = "crm.case"
|
_inherit = "mailgate.thread"
|
||||||
|
|
||||||
def message_new(self, cr, uid, msg, context):
|
def message_new(self, cr, uid, msg, context):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,38 +19,50 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
from osv import fields, osv
|
|
||||||
import crm
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from tools.translate import _
|
|
||||||
from base_calendar import base_calendar
|
from base_calendar import base_calendar
|
||||||
|
from crm import crm_case
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from osv import fields, osv
|
||||||
|
from tools.translate import _
|
||||||
|
import time
|
||||||
|
|
||||||
class crm_opportunity(osv.osv):
|
class crm_lead(osv.osv, crm_case):
|
||||||
_name = 'crm.opportunity'
|
""" CRM Leads """
|
||||||
crm_opportunity()
|
_name = 'crm.lead'
|
||||||
|
crm_lead()
|
||||||
|
|
||||||
|
class crm_phonecall(osv.osv, crm_case):
|
||||||
class crm_phonecall(osv.osv):
|
|
||||||
""" CRM Phonecall """
|
""" CRM Phonecall """
|
||||||
_name = 'crm.phonecall'
|
_name = 'crm.phonecall'
|
||||||
crm_phonecall()
|
crm_phonecall()
|
||||||
|
|
||||||
|
|
||||||
class crm_meeting(osv.osv):
|
class crm_meeting(osv.osv, crm_case):
|
||||||
""" CRM Meeting Cases """
|
""" CRM Meeting Cases """
|
||||||
|
|
||||||
_name = 'crm.meeting'
|
_name = 'crm.meeting'
|
||||||
_description = "Meeting Cases"
|
_description = "Meeting Cases"
|
||||||
_order = "id desc"
|
_order = "id desc"
|
||||||
_inherit = ["crm.case", "calendar.event"]
|
_inherit = ["mailgate.thread", "calendar.event"]
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
# From crm.case
|
||||||
|
'name': fields.char('Summary', size=124, required=True),
|
||||||
|
'partner_id': fields.many2one('res.partner', 'Partner'),
|
||||||
|
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
|
||||||
|
domain="[('partner_id','=',partner_id)]"),
|
||||||
|
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
|
||||||
|
select=True, help='Sales team to which Case belongs to.\
|
||||||
|
Define Responsible user and Email account for mail gateway.'),
|
||||||
|
'email_from': fields.char('Email', size=128, help="These people will receive email."),
|
||||||
|
'id': fields.integer('ID'),
|
||||||
|
|
||||||
|
# Meeting fields
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Meeting Type', \
|
'categ_id': fields.many2one('crm.case.categ', 'Meeting Type', \
|
||||||
domain="[('object_id.model', '=', 'crm.meeting')]", \
|
domain="[('object_id.model', '=', 'crm.meeting')]", \
|
||||||
),
|
),
|
||||||
'phonecall_id': fields.many2one ('crm.phonecall', 'Phonecall'),
|
'phonecall_id': fields.many2one ('crm.phonecall', 'Phonecall'),
|
||||||
'opportunity_id': fields.many2one ('crm.opportunity', 'Opportunity'),
|
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity', domain="[('type', '=', 'opportunity')]"),
|
||||||
'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
|
'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
|
||||||
'event_id', 'attendee_id', 'Attendees'),
|
'event_id', 'attendee_id', 'Attendees'),
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
'date_closed': fields.datetime('Closed', readonly=True),
|
||||||
|
@ -63,9 +75,10 @@ class crm_meeting(osv.osv):
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'state': lambda *a: 'draft',
|
'state': lambda *a: 'draft',
|
||||||
|
'active': lambda *a: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
def open_meeting(self, cr, uid, ids, context=None):
|
def open_meeting(self, cr, uid, ids, context=None):
|
||||||
"""
|
"""
|
||||||
Open Crm Meeting Form for Crm Meeting.
|
Open Crm Meeting Form for Crm Meeting.
|
||||||
|
@ -75,13 +88,14 @@ class crm_meeting(osv.osv):
|
||||||
@param context: A standard dictionary for contextual values
|
@param context: A standard dictionary for contextual values
|
||||||
@return: Dictionary value which open Crm Meeting form.
|
@return: Dictionary value which open Crm Meeting form.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not context:
|
if not context:
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
data_obj = self.pool.get('ir.model.data')
|
data_obj = self.pool.get('ir.model.data')
|
||||||
|
|
||||||
value = {}
|
value = {}
|
||||||
|
|
||||||
id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
|
id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
|
||||||
id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
|
id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
|
||||||
id4 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
|
id4 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
|
||||||
|
@ -93,19 +107,19 @@ class crm_meeting(osv.osv):
|
||||||
id4 = data_obj.browse(cr, uid, id4, context=context).res_id
|
id4 = data_obj.browse(cr, uid, id4, context=context).res_id
|
||||||
for id in ids:
|
for id in ids:
|
||||||
value = {
|
value = {
|
||||||
'name': _('Meeting'),
|
'name': _('Meeting'),
|
||||||
'view_type': 'form',
|
'view_type': 'form',
|
||||||
'view_mode': 'form,tree',
|
'view_mode': 'form,tree',
|
||||||
'res_model': 'crm.meeting',
|
'res_model': 'crm.meeting',
|
||||||
'view_id': False,
|
'view_id': False,
|
||||||
'views': [(id2, 'form'), (id3, 'tree'), (id4, 'calendar')],
|
'views': [(id2, 'form'), (id3, 'tree'), (id4, 'calendar')],
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'res_id': base_calendar.base_calendar_id2real_id(id),
|
'res_id': base_calendar.base_calendar_id2real_id(id),
|
||||||
'nodestroy': True
|
'nodestroy': True
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
crm_meeting()
|
crm_meeting()
|
||||||
|
|
||||||
class calendar_attendee(osv.osv):
|
class calendar_attendee(osv.osv):
|
||||||
|
@ -143,4 +157,16 @@ class calendar_attendee(osv.osv):
|
||||||
|
|
||||||
calendar_attendee()
|
calendar_attendee()
|
||||||
|
|
||||||
|
class res_partner(osv.osv):
|
||||||
|
""" Inherits partner and adds meetings information in the partner form """
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'meeting_ids': fields.one2many('crm.meeting', 'partner_id',\
|
||||||
|
'Meetings'),
|
||||||
|
}
|
||||||
|
|
||||||
|
res_partner()
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<field eval="time.strftime('%Y-%m-03 10:20:03')" name="date"/>
|
<field eval="time.strftime('%Y-%m-03 10:20:03')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_id" ref="crm.categ_meet2"/>
|
||||||
<field eval=""Follow-up on proposal"" name="name"/>
|
<field eval=""Follow-up on proposal"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-10')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-03 16:38:03')" name="date_deadline"/>
|
||||||
<field eval="6.3" name="duration"/>
|
<field eval="6.3" name="duration"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
<field eval="time.strftime('%Y-%m-05 12:01:01')" name="date"/>
|
<field eval="time.strftime('%Y-%m-05 12:01:01')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet3"/>
|
<field name="categ_id" ref="crm.categ_meet3"/>
|
||||||
<field eval=""Initial discussion"" name="name"/>
|
<field eval=""Initial discussion"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-12')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-05 19:01:01')" name="date_deadline"/>
|
||||||
<field eval=""contact@tecsas.fr"" name="email_from"/>
|
<field eval=""contact@tecsas.fr"" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@
|
||||||
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
|
<field eval="time.strftime('%Y-%m-12 15:55:05')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet1"/>
|
<field name="categ_id" ref="crm.categ_meet1"/>
|
||||||
<field eval=""Discuss pricing"" name="name"/>
|
<field eval=""Discuss pricing"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-20')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-12 18:55:05')" name="date_deadline"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="crm_case_reviewneeds0" model="crm.meeting">
|
<record id="crm_case_reviewneeds0" model="crm.meeting">
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_15"/>
|
<field name="partner_address_id" ref="base.res_partner_address_15"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
@ -62,9 +62,9 @@
|
||||||
<field eval="time.strftime('%Y-%m-20 10:02:02')" name="date"/>
|
<field eval="time.strftime('%Y-%m-20 10:02:02')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet3"/>
|
<field name="categ_id" ref="crm.categ_meet3"/>
|
||||||
<field eval=""Review needs"" name="name"/>
|
<field eval=""Review needs"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-25')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-20 16:02:02')" name="date_deadline"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="crm_case_changesindesigning0" model="crm.meeting">
|
<record id="crm_case_changesindesigning0" model="crm.meeting">
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_1"/>
|
<field name="partner_address_id" ref="base.res_partner_address_1"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
<field eval="time.strftime('%Y-%m-22 11:05:05')" name="date"/>
|
<field eval="time.strftime('%Y-%m-22 11:05:05')" name="date"/>
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_id" ref="crm.categ_meet2"/>
|
||||||
<field eval=""Changes in Designing"" name="name"/>
|
<field eval=""Changes in Designing"" name="name"/>
|
||||||
<field eval="time.strftime('%Y-%m-28')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-22 16:05:05')" name="date_deadline"/>
|
||||||
<field eval=""info@opensides.be"" name="email_from"/>
|
<field eval=""info@opensides.be"" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
<field name="categ_id" ref="crm.categ_meet2"/>
|
<field name="categ_id" ref="crm.categ_meet2"/>
|
||||||
<field eval=""Update the data"" name="name"/>
|
<field eval=""Update the data"" name="name"/>
|
||||||
<field eval="13.3" name="duration"/>
|
<field eval="13.3" name="duration"/>
|
||||||
<field eval="time.strftime('%Y-%m-28')" name="date_deadline"/>
|
<field eval="time.strftime('%Y-%m-19 02:30:49')" name="date_deadline"/>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
domain="[('partner_id', '=', active_id)]"
|
domain="[('partner_id', '=', active_id)]"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="crm_case_categ_meet_create_partner">
|
<record model="ir.actions.act_window" id="crm_case_categ_meet_create_partner">
|
||||||
<field name="context" eval="{'default_state':'draft'}"/>
|
<field name="context" eval="{'default_state':'draft'}"/>
|
||||||
</record>
|
</record>
|
||||||
|
@ -24,18 +23,21 @@
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_meet_partner">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_meet_partner">
|
||||||
<field name="sequence" eval="2"/>
|
<field name="sequence" eval="2"/>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_meet_partner">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_form_meet_partner">
|
||||||
<field name="sequence" eval="3"/>
|
<field name="sequence" eval="3"/>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="crm_case_form_view_meet"/>
|
<field name="view_id" ref="crm_case_form_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_meet_partner">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_meet_partner">
|
||||||
<field name="sequence" eval="3"/>
|
<field name="sequence" eval="3"/>
|
||||||
<field name="view_mode">gantt</field>
|
<field name="view_mode">gantt</field>
|
||||||
|
@ -43,68 +45,36 @@
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
<field name="act_window_id" ref="crm_case_categ_meet_create_partner"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<act_window
|
|
||||||
id="crm_case_categ_meet_case"
|
|
||||||
name="Schedule a Meeting"
|
|
||||||
res_model="crm.meeting"
|
|
||||||
src_model="crm.case"
|
|
||||||
view_mode="calendar,tree,form,gantt"
|
|
||||||
context="{'default_case_id': active_id, 'default_duration': 4.0}"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="crm_case_categ_meet_case">
|
|
||||||
<field name="context" eval="{'default_state':'draft'}"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_calendar_meet_case">
|
|
||||||
<field name="sequence" eval="1"/>
|
|
||||||
<field name="view_mode">calendar</field>
|
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_case"/>
|
|
||||||
</record>
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_meet_case">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_case"/>
|
|
||||||
</record>
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_meet_case">
|
|
||||||
<field name="sequence" eval="3"/>
|
|
||||||
<field name="view_mode">form</field>
|
|
||||||
<field name="view_id" ref="crm_case_form_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_case"/>
|
|
||||||
</record>
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_meet_case">
|
|
||||||
<field name="sequence" eval="3"/>
|
|
||||||
<field name="view_mode">gantt</field>
|
|
||||||
<field name="view_id" ref="crm_case_gantt_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet_case"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="crm_case_categ_meet">
|
<record model="ir.actions.act_window" id="crm_case_categ_meet">
|
||||||
|
<field name="name">Meetings</field>
|
||||||
<field name="res_model">crm.meeting</field>
|
<field name="res_model">crm.meeting</field>
|
||||||
<field name="view_mode">calendar,tree,form,gantt</field>
|
<field name="view_mode">calendar,tree,form,gantt</field>
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
||||||
|
<field name="context">{'search_default_current':1}</field>
|
||||||
<field name="search_view_id" ref="view_crm_case_meetings_filter"/>
|
<field name="search_view_id" ref="view_crm_case_meetings_filter"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_calendar_meet">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_calendar_meet">
|
||||||
<field name="sequence" eval="1"/>
|
<field name="sequence" eval="1"/>
|
||||||
<field name="view_mode">calendar</field>
|
<field name="view_mode">calendar</field>
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_meet">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_meet">
|
||||||
<field name="sequence" eval="2"/>
|
<field name="sequence" eval="2"/>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form__meet">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_form__meet">
|
||||||
<field name="sequence" eval="3"/>
|
<field name="sequence" eval="3"/>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="crm_case_form_view_meet"/>
|
<field name="view_id" ref="crm_case_form_view_meet"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
<field name="act_window_id" ref="crm_case_categ_meet"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_meet">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_meet">
|
||||||
<field name="sequence" eval="1"/>
|
<field name="sequence" eval="1"/>
|
||||||
<field name="view_mode">gantt</field>
|
<field name="view_mode">gantt</field>
|
||||||
|
@ -142,43 +112,5 @@
|
||||||
name="Meeting Invitations" parent="crm.menu_meeting_sale"
|
name="Meeting Invitations" parent="crm.menu_meeting_sale"
|
||||||
sequence="10" action="action_view_attendee_form" />
|
sequence="10" action="action_view_attendee_form" />
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="crm_case_category_act_meetall3">
|
|
||||||
<field name="name">Next Meetings</field>
|
|
||||||
<field name="res_model">crm.meeting</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,calendar,form,gantt</field>
|
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
|
||||||
<field name="domain"
|
|
||||||
eval="[('state','in',('pending','draft','open')),('date','>=',time.strftime('%Y-%m-%d'))]" />
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_calendar_view_meetall3">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">calendar</field>
|
|
||||||
<field name="view_id" ref="crm_case_calendar_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_category_act_meetall3"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_meetall3">
|
|
||||||
<field name="sequence" eval="1"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="crm_case_tree_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_category_act_meetall3"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_meetall3">
|
|
||||||
<field name="sequence" eval="3"/>
|
|
||||||
<field name="view_mode">form</field>
|
|
||||||
<field name="view_id" ref="crm_case_form_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_category_act_meetall3"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_gantt_view_meetall3">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">gantt</field>
|
|
||||||
<field name="view_id" ref="crm_case_gantt_view_meet"/>
|
|
||||||
<field name="act_window_id" ref="crm_case_category_act_meetall3"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -24,15 +24,15 @@
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Meetings">
|
<form string="Meetings">
|
||||||
<group col="6" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="name" select="1" string="Summary"
|
<field name="name" select="1" string="Summary"
|
||||||
colspan="4" />
|
colspan="2" />
|
||||||
<field name="categ_id" widget="selection"
|
<field name="categ_id" widget="selection"
|
||||||
string="Meeting Type"
|
string="Meeting Type"
|
||||||
groups="base.group_extended"
|
groups="base.group_extended"
|
||||||
domain="[('object_id.model', '=', 'crm.meeting')]" />
|
domain="[('object_id.model', '=', 'crm.meeting')]" />
|
||||||
<newline/>
|
<field name="allday" colspan="2" on_change="onchange_allday(allday)" />
|
||||||
<field name="date" string="Start Date" required="1"
|
<field name="date" string="Start Date" required="1"
|
||||||
on_change="onchange_dates(date,duration,False)" />
|
on_change="onchange_dates(date,duration,False)" />
|
||||||
<field name="duration" widget="float_time"
|
<field name="duration" widget="float_time"
|
||||||
on_change="onchange_dates(date,duration,False)" />
|
on_change="onchange_dates(date,duration,False)" />
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group col="4" colspan="4" name="rrule" attrs="{'invisible': [('rrule_type','!=','custom')]}">
|
<group col="4" colspan="4" name="rrule" attrs="{'invisible': [('rrule_type','!=','custom')]}">
|
||||||
<separator string="Select data for Custom Rule" colspan="8"/>
|
<separator string="Data for Custom Rule" colspan="8"/>
|
||||||
<group col="8" colspan="4">
|
<group col="8" colspan="4">
|
||||||
<field name="freq" />
|
<field name="freq" />
|
||||||
<field name="interval" />
|
<field name="interval" />
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
<separator colspan="2" string="Visibility"/>
|
<separator colspan="2" string="Visibility"/>
|
||||||
<field name="class" string="Privacy"/>
|
<field name="class" string="Privacy"/>
|
||||||
<field name="show_as" string="Show time as" groups="base.group_extended"/>
|
<field name="show_as" string="Show time as" groups="base.group_extended"/>
|
||||||
<field name="rrule" groups="base.group_extended" />
|
<field name="rrule" invisible="1" />
|
||||||
<field name="recurrent_id" invisible="1"/>
|
<field name="recurrent_id" invisible="1"/>
|
||||||
<field name="recurrent_uid" invisible="1"/>
|
<field name="recurrent_uid" invisible="1"/>
|
||||||
</group>
|
</group>
|
||||||
|
@ -129,6 +129,9 @@
|
||||||
<button name="case_close" string="Done"
|
<button name="case_close" string="Done"
|
||||||
states="open" type="object"
|
states="open" type="object"
|
||||||
icon="gtk-jump-to" />
|
icon="gtk-jump-to" />
|
||||||
|
<button name="case_reset" string="Reset to Unconfirmed"
|
||||||
|
states="open,done,cancel" type="object"
|
||||||
|
icon="gtk-convert" />
|
||||||
<button name="case_open" string="Confirm"
|
<button name="case_open" string="Confirm"
|
||||||
states="draft" type="object"
|
states="draft" type="object"
|
||||||
icon="gtk-go-forward" />
|
icon="gtk-go-forward" />
|
||||||
|
@ -211,7 +214,7 @@
|
||||||
<field name="date" string="Meeting Date" />
|
<field name="date" string="Meeting Date" />
|
||||||
<field name="duration" />
|
<field name="duration" />
|
||||||
<field name="user_id" />
|
<field name="user_id" />
|
||||||
<field name="state" invisible="1"/>
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -257,12 +260,12 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="Search Meetings">
|
<search string="Search Meetings">
|
||||||
<group col="12" colspan="4">
|
<group col="12" colspan="4">
|
||||||
<filter icon="terp-crm" string="My Meetings"
|
<filter icon="terp-project" name="current" string="Current"
|
||||||
|
domain="[('state','in',('draft', 'open'))]"
|
||||||
|
help="Current Meetings"/>
|
||||||
|
<filter icon="terp-partner" string="My Meetings"
|
||||||
domain="[('user_id','=',uid)]"
|
domain="[('user_id','=',uid)]"
|
||||||
help="My Meetings" />
|
help="My Meetings" />
|
||||||
<filter icon="terp-crm" name="current" string="Current"
|
|
||||||
domain="[('state','in',('draft', 'open'))]"
|
|
||||||
help="Current Meetings"/>
|
|
||||||
<separator orientation="vertical"/>
|
<separator orientation="vertical"/>
|
||||||
<field name="name" select="1" string="Subject"/>
|
<field name="name" select="1" string="Subject"/>
|
||||||
<field name="partner_id" select="1" />
|
<field name="partner_id" select="1" />
|
||||||
|
@ -274,6 +277,22 @@
|
||||||
</field>
|
</field>
|
||||||
<field name="user_id" select="1" widget="selection"/>
|
<field name="user_id" select="1" widget="selection"/>
|
||||||
</group>
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="0" string="Group By..." colspan="16">
|
||||||
|
<filter string="Date" icon="terp-project"
|
||||||
|
domain="[]" context="{'group_by':'date'}" />
|
||||||
|
<filter string="Privacy" icon="terp-crm"
|
||||||
|
domain="[]" context="{'group_by':'class'}" />
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="Responsible" icon="terp-crm" domain="[]"
|
||||||
|
context="{'group_by':'user_id'}" />
|
||||||
|
<filter string="Partner" icon="terp-partner" domain="[]"
|
||||||
|
context="{'group_by':'partner_id'}" />
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="State" icon="terp-crm" domain="[]"
|
||||||
|
context="{'group_by':'state'}" />
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
</group>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -305,5 +324,22 @@
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Partners inherited form -->
|
||||||
|
|
||||||
|
<record id="view_meeting_partner_info_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.meeting.info.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<notebook position="inside">
|
||||||
|
<page string="CRM Info">
|
||||||
|
<field name="meeting_ids" colspan="4" nolabel="1" />
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -37,112 +37,62 @@ AVAILABLE_STATES = [
|
||||||
class crm_opportunity(osv.osv):
|
class crm_opportunity(osv.osv):
|
||||||
""" Opportunity Cases """
|
""" Opportunity Cases """
|
||||||
|
|
||||||
_name = "crm.opportunity"
|
_name = "crm.lead"
|
||||||
_description = "Opportunity Cases"
|
_description = "Opportunity Cases"
|
||||||
_order = "priority,date_action,id desc"
|
_order = "priority,date_action,id desc"
|
||||||
_inherit = 'crm.case'
|
_inherit = 'crm.lead'
|
||||||
|
|
||||||
def case_open(self, cr, uid, ids, *args):
|
_columns = {
|
||||||
|
# From crm.case
|
||||||
|
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
|
||||||
|
domain="[('partner_id','=',partner_id)]"),
|
||||||
|
|
||||||
|
# Opportunity fields
|
||||||
|
'probability': fields.float('Probability (%)'),
|
||||||
|
'planned_revenue': fields.float('Expected Revenue'),
|
||||||
|
'ref': fields.reference('Reference', selection=crm._links_get, size=128),
|
||||||
|
'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
|
||||||
|
'phone': fields.char("Phone", size=64),
|
||||||
|
'date_deadline': fields.date('Expected Closing'),
|
||||||
|
'date_action': fields.date('Next Action'),
|
||||||
|
}
|
||||||
|
|
||||||
|
def case_close(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides close for crm_case for setting probability and close date
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
"""
|
"""
|
||||||
|
res = super(crm_opportunity, self).case_close(cr, uid, ids, args)
|
||||||
|
self.write(cr, uid, ids, {'probability' : 100.0, 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def case_cancel(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides cancel for crm_case for setting probability
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
res = super(crm_opportunity, self).case_cancel(cr, uid, ids, args)
|
||||||
|
self.write(cr, uid, ids, {'probability' : 0.0})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def case_open(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides cancel for crm_case for setting Open Date
|
||||||
@param self: The object pointer
|
@param self: The object pointer
|
||||||
@param cr: the current row, from the database cursor,
|
@param cr: the current row, from the database cursor,
|
||||||
@param uid: the current user’s ID for security checks,
|
@param uid: the current user’s ID for security checks,
|
||||||
@param ids: List of case's Ids
|
@param ids: List of case's Ids
|
||||||
@param *args: Give Tuple Value
|
@param *args: Give Tuple Value
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res = super(crm_opportunity, self).case_open(cr, uid, ids, *args)
|
res = super(crm_opportunity, self).case_open(cr, uid, ids, *args)
|
||||||
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _compute_day(self, cr, uid, ids, fields, args, context={}):
|
|
||||||
"""
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of Openday’s IDs
|
|
||||||
@return: difference between current date and log date
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
cal_obj = self.pool.get('resource.calendar')
|
|
||||||
res_obj = self.pool.get('resource.resource')
|
|
||||||
|
|
||||||
res = {}
|
|
||||||
for opportunity in self.browse(cr, uid, ids , context):
|
|
||||||
for field in fields:
|
|
||||||
res[opportunity.id] = {}
|
|
||||||
duration = 0
|
|
||||||
ans = False
|
|
||||||
if field == 'day_open':
|
|
||||||
if opportunity.date_open:
|
|
||||||
date_create = datetime.strptime(opportunity.create_date, "%Y-%m-%d %H:%M:%S")
|
|
||||||
date_open = datetime.strptime(opportunity.date_open, "%Y-%m-%d %H:%M:%S")
|
|
||||||
ans = date_open - date_create
|
|
||||||
date_until = opportunity.date_open
|
|
||||||
elif field == 'day_close':
|
|
||||||
if opportunity.date_closed:
|
|
||||||
date_create = datetime.strptime(opportunity.create_date, "%Y-%m-%d %H:%M:%S")
|
|
||||||
date_close = datetime.strptime(opportunity.date_closed, "%Y-%m-%d %H:%M:%S")
|
|
||||||
date_until = opportunity.date_closed
|
|
||||||
ans = date_close - date_create
|
|
||||||
if ans:
|
|
||||||
resource_id = False
|
|
||||||
if opportunity.user_id:
|
|
||||||
resource_ids = res_obj.search(cr, uid, [('user_id','=',opportunity.user_id.id)])
|
|
||||||
if resource_ids and len(resource_ids):
|
|
||||||
resource_id = resource_ids[0]
|
|
||||||
|
|
||||||
duration = float(ans.days)
|
|
||||||
if opportunity.section_id.resource_calendar_id:
|
|
||||||
duration = float(ans.days) * 24
|
|
||||||
new_dates = cal_obj.interval_get(cr,
|
|
||||||
uid,
|
|
||||||
opportunity.section_id.resource_calendar_id and opportunity.section_id.resource_calendar_id.id or False,
|
|
||||||
mx.DateTime.strptime(opportunity.create_date, '%Y-%m-%d %H:%M:%S'),
|
|
||||||
duration,
|
|
||||||
resource=resource_id
|
|
||||||
)
|
|
||||||
no_days = []
|
|
||||||
date_until = mx.DateTime.strptime(date_until, '%Y-%m-%d %H:%M:%S')
|
|
||||||
for in_time, out_time in new_dates:
|
|
||||||
if in_time.date not in no_days:
|
|
||||||
no_days.append(in_time.date)
|
|
||||||
if out_time > date_until:
|
|
||||||
break
|
|
||||||
duration = len(no_days)
|
|
||||||
res[opportunity.id][field] = abs(int(duration))
|
|
||||||
return res
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'stage_id': fields.many2one ('crm.case.stage', 'Stage', \
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.opportunity')]"),
|
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
|
||||||
domain="[('section_id','=',section_id), \
|
|
||||||
('object_id.model', '=', 'crm.opportunity')]"),
|
|
||||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
|
||||||
'referred': fields.char('Referred By', size=64),
|
|
||||||
'probability': fields.float('Probability (%)'),
|
|
||||||
'planned_revenue': fields.float('Expected Revenue'),
|
|
||||||
'ref': fields.reference('Reference', selection=crm._links_get, size=128),
|
|
||||||
'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
|
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
|
||||||
'user_id': fields.many2one('res.users', 'Salesman'),
|
|
||||||
'phone': fields.char("Phone", size=64),
|
|
||||||
'date_deadline': fields.date('Expected Closing'),
|
|
||||||
'date_action': fields.date('Next Action'),
|
|
||||||
'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True,
|
|
||||||
help='The state is set to \'Draft\', when a case is created.\
|
|
||||||
\nIf the case is in progress the state is set to \'Open\'.\
|
|
||||||
\nWhen the case is over, the state is set to \'Done\'.\
|
|
||||||
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
|
|
||||||
|
|
||||||
'date_open': fields.datetime('Opened', readonly=True),
|
|
||||||
'day_open': fields.function(_compute_day, string='Days to Open', \
|
|
||||||
method=True, multi='day_open', type="float", store=True),
|
|
||||||
'day_close': fields.function(_compute_day, string='Days to Close', \
|
|
||||||
method=True, multi='day_close', type="float", store=True),
|
|
||||||
}
|
|
||||||
|
|
||||||
def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
|
def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
|
||||||
|
|
||||||
""" @param self: The object pointer
|
""" @param self: The object pointer
|
||||||
|
@ -157,40 +107,11 @@ class crm_opportunity(osv.osv):
|
||||||
stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
|
stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
|
||||||
if not stage.on_change:
|
if not stage.on_change:
|
||||||
return {'value':{}}
|
return {'value':{}}
|
||||||
return {'value':{'probability':stage.probability}}
|
return {'value':{'probability': stage.probability}}
|
||||||
|
|
||||||
def stage_next(self, cr, uid, ids, context={}):
|
|
||||||
|
|
||||||
""" @param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of stage next’s IDs
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
res = super(crm_opportunity, self).stage_next(cr, uid, ids, context=context)
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
if case.stage_id and case.stage_id.on_change:
|
|
||||||
self.write(cr, uid, [case.id], {'probability': case.stage_id.probability})
|
|
||||||
return res
|
|
||||||
|
|
||||||
def stage_previous(self, cr, uid, ids, context={}):
|
|
||||||
|
|
||||||
""" @param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of stage previous’s IDs
|
|
||||||
@param context: A standard dictionary for contextual values """
|
|
||||||
|
|
||||||
res = super(crm_opportunity, self).stage_previous(cr, uid, ids, context=context)
|
|
||||||
for case in self.browse(cr, uid, ids, context):
|
|
||||||
if case.stage_id and case.stage_id.on_change:
|
|
||||||
self.write(cr, uid, [case.id], {'probability': case.stage_id.probability})
|
|
||||||
return res
|
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.opportunity', context=c),
|
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
|
||||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
||||||
'state' : 'draft',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def action_makeMeeting(self, cr, uid, ids, context=None):
|
def action_makeMeeting(self, cr, uid, ids, context=None):
|
||||||
|
@ -231,7 +152,6 @@ class crm_opportunity(osv.osv):
|
||||||
}
|
}
|
||||||
value = {
|
value = {
|
||||||
'name': _('Meetings'),
|
'name': _('Meetings'),
|
||||||
'domain': "[('user_id','=',%s)]" % (uid),
|
|
||||||
'context': context,
|
'context': context,
|
||||||
'view_type': 'form',
|
'view_type': 'form',
|
||||||
'view_mode': 'calendar,form,tree',
|
'view_mode': 'calendar,form,tree',
|
||||||
|
@ -242,8 +162,20 @@ class crm_opportunity(osv.osv):
|
||||||
'search_view_id': res['res_id'],
|
'search_view_id': res['res_id'],
|
||||||
'nodestroy': True
|
'nodestroy': True
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
crm_opportunity()
|
crm_opportunity()
|
||||||
|
|
||||||
|
class res_partner(osv.osv):
|
||||||
|
""" Inherits partner and adds Opportunities information in the partner form """
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'opportunity_ids': fields.one2many('crm.lead', 'partner_id',\
|
||||||
|
'Opportunities', domain=[('type', '=', 'opportunity')]),
|
||||||
|
}
|
||||||
|
|
||||||
|
res_partner()
|
||||||
|
|
||||||
|
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -4,91 +4,91 @@
|
||||||
<record model="crm.case.categ" id="categ_oppor1">
|
<record model="crm.case.categ" id="categ_oppor1">
|
||||||
<field name="name">Existing Customer</field>
|
<field name="name">Existing Customer</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor2">
|
<record model="crm.case.categ" id="categ_oppor2">
|
||||||
<field name="name">Self Generated</field>
|
<field name="name">Self Generated</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor3">
|
<record model="crm.case.categ" id="categ_oppor3">
|
||||||
<field name="name">Employee</field>
|
<field name="name">Employee</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor4">
|
<record model="crm.case.categ" id="categ_oppor4">
|
||||||
<field name="name">Partner</field>
|
<field name="name">Partner</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor5">
|
<record model="crm.case.categ" id="categ_oppor5">
|
||||||
<field name="name">Campaign</field>
|
<field name="name">Campaign</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor6">
|
<record model="crm.case.categ" id="categ_oppor6">
|
||||||
<field name="name">Website</field>
|
<field name="name">Website</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor7">
|
<record model="crm.case.categ" id="categ_oppor7">
|
||||||
<field name="name">Word of mouth</field>
|
<field name="name">Word of mouth</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.categ" id="categ_oppor8">
|
<record model="crm.case.categ" id="categ_oppor8">
|
||||||
<field name="name">Other</field>
|
<field name="name">Other</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- CASE STATUS(stage_id) -->
|
<!-- CASE STATUS(stage_id) -->
|
||||||
<record model="crm.case.stage" id="stage_oppor1">
|
<record model="crm.case.stage" id="stage_oppor1">
|
||||||
<field name="name">Prospecting</field>
|
<field name="name">Prospecting</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor2">
|
<record model="crm.case.stage" id="stage_oppor2">
|
||||||
<field name="name">Needs Analysis</field>
|
<field name="name">Needs Analysis</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor3">
|
<record model="crm.case.stage" id="stage_oppor3">
|
||||||
<field name="name">Value Proposition</field>
|
<field name="name">Value Proposition</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor4">
|
<record model="crm.case.stage" id="stage_oppor4">
|
||||||
<field name="name">Proposal/Price Quote</field>
|
<field name="name">Proposal/Price Quote</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor5">
|
<record model="crm.case.stage" id="stage_oppor5">
|
||||||
<field name="name">Negotiation/Review</field>
|
<field name="name">Negotiation/Review</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor6">
|
<record model="crm.case.stage" id="stage_oppor6">
|
||||||
<field name="name">Closed Won</field>
|
<field name="name">Closed Won</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.stage" id="stage_oppor7">
|
<record model="crm.case.stage" id="stage_oppor7">
|
||||||
<field name="name">Closed Lost</field>
|
<field name="name">Closed Lost</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Case Resource(type_id) -->
|
<!-- Case Resource(type_id) -->
|
||||||
<record model="crm.case.resource.type" id="type_oppor1">
|
<record model="crm.case.resource.type" id="type_oppor1">
|
||||||
<field name="name">Existing Business</field>
|
<field name="name">Existing Business</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
<record model="crm.case.resource.type" id="type_oppor2">
|
<record model="crm.case.resource.type" id="type_oppor2">
|
||||||
<field name="name">New Business</field>
|
<field name="name">New Business</field>
|
||||||
<field name="section_id" ref="section_sales_department"/>
|
<field name="section_id" ref="section_sales_department"/>
|
||||||
<field name="object_id" search="[('model','=','crm.opportunity')]" model="ir.model"/>
|
<field name="object_id" search="[('model','=','crm.lead')]" model="ir.model"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
|
|
||||||
<!--For Opportunity-->
|
<!--For Opportunity-->
|
||||||
|
|
||||||
<record id="crm_case_construstazunits0" model="crm.opportunity">
|
<record id="crm_case_construstazunits0" model="crm.lead">
|
||||||
<field eval="60" name="probability"/>
|
<field eval="60" name="probability"/>
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_zen"/>
|
<field name="partner_address_id" ref="base.res_partner_address_zen"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
<field name="type">opportunity</field>
|
||||||
<field name="type_id" ref="crm.type_oppor1"/>
|
<field name="type_id" ref="crm.type_oppor1"/>
|
||||||
<field name="partner_id" ref="base.res_partner_3"/>
|
<field name="partner_id" ref="base.res_partner_3"/>
|
||||||
<field eval=""3"" name="priority"/>
|
<field eval=""3"" name="priority"/>
|
||||||
|
@ -22,9 +23,10 @@
|
||||||
<field name="stage_id" ref="crm.stage_oppor3"/>
|
<field name="stage_id" ref="crm.stage_oppor3"/>
|
||||||
<field eval=""CONS TRUST (AZ) 529701 - 1000 units"" name="name"/>
|
<field eval=""CONS TRUST (AZ) 529701 - 1000 units"" name="name"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_rdroundfundingunits0" model="crm.opportunity">
|
<record id="crm_case_rdroundfundingunits0" model="crm.lead">
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_15"/>
|
<field name="partner_address_id" ref="base.res_partner_address_15"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
<field name="type">opportunity</field>
|
||||||
<field name="type_id" ref="crm.type_oppor2"/>
|
<field name="type_id" ref="crm.type_oppor2"/>
|
||||||
<field name="partner_id" ref="base.res_partner_11"/>
|
<field name="partner_id" ref="base.res_partner_11"/>
|
||||||
<field eval=""3"" name="priority"/>
|
<field eval=""3"" name="priority"/>
|
||||||
|
@ -37,10 +39,11 @@
|
||||||
<field name="stage_id" ref="crm.stage_oppor1"/>
|
<field name="stage_id" ref="crm.stage_oppor1"/>
|
||||||
<field eval=""3rd Round Funding - 1000 units "" name="name"/>
|
<field eval=""3rd Round Funding - 1000 units "" name="name"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_mediapoleunits0" model="crm.opportunity">
|
<record id="crm_case_mediapoleunits0" model="crm.lead">
|
||||||
<field eval="10" name="probability"/>
|
<field eval="10" name="probability"/>
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_3"/>
|
<field name="partner_address_id" ref="base.res_partner_address_3"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
<field name="type">opportunity</field>
|
||||||
<field name="type_id" ref="crm.type_oppor1"/>
|
<field name="type_id" ref="crm.type_oppor1"/>
|
||||||
<field name="partner_id" ref="base.res_partner_8"/>
|
<field name="partner_id" ref="base.res_partner_8"/>
|
||||||
<field eval=""3"" name="priority"/>
|
<field eval=""3"" name="priority"/>
|
||||||
|
@ -54,10 +57,11 @@
|
||||||
<field eval=""Mediapole - 5000 units"" name="name"/>
|
<field eval=""Mediapole - 5000 units"" name="name"/>
|
||||||
<field eval=""info@mediapole.net"" name="email_from"/>
|
<field eval=""info@mediapole.net"" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_abcfuelcounits0" model="crm.opportunity">
|
<record id="crm_case_abcfuelcounits0" model="crm.lead">
|
||||||
<field eval="40" name="probability"/>
|
<field eval="40" name="probability"/>
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_1"/>
|
<field name="partner_address_id" ref="base.res_partner_address_1"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
<field name="type">opportunity</field>
|
||||||
<field name="type_id" ref="crm.type_oppor1"/>
|
<field name="type_id" ref="crm.type_oppor1"/>
|
||||||
<field name="partner_id" ref="base.res_partner_9"/>
|
<field name="partner_id" ref="base.res_partner_9"/>
|
||||||
<field eval=""3"" name="priority"/>
|
<field eval=""3"" name="priority"/>
|
||||||
|
@ -70,10 +74,11 @@
|
||||||
<field eval=""ABC FUEL CO 829264 - 1000 units "" name="name"/>
|
<field eval=""ABC FUEL CO 829264 - 1000 units "" name="name"/>
|
||||||
<field eval=""info@opensides.be"" name="email_from"/>
|
<field eval=""info@opensides.be"" name="email_from"/>
|
||||||
</record>
|
</record>
|
||||||
<record id="crm_case_dirtminingltdunits0" model="crm.opportunity">
|
<record id="crm_case_dirtminingltdunits0" model="crm.lead">
|
||||||
<field eval="80" name="probability"/>
|
<field eval="80" name="probability"/>
|
||||||
<field name="partner_address_id" ref="base.res_partner_address_wong"/>
|
<field name="partner_address_id" ref="base.res_partner_address_wong"/>
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
|
<field name="type">opportunity</field>
|
||||||
<field name="partner_id" ref="base.res_partner_maxtor"/>
|
<field name="partner_id" ref="base.res_partner_maxtor"/>
|
||||||
<field eval=""3"" name="priority"/>
|
<field eval=""3"" name="priority"/>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
id="act_crm_opportunity_crm_meeting_new"
|
id="act_crm_opportunity_crm_meeting_new"
|
||||||
name="Meetings"
|
name="Meetings"
|
||||||
res_model="crm.meeting"
|
res_model="crm.meeting"
|
||||||
src_model="crm.opportunity"
|
src_model="crm.lead"
|
||||||
view_mode="tree,form,calendar,"
|
view_mode="tree,form,calendar,"
|
||||||
context="{'default_duration': 4.0, 'default_opportunity_id': active_id}"
|
context="{'default_duration': 4.0, 'default_opportunity_id': active_id}"
|
||||||
domain="[('user_id','=',uid), ('opportunity_id', '=', active_id)]"
|
domain="[('user_id','=',uid), ('opportunity_id', '=', active_id)]"
|
||||||
|
@ -16,39 +16,46 @@
|
||||||
id="act_crm_opportunity_crm_phonecall_new"
|
id="act_crm_opportunity_crm_phonecall_new"
|
||||||
name="Phone calls"
|
name="Phone calls"
|
||||||
res_model="crm.phonecall"
|
res_model="crm.phonecall"
|
||||||
src_model="crm.opportunity"
|
src_model="crm.lead"
|
||||||
view_mode="calendar,tree,form"
|
view_mode="calendar,tree,form"
|
||||||
context="{'default_duration': 1.0}"
|
context="{'default_duration': 1.0 ,'default_opportunity_id': active_id}"
|
||||||
domain="[('user_id','=',uid)]"
|
domain="[('user_id','=',uid),('opportunity_id', '=', active_id)]"
|
||||||
view_type="form"/>
|
view_type="form"/>
|
||||||
|
|
||||||
<record model="ir.actions.act_window" id="crm_case_category_act_oppor11">
|
<record model="ir.actions.act_window" id="crm_case_category_act_oppor11">
|
||||||
<field name="name">Opportunities</field>
|
<field name="name">Opportunities</field>
|
||||||
<field name="res_model">crm.opportunity</field>
|
<field name="res_model">crm.lead</field>
|
||||||
<field name="view_mode">tree,form,graph</field>
|
<field name="view_mode">tree,form,graph</field>
|
||||||
|
<field name="domain">[('type','=','opportunity')]</field>
|
||||||
|
<field name="context">{'search_default_current':1}</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
||||||
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
|
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_oppor11">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_oppor11">
|
||||||
<field name="sequence" eval="1"/>
|
<field name="sequence" eval="1"/>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
<field name="view_id" ref="crm_case_tree_view_oppor"/>
|
||||||
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_oppor11">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_oppor11">
|
||||||
<field name="sequence" eval="2"/>
|
<field name="sequence" eval="2"/>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="crm_case_form_view_oppor"/>
|
<field name="view_id" ref="crm_case_form_view_oppor"/>
|
||||||
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_graph_view_oppor11">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_graph_view_oppor11">
|
||||||
<field name="sequence" eval="4"/>
|
<field name="sequence" eval="4"/>
|
||||||
<field name="view_mode">graph</field>
|
<field name="view_mode">graph</field>
|
||||||
<field name="view_id" ref="crm_case_graph_view_opportunity"/>
|
<field name="view_id" ref="crm_case_graph_view_opportunity"/>
|
||||||
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="base.menu_sales" name="Sales"
|
<menuitem id="base.menu_sales" name="Sales"
|
||||||
parent="base.menu_base_partner" sequence="1" />
|
parent="base.menu_base_partner" sequence="1" />
|
||||||
|
|
||||||
<menuitem name="Opportunities" id="menu_crm_case_opp"
|
<menuitem name="Opportunities" id="menu_crm_case_opp"
|
||||||
parent="base.menu_sales" action="crm_case_category_act_oppor11"
|
parent="base.menu_sales" action="crm_case_category_act_oppor11"
|
||||||
sequence="2" />
|
sequence="2" />
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<field name="res_model">crm.case.categ</field>
|
<field name="res_model">crm.case.categ</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm.crm_case_categ_tree-view"/>
|
<field name="view_id" ref="crm.crm_case_categ_tree-view"/>
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.opportunity')]</field>
|
<field name="domain">[('object_id.model', '=', 'crm.lead')]</field>
|
||||||
<field name="context">{'object_id':'crm.opportunity'}</field>
|
<field name="context">{'object_id':'crm.lead'}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem action="crm_opportunity_categ_action"
|
<menuitem action="crm_opportunity_categ_action"
|
||||||
|
@ -24,8 +24,8 @@
|
||||||
<field name="res_model">crm.case.stage</field>
|
<field name="res_model">crm.case.stage</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm.crm_case_stage_tree"/>
|
<field name="view_id" ref="crm.crm_case_stage_tree"/>
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.opportunity')]</field>
|
<field name="domain">[('object_id.model', '=', 'crm.lead')]</field>
|
||||||
<field name="context">{'object_id':'crm.opportunity'}</field>
|
<field name="context">{'object_id':'crm.lead'}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem action="crm_opportunity_stage_act"
|
<menuitem action="crm_opportunity_stage_act"
|
||||||
|
@ -39,8 +39,8 @@
|
||||||
<field name="res_model">crm.case.resource.type</field>
|
<field name="res_model">crm.case.resource.type</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm.crm_case_resource_type_tree"/>
|
<field name="view_id" ref="crm.crm_case_resource_type_tree"/>
|
||||||
<field name="domain">[('object_id.model', '=', 'crm.opportunity')]</field>
|
<field name="domain">[('object_id.model', '=', 'crm.lead')]</field>
|
||||||
<field name="context">{'object_id':'crm.opportunity'}</field>
|
<field name="context">{'object_id':'crm.lead'}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem action="crm_opportunity_resource_act"
|
<menuitem action="crm_opportunity_resource_act"
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_form_view_oppor">
|
<record model="ir.ui.view" id="crm_case_form_view_oppor">
|
||||||
<field name="name">Opportunities</field>
|
<field name="name">Opportunities</field>
|
||||||
<field name="model">crm.opportunity</field>
|
<field name="model">crm.lead</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Opportunities">
|
<form string="Opportunities">
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
<field name="stage_id" nolabel="1"
|
<field name="stage_id" nolabel="1"
|
||||||
on_change="onchange_stage_id(stage_id)"
|
on_change="onchange_stage_id(stage_id)"
|
||||||
widget="selection"
|
widget="selection"
|
||||||
domain="[('object_id.model', '=', 'crm.opportunity')]" />
|
domain="[('object_id.model', '=', 'crm.lead')]" />
|
||||||
<button name="stage_previous"
|
<button name="stage_previous"
|
||||||
states="open,pending" type="object"
|
states="open,pending" type="object"
|
||||||
icon="gtk-go-back" string="" />
|
icon="gtk-go-back" string="" />
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
</group>
|
</group>
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Opportunity">
|
<page string="Opportunity">
|
||||||
<group col="3" colspan="2">
|
<group col="4" colspan="2">
|
||||||
<separator colspan="4" string="Contacts"/>
|
<separator colspan="4" string="Contacts"/>
|
||||||
<field name="partner_id" select="1"
|
<field name="partner_id" select="1"
|
||||||
on_change="onchange_partner_id(partner_id, email_from)"
|
on_change="onchange_partner_id(partner_id, email_from)"
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
<field name="section_id" colspan="1" widget="selection"/>
|
<field name="section_id" colspan="1" widget="selection"/>
|
||||||
<field name="categ_id" select="1" groups="base.group_extended"
|
<field name="categ_id" select="1" groups="base.group_extended"
|
||||||
string="Category" widget="selection"
|
string="Category" widget="selection"
|
||||||
domain="[('object_id.model', '=', 'crm.opportunity')]" />
|
domain="[('object_id.model', '=', 'crm.lead')]" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<separator colspan="4" string="Details"/>
|
<separator colspan="4" string="Details"/>
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
icon="gtk-media-pause" />
|
icon="gtk-media-pause" />
|
||||||
<button name="case_close" string="Mark Won"
|
<button name="case_close" string="Mark Won"
|
||||||
states="open,draft,pending" type="object"
|
states="open,draft,pending" type="object"
|
||||||
icon="gtk-close" />
|
icon="gtk-apply" />
|
||||||
<button name="case_cancel" string="Mark Lost"
|
<button name="case_cancel" string="Mark Lost"
|
||||||
states="draft,open,pending" type="object"
|
states="draft,open,pending" type="object"
|
||||||
icon="gtk-cancel" />
|
icon="gtk-cancel" />
|
||||||
|
@ -153,7 +153,12 @@
|
||||||
<field name="ref"/>
|
<field name="ref"/>
|
||||||
<field name="ref2"/>
|
<field name="ref2"/>
|
||||||
<field name="log_ids" nolabel="1" colspan="4">
|
<field name="log_ids" nolabel="1" colspan="4">
|
||||||
<form string="Actions">
|
<tree string="Logs">
|
||||||
|
<field name="name" colspan="4"/>
|
||||||
|
<field name="date"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
</tree>
|
||||||
|
<form string="Logs">
|
||||||
<separator string="Action Information" colspan="4"/>
|
<separator string="Action Information" colspan="4"/>
|
||||||
<field name="name" colspan="4"/>
|
<field name="name" colspan="4"/>
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
|
@ -165,23 +170,19 @@
|
||||||
<group colspan="4">
|
<group colspan="4">
|
||||||
<field colspan="4" name="email_cc" string="CC"/>
|
<field colspan="4" name="email_cc" string="CC"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="history_line" colspan="4" nolabel="1" mode="form,tree">
|
<field name="message_ids" colspan="4" nolabel="1" mode="form,tree">
|
||||||
<form string="Communication history">
|
<form string="Communication history">
|
||||||
<group col="7" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<field name="email_to"/>
|
<field name="email_to"/>
|
||||||
<field name="email_from"/>
|
<field name="email_from"/>
|
||||||
<button
|
</group>
|
||||||
string="Add a CC"
|
|
||||||
name="%(crm.action_view_crm_email_add_cc_wizard)d"
|
|
||||||
icon="gtk-add" type="action"/>
|
|
||||||
</group>
|
|
||||||
<newline/>
|
<newline/>
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
<field name="description" colspan="4" nolabel="1"/>
|
||||||
<button colspan="4"
|
<button colspan="4"
|
||||||
string="Reply to Last Email"
|
string="Reply to Last Email"
|
||||||
name="%(action_crm_send_mail)d"
|
name="%(action_crm_send_mail)d"
|
||||||
context="{'mail':'reply', 'model': 'crm.opportunity'}"
|
context="{'mail':'reply', 'model': 'crm.lead'}"
|
||||||
icon="gtk-undo" type="action" />
|
icon="gtk-undo" type="action" />
|
||||||
</form>
|
</form>
|
||||||
<tree string="Communication history">
|
<tree string="Communication history">
|
||||||
|
@ -192,10 +193,10 @@
|
||||||
</field>
|
</field>
|
||||||
<button colspan="2" string="Send New Email"
|
<button colspan="2" string="Send New Email"
|
||||||
name="%(action_crm_send_mail)d"
|
name="%(action_crm_send_mail)d"
|
||||||
context="{'mail':'new', 'model': 'crm.opportunity'}"
|
context="{'mail':'new', 'model': 'crm.lead'}"
|
||||||
icon="gtk-go-forward" type="action" />
|
icon="gtk-go-forward" type="action" />
|
||||||
<button colspan="2" string="Forward to Partner"
|
<button colspan="2" string="Forward to Partner"
|
||||||
name="%(crm_opportunity_forward_to_partner_act)d"
|
name="%(crm_lead_forward_to_partner_act)d"
|
||||||
icon="gtk-go-forward" type="action" />
|
icon="gtk-go-forward" type="action" />
|
||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
|
@ -207,7 +208,7 @@
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_tree_view_oppor">
|
<record model="ir.ui.view" id="crm_case_tree_view_oppor">
|
||||||
<field name="name">Opportunities Tree</field>
|
<field name="name">Opportunities Tree</field>
|
||||||
<field name="model">crm.opportunity</field>
|
<field name="model">crm.lead</field>
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Opportunities" colors="blue:state=='pending';grey:state in ('cancel','done')">
|
<tree string="Opportunities" colors="blue:state=='pending';grey:state in ('cancel','done')">
|
||||||
|
@ -248,7 +249,7 @@
|
||||||
|
|
||||||
<record model="ir.ui.view" id="crm_case_graph_view_opportunity">
|
<record model="ir.ui.view" id="crm_case_graph_view_opportunity">
|
||||||
<field name="name">CRM - Opportunity Graph</field>
|
<field name="name">CRM - Opportunity Graph</field>
|
||||||
<field name="model">crm.opportunity</field>
|
<field name="model">crm.lead</field>
|
||||||
<field name="type">graph</field>
|
<field name="type">graph</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<graph string="Opportunity by Categories" type="bar" orientation="horizontal">
|
<graph string="Opportunity by Categories" type="bar" orientation="horizontal">
|
||||||
|
@ -263,13 +264,13 @@
|
||||||
|
|
||||||
<record id="view_crm_case_opportunities_filter" model="ir.ui.view">
|
<record id="view_crm_case_opportunities_filter" model="ir.ui.view">
|
||||||
<field name="name">CRM - Opportunities Search</field>
|
<field name="name">CRM - Opportunities Search</field>
|
||||||
<field name="model">crm.opportunity</field>
|
<field name="model">crm.lead</field>
|
||||||
<field name="type">search</field>
|
<field name="type">search</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="Search Opportunities">
|
<search string="Search Opportunities">
|
||||||
<filter icon="terp-project"
|
<filter icon="terp-project"
|
||||||
string="Current"
|
string="Current"
|
||||||
default="1"
|
name="current"
|
||||||
domain="[('state','in',('draft','open'))]"/>
|
domain="[('state','in',('draft','open'))]"/>
|
||||||
<filter icon="terp-project"
|
<filter icon="terp-project"
|
||||||
string="Open"
|
string="Open"
|
||||||
|
@ -305,7 +306,7 @@
|
||||||
help="My section" />
|
help="My section" />
|
||||||
</field>
|
</field>
|
||||||
<newline/>
|
<newline/>
|
||||||
<group expand="1" string="Group By..." colspan="16">
|
<group expand="0" string="Group By..." colspan="16">
|
||||||
<filter string="Stage" icon="terp-crm" domain="[]"
|
<filter string="Stage" icon="terp-crm" domain="[]"
|
||||||
context="{'group_by':'stage_id'}" />
|
context="{'group_by':'stage_id'}" />
|
||||||
<filter string="Priority" icon="terp-crm" domain="[]"
|
<filter string="Priority" icon="terp-crm" domain="[]"
|
||||||
|
@ -327,5 +328,60 @@
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Partners inherited form -->
|
||||||
|
|
||||||
|
<record id="view_opp_partner_info_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.opportunity.info.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="phonecall_ids" position="after">
|
||||||
|
<field name="opportunity_ids" colspan="4" nolabel="1" domain="[('type', '=', 'opportunity')]">
|
||||||
|
<tree string="Opportunities" colors="blue:state=='pending';grey:state in ('cancel', 'done')">
|
||||||
|
<field name="create_date"/>
|
||||||
|
<field name="partner_name"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="email_from"/>
|
||||||
|
<field name="phone"/>
|
||||||
|
|
||||||
|
<field name="categ_id" invisible="1"/>
|
||||||
|
<field name="type_id" invisible="1"/>
|
||||||
|
<field name="referred" invisible="1"/>
|
||||||
|
|
||||||
|
<field name="stage_id"/>
|
||||||
|
<button name="stage_previous" string="Previous"
|
||||||
|
states="open,pending" type="object" icon="gtk-go-back" />
|
||||||
|
<button name="stage_next" string="Next"
|
||||||
|
states="open,pending" type="object"
|
||||||
|
icon="gtk-go-forward" />
|
||||||
|
<field name="section_id"
|
||||||
|
invisible="context.get('invisible_section', True)" />
|
||||||
|
<field name="user_id" />
|
||||||
|
<field name="state" />
|
||||||
|
<button name="case_open" string="Open"
|
||||||
|
states="draft,pending" type="object"
|
||||||
|
icon="gtk-go-forward" />
|
||||||
|
<button name="case_close" string="Close"
|
||||||
|
states="open,draft,pending" type="object"
|
||||||
|
icon="gtk-close" />
|
||||||
|
<button string="Convert to Opportunity"
|
||||||
|
name="convert_opportunity"
|
||||||
|
states="draft,open,pending" icon="gtk-index"
|
||||||
|
type="object" />
|
||||||
|
<button name="case_escalate" string="Escalate"
|
||||||
|
states="open,draft,pending" type="object"
|
||||||
|
icon="gtk-go-up" />
|
||||||
|
<button name="case_cancel" string="Cancel"
|
||||||
|
states="draft,open,pending" type="object"
|
||||||
|
icon="gtk-cancel" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -19,43 +19,108 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
from crm import crm_case
|
||||||
from osv import fields, osv
|
from osv import fields, osv
|
||||||
from tools.translate import _
|
from tools.translate import _
|
||||||
import crm
|
import crm
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class crm_phonecall(osv.osv):
|
class crm_phonecall(osv.osv, crm_case):
|
||||||
""" Phonecall Cases """
|
""" Phonecall Cases """
|
||||||
|
|
||||||
_name = "crm.phonecall"
|
_name = "crm.phonecall"
|
||||||
_description = "Phonecall Cases"
|
_description = "Phonecall Cases"
|
||||||
_order = "id desc"
|
_order = "id desc"
|
||||||
_inherit = 'crm.case'
|
_inherit = 'mailgate.thread'
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'duration': fields.float('Duration'),
|
# From crm.case
|
||||||
|
'section_id': fields.many2one('crm.case.section', 'Sales Team', \
|
||||||
|
select=True, help='Sales team to which Case belongs to.\
|
||||||
|
Define Responsible user and Email account for mail gateway.'),
|
||||||
|
'user_id': fields.many2one('res.users', 'Responsible'),
|
||||||
|
'partner_id': fields.many2one('res.partner', 'Partner'),
|
||||||
|
'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
|
||||||
|
domain="[('partner_id','=',partner_id)]"),
|
||||||
|
'company_id': fields.many2one('res.company', 'Company'),
|
||||||
|
'description': fields.text('Description'),
|
||||||
|
'state': fields.selection([
|
||||||
|
('draft', 'Draft'),
|
||||||
|
('open', 'Todo'),
|
||||||
|
('cancel', 'Cancelled'),
|
||||||
|
('done', 'Closed'),
|
||||||
|
('pending', 'Pending'),
|
||||||
|
], 'State', size=16, readonly=True,
|
||||||
|
help='The state is set to \'Draft\', when a case is created.\
|
||||||
|
\nIf the case is in progress the state is set to \'Open\'.\
|
||||||
|
\nWhen the case is over, the state is set to \'Done\'.\
|
||||||
|
\nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
|
||||||
|
'email_from': fields.char('Email', size=128, help="These people will receive email."),
|
||||||
|
'stage_id': fields.many2one('crm.case.stage', 'Stage', \
|
||||||
|
domain="[('section_id','=',section_id),\
|
||||||
|
('object_id.model', '=', 'crm.phonecall')]"),
|
||||||
|
'date_open': fields.datetime('Opened', readonly=True),
|
||||||
|
# phonecall fields
|
||||||
|
'duration': fields.float('Duration'),
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
('object_id.model', '=', 'crm.phonecall')]"),
|
('object_id.model', '=', 'crm.phonecall')]"),
|
||||||
'partner_phone': fields.char('Phone', size=32),
|
'partner_phone': fields.char('Phone', size=32),
|
||||||
'partner_contact': fields.related('partner_address_id', 'name',\
|
'partner_contact': fields.related('partner_address_id', 'name', \
|
||||||
type="char", string="Contact", size=128),
|
type="char", string="Contact", size=128),
|
||||||
'partner_mobile': fields.char('Mobile', size=32),
|
'partner_mobile': fields.char('Mobile', size=32),
|
||||||
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
|
||||||
'canal_id': fields.many2one('res.partner.canal', 'Channel',\
|
'canal_id': fields.many2one('res.partner.canal', 'Channel', \
|
||||||
help="The channels represent the different communication\
|
help="The channels represent the different communication\
|
||||||
modes available with the customer." \
|
modes available with the customer." \
|
||||||
" With each commercial opportunity, you can indicate\
|
" With each commercial opportunity, you can indicate\
|
||||||
the canall which is this opportunity source."),
|
the canall which is this opportunity source."),
|
||||||
'date_closed': fields.datetime('Closed', readonly=True),
|
'date_closed': fields.datetime('Closed', readonly=True),
|
||||||
'date': fields.datetime('Date'),
|
'date': fields.datetime('Date'),
|
||||||
'opportunity_id': fields.many2one ('crm.opportunity', 'Opportunity'),
|
'opportunity_id': fields.many2one ('crm.lead', 'Opportunity'),
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
|
||||||
|
'state': lambda *a: 'draft',
|
||||||
|
'active': lambda *a: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# From crm.case
|
||||||
|
|
||||||
|
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
|
||||||
|
res = super(crm_phonecall, self).onchange_partner_address_id(cr, uid, ids, add, email)
|
||||||
|
res.setdefault('value', {})
|
||||||
|
if add:
|
||||||
|
address = self.pool.get('res.partner.address').browse(cr, uid, add)
|
||||||
|
res['value']['partner_phone'] = address.phone
|
||||||
|
res['value']['partner_mobile'] = address.mobile
|
||||||
|
return res
|
||||||
|
|
||||||
|
def case_close(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides close for crm_case for setting close date
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case Ids
|
||||||
|
@param *args: Tuple Value for additional Params
|
||||||
|
"""
|
||||||
|
res = super(crm_phonecall, self).case_close(cr, uid, ids, args)
|
||||||
|
self.write(cr, uid, ids, {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def case_open(self, cr, uid, ids, *args):
|
||||||
|
"""Overrides cancel for crm_case for setting Open Date
|
||||||
|
@param self: The object pointer
|
||||||
|
@param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case's Ids
|
||||||
|
@param *args: Give Tuple Value
|
||||||
|
"""
|
||||||
|
res = super(crm_phonecall, self).case_open(cr, uid, ids, *args)
|
||||||
|
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
return res
|
||||||
|
|
||||||
def action_make_meeting(self, cr, uid, ids, context=None):
|
def action_make_meeting(self, cr, uid, ids, context=None):
|
||||||
"""
|
"""
|
||||||
|
@ -86,36 +151,39 @@ class crm_phonecall(osv.osv):
|
||||||
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
|
id3 = data_obj.browse(cr, uid, id3, context=context).res_id
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'default_phonecall_id': phonecall.id,
|
'default_phonecall_id': phonecall.id,
|
||||||
'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
|
'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
|
||||||
'default_email': phonecall.email_from ,
|
'default_email': phonecall.email_from ,
|
||||||
'default_name': phonecall.name
|
'default_name': phonecall.name
|
||||||
}
|
}
|
||||||
|
|
||||||
value = {
|
value = {
|
||||||
'name': _('Meetings'),
|
'name': _('Meetings'),
|
||||||
'domain' : "[('user_id','=',%s)]" % (uid),
|
'domain' : "[('user_id','=',%s)]" % (uid),
|
||||||
'context': context,
|
'context': context,
|
||||||
'view_type': 'form',
|
'view_type': 'form',
|
||||||
'view_mode': 'calendar,form,tree',
|
'view_mode': 'calendar,form,tree',
|
||||||
'res_model': 'crm.meeting',
|
'res_model': 'crm.meeting',
|
||||||
'view_id': False,
|
'view_id': False,
|
||||||
'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
|
'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
|
||||||
'type': 'ir.actions.act_window',
|
'type': 'ir.actions.act_window',
|
||||||
'search_view_id': res['res_id'],
|
'search_view_id': res['res_id'],
|
||||||
'nodestroy': True
|
'nodestroy': True
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
|
|
||||||
res = super(crm_phonecall, self).onchange_partner_address_id(cr, uid, ids, add, email)
|
|
||||||
res.setdefault('value', {})
|
|
||||||
if add:
|
|
||||||
address = self.pool.get('res.partner.address').browse(cr, uid, add)
|
|
||||||
res['value']['partner_phone'] = address.mobile
|
|
||||||
res['value']['partner_mobile'] = address.phone
|
|
||||||
return res
|
|
||||||
crm_phonecall()
|
crm_phonecall()
|
||||||
|
|
||||||
|
class res_partner(osv.osv):
|
||||||
|
""" Inherits partner and adds Phonecalls information in the partner form """
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'phonecall_ids': fields.one2many('crm.phonecall', 'partner_id', 'Phonecalls'),
|
||||||
|
}
|
||||||
|
|
||||||
|
res_partner()
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_phone_incoming0">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_phone_incoming0">
|
||||||
<field name="sequence" eval="1"/>
|
<field name="sequence" eval="1"/>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="view_id" ref="crm_case_phone_tree_view"/>
|
<field name="view_id" ref="crm_case_inbound_phone_tree_view"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_phone_incoming0"/>
|
<field name="act_window_id" ref="crm_case_categ_phone_incoming0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
<record model="ir.actions.act_window.view" id="action_crm_tag_form_phone_incoming0">
|
<record model="ir.actions.act_window.view" id="action_crm_tag_form_phone_incoming0">
|
||||||
<field name="sequence" eval="3"/>
|
<field name="sequence" eval="3"/>
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="crm_case_phone_form_view"/>
|
<field name="view_id" ref="crm_case_inbound_phone_form_view"/>
|
||||||
<field name="act_window_id" ref="crm_case_categ_phone_incoming0"/>
|
<field name="act_window_id" ref="crm_case_categ_phone_incoming0"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" />
|
type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" />
|
||||||
<button string="Meeting"
|
<button string="Meeting"
|
||||||
states="draft,open,pending"
|
states="draft,open,pending"
|
||||||
name="action_make_meeting" type="action" />
|
name="action_make_meeting" type="object" />
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward"/>
|
<button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward"/>
|
||||||
<button name="case_close" string="Held" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
|
<button name="case_close" string="Held" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Phone Call">
|
<form string="Phone Call">
|
||||||
<group colspan="4" col="7">
|
<group colspan="4" col="7">
|
||||||
<field name="name" string="Call Summary"/>
|
<field name="name" string="Summary"/>
|
||||||
<field name="date" string="Planned Date"/>
|
<field name="date" string="Planned Date"/>
|
||||||
<field name="user_id"/>
|
<field name="user_id"/>
|
||||||
<button string="Schedule a Meeting"
|
<button string="Schedule a Meeting"
|
||||||
|
@ -137,6 +137,93 @@
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!--Inbound Phonecalls Form View -->
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="crm_case_inbound_phone_form_view">
|
||||||
|
<field name="name">CRM - Inbound Phone Call Form</field>
|
||||||
|
<field name="model">crm.phonecall</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Phone Call">
|
||||||
|
<group colspan="4" col="7">
|
||||||
|
<field name="name" string="Summary"/>
|
||||||
|
<field name="date" string="Planned Date"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<button string="Schedule a Meeting"
|
||||||
|
name="action_make_meeting"
|
||||||
|
icon="gtk-redo"
|
||||||
|
type="object" />
|
||||||
|
|
||||||
|
<field name="partner_phone"/>
|
||||||
|
<field name="duration"/>
|
||||||
|
<field name="section_id" colspan="1" widget="selection" />
|
||||||
|
<button string="Convert to Opportunity"
|
||||||
|
name="%(phonecall2opportunity_act)d"
|
||||||
|
icon="gtk-index" type="action"
|
||||||
|
attrs="{'invisible':[('opportunity_id','!=',False)]}" />
|
||||||
|
<label colspan="6" string=""/>
|
||||||
|
<button string="Schedule Other Call"
|
||||||
|
icon="gtk-redo"
|
||||||
|
name="%(phonecall_to_phonecall_act)d"
|
||||||
|
type="action" />
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<group col="3" colspan="2">
|
||||||
|
<separator colspan="3" string="Contacts" />
|
||||||
|
<field name="partner_id"
|
||||||
|
on_change="onchange_partner_id(partner_id, email_from)" />
|
||||||
|
<button string="Create a Partner"
|
||||||
|
icon="terp-crm"
|
||||||
|
name="%(action_crm_phonecall2partner)d"
|
||||||
|
type="action"
|
||||||
|
attrs="{'invisible':[('partner_id','!=',False)]}" />
|
||||||
|
<newline/>
|
||||||
|
<field name="partner_address_id" string="Contact"
|
||||||
|
on_change="onchange_partner_address_id(partner_address_id, email_from)" />
|
||||||
|
<newline/>
|
||||||
|
<field name="partner_phone" />
|
||||||
|
<newline/>
|
||||||
|
<field name="partner_mobile" />
|
||||||
|
</group>
|
||||||
|
<group col="2" colspan="2">
|
||||||
|
<separator colspan="2" string="Categorization" />
|
||||||
|
<field name="priority"/>
|
||||||
|
<field name="opportunity_id"/>
|
||||||
|
</group>
|
||||||
|
<separator string="Description" colspan="4" />
|
||||||
|
<field name="description" nolabel="1" colspan="4" />
|
||||||
|
<separator colspan="4" />
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Inbound Phonecalls Tree View -->
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="crm_case_inbound_phone_tree_view">
|
||||||
|
<field name="name">CRM - Inbound Phone Calls Tree</field>
|
||||||
|
<field name="model">crm.phonecall</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Phone Calls" colors="grey:state in ('cancel','done');blue:state in ('pending',)">
|
||||||
|
<field name="date" string="Date"/>
|
||||||
|
<field name="name" string="Call Summary"/>
|
||||||
|
<field name="partner_id" string="Partner"/>
|
||||||
|
<field name="partner_contact"/>
|
||||||
|
<field name="partner_phone"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
<field name="state" invisible="1"/>
|
||||||
|
<button string="Opportunity"
|
||||||
|
name="%(phonecall2opportunity_act)d"
|
||||||
|
states="draft,open,pending"
|
||||||
|
icon="gtk-convert"
|
||||||
|
type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}" />
|
||||||
|
<button string="Meeting"
|
||||||
|
states="draft,open,pending" icon="gtk-redo"
|
||||||
|
name="action_make_meeting" type="object" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<!-- Phonecalls Calendar View -->
|
<!-- Phonecalls Calendar View -->
|
||||||
|
|
||||||
|
@ -190,7 +277,7 @@
|
||||||
help="My section" />
|
help="My section" />
|
||||||
</field>
|
</field>
|
||||||
<newline/>
|
<newline/>
|
||||||
<group expand="1" string="Group By..." colspan="4">
|
<group expand="0" string="Group By..." colspan="4">
|
||||||
<filter string="Partner" icon="terp-crm" domain="[]"
|
<filter string="Partner" icon="terp-crm" domain="[]"
|
||||||
context="{'group_by':'partner_id'}" />
|
context="{'group_by':'partner_id'}" />
|
||||||
<filter string="Responsible" icon="terp-crm"
|
<filter string="Responsible" icon="terp-crm"
|
||||||
|
@ -204,5 +291,20 @@
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- Partners inherited form -->
|
||||||
|
|
||||||
|
<record id="view_phonecall_partner_info_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.phonecall.info.inherit</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="meeting_ids" position="after">
|
||||||
|
<field name="phonecall_ids" colspan="4" nolabel="1" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -37,39 +37,26 @@ class crm_segmentation(osv.osv):
|
||||||
'description': fields.text('Description'),
|
'description': fields.text('Description'),
|
||||||
'categ_id': fields.many2one('res.partner.category', 'Partner Category',\
|
'categ_id': fields.many2one('res.partner.category', 'Partner Category',\
|
||||||
required=True, help='The partner category that will be \
|
required=True, help='The partner category that will be \
|
||||||
added to partners that match the segmentation criterions after computation.'),
|
added to partners that match the segmentation criterions after computation.'),
|
||||||
'exclusif': fields.boolean('Exclusive', help='Check if the category is \
|
'exclusif': fields.boolean('Exclusive', help='Check if the category is limited to partners that match the segmentation criterions.\
|
||||||
limited to partners that match the segmentation criterions.\
|
\nIf checked, remove the category from partners that doesn\'t match segmentation criterions'),
|
||||||
If checked, remove the category from partners that doesn\'t \
|
|
||||||
match segmentation criterions'),
|
|
||||||
'state': fields.selection([('not running','Not Running'),\
|
'state': fields.selection([('not running','Not Running'),\
|
||||||
('running','Running')], 'Execution Status', readonly=True),
|
('running','Running')], 'Execution Status', readonly=True),
|
||||||
'partner_id': fields.integer('Max Partner ID processed'),
|
'partner_id': fields.integer('Max Partner ID processed'),
|
||||||
'segmentation_line': fields.one2many('crm.segmentation.line', \
|
'segmentation_line': fields.one2many('crm.segmentation.line', \
|
||||||
'segmentation_id', 'Criteria', required=True),
|
'segmentation_id', 'Criteria', required=True),
|
||||||
'som_interval': fields.integer('Days per Periode', help="A period is the \
|
'som_interval': fields.integer('Days per Periode', help="A period is the average number of days between two cycle of sale or purchase for this segmentation.\
|
||||||
average number of days between two cycle of sale or\
|
\nIt's mainly used to detect if a partner has not purchased or buy for a too long time, \
|
||||||
purchase for this segmentation. It's mainly used to\
|
\nso we suppose that his state of mind has decreased because he probably bought goods to another supplier. \
|
||||||
detect if a partner has not purchased or buy for a too \
|
\nUse this functionality for recurring businesses."),
|
||||||
long time, so we suppose that his state of mind has \
|
'som_interval_max': fields.integer('Max Interval', help="The computation is made on all events that occured during this interval, the past X periods."),
|
||||||
decreased because he probably bought goods to another \
|
'som_interval_decrease': fields.float('Decrease (0>1)', help="If the "\
|
||||||
supplier. Use this functionality for recurring businesses."),
|
"partner has not purchased (or bought) during a "\
|
||||||
'som_interval_max': fields.integer('Max Interval', help="The computation \
|
"period, decrease the state of mind by this factor. It\'s a multiplication"),
|
||||||
is made on all events that occured during this \
|
|
||||||
interval, the past X periods."),
|
|
||||||
'som_interval_decrease': fields.float('Decrease (0>1)', help="If the \
|
|
||||||
partner has not purchased (or bought) \
|
|
||||||
during a period, decrease the state of \
|
|
||||||
mind by this factor. It\'s a multiplication"),
|
|
||||||
'som_interval_default': fields.float('Default (0=None)', help="Default \
|
'som_interval_default': fields.float('Default (0=None)', help="Default \
|
||||||
state of mind for period preceeding the \
|
state of mind for period preceeding the 'Max Interval' computation. \
|
||||||
'Max Interval' computation. This is the \
|
This is the starting state of mind by default if the partner has no event."),
|
||||||
starting state of mind by default if the \
|
'sales_purchase_active': fields.boolean('Use The Sales Purchase Rules', help='Check if you want to use this tab as part of the segmentation rule. If not checked, the criteria beneath will be ignored')
|
||||||
partner has no event."),
|
|
||||||
'sales_purchase_active': fields.boolean('Use The Sales Purchase Rules',\
|
|
||||||
help='Check if you want to use this tab as\
|
|
||||||
part of the segmentation rule. If not \
|
|
||||||
checked, the criteria beneath will be ignored')
|
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'partner_id': lambda *a: 0,
|
'partner_id': lambda *a: 0,
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
<menuitem id="menu_crm_configuration" name="Cases"
|
<menuitem id="base.menu_crm_configuration" name="Cases"
|
||||||
parent="base.menu_base_config" sequence="0" groups="base.group_extended"/>
|
parent="base.menu_base_config" sequence="0" groups="base.group_extended"/>
|
||||||
|
|
||||||
<!-- Case Sections Form View -->
|
<menuitem id="base.next_id_64" name="Reporting"
|
||||||
|
parent="base.menu_base_partner" sequence="8" />
|
||||||
|
|
||||||
|
<!-- Case Sections Form View -->
|
||||||
|
|
||||||
<record id="crm_case_section_view_form" model="ir.ui.view">
|
<record id="crm_case_section_view_form" model="ir.ui.view">
|
||||||
<field name="name">crm.case.section.form</field>
|
<field name="name">crm.case.section.form</field>
|
||||||
<field name="model">crm.case.section</field>
|
<field name="model">crm.case.section</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Case Section">
|
<form string="Sales Team">
|
||||||
<group col="6" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="name" select="1" colspan="4"/>
|
<field name="name" select="1" colspan="4"/>
|
||||||
<field name="code" select="1"/>
|
<field name="code" select="1"/>
|
||||||
|
@ -19,22 +22,24 @@
|
||||||
<field name="resource_calendar_id" select="2"/>
|
<field name="resource_calendar_id" select="2"/>
|
||||||
</group>
|
</group>
|
||||||
<notebook colspan="4">
|
<notebook colspan="4">
|
||||||
<page string="Case section">
|
<page string="Sales Team">
|
||||||
<group col="2" colspan="2">
|
<group col="2" colspan="1">
|
||||||
<separator string="Responsible" colspan="2"/>
|
<separator string="Responsible" colspan="2"/>
|
||||||
<field name="parent_id" select="2" widget="selection"/>
|
<field name="parent_id" select="2" widget="selection"/>
|
||||||
<field name="user_id" select="2"/>
|
<field name="user_id" select="2"/>
|
||||||
</group>
|
</group>
|
||||||
<group col="2" colspan="2">
|
<group col="2" colspan="1">
|
||||||
<separator string="Contact Information" colspan="2"/>
|
<separator string="Contact Information" colspan="2"/>
|
||||||
<field name="server_id" select="2"/>
|
<field name="server_id" select="2"/>
|
||||||
<field name="reply_to" select="2"/>
|
<field name="reply_to" select="2"/>
|
||||||
</group>
|
</group>
|
||||||
<group col="2" colspan="2">
|
<group col="2" colspan="1">
|
||||||
<separator string="Section Property" colspan="2"/>
|
<separator string="Sales Team Property" colspan="2"/>
|
||||||
<field name="active" select="2"/>
|
<field name="active" select="2"/>
|
||||||
<field name="allow_unlink" select="2"/>
|
<field name="allow_unlink" select="2"/>
|
||||||
</group>
|
</group>
|
||||||
|
<separator string="Members List" colspan="4"/>
|
||||||
|
<field name="member_ids" nolabel="1" colspan="4"/>
|
||||||
<separator string="Note" colspan="4"/>
|
<separator string="Note" colspan="4"/>
|
||||||
<field name="note" select="1" colspan="4" nolabel="1"/>
|
<field name="note" select="1" colspan="4" nolabel="1"/>
|
||||||
</page>
|
</page>
|
||||||
|
@ -51,7 +56,7 @@
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
<field name="field_parent">child_ids</field>
|
<field name="field_parent">child_ids</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Case Section">
|
<tree string="Sales Team">
|
||||||
<field name="name" select="1"/>
|
<field name="name" select="1"/>
|
||||||
<field name="code" select="1"/>
|
<field name="code" select="1"/>
|
||||||
<field name="user_id" select="1"/>
|
<field name="user_id" select="1"/>
|
||||||
|
@ -62,7 +67,7 @@
|
||||||
<!-- Case Sections Action -->
|
<!-- Case Sections Action -->
|
||||||
|
|
||||||
<record id="crm_case_section_act" model="ir.actions.act_window">
|
<record id="crm_case_section_act" model="ir.actions.act_window">
|
||||||
<field name="name">Sections</field>
|
<field name="name">Sales Team</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm_case_section_view_tree"/>
|
<field name="view_id" ref="crm_case_section_view_tree"/>
|
||||||
|
@ -115,7 +120,7 @@
|
||||||
<field name="view_id" ref="crm_case_stage_tree"/>
|
<field name="view_id" ref="crm_case_stage_tree"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="menu_crm_case_stage" name="Stages" parent="crm.menu_crm_configuration" groups="base.group_extended"/>
|
<menuitem id="menu_crm_case_stage" name="Stages" parent="base.menu_crm_configuration" groups="base.group_extended"/>
|
||||||
|
|
||||||
|
|
||||||
<!-- Case Categories Form View -->
|
<!-- Case Categories Form View -->
|
||||||
|
@ -155,11 +160,11 @@
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm_case_categ_tree-view"/>
|
<field name="view_id" ref="crm_case_categ_tree-view"/>
|
||||||
</record>
|
</record>
|
||||||
<menuitem id="menu_crm_case_categ" name="Categories" parent="crm.menu_crm_configuration" groups="base.group_extended"/>
|
<menuitem id="menu_crm_case_categ" name="Categories" parent="base.menu_crm_configuration" groups="base.group_extended"/>
|
||||||
|
|
||||||
<menuitem action="crm_case_section_act"
|
<menuitem action="crm_case_section_act"
|
||||||
id="menu_crm_case_section_act"
|
id="menu_crm_case_section_act"
|
||||||
parent="crm.menu_crm_configuration" />
|
parent="base.menu_crm_configuration" />
|
||||||
|
|
||||||
|
|
||||||
<!-- Resource Type of case Tree View -->
|
<!-- Resource Type of case Tree View -->
|
||||||
|
@ -201,7 +206,7 @@
|
||||||
<field name="view_id" ref="crm_case_resource_type_tree"/>
|
<field name="view_id" ref="crm_case_resource_type_tree"/>
|
||||||
</record>
|
</record>
|
||||||
<menuitem id="menu_crm_case_resource_type" name="Resource Type"
|
<menuitem id="menu_crm_case_resource_type" name="Resource Type"
|
||||||
parent="crm.menu_crm_configuration" groups="base.group_extended" />
|
parent="base.menu_crm_configuration" groups="base.group_extended" />
|
||||||
|
|
||||||
<record id="crm_case_section_act_tree" model="ir.actions.act_window">
|
<record id="crm_case_section_act_tree" model="ir.actions.act_window">
|
||||||
<field name="name">Cases by section</field>
|
<field name="name">Cases by section</field>
|
||||||
|
@ -254,7 +259,7 @@
|
||||||
|
|
||||||
<record id="crm_case_calendar-view" model="ir.ui.view">
|
<record id="crm_case_calendar-view" model="ir.ui.view">
|
||||||
<field name="name">crm.case.calendar</field>
|
<field name="name">crm.case.calendar</field>
|
||||||
<field name="model">crm.case</field>
|
<field name="model">mailgate.thread</field>
|
||||||
<field name="type">calendar</field>
|
<field name="type">calendar</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<calendar color="user_id" date_start="create_date"
|
<calendar color="user_id" date_start="create_date"
|
||||||
|
@ -271,7 +276,7 @@
|
||||||
|
|
||||||
<record id="crm_case_tree-view" model="ir.ui.view">
|
<record id="crm_case_tree-view" model="ir.ui.view">
|
||||||
<field name="name">crm.case.tree</field>
|
<field name="name">crm.case.tree</field>
|
||||||
<field name="model">crm.case</field>
|
<field name="model">mailgate.thread</field>
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree colors="red:date_deadline<current_date and state=='open';black:state in ('draft', 'cancel','done','pending')" string="Cases">
|
<tree colors="red:date_deadline<current_date and state=='open';black:state in ('draft', 'cancel','done','pending')" string="Cases">
|
||||||
|
@ -309,7 +314,7 @@
|
||||||
|
|
||||||
<record id="crm_case-view" model="ir.ui.view">
|
<record id="crm_case-view" model="ir.ui.view">
|
||||||
<field name="name">crm.case.form</field>
|
<field name="name">crm.case.form</field>
|
||||||
<field name="model">crm.case</field>
|
<field name="model">mailgate.thread</field>
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="priority" eval="1"/>
|
<field name="priority" eval="1"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
@ -390,16 +395,12 @@
|
||||||
<group colspan="4">
|
<group colspan="4">
|
||||||
<field colspan="4" name="email_cc" string="CC"/>
|
<field colspan="4" name="email_cc" string="CC"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="history_line" colspan="4" nolabel="1" mode="form,tree">
|
<field name="message_ids" colspan="4" nolabel="1" mode="form,tree">
|
||||||
<form string="Communication history">
|
<form string="Communication history">
|
||||||
<group col="7" colspan="4">
|
<group col="6" colspan="4">
|
||||||
<field name="date"/>
|
<field name="date"/>
|
||||||
<field name="email_to"/>
|
<field name="email_to"/>
|
||||||
<field name="email_from"/>
|
<field name="email_from"/>
|
||||||
<button
|
|
||||||
string="Add a CC"
|
|
||||||
name="%(crm.action_view_crm_email_add_cc_wizard)d"
|
|
||||||
icon="gtk-add" type="action"/>
|
|
||||||
</group>
|
</group>
|
||||||
<newline/>
|
<newline/>
|
||||||
<field name="description" colspan="4" nolabel="1"/>
|
<field name="description" colspan="4" nolabel="1"/>
|
||||||
|
@ -427,9 +428,10 @@
|
||||||
|
|
||||||
<!-- Case Search View -->
|
<!-- Case Search View -->
|
||||||
|
|
||||||
|
|
||||||
<record id="view_crm_case_filter" model="ir.ui.view">
|
<record id="view_crm_case_filter" model="ir.ui.view">
|
||||||
<field name="name">crm.case.select</field>
|
<field name="name">crm.case.select</field>
|
||||||
<field name="model">crm.case</field>
|
<field name="model">mailgate.thread</field>
|
||||||
<field name="type">search</field>
|
<field name="type">search</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="Search Case">
|
<search string="Search Case">
|
||||||
|
@ -461,7 +463,7 @@
|
||||||
|
|
||||||
<record id="crm_case_categ0-act" model="ir.actions.act_window">
|
<record id="crm_case_categ0-act" model="ir.actions.act_window">
|
||||||
<field name="name">Cases</field>
|
<field name="name">Cases</field>
|
||||||
<field name="res_model">crm.case</field>
|
<field name="res_model">mailgate.thread</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_id" ref="crm_case_tree-view"/>
|
<field name="view_id" ref="crm_case_tree-view"/>
|
||||||
<field name="search_view_id" ref="view_crm_case_filter"/>
|
<field name="search_view_id" ref="view_crm_case_filter"/>
|
||||||
|
@ -469,7 +471,7 @@
|
||||||
|
|
||||||
<record id="crm_case_categ0-act_open" model="ir.actions.act_window">
|
<record id="crm_case_categ0-act_open" model="ir.actions.act_window">
|
||||||
<field name="name">Open Cases</field>
|
<field name="name">Open Cases</field>
|
||||||
<field name="res_model">crm.case</field>
|
<field name="res_model">mailgate.thread</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="domain">
|
<field name="domain">
|
||||||
[('state','<>','done'),('state','<>','cancel'),('state','<>','pending')]
|
[('state','<>','done'),('state','<>','cancel'),('state','<>','pending')]
|
||||||
|
@ -479,7 +481,7 @@
|
||||||
|
|
||||||
<record id="crm_case_section_open_act" model="ir.actions.act_window">
|
<record id="crm_case_section_open_act" model="ir.actions.act_window">
|
||||||
<field name="name">Cases</field>
|
<field name="name">Cases</field>
|
||||||
<field name="res_model">crm.case</field>
|
<field name="res_model">mailgate.thread</field>
|
||||||
<field name="domain">[('section_id','child_of',[active_id])]</field>
|
<field name="domain">[('section_id','child_of',[active_id])]</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form,calendar</field>
|
<field name="view_mode">tree,form,calendar</field>
|
||||||
|
@ -660,7 +662,7 @@
|
||||||
<menuitem action="crm_segmentation_tree-act"
|
<menuitem action="crm_segmentation_tree-act"
|
||||||
id="menu_crm_segmentation-act"
|
id="menu_crm_segmentation-act"
|
||||||
groups="base.group_extended"
|
groups="base.group_extended"
|
||||||
parent="crm.menu_crm_configuration" />
|
parent="base.menu_crm_configuration" />
|
||||||
|
|
||||||
<record model="ir.ui.view" id="view_users_form_simple_modif_inherited1">
|
<record model="ir.ui.view" id="view_users_form_simple_modif_inherited1">
|
||||||
<field name="name">view.users.form.crm.modif.inherited1</field>
|
<field name="name">view.users.form.crm.modif.inherited1</field>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<record id="process_process_contractprocess0" model="process.process">
|
<record id="process_process_contractprocess0" model="process.process">
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="model_id" ref="crm.model_crm_case"/>
|
<field name="model_id" ref="mail_gateway.model_mailgate_thread"/>
|
||||||
<field eval=""""Contract"""" name="name"/>
|
<field eval=""""Contract"""" name="name"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<record id="process_node_leads0" model="process.node">
|
<record id="process_node_leads0" model="process.node">
|
||||||
<field name="model_id" ref="crm.model_crm_case"/>
|
<field name="model_id" ref="mail_gateway.model_mailgate_thread"/>
|
||||||
<field eval=""""state"""" name="kind"/>
|
<field eval=""""state"""" name="kind"/>
|
||||||
<field eval=""""Leads"""" name="name"/>
|
<field eval=""""Leads"""" name="name"/>
|
||||||
<field eval=""""Very first contact with new prospect"""" name="note"/>
|
<field eval=""""Very first contact with new prospect"""" name="note"/>
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="process_node_opportunities0" model="process.node">
|
<record id="process_node_opportunities0" model="process.node">
|
||||||
<field name="model_id" ref="crm.model_crm_case"/>
|
<field name="model_id" ref="mail_gateway.model_mailgate_thread"/>
|
||||||
<field eval=""""state"""" name="kind"/>
|
<field eval=""""state"""" name="kind"/>
|
||||||
<field eval=""""Opportunities"""" name="name"/>
|
<field eval=""""Opportunities"""" name="name"/>
|
||||||
<field eval=""""When a real project/opportunity is detected"""" name="note"/>
|
<field eval=""""When a real project/opportunity is detected"""" name="note"/>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="process_node_meeting0" model="process.node">
|
<record id="process_node_meeting0" model="process.node">
|
||||||
<field name="model_id" ref="crm.model_crm_case"/>
|
<field name="model_id" ref="mail_gateway.model_mailgate_thread"/>
|
||||||
<field eval=""""state"""" name="kind"/>
|
<field eval=""""state"""" name="kind"/>
|
||||||
<field eval=""""Meeting"""" name="name"/>
|
<field eval=""""Meeting"""" name="name"/>
|
||||||
<field eval=""""Schedule a normal or phone meeting"""" name="note"/>
|
<field eval=""""Schedule a normal or phone meeting"""" name="note"/>
|
||||||
|
|
|
@ -19,14 +19,8 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
#import report_businessopp
|
|
||||||
import crm_report
|
|
||||||
import crm_claim_report
|
|
||||||
import crm_lead_report
|
import crm_lead_report
|
||||||
import crm_phonecall_report
|
import crm_phonecall_report
|
||||||
import crm_fundraising_report
|
|
||||||
import crm_opportunity_report
|
|
||||||
import crm_helpdesk_report
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from osv import fields,osv
|
|
||||||
import tools
|
|
||||||
import crm_report
|
|
||||||
|
|
||||||
|
|
||||||
class crm_claim_report(osv.osv):
|
|
||||||
""" CRM Claim Report"""
|
|
||||||
|
|
||||||
_name = "crm.claim.report"
|
|
||||||
_auto = False
|
|
||||||
_inherit = "crm.case.report"
|
|
||||||
_description = "CRM Claim Report"
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
|
||||||
'stage_id': fields.many2one ('crm.case.stage', 'Stage', \
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]", readonly=True),
|
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]", readonly=True),
|
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
|
|
||||||
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
|
||||||
'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'),
|
|
||||||
'type_id': fields.many2one('crm.case.resource.type', 'Claim Type',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.claim')]"),
|
|
||||||
}
|
|
||||||
|
|
||||||
def init(self, cr):
|
|
||||||
|
|
||||||
""" Display Number of cases And Section Name
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools.drop_view_if_exists(cr, 'crm_claim_report')
|
|
||||||
cr.execute("""
|
|
||||||
create or replace view crm_claim_report as (
|
|
||||||
select
|
|
||||||
min(c.id) as id,
|
|
||||||
to_char(c.create_date, 'YYYY') as name,
|
|
||||||
to_char(c.create_date, 'MM') as month,
|
|
||||||
to_char(c.create_date, 'YYYY-MM-DD') as day,
|
|
||||||
c.state,
|
|
||||||
c.user_id,
|
|
||||||
c.stage_id,
|
|
||||||
c.section_id,
|
|
||||||
c.partner_id,
|
|
||||||
c.company_id,
|
|
||||||
c.categ_id,
|
|
||||||
count(*) as nbr,
|
|
||||||
0 as avg_answers,
|
|
||||||
0.0 as perc_done,
|
|
||||||
0.0 as perc_cancel,
|
|
||||||
c.priority as priority,
|
|
||||||
c.type_id as type_id,
|
|
||||||
date_trunc('day',c.create_date) as create_date,
|
|
||||||
avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close
|
|
||||||
from
|
|
||||||
crm_claim c
|
|
||||||
group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'), \
|
|
||||||
c.state, c.user_id,c.section_id, c.stage_id,\
|
|
||||||
c.categ_id,c.partner_id,c.company_id,c.create_date,to_char(c.create_date, 'YYYY-MM-DD')
|
|
||||||
,c.priority,c.type_id,c.som
|
|
||||||
)""")
|
|
||||||
|
|
||||||
crm_claim_report()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
|
@ -1,135 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- CRM Claim Report Tree View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_claim_tree" model="ir.ui.view">
|
|
||||||
<field name="name">crm.claim.report.tree</field>
|
|
||||||
<field name="model">crm.claim.report</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Claims">
|
|
||||||
<field name="name" invisible="1"/>
|
|
||||||
<field name="month" invisible="1"/>
|
|
||||||
<field name="section_id" invisible="1"/>
|
|
||||||
<field name="user_id" invisible="1"/>
|
|
||||||
<field name="company_id" invisible="1"/>
|
|
||||||
<field name="partner_id" invisible="1"/>
|
|
||||||
<field name="day" invisible="1"/>
|
|
||||||
<field name="nbr" string="#Claim" sum="#Claim"/>
|
|
||||||
<field name="delay_close" avg="Avg Closing Delay"/>
|
|
||||||
<field name="state" invisible="1"/>
|
|
||||||
<field name="stage_id" invisible="1"/>
|
|
||||||
<field name="categ_id" invisible="1"/>
|
|
||||||
<field name="priority" invisible="1"/>
|
|
||||||
<field name="type_id" invisible="1"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- CRM Claim Report Form View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_claim_form" model="ir.ui.view">
|
|
||||||
<field name="name">crm.claim.report.form</field>
|
|
||||||
<field name="model">crm.claim.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_form"/>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="nbr" position="after">
|
|
||||||
<field name="delay_close"/>
|
|
||||||
<field name="amount_revenue"/>
|
|
||||||
<field name="amount_costs"/>
|
|
||||||
<field name="amount_revenue_prob"/>
|
|
||||||
<field name="probability"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- CRM Claim Report Graph View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_claim_graph" model="ir.ui.view">
|
|
||||||
<field name="name">crm.claim.report.graph</field>
|
|
||||||
<field name="model">crm.claim.report</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph orientation="horizontal" string="Claims" type="bar">
|
|
||||||
<field name="state"/>
|
|
||||||
<field name="nbr" operator="+"/>
|
|
||||||
<field group="True" name="user_id"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- CRM Claim Report Search View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_claim_filter" model="ir.ui.view">
|
|
||||||
<field name="name">crm.claim.report.select</field>
|
|
||||||
<field name="model">crm.claim.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_filter"/>
|
|
||||||
<field name="type">search</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<data>
|
|
||||||
<xpath expr='//search[@string="Search"]/group[@string="Extended options..."]/filter[@string="Cancel"]' position='after'>
|
|
||||||
<group>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<field name="stage_id" widget="selection" domain="[('object_id.model', '=', 'crm.claim')]"/>
|
|
||||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.claim')]"/>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<field name="priority" />
|
|
||||||
<field name="type_id" widget="selection" domain="[('object_id.model', '=', 'crm.claim')]"/>
|
|
||||||
</group>
|
|
||||||
</xpath>
|
|
||||||
<xpath
|
|
||||||
expr='//search[@string="Search"]/group[@string="Group By..."]/filter[@string="State"]'
|
|
||||||
position='after'>
|
|
||||||
<filter string="Stage" icon="terp-sale" domain="[]"
|
|
||||||
context="{'group_by':'stage_id'}" />
|
|
||||||
</xpath>
|
|
||||||
<xpath
|
|
||||||
expr='//search[@string="Search"]/group[@string="Group By..."]/filter[@string="Category"]'
|
|
||||||
position='after'>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<filter string="Priority" icon="terp-sale" domain="[]"
|
|
||||||
context="{'group_by':'priority'}" />
|
|
||||||
<filter string="Type" icon="terp-sale" domain="[]"
|
|
||||||
context="{'group_by':'type_id'}" />
|
|
||||||
</xpath>
|
|
||||||
</data>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- CRM Claim Report Action -->
|
|
||||||
|
|
||||||
<record id="action_report_crm_claim" model="ir.actions.act_window">
|
|
||||||
<field name="name">Claims</field>
|
|
||||||
<field name="res_model">crm.claim.report</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_claim_tree"/>
|
|
||||||
<field name="search_view_id" ref="view_report_crm_claim_filter"/>
|
|
||||||
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_claim_tree">
|
|
||||||
<field name="sequence" eval="1"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_claim_tree"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_claim"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_claim_graph">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">graph</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_claim_graph"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_claim"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem name="Claims" id="menu_report_crm_claim_tree"
|
|
||||||
groups="base.group_extended"
|
|
||||||
action="action_report_crm_claim" parent="base.next_id_64" />
|
|
||||||
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -1,115 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- Fundraising by user and section Tree View-->
|
|
||||||
|
|
||||||
<record id="view_report_crm_fundraising_tree" model="ir.ui.view">
|
|
||||||
<field name="name">crm.fundraising.report.tree</field>
|
|
||||||
<field name="model">crm.fundraising.report</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Fundraising">
|
|
||||||
<field name="name" invisible="1"/>
|
|
||||||
<field name="month" invisible="1"/>
|
|
||||||
<field name="section_id" invisible="1"/>
|
|
||||||
<field name="user_id" invisible="1"/>
|
|
||||||
<field name="company_id" invisible="1"/>
|
|
||||||
<field name="partner_id" invisible="1"/>
|
|
||||||
<field name="nbr" string='#Fundraising' sum="#Fundraising"/>
|
|
||||||
<field name="amount_revenue" sum='Est.Revenue'/>
|
|
||||||
<field name="probability" avg='Avg. Probability' widget="progressbar"/>
|
|
||||||
<field name="amount_revenue_prob" sum='Est. Rev*Prob.'/>
|
|
||||||
<field name="delay_close" avg='Avg Closing Delay'/>
|
|
||||||
<field name="state" invisible="1"/>
|
|
||||||
<field name="categ_id" invisible="1"/>
|
|
||||||
<field name="day" invisible="1"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
|
|
||||||
<!-- Fundraising by user and section Form View-->
|
|
||||||
|
|
||||||
</record>
|
|
||||||
<record id="view_report_crm_fundraising_form" model="ir.ui.view">
|
|
||||||
<field name="name">crm.fundraising.report.form</field>
|
|
||||||
<field name="model">crm.fundraising.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_form"/>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="nbr" position="after">
|
|
||||||
<field name="delay_close"/>
|
|
||||||
<field name="amount_revenue"/>
|
|
||||||
<field name="amount_revenue_prob"/>
|
|
||||||
<field name="probability"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fundraising by user and section Graph View-->
|
|
||||||
|
|
||||||
<record id="view_report_crm_fundraising_graph" model="ir.ui.view">
|
|
||||||
<field name="name">crm.fundraising.report.graph</field>
|
|
||||||
<field name="model">crm.fundraising.report</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph orientation="horizontal" string="Fundraising" type="bar">
|
|
||||||
<field name="state"/>
|
|
||||||
<field name="nbr" operator="+"/>
|
|
||||||
<field group="True" name="user_id"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fundraising by user and section Search View-->
|
|
||||||
|
|
||||||
<record id="view_report_crm_fundraising_filter" model="ir.ui.view">
|
|
||||||
<field name="name">crm.fundraising.report.select</field>
|
|
||||||
<field name="model">crm.fundraising.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_filter"/>
|
|
||||||
<field name="type">search</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<data>
|
|
||||||
<xpath expr='//search[@string="Search"]/group[@string="Extended options..."]/filter[@string="Cancel"]' position='after'>
|
|
||||||
<group>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.fundraising')]"/>
|
|
||||||
</group>
|
|
||||||
</xpath>
|
|
||||||
</data>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Fundraising by user and section Action -->
|
|
||||||
|
|
||||||
<record id="action_report_crm_fundraising" model="ir.actions.act_window">
|
|
||||||
<field name="name">Fundraising</field>
|
|
||||||
<field name="res_model">crm.fundraising.report</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_fundraising_tree"/>
|
|
||||||
<field name="search_view_id" ref="view_report_crm_fundraising_filter"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_fundrising_tree">
|
|
||||||
<field name="sequence" eval="1"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_fundraising_tree"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_fundraising"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_fundrising_graph">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">graph</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_fundraising_graph"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_fundraising"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem name="Fundraising"
|
|
||||||
action="action_report_crm_fundraising"
|
|
||||||
groups="base.group_extended"
|
|
||||||
id="menu_report_crm_fundraising_tree" parent="base.next_id_64" />
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -34,10 +34,62 @@ class crm_lead_report(osv.osv):
|
||||||
""" CRM Lead Report """
|
""" CRM Lead Report """
|
||||||
_name = "crm.lead.report"
|
_name = "crm.lead.report"
|
||||||
_auto = False
|
_auto = False
|
||||||
_inherit = "crm.case.report"
|
|
||||||
_description = "CRM Lead Report"
|
_description = "CRM Lead Report"
|
||||||
|
|
||||||
|
def _get_data(self, cr, uid, ids, field_name, arg, context={}):
|
||||||
|
|
||||||
|
""" @param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case and section Data’s IDs
|
||||||
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
|
res = {}
|
||||||
|
state_perc = 0.0
|
||||||
|
avg_ans = 0.0
|
||||||
|
|
||||||
|
for case in self.browse(cr, uid, ids, context):
|
||||||
|
if field_name != 'avg_answers':
|
||||||
|
state = field_name[5:]
|
||||||
|
cr.execute("select count(*) from crm_opportunity where \
|
||||||
|
section_id =%s and state='%s'"%(case.section_id.id, state))
|
||||||
|
state_cases = cr.fetchone()[0]
|
||||||
|
perc_state = (state_cases / float(case.nbr)) * 100
|
||||||
|
|
||||||
|
res[case.id] = perc_state
|
||||||
|
else:
|
||||||
|
model_name = self._name.split('report.')
|
||||||
|
if len(model_name) < 2:
|
||||||
|
res[case.id] = 0.0
|
||||||
|
else:
|
||||||
|
model_name = model_name[1]
|
||||||
|
|
||||||
|
cr.execute("select count(*) from crm_case_log l, ir_model m \
|
||||||
|
where l.model_id=m.id and m.model = '%s'" , model_name)
|
||||||
|
logs = cr.fetchone()[0]
|
||||||
|
|
||||||
|
avg_ans = logs / case.nbr
|
||||||
|
res[case.id] = avg_ans
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
'name': fields.char('Year', size=64, required=False, readonly=True),
|
||||||
|
'user_id':fields.many2one('res.users', 'User', readonly=True),
|
||||||
|
'section_id':fields.many2one('crm.case.section', 'Section', readonly=True),
|
||||||
|
'nbr': fields.integer('# of Cases', readonly=True),
|
||||||
|
'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True),
|
||||||
|
'avg_answers': fields.function(_get_data, string='Avg. Answers', method=True, type="integer"),
|
||||||
|
'perc_done': fields.function(_get_data, string='%Done', method=True, type="float"),
|
||||||
|
'perc_cancel': fields.function(_get_data, string='%Cancel', method=True, type="float"),
|
||||||
|
'month':fields.selection([('01', 'January'), ('02', 'February'), \
|
||||||
|
('03', 'March'), ('04', 'April'),\
|
||||||
|
('05', 'May'), ('06', 'June'), \
|
||||||
|
('07', 'July'), ('08', 'August'),\
|
||||||
|
('09', 'September'), ('10', 'October'),\
|
||||||
|
('11', 'November'), ('12', 'December')], 'Month', readonly=True),
|
||||||
|
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
||||||
|
'create_date': fields.datetime('Create Date', readonly=True),
|
||||||
|
'day': fields.char('Day', size=128, readonly=True),
|
||||||
'delay_close': fields.float('Delay to close',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
'delay_close': fields.float('Delay to close',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category',\
|
'categ_id': fields.many2one('crm.case.categ', 'Category',\
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
|
@ -46,7 +98,12 @@ class crm_lead_report(osv.osv):
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
('object_id.model', '=', 'crm.lead')]", readonly=True),
|
('object_id.model', '=', 'crm.lead')]", readonly=True),
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True),
|
'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True),
|
||||||
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
||||||
|
'type':fields.selection([
|
||||||
|
('lead','Lead'),
|
||||||
|
('opportunity','Opportunity'),
|
||||||
|
|
||||||
|
],'Type', help="Type is used to separate Leads and Opportunities"),
|
||||||
}
|
}
|
||||||
def init(self, cr):
|
def init(self, cr):
|
||||||
|
|
||||||
|
@ -66,6 +123,7 @@ class crm_lead_report(osv.osv):
|
||||||
c.state as state,
|
c.state as state,
|
||||||
c.user_id,
|
c.user_id,
|
||||||
c.stage_id,
|
c.stage_id,
|
||||||
|
c.type as type,
|
||||||
c.company_id,
|
c.company_id,
|
||||||
c.section_id,
|
c.section_id,
|
||||||
c.categ_id,
|
c.categ_id,
|
||||||
|
@ -79,7 +137,7 @@ class crm_lead_report(osv.osv):
|
||||||
from
|
from
|
||||||
crm_lead c
|
crm_lead c
|
||||||
group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\
|
group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\
|
||||||
c.state, c.user_id,c.section_id,c.stage_id,categ_id,c.partner_id,c.company_id
|
c.state, c.user_id,c.section_id,c.stage_id,categ_id,c.partner_id,c.company_id, c.type
|
||||||
,c.create_date,to_char(c.create_date, 'YYYY-MM-DD')
|
,c.create_date,to_char(c.create_date, 'YYYY-MM-DD')
|
||||||
)""")
|
)""")
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
<field name="company_id" invisible="1"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="partner_id" invisible="1"/>
|
<field name="partner_id" invisible="1"/>
|
||||||
<field name="day" invisible="1"/>
|
<field name="day" invisible="1"/>
|
||||||
<field name="nbr" string="#Leads" sum="#Leads"/>
|
<field name="nbr" string="#Leads" sum="#Leads" attrs="{'string': 'type'=='opportunity' and '#opp' or '#lead'}"/>
|
||||||
<field name="delay_close" sum='Avg Closing Delay'/>
|
<field name="delay_close" sum='Avg Closing Delay'/>
|
||||||
<field name="state" invisible="1"/>
|
<field name="state" invisible="1"/>
|
||||||
<field name="stage_id" invisible="1"/>
|
<field name="stage_id" invisible="1"/>
|
||||||
<field name="categ_id" invisible="1"/>
|
<field name="categ_id" invisible="1"/>
|
||||||
|
<field name="type" invisible="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -31,13 +32,18 @@
|
||||||
<record id="view_report_crm_lead_form" model="ir.ui.view">
|
<record id="view_report_crm_lead_form" model="ir.ui.view">
|
||||||
<field name="name">crm.lead.report.form</field>
|
<field name="name">crm.lead.report.form</field>
|
||||||
<field name="model">crm.lead.report</field>
|
<field name="model">crm.lead.report</field>
|
||||||
<field name="inherit_id" ref="view_crm_case_form"/>
|
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="nbr" position="after">
|
<form string="Cases">
|
||||||
|
<field name="name" select="1"/>
|
||||||
|
<field name="month" select="1"/>
|
||||||
|
<field name="user_id" select="1"/>
|
||||||
|
<field name="section_id" select="1"/>
|
||||||
|
<field name="nbr" select="1"/>
|
||||||
<field name="delay_close"/>
|
<field name="delay_close"/>
|
||||||
<field name="stage_id"/>
|
<field name="stage_id"/>
|
||||||
</field>
|
<field name="state" select="1"/>
|
||||||
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -57,28 +63,135 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Leads by user and section Search View -->
|
<!-- Leads by user and section Search View -->
|
||||||
|
|
||||||
<record id="view_report_crm_lead_filter" model="ir.ui.view">
|
<record id="view_report_crm_lead_filter" model="ir.ui.view">
|
||||||
<field name="name">crm.lead.report.select</field>
|
<field name="name">crm.lead.report.select</field>
|
||||||
<field name="model">crm.lead.report</field>
|
<field name="model">crm.lead.report</field>
|
||||||
<field name="inherit_id" ref="view_crm_case_filter"/>
|
|
||||||
<field name="type">search</field>
|
<field name="type">search</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<data>
|
<search string="Search">
|
||||||
<xpath expr='//search[@string="Search"]/group[@string="Extended options..."]/filter[@string="Cancel"]' position='after'>
|
<group col="16" colspan="8">
|
||||||
<group>
|
<!-- <filter string="This Year" name="This Year" icon="terp-hr"
|
||||||
<separator orientation="vertical"/>
|
domain="[('name','=',time.localtime()[0])]"/>-->
|
||||||
<field name="stage_id" widget="selection" domain="[('object_id.model', '=', 'crm.lead')]"/>
|
<filter string="This Year" icon="terp-hr"
|
||||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.lead')]"/>
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
</group>
|
|
||||||
</xpath>
|
<!-- <filter string="This Month" name="This Year" icon="terp-hr"
|
||||||
<xpath
|
domain="[('month','=',time.strftime('%%m'))]" />-->
|
||||||
expr='//search[@string="Search"]/group[@string="Group By..."]/filter[@string="State"]'
|
<filter string="This Month" icon="terp-hr" name="This Month"
|
||||||
position='after'>
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
<filter string="Stage" icon="terp-sale" domain="[]"
|
|
||||||
context="{'group_by':'stage_id'}" />
|
<filter icon="gtk-media-rewind" string="7 Days" separator="1"
|
||||||
</xpath>
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
</data>
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Draft"
|
||||||
|
domain="[('state','=','draft')]"/>
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Open"
|
||||||
|
domain="[('state','=','open')]"/>
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Pending"
|
||||||
|
domain="[('state','=','pending')]"/>
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<field name="section_id"
|
||||||
|
default="context.get('section_id', False)"
|
||||||
|
widget="selection"
|
||||||
|
context="{'invisible_section': False}">
|
||||||
|
|
||||||
|
<filter icon="terp-crm"
|
||||||
|
context="{'invisible_section': False}"
|
||||||
|
domain="[('section_id.user_id','=',uid)]"
|
||||||
|
help="My section" />
|
||||||
|
|
||||||
|
</field>
|
||||||
|
<field name="company_id" widget="selection">
|
||||||
|
<filter icon="terp-crm"
|
||||||
|
context="{'invisible_section': False}"
|
||||||
|
domain="[('section_id.user_id.company_id','=',uid)]"
|
||||||
|
help="My company"/>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<field name="user_id" select="1" widget="selection">
|
||||||
|
<filter icon="terp-crm" string="My Case" help="My Case" domain="[('user_id','=',uid)]" />
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="1" string="Extended options..." colspan="10" col="12">
|
||||||
|
<filter icon="terp-sale"
|
||||||
|
string="Done"
|
||||||
|
domain="[('state','=','done')]"/>
|
||||||
|
<filter icon="terp-sale"
|
||||||
|
string="Cancel"
|
||||||
|
domain="[('state','=','cancel')]"/>
|
||||||
|
<group>
|
||||||
|
<separator orientation="vertical"/>
|
||||||
|
<field name="stage_id" widget="selection" domain="[('object_id.model', '=', 'crm.lead')]"/>
|
||||||
|
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.lead')]"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="1" string="Group By..." colspan="4" col="8">
|
||||||
|
<filter string="User" name="User" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'user_id'}" />
|
||||||
|
|
||||||
|
<filter string="Company" icon="terp-sale"
|
||||||
|
domain="[]"
|
||||||
|
context="{'group_by':'company_id'}" />
|
||||||
|
|
||||||
|
<filter string="Section" icon="terp-sale"
|
||||||
|
domain="[]"
|
||||||
|
context="{'group_by':'section_id'}" />
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="State" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'state'}" />
|
||||||
|
|
||||||
|
<filter string="Stage" icon="terp-sale" domain="[]"
|
||||||
|
context="{'group_by':'stage_id'}" />
|
||||||
|
|
||||||
|
<filter string="Category" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'categ_id'}" />
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="Day" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'day'}"/>
|
||||||
|
|
||||||
|
<filter string="Month" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'month'}" />
|
||||||
|
|
||||||
|
<filter string="Year" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'name'}" />
|
||||||
|
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Opportunity tree view -->
|
||||||
|
|
||||||
|
<record id="view_report_crm_opportunity_tree" model="ir.ui.view">
|
||||||
|
<field name="name">crm.lead.report.tree</field>
|
||||||
|
<field name="model">crm.lead.report</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Opportunities">
|
||||||
|
<field name="name" invisible="1"/>
|
||||||
|
<field name="month" invisible="1"/>
|
||||||
|
<field name="section_id" invisible="1"/>
|
||||||
|
<field name="user_id" invisible="1"/>
|
||||||
|
<field name="company_id" invisible="1"/>
|
||||||
|
<field name="partner_id" invisible="1"/>
|
||||||
|
<field name="day" invisible="1"/>
|
||||||
|
<field name="nbr" string="#Opportunities" sum="#Opportunities"/>
|
||||||
|
<field name="delay_close" sum='Avg Closing Delay'/>
|
||||||
|
<field name="state" invisible="1"/>
|
||||||
|
<field name="stage_id" invisible="1"/>
|
||||||
|
<field name="categ_id" invisible="1"/>
|
||||||
|
<field name="type" invisible="1"/>
|
||||||
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -90,8 +203,8 @@
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
||||||
<field name="view_mode">tree,graph</field>
|
<field name="view_mode">tree,graph</field>
|
||||||
|
<field name="domain">[('type', '=', 'lead')]</field>
|
||||||
<field name="search_view_id" ref="view_report_crm_lead_filter"/>
|
<field name="search_view_id" ref="view_report_crm_lead_filter"/>
|
||||||
|
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_lead_tree">
|
<record model="ir.actions.act_window.view" id="action_report_crm_lead_tree">
|
||||||
|
@ -107,10 +220,38 @@
|
||||||
<field name="view_id" ref="view_report_crm_lead_graph"/>
|
<field name="view_id" ref="view_report_crm_lead_graph"/>
|
||||||
<field name="act_window_id" ref="action_report_crm_lead"/>
|
<field name="act_window_id" ref="action_report_crm_lead"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="action_report_crm_opportunity" model="ir.actions.act_window">
|
||||||
|
<field name="name">Opportunities</field>
|
||||||
|
<field name="res_model">crm.lead.report</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
||||||
|
<field name="view_mode">tree,graph</field>
|
||||||
|
<field name="domain">[('type', '=', 'opportunity')]</field>
|
||||||
|
<field name="search_view_id" ref="view_report_crm_lead_filter"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window.view" id="action_report_crm_opportunity_tree">
|
||||||
|
<field name="sequence" eval="1"/>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="view_id" ref="view_report_crm_opportunity_tree"/>
|
||||||
|
<field name="act_window_id" ref="action_report_crm_opportunity"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window.view" id="action_report_crm_opportunity_graph">
|
||||||
|
<field name="sequence" eval="2"/>
|
||||||
|
<field name="view_mode">graph</field>
|
||||||
|
<field name="view_id" ref="view_report_crm_lead_graph"/>
|
||||||
|
<field name="act_window_id" ref="action_report_crm_opportunity"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
<menuitem name="Leads" id="menu_report_crm_leads_tree"
|
<menuitem name="Leads" id="menu_report_crm_leads_tree"
|
||||||
groups="base.group_extended"
|
groups="base.group_extended"
|
||||||
parent="base.next_id_64" action="action_report_crm_lead" />
|
parent="base.next_id_64" action="action_report_crm_lead" />
|
||||||
|
|
||||||
|
<menuitem name="Opportunities" id="menu_report_crm_opportunities_tree"
|
||||||
|
groups="base.group_extended"
|
||||||
|
parent="base.next_id_64" action="action_report_crm_opportunity" />
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from osv import fields,osv
|
|
||||||
import tools
|
|
||||||
|
|
||||||
AVAILABLE_STATES = [
|
|
||||||
('draft','Draft'),
|
|
||||||
('open','Open'),
|
|
||||||
('cancel', 'Cancelled'),
|
|
||||||
('done', 'Closed'),
|
|
||||||
('pending','Pending')
|
|
||||||
]
|
|
||||||
|
|
||||||
class crm_opportunity_report(osv.osv):
|
|
||||||
""" CRM Opportunity Report """
|
|
||||||
_name = "crm.opportunity.report"
|
|
||||||
_auto = False
|
|
||||||
_inherit = "crm.case.report"
|
|
||||||
_description = "CRM Opportunity Report"
|
|
||||||
_columns = {
|
|
||||||
'probability': fields.float('Avg. Probability', readonly=True,group_operator='avg'),
|
|
||||||
'amount_revenue': fields.float('Est.Revenue', readonly=True),
|
|
||||||
'amount_revenue_prob': fields.float('Est. Rev*Prob.', readonly=True),
|
|
||||||
'delay_close': fields.float('Delay to close',digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.opportunity')]", readonly=True),
|
|
||||||
'stage_id':fields.many2one('crm.case.stage', 'Stage',\
|
|
||||||
domain="[('section_id','=',section_id),\
|
|
||||||
('object_id.model', '=', 'crm.opportunity')]", readonly=True),
|
|
||||||
'partner_id': fields.many2one('res.partner', 'Partner', readonly=True),
|
|
||||||
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
|
||||||
'user_id':fields.many2one('res.users', 'User', readonly=True),
|
|
||||||
}
|
|
||||||
|
|
||||||
def init(self, cr):
|
|
||||||
""" Display Est.Revenue , Average Probability ,Est.Revenue Probability
|
|
||||||
@param cr: the current row, from the database cursor
|
|
||||||
"""
|
|
||||||
|
|
||||||
tools.drop_view_if_exists(cr, 'crm_opportunity_report')
|
|
||||||
cr.execute("""
|
|
||||||
create or replace view crm_opportunity_report as (
|
|
||||||
select
|
|
||||||
min(c.id) as id,
|
|
||||||
to_char(c.create_date, 'YYYY') as name,
|
|
||||||
to_char(c.create_date, 'MM') as month,
|
|
||||||
to_char(c.create_date, 'YYYY-MM-DD') as day,
|
|
||||||
c.state as state,
|
|
||||||
c.user_id,
|
|
||||||
c.section_id,
|
|
||||||
c.categ_id,
|
|
||||||
c.stage_id,
|
|
||||||
c.partner_id,
|
|
||||||
c.company_id,
|
|
||||||
count(*) as nbr,
|
|
||||||
0 as avg_answers,
|
|
||||||
0.0 as perc_done,
|
|
||||||
0.0 as perc_cancel,
|
|
||||||
date_trunc('day',c.create_date) as create_date,
|
|
||||||
sum(planned_revenue) as amount_revenue,
|
|
||||||
sum((planned_revenue*probability)/100.0)::decimal(16,2) as amount_revenue_prob,
|
|
||||||
avg(probability)::decimal(16,2) as probability,
|
|
||||||
avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close
|
|
||||||
from
|
|
||||||
crm_opportunity c
|
|
||||||
group by
|
|
||||||
to_char(c.create_date, 'YYYY'),
|
|
||||||
to_char(c.create_date, 'MM'),
|
|
||||||
to_char(c.create_date, 'YYYY-MM-DD'),
|
|
||||||
c.state,
|
|
||||||
c.user_id,
|
|
||||||
c.section_id,
|
|
||||||
c.stage_id,
|
|
||||||
c.categ_id,
|
|
||||||
c.partner_id,
|
|
||||||
company_id,
|
|
||||||
create_date
|
|
||||||
)""")
|
|
||||||
|
|
||||||
crm_opportunity_report()
|
|
|
@ -1,122 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<openerp>
|
|
||||||
<data>
|
|
||||||
|
|
||||||
<!-- Opportunities by user and section Tree View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_opportunity_tree" model="ir.ui.view">
|
|
||||||
<field name="name">crm.opportunity.report.tree</field>
|
|
||||||
<field name="model">crm.opportunity.report</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Opportunities">
|
|
||||||
<field name="name" invisible="1"/>
|
|
||||||
<field name="month" invisible="1"/>
|
|
||||||
<field name="section_id" invisible="1"/>
|
|
||||||
<field name="user_id" invisible="1"/>
|
|
||||||
<field name="company_id" invisible="1"/>
|
|
||||||
<field name="partner_id" invisible="1"/>
|
|
||||||
<field name="nbr" string="#Opportunities" sum="#Opportunities"/>
|
|
||||||
<field name="amount_revenue" sum="Total Revenue"/>
|
|
||||||
<field name="probability" widget="progressbar" avg="Probability"/>
|
|
||||||
<field name="amount_revenue_prob" sum="Est. Rev*Prob"/>
|
|
||||||
<field name="delay_close" avg="Avg Closing Delay"/>
|
|
||||||
<field name="state" invisible="1"/>
|
|
||||||
<field name="stage_id" invisible="1"/>
|
|
||||||
<field name="categ_id" invisible="1"/>
|
|
||||||
<field name="day" invisible="1"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Opportunities by user and section Form View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_opportunity_form" model="ir.ui.view">
|
|
||||||
<field name="name">crm.opportunity.report.form</field>
|
|
||||||
<field name="model">crm.opportunity.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_form"/>
|
|
||||||
<field name="type">form</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="nbr" position="after">
|
|
||||||
<field name="delay_close"/>
|
|
||||||
<field name="amount_revenue"/>
|
|
||||||
<field name="amount_costs"/>
|
|
||||||
<field name="amount_revenue_prob"/>
|
|
||||||
<field name="probability"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Opportunities by user and section Graph View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_opportunity_graph" model="ir.ui.view">
|
|
||||||
<field name="name">crm.opportunity.report.graph</field>
|
|
||||||
<field name="model">crm.opportunity.report</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<graph orientation="horizontal" string="Opportunity" type="bar">
|
|
||||||
<field name="state"/>
|
|
||||||
<field name="nbr" operator="+"/>
|
|
||||||
<field group="True" name="user_id"/>
|
|
||||||
</graph>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Opportunities by user and section Search View -->
|
|
||||||
|
|
||||||
<record id="view_report_crm_opportunity_filter" model="ir.ui.view">
|
|
||||||
<field name="name">crm.opportunity.report.select</field>
|
|
||||||
<field name="model">crm.opportunity.report</field>
|
|
||||||
<field name="inherit_id" ref="view_crm_case_filter"/>
|
|
||||||
<field name="type">search</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<data>
|
|
||||||
<xpath expr='//search[@string="Search"]/group[@string="Extended options..."]/filter[@string="Cancel"]' position='after'>
|
|
||||||
<group>
|
|
||||||
<separator orientation="vertical"/>
|
|
||||||
<field name="stage_id" widget="selection" domain="[('object_id.model', '=', 'crm.opportunity')]"/>
|
|
||||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.opportunity')]"/>
|
|
||||||
</group>
|
|
||||||
</xpath>
|
|
||||||
<xpath
|
|
||||||
expr='//search[@string="Search"]/group[@string="Group By..."]/filter[@string="State"]'
|
|
||||||
position='after'>
|
|
||||||
<filter string="Stage" icon="terp-sale" domain="[]"
|
|
||||||
context="{'group_by':'stage_id'}" />
|
|
||||||
<separator orientation="vertical" />
|
|
||||||
<filter string="Partner" icon="terp-partner"
|
|
||||||
domain="[]" context="{'group_by':'partner_id'}" />
|
|
||||||
</xpath>
|
|
||||||
</data>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Opportunities by user and section Action -->
|
|
||||||
|
|
||||||
<record id="action_report_crm_opportunity" model="ir.actions.act_window">
|
|
||||||
<field name="name">Opportunities</field>
|
|
||||||
<field name="res_model">crm.opportunity.report</field>
|
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="context">{"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
|
|
||||||
<field name="search_view_id" ref="view_report_crm_opportunity_filter"/>
|
|
||||||
</record>
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_opportunity_tree">
|
|
||||||
<field name="sequence" eval="1"/>
|
|
||||||
<field name="view_mode">tree</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_opportunity_tree"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_opportunity"/>
|
|
||||||
</record>
|
|
||||||
<record model="ir.actions.act_window.view" id="action_report_crm_opportunity_graph">
|
|
||||||
<field name="sequence" eval="2"/>
|
|
||||||
<field name="view_mode">graph</field>
|
|
||||||
<field name="view_id" ref="view_report_crm_opportunity_graph"/>
|
|
||||||
<field name="act_window_id" ref="action_report_crm_opportunity"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem name="Opportunities"
|
|
||||||
action="action_report_crm_opportunity"
|
|
||||||
id="menu_report_crm_opportunity_tree" parent="base.next_id_64" />
|
|
||||||
|
|
||||||
</data>
|
|
||||||
</openerp>
|
|
|
@ -22,15 +22,76 @@
|
||||||
from osv import fields,osv
|
from osv import fields,osv
|
||||||
import tools
|
import tools
|
||||||
|
|
||||||
|
AVAILABLE_STATES = [
|
||||||
|
('draft','Draft'),
|
||||||
|
('open','Open'),
|
||||||
|
('cancel', 'Cancelled'),
|
||||||
|
('done', 'Closed'),
|
||||||
|
('pending','Pending')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class crm_phonecall_report(osv.osv):
|
class crm_phonecall_report(osv.osv):
|
||||||
""" Phone calls by user and section """
|
""" Phone calls by user and section """
|
||||||
|
|
||||||
_name = "crm.phonecall.report"
|
_name = "crm.phonecall.report"
|
||||||
_description = "Phone calls by user and section"
|
_description = "Phone calls by user and section"
|
||||||
_auto = False
|
_auto = False
|
||||||
_inherit = "crm.case.report"
|
|
||||||
|
def _get_data(self, cr, uid, ids, field_name, arg, context={}):
|
||||||
|
|
||||||
|
""" @param cr: the current row, from the database cursor,
|
||||||
|
@param uid: the current user’s ID for security checks,
|
||||||
|
@param ids: List of case and section Data’s IDs
|
||||||
|
@param context: A standard dictionary for contextual values """
|
||||||
|
|
||||||
|
res = {}
|
||||||
|
state_perc = 0.0
|
||||||
|
avg_ans = 0.0
|
||||||
|
|
||||||
|
for case in self.browse(cr, uid, ids, context):
|
||||||
|
if field_name != 'avg_answers':
|
||||||
|
state = field_name[5:]
|
||||||
|
cr.execute("select count(*) from crm_opportunity where \
|
||||||
|
section_id =%s and state='%s'"%(case.section_id.id, state))
|
||||||
|
state_cases = cr.fetchone()[0]
|
||||||
|
perc_state = (state_cases / float(case.nbr)) * 100
|
||||||
|
|
||||||
|
res[case.id] = perc_state
|
||||||
|
else:
|
||||||
|
model_name = self._name.split('report.')
|
||||||
|
if len(model_name) < 2:
|
||||||
|
res[case.id] = 0.0
|
||||||
|
else:
|
||||||
|
model_name = model_name[1]
|
||||||
|
|
||||||
|
cr.execute("select count(*) from crm_case_log l, ir_model m \
|
||||||
|
where l.model_id=m.id and m.model = '%s'" , model_name)
|
||||||
|
logs = cr.fetchone()[0]
|
||||||
|
|
||||||
|
avg_ans = logs / case.nbr
|
||||||
|
res[case.id] = avg_ans
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
'name': fields.char('Year', size=64, required=False, readonly=True),
|
||||||
|
'user_id':fields.many2one('res.users', 'User', readonly=True),
|
||||||
|
'section_id':fields.many2one('crm.case.section', 'Section', readonly=True),
|
||||||
|
'nbr': fields.integer('# of Cases', readonly=True),
|
||||||
|
'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True),
|
||||||
|
'avg_answers': fields.function(_get_data, string='Avg. Answers', method=True, type="integer"),
|
||||||
|
'perc_done': fields.function(_get_data, string='%Done', method=True, type="float"),
|
||||||
|
'perc_cancel': fields.function(_get_data, string='%Cancel', method=True, type="float"),
|
||||||
|
'month':fields.selection([('01', 'January'), ('02', 'February'), \
|
||||||
|
('03', 'March'), ('04', 'April'),\
|
||||||
|
('05', 'May'), ('06', 'June'), \
|
||||||
|
('07', 'July'), ('08', 'August'),\
|
||||||
|
('09', 'September'), ('10', 'October'),\
|
||||||
|
('11', 'November'), ('12', 'December')], 'Month', readonly=True),
|
||||||
|
'company_id': fields.many2one('res.company', 'Company', readonly=True),
|
||||||
|
'create_date': fields.datetime('Create Date', readonly=True),
|
||||||
|
'day': fields.char('Day', size=128, readonly=True),
|
||||||
'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"),
|
||||||
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
'categ_id': fields.many2one('crm.case.categ', 'Category', \
|
||||||
domain="[('section_id','=',section_id),\
|
domain="[('section_id','=',section_id),\
|
||||||
|
|
|
@ -30,15 +30,20 @@
|
||||||
<record id="view_report_crm_phonecall_form" model="ir.ui.view">
|
<record id="view_report_crm_phonecall_form" model="ir.ui.view">
|
||||||
<field name="name">crm.phonecall.report.form</field>
|
<field name="name">crm.phonecall.report.form</field>
|
||||||
<field name="model">crm.phonecall.report</field>
|
<field name="model">crm.phonecall.report</field>
|
||||||
<field name="inherit_id" ref="view_crm_case_form"/>
|
|
||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="nbr" position="after">
|
<form string="Cases">
|
||||||
<field name="delay_close"/>
|
<field name="name" select="1"/>
|
||||||
|
<field name="month" select="1"/>
|
||||||
|
<field name="user_id" select="1"/>
|
||||||
|
<field name="section_id" select="1"/>
|
||||||
|
<field name="nbr" select="1"/>
|
||||||
|
<field name="delay_close"/>
|
||||||
<field name="amount_revenue"/>
|
<field name="amount_revenue"/>
|
||||||
<field name="amount_revenue_prob"/>
|
<field name="amount_revenue_prob"/>
|
||||||
<field name="probability"/>
|
<field name="probability"/>
|
||||||
</field>
|
<field name="state" select="1"/>
|
||||||
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
@ -58,21 +63,108 @@
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- Phone calls by user and section Search View -->
|
<!-- Phone calls by user and section Search View -->
|
||||||
|
|
||||||
<record id="view_report_crm_phonecall_filter" model="ir.ui.view">
|
<record id="view_report_crm_phonecall_filter" model="ir.ui.view">
|
||||||
<field name="name">crm.phonecall.report.select</field>
|
<field name="name">crm.phonecall.report.select</field>
|
||||||
<field name="model">crm.phonecall.report</field>
|
<field name="model">crm.phonecall.report</field>
|
||||||
<field name="inherit_id" ref="view_crm_case_filter"/>
|
|
||||||
<field name="type">search</field>
|
<field name="type">search</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<data>
|
<search string="Search">
|
||||||
<xpath expr='//search[@string="Search"]/group[@string="Extended options..."]/filter[@string="Cancel"]' position='after'>
|
<group col="16" colspan="8">
|
||||||
<group>
|
<!-- <filter string="This Year" name="This Year" icon="terp-hr"
|
||||||
<separator orientation="vertical"/>
|
domain="[('name','=',time.localtime()[0])]"/>-->
|
||||||
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.phonecall')]"/>
|
<filter string="This Year" icon="terp-hr"
|
||||||
</group>
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=365)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
</xpath>
|
|
||||||
</data>
|
<!-- <filter string="This Month" name="This Year" icon="terp-hr"
|
||||||
|
domain="[('month','=',time.strftime('%%m'))]" />-->
|
||||||
|
<filter string="This Month" icon="terp-hr" name="This Month"
|
||||||
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=30)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
|
|
||||||
|
<filter icon="gtk-media-rewind" string="7 Days" separator="1"
|
||||||
|
domain="[('create_date','<=', time.strftime('%%Y-%%m-%%d')), ('create_date','>',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d'))]"/>
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Draft"
|
||||||
|
domain="[('state','=','draft')]"/>
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Open"
|
||||||
|
domain="[('state','=','open')]"/>
|
||||||
|
<filter icon="terp-hr"
|
||||||
|
string="Pending"
|
||||||
|
domain="[('state','=','pending')]"/>
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<field name="section_id"
|
||||||
|
default="context.get('section_id', False)"
|
||||||
|
widget="selection"
|
||||||
|
context="{'invisible_section': False}">
|
||||||
|
|
||||||
|
<filter icon="terp-crm"
|
||||||
|
context="{'invisible_section': False}"
|
||||||
|
domain="[('section_id.user_id','=',uid)]"
|
||||||
|
help="My section" />
|
||||||
|
|
||||||
|
</field>
|
||||||
|
<field name="company_id" widget="selection">
|
||||||
|
<filter icon="terp-crm"
|
||||||
|
context="{'invisible_section': False}"
|
||||||
|
domain="[('section_id.user_id.company_id','=',uid)]"
|
||||||
|
help="My company"/>
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<field name="user_id" select="1" widget="selection">
|
||||||
|
<filter icon="terp-crm" string="My Case" help="My Case" domain="[('user_id','=',uid)]" />
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="1" string="Extended options..." colspan="10" col="12">
|
||||||
|
<filter icon="terp-sale"
|
||||||
|
string="Done"
|
||||||
|
domain="[('state','=','done')]"/>
|
||||||
|
|
||||||
|
<filter icon="terp-sale"
|
||||||
|
string="Cancel"
|
||||||
|
domain="[('state','=','cancel')]"/>
|
||||||
|
<group>
|
||||||
|
<separator orientation="vertical"/>
|
||||||
|
<field name="categ_id" widget="selection" domain="[('object_id.model', '=', 'crm.phonecall')]"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
</group>
|
||||||
|
<newline/>
|
||||||
|
<group expand="1" string="Group By..." colspan="4" col="8">
|
||||||
|
<filter string="User" name="User" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'user_id'}" />
|
||||||
|
|
||||||
|
<filter string="Company" icon="terp-sale"
|
||||||
|
domain="[]"
|
||||||
|
context="{'group_by':'company_id'}" />
|
||||||
|
|
||||||
|
<filter string="Section" icon="terp-sale"
|
||||||
|
domain="[]"
|
||||||
|
context="{'group_by':'section_id'}" />
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="State" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'state'}" />
|
||||||
|
|
||||||
|
<filter string="Category" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'categ_id'}" />
|
||||||
|
|
||||||
|
<separator orientation="vertical" />
|
||||||
|
<filter string="Day" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'day'}"/>
|
||||||
|
|
||||||
|
<filter string="Month" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'month'}" />
|
||||||
|
|
||||||
|
<filter string="Year" icon="terp-sale"
|
||||||
|
domain="[]" context="{'group_by':'name'}" />
|
||||||
|
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
include("xmlrpc.inc");
|
||||||
|
|
||||||
|
$user = "";
|
||||||
|
$password = "";
|
||||||
|
$db = "";
|
||||||
|
$serverUri = "";
|
||||||
|
|
||||||
|
$client = new xmlrpc_client($serverUri.'common');
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('login');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($user, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
|
||||||
|
|
||||||
|
$res = &$client->send($msg);
|
||||||
|
|
||||||
|
if(!$res->faultCode()){
|
||||||
|
|
||||||
|
$id = $res->value()->scalarval();
|
||||||
|
|
||||||
|
$client = new xmlrpc_client($serverUri.'object');
|
||||||
|
|
||||||
|
$key = array(new xmlrpcval(array(new xmlrpcval("id", "string"),
|
||||||
|
new xmlrpcval(">", "string"),
|
||||||
|
new xmlrpcval(0, "int")),"array"),);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('execute');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($id, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("res.country","string"));
|
||||||
|
$msg->addParam(new xmlrpcval("search", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($key, "array"));
|
||||||
|
$msg->addParam(new xmlrpcval(0, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval(0, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval("id ASC", "string"));
|
||||||
|
|
||||||
|
$res = &$client->send($msg);
|
||||||
|
|
||||||
|
if(!$res->faultCode())
|
||||||
|
{
|
||||||
|
$val = $res->value()->scalarval();
|
||||||
|
|
||||||
|
$ides = array();
|
||||||
|
|
||||||
|
for ($i=0 ; $i<count($val); $i++)
|
||||||
|
{
|
||||||
|
array_push($ides, new xmlrpcval($val[$i]->scalarval(), "int"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = new xmlrpc_client($serverUri.'object');
|
||||||
|
|
||||||
|
$fields = array(new xmlrpcval("code", "string"), new xmlrpcval("name", "string"));
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('execute');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($id, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("res.country","string"));
|
||||||
|
$msg->addParam(new xmlrpcval("read", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($ides, "array"));
|
||||||
|
$msg->addParam(new xmlrpcval($fields, "array"));
|
||||||
|
|
||||||
|
$res = &$client->send($msg);
|
||||||
|
|
||||||
|
if (!$res->faultCode())
|
||||||
|
{
|
||||||
|
$val = $res->value()->scalarval();
|
||||||
|
|
||||||
|
$select ='<select class="inputbox required" name="country" id="country" >
|
||||||
|
<option value="" selected="selected"> -- Select an Option -- </option>';
|
||||||
|
|
||||||
|
for ($i=0; $i<count($val);$i++)
|
||||||
|
{
|
||||||
|
$field = $val[$i]->scalarval();
|
||||||
|
$select .= '<option value="'.$field['code']->scalarval().'">'.$field['name']->scalarval().'</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$select .= '</select>';
|
||||||
|
|
||||||
|
echo $select;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "Country not getting";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "Country list id empty";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "connection not establish";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
||||||
|
?>
|
|
@ -0,0 +1,201 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include("xmlrpc.inc");
|
||||||
|
|
||||||
|
class Contact
|
||||||
|
{
|
||||||
|
private $subject = '', $to = '';
|
||||||
|
|
||||||
|
function __construct($to, $sub)
|
||||||
|
{
|
||||||
|
$this->to = $to;
|
||||||
|
$this->subject = $sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mailTo($post)
|
||||||
|
{
|
||||||
|
if (!empty($post))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (empty($this->to))
|
||||||
|
{
|
||||||
|
$this->to = 'sales@tinyerp.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->subject))
|
||||||
|
{
|
||||||
|
$this->subject = 'Contact form information';
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = '<table width="90%" border="0" cellspacing="2" cellpadding="2">
|
||||||
|
<tr>
|
||||||
|
<td width="25%"></td>
|
||||||
|
<td width="2%"></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>';
|
||||||
|
|
||||||
|
foreach ($post as $key => $value)
|
||||||
|
{
|
||||||
|
$body .= "<tr>
|
||||||
|
<td align='right'>$key</td>
|
||||||
|
<td align='center'><b>:</b></td>
|
||||||
|
<td align='left'>$value</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$body .= '</table>';
|
||||||
|
|
||||||
|
$header = 'MEME-version : 1.0'."\r\n";
|
||||||
|
$header .= 'Content-type: text/html; charset=iso-8859-1'."\r\n";
|
||||||
|
$header .= 'From: '.$post['name'].' <'.$post['email'].'>'."\r\n";
|
||||||
|
$header .= 'Reply-To: '.$post['name'].' <'.$post['email'].'>'."\r\n";
|
||||||
|
$header .= 'Return-Path: '.$post['name'].' <'.$post['email'].'>'."\r\n";
|
||||||
|
|
||||||
|
if (mail($this->to, $this->subject, $body, $header))
|
||||||
|
{
|
||||||
|
echo "<br /><h3>Thank You, Your information has been successfully submitted.</h3><br /><p><b>We will be in touch very soon.</b></p>";
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<br /><h4>Sorry, Due to some problem Your information has not been submitted.</h4>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function xmlCallTo($usr, $password, $database, $server, $post)
|
||||||
|
{
|
||||||
|
$user = $usr;
|
||||||
|
$pass = $password;
|
||||||
|
$db = $database;
|
||||||
|
$server_url = $server; //'http://localhost:8069/xmlrpc/'
|
||||||
|
|
||||||
|
$client = new xmlrpc_client($server_url.'common');
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('login');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($user, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
|
||||||
|
|
||||||
|
$res = &$client->send($msg);
|
||||||
|
|
||||||
|
if(!$res->faultCode()){
|
||||||
|
|
||||||
|
$val = $res->value();
|
||||||
|
$id = $val->scalarval();
|
||||||
|
|
||||||
|
if (empty($id)){
|
||||||
|
|
||||||
|
echo "Connection error = ";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$client2 = new xmlrpc_client($server_url.'object');
|
||||||
|
|
||||||
|
$key = array(new xmlrpcval(array(new xmlrpcval("code", "string"), new xmlrpcval("=", "string"), new xmlrpcval($post['country'], "string")),"array"),);
|
||||||
|
$msg = new xmlrpcmsg('execute');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($id, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("res.country","string"));
|
||||||
|
$msg->addParam(new xmlrpcval("search", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($key, "array"));
|
||||||
|
$msg->addParam(new xmlrpcval(0, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval(1, "int"));
|
||||||
|
|
||||||
|
$res = &$client2->send($msg);
|
||||||
|
|
||||||
|
$val = $res->value()->scalarval();
|
||||||
|
|
||||||
|
$countryId = $val[0]->scalarval();
|
||||||
|
|
||||||
|
$val = array ("name" => new xmlrpcval($post['company'],"string"),
|
||||||
|
"title" => new xmlrpcval($post['salutation'], "string"),
|
||||||
|
"email_from" => new xmlrpcval($post['email'], "string"),
|
||||||
|
"phone" => new xmlrpcval($post['phone'], "string"),
|
||||||
|
"partner_name" => new xmlrpcval($post['name'], "string"),
|
||||||
|
"function_name" => new xmlrpcval($post["jobtitle"], "string"),
|
||||||
|
"zip" => new xmlrpcval($post['zip'], "string"),
|
||||||
|
"stage_id" => new xmlrpcval(6, "int"),
|
||||||
|
"city" => new xmlrpcval($post['city'], "string"),
|
||||||
|
"country_id" => new xmlrpcval($countryId, "int"),
|
||||||
|
"state" => new xmlrpcval("draft", "string"),
|
||||||
|
"user_id" => new xmlrpcval(false, "boolean"),
|
||||||
|
"description" => new xmlrpcval("No.of Employees: ".$post['employees']."\nState: ".$post['state']."\nIndustry: ".$post['industry']."\nReasons: ".$post['reasons']."\nAbout: ".$post['about']."\nQuestions: ".stripslashes($post['questions'])."\nUpdate: ".$post['update'], "string")
|
||||||
|
);
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('execute');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($id, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("crm.lead", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("create", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($val, "struct"));
|
||||||
|
|
||||||
|
|
||||||
|
$res2 = &$client2->send($msg);
|
||||||
|
|
||||||
|
if(!$res2->faultCode())
|
||||||
|
{
|
||||||
|
$readVal = $res2->value()->scalarval();
|
||||||
|
|
||||||
|
if (!empty($readVal))
|
||||||
|
{
|
||||||
|
$val = array ( "description" => new xmlrpcval("About: ".$post['about']."\nQuestions: ".$post['questions']),
|
||||||
|
"model_id" => new xmlrpcval(276, "int"),
|
||||||
|
"res_id" => new xmlrpcval($readVal,"int"),
|
||||||
|
"email_from" => new xmlrpcval($post['email'], "string"),
|
||||||
|
"email_to" => new xmlrpcval("sales@openerp.com", "string")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$msg = new xmlrpcmsg('execute');
|
||||||
|
$msg->addParam(new xmlrpcval($db, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($id, "int"));
|
||||||
|
$msg->addParam(new xmlrpcval($pass, "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("crm.case.history", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval("create", "string"));
|
||||||
|
$msg->addParam(new xmlrpcval($val, "struct"));
|
||||||
|
|
||||||
|
$res2 = &$client2->send($msg);
|
||||||
|
|
||||||
|
//echo "<br />Successfully created lead";
|
||||||
|
echo "<br /><h3>Thank You for your interest in openerp, we'll respond to your request shortly.</h3><br />";
|
||||||
|
if(strstr($post["about"],"Book")) {
|
||||||
|
echo '<script>window.location="http://www.openerp.com/index.php?option=com_content&id=54"</script>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<br />Lead is not created";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<br />Problem in message sending for create lead";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<br />Connection not established";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$arrData = array();
|
||||||
|
$arrData = array_merge($arrData, (array)$_POST);
|
||||||
|
|
||||||
|
$cnt = new Contact('sales5@openerp.com', 'Country: '.$arrData['country']. ' About: ' .$arrData['about']);
|
||||||
|
|
||||||
|
/* This function use for sending mail on perticular mail account */
|
||||||
|
/*$cnt->mailTo($arrData); */
|
||||||
|
|
||||||
|
/* This function use ceating lead in crm of opener erp database */
|
||||||
|
$cnt->xmlCallTo('<username>', '<password>', '<db>', '<server>', $arrData);
|
||||||
|
|
||||||
|
exit;
|
||||||
|
?>
|
File diff suppressed because it is too large
Load Diff
|
@ -3,43 +3,30 @@
|
||||||
"access_crm_segmentation_line","crm.segmentation.line","model_crm_segmentation_line","crm.group_crm_manager",1,1,1,1
|
"access_crm_segmentation_line","crm.segmentation.line","model_crm_segmentation_line","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_section","crm.case.section","model_crm_case_section","crm.group_crm_user",1,0,0,0
|
"access_crm_case_section","crm.case.section","model_crm_case_section","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_case_categ","crm.case.categ","model_crm_case_categ","crm.group_crm_user",1,0,0,0
|
"access_crm_case_categ","crm.case.categ","model_crm_case_categ","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_case_manger","crm.case manager","model_crm_case","crm.group_crm_user",1,1,1,1
|
|
||||||
"access_crm_case","crm.case","model_crm_case","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_claim","crm.claim","model_crm_claim","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_fundraising","crm.fundraising","model_crm_fundraising","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_helpdesk","crm.helpdesk","model_crm_helpdesk","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_meeting","crm.meeting","model_crm_meeting","crm.group_crm_manager",1,1,1,1
|
"access_crm_meeting","crm.meeting","model_crm_meeting","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_lead","crm.lead","model_crm_lead","crm.group_crm_manager",1,1,1,1
|
"access_crm_lead","crm.lead","model_crm_lead","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_opportunity","crm.opportunity","model_crm_opportunity","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_phonecall","crm.phonecall","model_crm_phonecall","crm.group_crm_manager",1,1,1,1
|
"access_crm_phonecall","crm.phonecall","model_crm_phonecall","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_log","crm.case.log","model_crm_case_log","crm.group_crm_user",1,1,1,1
|
"access_crm_case_log","crm.case.log","model_crm_case_log","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_case_history","crm.case.history","model_crm_case_history","crm.group_crm_user",1,1,1,1
|
"access_crm_case_history","crm.case.history","model_crm_case_history","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_case_section_manager","crm.case.section.manager","model_crm_case_section","crm.group_crm_manager",1,1,1,1
|
"o","crm.case.section.manager","model_crm_case_section","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_categ_manager","crm.case.categ.manager","model_crm_case_categ","crm.group_crm_manager",1,1,1,1
|
"access_crm_case_categ_manager","crm.case.categ.manager","model_crm_case_categ","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_log_manager","crm.case.log manager","model_crm_case_log","crm.group_crm_manager",1,1,1,1
|
"access_crm_case_log_manager","crm.case.log manager","model_crm_case_log","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_history_manager","crm.case.history manager","model_crm_case_history","crm.group_crm_manager",1,1,1,1
|
"access_crm_case_history_manager","crm.case.history manager","model_crm_case_history","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_email_add_cc_manager","crm.email.add.cc","model_crm_email_add_cc","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_email_add_cc_user","crm.email.add.cc","model_crm_email_add_cc","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_crm_case_stage","crm.case.stage","model_crm_case_stage","crm.group_crm_user",1,0,0,0
|
"access_crm_case_stage","crm.case.stage","model_crm_case_stage","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_case_stage_manager","crm.case.stage","model_crm_case_stage","crm.group_crm_manager",1,1,1,1
|
"access_crm_case_stage_manager","crm.case.stage","model_crm_case_stage","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_resource_type_user","crm_case_resource_type user","model_crm_case_resource_type","crm.group_crm_user",1,0,0,0
|
"access_crm_case_resource_type_user","crm_case_resource_type user","model_crm_case_resource_type","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_case_resource_type_manager","crm_case_resource_type manager","model_crm_case_resource_type","crm.group_crm_manager",1,1,1,1
|
"access_crm_case_resource_type_manager","crm_case_resource_type manager","model_crm_case_resource_type","crm.group_crm_manager",1,1,1,1
|
||||||
"access_crm_case_report_user","crm.case.report","model_crm_case_report","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_crm_case_report_manager","crm.case.report manager","model_crm_case_report","crm.group_crm_manager",1,1,1,1
|
|
||||||
"access_crm_claim_report_user","crm.claim.report","model_crm_claim_report","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_crm_fundraising_report_user","crm.fundraising.report","model_crm_fundraising_report","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_crm_lead_report_user","crm.lead.report","model_crm_lead_report","crm.group_crm_user",1,0,0,0
|
"access_crm_lead_report_user","crm.lead.report","model_crm_lead_report","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_phonecall_report_user","crm.phonecall.report","model_crm_phonecall_report","crm.group_crm_user",1,0,0,0
|
"access_crm_phonecall_report_user","crm.phonecall.report","model_crm_phonecall_report","crm.group_crm_user",1,0,0,0
|
||||||
"access_crm_opportunity_report_user","crm.opportunity.report","model_crm_opportunity_report","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_report_crm_case_service_dashboard","report.crm.case.service.dashboard","model_report_crm_case_service_dashboard","crm.group_crm_user",1,0,0,0
|
|
||||||
"access_crm_lead2partner","crm.lead2partner","model_crm_lead2partner","crm.group_crm_user",1,1,1,1
|
"access_crm_lead2partner","crm.lead2partner","model_crm_lead2partner","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_lead2opportunity","crm.lead2opportunity","model_crm_lead2opportunity","crm.group_crm_user",1,1,1,1
|
"access_crm_lead2opportunity","crm.lead2opportunity","model_crm_lead2opportunity","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_opportunity2phonecall","crm.opportunity2phonecall","model_crm_opportunity2phonecall","crm.group_crm_user",1,1,1,1
|
"access_crm_opportunity2phonecall","crm.opportunity2phonecall","model_crm_opportunity2phonecall","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_phonecall2phonecall","crm.phonecall2phonecall","model_crm_phonecall2phonecall","crm.group_crm_user",1,1,1,1
|
"access_crm_phonecall2phonecall","crm.phonecall2phonecall","model_crm_phonecall2phonecall","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_phonecall2partner","crm.phonecall2partner","model_crm_phonecall2partner","crm.group_crm_user",1,1,1,1
|
"access_crm_phonecall2partner","crm.phonecall2partner","model_crm_phonecall2partner","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_phonecall2opportunity","crm.phonecall2opportunity","model_crm_phonecall2opportunity","crm.group_crm_user",1,1,1,1
|
"access_crm_phonecall2opportunity","crm.phonecall2opportunity","model_crm_phonecall2opportunity","crm.group_crm_user",1,1,1,1
|
||||||
"access_report_crm_helpdesk","report.crm.helpdesk","model_crm_helpdesk_report","crm.group_crm_user",1,1,1,1
|
|
||||||
"access_crm_send_mail","crm.send.mail","model_crm_send_mail","crm.group_crm_user",1,1,1,1
|
"access_crm_send_mail","crm.send.mail","model_crm_send_mail","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_send_mail_attachment","crm.send.mail.attachment","model_crm_send_mail_attachment","crm.group_crm_user",1,1,1,1
|
"access_crm_send_mail_attachment","crm.send.mail.attachment","model_crm_send_mail_attachment","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_partner2opportunity","crm.partner2opportunity","model_crm_partner2opportunity","crm.group_crm_user",1,1,1,1
|
"access_crm_partner2opportunity","crm.partner2opportunity","model_crm_partner2opportunity","crm.group_crm_user",1,1,1,1
|
||||||
"access_crm_lead2opportunity_partner","crm.lead2opportunity.partner","model_crm_lead2opportunity_partner","crm.group_crm_user",1,1,1,1
|
"access_crm_lead2opportunity_partner","crm.lead2opportunity.partner","model_crm_lead2opportunity_partner","crm.group_crm_user",1,1,1,1
|
||||||
|
"access_crm_installer","crm.installer.rule","model_crm_installer","crm.group_crm_user",1,1,1,1
|
||||||
|
"access_crm_lead_forward_to_partner","crm.lead.forward.to.partner","model_crm_lead_forward_to_partner","crm.group_crm_user",2,2,2,2
|
||||||
|
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
- |
|
||||||
|
In order to test the CRM in OpenERP,
|
||||||
|
I will do a customer qualification process that
|
||||||
|
starts with a fist contact with a customer (a lead), which will be converted to a
|
||||||
|
business opportunity and a partner.
|
||||||
|
- |
|
||||||
|
In order to test the flow, I create a two new users "user_crm" and
|
||||||
|
I assign the group "salesman".
|
||||||
|
-
|
||||||
|
!record {model: res.users, id: res_users_usercrm0}:
|
||||||
|
company_id: base.main_company
|
||||||
|
context_lang: en_US
|
||||||
|
context_section_id: crm.section_sales_department
|
||||||
|
groups_id:
|
||||||
|
- crm.group_crm_user
|
||||||
|
login: user_crm
|
||||||
|
name: user_crm
|
||||||
|
password: user_crm
|
||||||
|
|
||||||
|
|
||||||
|
- |
|
||||||
|
I start by creating a new lead "New Customer" and I provide an address to this
|
||||||
|
new customer, as well as an email "info@mycustomer.com".
|
||||||
|
-
|
||||||
|
!record {model: crm.lead, id: crm_lead_newcustomer0}:
|
||||||
|
email_from: info@mycustomer.com
|
||||||
|
name: New Customer
|
||||||
|
partner_name: Sandip zala
|
||||||
|
phone: (855) 924-4364
|
||||||
|
mobile: (333) 715-1450
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
referred: False
|
||||||
|
title: M.
|
||||||
|
- |
|
||||||
|
I check that the lead is in 'draft' state,
|
||||||
|
-
|
||||||
|
!assert {model: crm.lead, id: crm_lead_newcustomer0, string: Lead in Draft}:
|
||||||
|
- state == "draft"
|
||||||
|
-
|
||||||
|
I open lead by click on "Open" button,
|
||||||
|
-
|
||||||
|
!python {model: crm.lead}: |
|
||||||
|
self.case_open(cr, uid, [ref("crm_lead_newcustomer0")])
|
||||||
|
|
||||||
|
- |
|
||||||
|
As the lead seems to be a real business opportunity, I will convert it to a
|
||||||
|
partner
|
||||||
|
and a business opportunity by clicking on the "Convert" button.
|
||||||
|
-
|
||||||
|
!python {model: crm.lead}: |
|
||||||
|
lead = self.browse(cr, uid, ref('crm_lead_newcustomer0'))
|
||||||
|
if not lead.partner_id:
|
||||||
|
self.convert_opportunity(cr, uid, [ref("crm_lead_newcustomer0")], {'active_ids': [ref("crm_lead_newcustomer0")]})
|
||||||
|
-
|
||||||
|
|
|
||||||
|
Now, select "create a new partner" option in this wizard.
|
||||||
|
-
|
||||||
|
!record {model: crm.lead2opportunity.partner, id: crm_lead2opportunity_partner_create_0}:
|
||||||
|
action: 'create'
|
||||||
|
-
|
||||||
|
I click on "Continue" button of this wizard.
|
||||||
|
-
|
||||||
|
!python {model: crm.lead2opportunity.partner}: |
|
||||||
|
self.make_partner(cr, uid, [ref("crm_lead2opportunity_partner_create_0")], {'active_ids': [ref("crm_lead_newcustomer0")]})
|
||||||
|
-
|
||||||
|
Now, I give value to this wizard field.
|
||||||
|
-
|
||||||
|
!record {model: crm.lead2opportunity, id: crm_lead2opportunity_stonage_0}:
|
||||||
|
name: Sandip zala
|
||||||
|
planned_revenue: 0.00
|
||||||
|
probability: 0.00
|
||||||
|
-
|
||||||
|
Then, Click on "Create Opportunity" button of this wizard.
|
||||||
|
-
|
||||||
|
!python {model: crm.lead2opportunity}: |
|
||||||
|
self.action_apply(cr, uid, [ref('crm_lead2opportunity_stonage_0')], {'active_id': ref('crm_lead_newcustomer0')})
|
||||||
|
|
||||||
|
- |
|
||||||
|
I can check that a lead and a business opportunity is now assigned to this
|
||||||
|
lead.
|
||||||
|
-
|
||||||
|
# !python {model: crm.lead, id: crm_lead_newcustomer0}:
|
||||||
|
# - opportunity_id.id != False
|
||||||
|
|
||||||
|
- |
|
||||||
|
I check that the partner associated to this lead as the same country, phone number
|
||||||
|
and name than the opportunity.
|
||||||
|
-
|
||||||
|
!python {model: crm.lead}: |
|
||||||
|
lead = self.browse(cr, uid, ref("crm_lead_newcustomer0"))
|
||||||
|
obj_opportunity = self.pool.get('crm.lead')
|
||||||
|
ids = obj_opportunity.search(cr, uid, [('name', '=', lead.partner_name)])
|
||||||
|
opportunity = obj_opportunity.browse(cr, uid, ids)[0]
|
||||||
|
assert lead.partner_name == opportunity.partner_id.name
|
||||||
|
assert lead.phone == opportunity.phone
|
||||||
|
-
|
||||||
|
|
|
||||||
|
# yaml is also not working smpt server and send new email.
|
||||||
|
|
||||||
|
- |
|
||||||
|
I configure with smtp server.
|
||||||
|
- |
|
||||||
|
And I communicate with lead through send New mail to Lead using it email address from user which are loged.
|
||||||
|
- |
|
||||||
|
I check that communication history generated when send email to lead.
|
||||||
|
- |
|
||||||
|
Then, I add a cc which receive copy of future communication between partner and users by mail.
|
||||||
|
|
||||||
|
- |
|
||||||
|
I Reply to last Email to lead with some document attached.and check that communication history generated or not.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
- |
|
||||||
|
Now I will test Meetings which may be customer meeting or phonecall meeting or
|
||||||
|
internal Meeting.
|
||||||
|
- |
|
||||||
|
I start by creating a new Meeting.
|
||||||
|
-
|
||||||
|
!record {model: crm.meeting, id: crm_meeting_regardingpresentation0}:
|
||||||
|
categ_id: crm.categ_meet2
|
||||||
|
date: '2010-04-21 16:04:00'
|
||||||
|
date_deadline: '2010-04-22 00:04:00'
|
||||||
|
duration: 8.0
|
||||||
|
email_from: info@balmerinc.be
|
||||||
|
location: Ahmedabad
|
||||||
|
name: Regarding Presentation
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
rrule_type: weekly
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
- |
|
||||||
|
I check that the Meetings is in 'UnConfirmed' state.
|
||||||
|
-
|
||||||
|
!assert {model: crm.meeting, id: crm_meeting_regardingpresentation0}:
|
||||||
|
- state == "draft"
|
||||||
|
- |
|
||||||
|
I can set reminder on meeting if I put reminder "40 minutes before"
|
||||||
|
- |
|
||||||
|
For that, I first create alarm.
|
||||||
|
-
|
||||||
|
!record {model: res.alarm, id: res_alarm_minituesbefore0}:
|
||||||
|
name: 40 minutes before
|
||||||
|
trigger_duration: 40
|
||||||
|
trigger_interval: minutes
|
||||||
|
trigger_occurs: before
|
||||||
|
trigger_related: start
|
||||||
|
- |
|
||||||
|
Now I will assign this reminder.
|
||||||
|
|
||||||
|
- !python {model: crm.meeting}: |
|
||||||
|
self.write(cr, uid, [ref('crm_meeting_regardingpresentation0')], {'alarm_id': ref("res_alarm_minituesbefore0")})
|
||||||
|
- |
|
||||||
|
In order to check recurrence on meetings I will set Recurrency to Custom
|
||||||
|
and I set the fields so that the meeting will occure weekly on Monday and Friday 10 times
|
||||||
|
-
|
||||||
|
!python {model: crm.meeting}: |
|
||||||
|
self.write(cr, uid, [ref("crm_meeting_regardingpresentation0")], {'fr': 1, 'mo': 1, 'th': 1, 'tu': 1, 'we':1, 'count':10, 'interval': 1, 'freq': 'weekly', 'rrule_type': 'custom'})
|
||||||
|
|
||||||
|
- |
|
||||||
|
I can see from the calendar view that the meeting is scheduled on Monday and Friday
|
||||||
|
for 10 times,
|
||||||
|
|
||||||
|
-
|
||||||
|
!python {model: crm.meeting}: |
|
||||||
|
self.fields_view_get(cr, uid, False, 'calendar', context)
|
||||||
|
- |
|
||||||
|
I will search for one of the recurrent event and count the number of meeting.
|
||||||
|
-
|
||||||
|
!python {model: crm.meeting}: |
|
||||||
|
ids = self.search(cr, uid, [('date', '>=', '2010-05-21 00:00:00'), ('date', '<=', '2010-04-21 00:00:00')] )
|
||||||
|
assert len(ids) == 9
|
||||||
|
|
||||||
|
- |
|
||||||
|
Now If I want to edit meetings information for all occurence I click on "Edit All" button.
|
||||||
|
-
|
||||||
|
!python {model: crm.meeting}: |
|
||||||
|
self.open_meeting(cr, uid, [ref('crm_meeting_regardingpresentation0')])
|
||||||
|
- |
|
||||||
|
I can see that new meeting form is opened with same value
|
||||||
|
I change some data for meeting and save it
|
||||||
|
I can see from meeting's calendar view that all meeting occurences are changed accordingly
|
||||||
|
-
|
||||||
|
!record {model: crm.meeting, id: crm.crm_meeting_regardingpresentation0}:
|
||||||
|
alarm_id: base_calendar.alarm9
|
||||||
|
rrule_type: weekly
|
||||||
|
|
||||||
|
- |
|
||||||
|
In order to invite people for this meetings, I click on "Invite People" button
|
||||||
|
I can invite internal user.
|
||||||
|
-
|
||||||
|
!record {model: base_calendar.invite.attendee, id: base_calendar_invite_attendee_0}:
|
||||||
|
type: internal
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
user_ids:
|
||||||
|
- base.user_demo
|
||||||
|
- |
|
||||||
|
If I set Send mail boolean as True I can see that an email is send to
|
||||||
|
specified email address with proper meeting information.
|
||||||
|
-
|
||||||
|
#This is not working for send mail boolean as True.
|
||||||
|
-
|
||||||
|
I click on "Invite" button of "Invite attendee" wizard.
|
||||||
|
-
|
||||||
|
!python {model: base_calendar.invite.attendee}: |
|
||||||
|
self.do_invite(cr, uid, [ref('base_calendar_invite_attendee_0')], {'active_id': ref('crm_meeting_regardingpresentation0'), 'model' : 'crm.meeting', 'attendee_field':'attendee_ids'})
|
||||||
|
|
||||||
|
- |
|
||||||
|
After direct/indirect confirmation for meetings I can confirm meeting
|
||||||
|
-
|
||||||
|
!python {model: crm.meeting}: |
|
||||||
|
self.case_open(cr, uid, [ref('crm_meeting_regardingpresentation0')])
|
|
@ -0,0 +1,81 @@
|
||||||
|
- |
|
||||||
|
I start by creating a new Opportunity. And I select partner for opportunity.
|
||||||
|
I can see that after selecting partner his contact and email is automatically filled.
|
||||||
|
-
|
||||||
|
!record {model: crm.lead, id: crm_opportunity_abcfuelcounits0}:
|
||||||
|
email_from: info@balmerinc.be
|
||||||
|
name: 'ABC FUEL CO 829264 - 10002 units '
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
probability: 1.0
|
||||||
|
stage_id: crm.stage_oppor1
|
||||||
|
categ_id: crm.categ_oppor2
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
|
||||||
|
- |
|
||||||
|
I check that the opportunity is in 'New' state.
|
||||||
|
-
|
||||||
|
!assert {model: crm.lead, id: crm_opportunity_abcfuelcounits0}:
|
||||||
|
- state == "draft"
|
||||||
|
- |
|
||||||
|
I open opportunity by click on "Open" button,
|
||||||
|
-
|
||||||
|
!python {model: crm.lead}: |
|
||||||
|
self.case_open(cr, uid, [ref("crm_opportunity_abcfuelcounits0")])
|
||||||
|
|
||||||
|
- |
|
||||||
|
I schedule Meeting on this current opportunity by clicking on "schedule
|
||||||
|
Meeting".
|
||||||
|
-
|
||||||
|
!python {model: crm.lead}: |
|
||||||
|
self.action_makeMeeting(cr, uid, [ref("crm_opportunity_abcfuelcounits0")])
|
||||||
|
|
||||||
|
- |
|
||||||
|
I can see that Meeting's calendar view is shown.
|
||||||
|
then I click on the date on which I want schedule meeting.
|
||||||
|
I fill proper data for that meeting and save it
|
||||||
|
-
|
||||||
|
!record {model: crm.meeting, id: crm_meeting_abcfuelcounits0}:
|
||||||
|
alarm_id: base_calendar.alarm3
|
||||||
|
date: '2010-04-16 00:00:00'
|
||||||
|
date_deadline: '2010-04-16 08:00:00'
|
||||||
|
duration: 8.0
|
||||||
|
email_from: info@balmerinc.be
|
||||||
|
name: 'ABC FUEL CO 829264 - 10002 units '
|
||||||
|
opportunity_id: 'crm_opportunity_abcfuelcounits0'
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
rrule_type: weekly
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
state: open
|
||||||
|
- |
|
||||||
|
In order to schedule a phonecall to the partner
|
||||||
|
I click on "schedule call" button and select planned date for the call.
|
||||||
|
-
|
||||||
|
!record {model: crm.opportunity2phonecall, id: crm_opportunity2phonecall_abcfuelcounits0}:
|
||||||
|
date: '2010-04-17 11:15:00'
|
||||||
|
name: 'ABC FUEL CO 829264 - 10002 units '
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
user_id: base.user_demo
|
||||||
|
- |
|
||||||
|
schedule phonecall by apply (action_apply)function of opportunity2phoncall wizard
|
||||||
|
-
|
||||||
|
!python {model: crm.opportunity2phonecall}: |
|
||||||
|
self.action_apply(cr, uid, [ref('crm_opportunity2phonecall_abcfuelcounits0')], {'active_ids': [ref("crm_opportunity_abcfuelcounits0")]})
|
||||||
|
- |
|
||||||
|
I check that phonecall record is created for that opportunity.
|
||||||
|
-
|
||||||
|
!python {model: crm.phonecall}: |
|
||||||
|
ids = self.search(cr, uid, [('name', '=', 'ABC FUEL CO 829264 - 10002 units')])
|
||||||
|
assert len(ids)
|
||||||
|
- |
|
||||||
|
I can see phonecall record after click on "Schedule call" wizard.
|
||||||
|
-
|
||||||
|
!record {model: crm.phonecall, id: crm_phonecall_abcfuelcounits0}:
|
||||||
|
date: '2010-04-17 11:15:00'
|
||||||
|
duration: 3.0
|
||||||
|
name: 'ABC FUEL CO 829264 - 10002 units'
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
- |
|
||||||
|
I start by creating a new phonecall.
|
||||||
|
-
|
||||||
|
!record {model: crm.phonecall, id: crm_phonecall_interviewcall0}:
|
||||||
|
date: '2010-04-21 18:59:00'
|
||||||
|
name: Interview call
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
partner_mobile: (+32)2 211 34 83
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
|
||||||
|
- |
|
||||||
|
As the success of phonecall seems to be a real business opportunity, I will convert
|
||||||
|
it to opportunity by clicking on the "Convert to Opportunity" button.
|
||||||
|
-
|
||||||
|
# !python {model: crm.phonecall2opportunity}: |
|
||||||
|
# self.action_apply(cr, uid, [1], {'active_id': ref('crm_phonecall_interviewcall0')})
|
||||||
|
|
||||||
|
-
|
||||||
|
# This is not working, find a way to do that in YAML
|
||||||
|
|
||||||
|
- |
|
||||||
|
I can see that a business opportunity is now assigned to this phonecall
|
||||||
|
- |
|
||||||
|
I check that the partner associated to this as the same country, phone number
|
||||||
|
and name than the phonecall.
|
||||||
|
- |
|
||||||
|
In order to avoid the duplication of similar partner, OpenERP should be able to
|
||||||
|
detect if the partner exists or if it should be created from the phonecall during the
|
||||||
|
conversion.
|
||||||
|
- |
|
||||||
|
In order to test this, I log as the user "user_crm2"
|
||||||
|
- |
|
||||||
|
Then, I create the same phonecall than the preceeding one but I change the name
|
||||||
|
of the phonecall. But this customer keeps the same email.
|
||||||
|
- |
|
||||||
|
This time OpenERP should detect that this customer already exists in the
|
||||||
|
partner base and I check that in the wizard, proposes me to link to this existing
|
||||||
|
partner instead of creating another one.
|
||||||
|
- |
|
||||||
|
I confirm the conversion wizard.
|
||||||
|
- |
|
||||||
|
And I check that the phonecall and the newly created business opportunity is linked
|
||||||
|
to the partner
|
||||||
|
- |
|
||||||
|
I schedule Meeting on this current phonecall by clicking on "schedule
|
||||||
|
Meeting"
|
||||||
|
-
|
||||||
|
!python {model: crm.phonecall}: |
|
||||||
|
self.action_make_meeting(cr, uid, [ref('crm_phonecall_interviewcall0')])
|
||||||
|
|
||||||
|
- |
|
||||||
|
I can see that Meeting's calendar view is shown.
|
||||||
|
then I click on the date on which I want schedule meeting.
|
||||||
|
I fill proper data for that meeting and save it
|
||||||
|
-
|
||||||
|
!record {model: crm.meeting, id: crm_meeting_interviewcall0}:
|
||||||
|
alarm_id: base_calendar.alarm3
|
||||||
|
date: '2010-04-20 00:00:00'
|
||||||
|
date_deadline: '2010-04-20 08:00:00'
|
||||||
|
duration: 8.0
|
||||||
|
email_from: info@balmerinc.be
|
||||||
|
name: Interview call
|
||||||
|
partner_address_id: base.res_partner_address_1
|
||||||
|
partner_id: base.res_partner_9
|
||||||
|
phonecall_id: 'crm_phonecall_interviewcall0'
|
||||||
|
rrule_type: weekly
|
||||||
|
state: open
|
||||||
|
|
||||||
|
-
|
||||||
|
#This is not working for yaml
|
||||||
|
- |
|
||||||
|
I can jump to this meeting by "Meetings" shortcut from my phonecall's form view
|
||||||
|
-
|
||||||
|
|
||||||
|
- |
|
||||||
|
In order to schedule other phonecall to the partner
|
||||||
|
I click on "schedule other call" button. and plan for other call
|
||||||
|
I can see that it will open other phonecall view with some data same as current
|
||||||
|
phonecall.
|
||||||
|
-
|
||||||
|
!record {model: crm.phonecall2phonecall, id: crm_phonecall2phonecall_interviewcall0}:
|
||||||
|
date: '2010-04-21 19:49:00'
|
||||||
|
name: Interview call
|
||||||
|
section_id: crm.section_sales_department
|
||||||
|
user_id: base.user_root
|
||||||
|
- |
|
||||||
|
I click on "Schedule" button of this wizard.
|
||||||
|
-
|
||||||
|
!python {model: crm.phonecall2phonecall}: |
|
||||||
|
self.action_apply(cr, uid, [ref('crm_phonecall2phonecall_interviewcall0')], {'active_id': ref('crm_phonecall_interviewcall0')})
|
|
@ -21,7 +21,6 @@
|
||||||
|
|
||||||
import crm_send_email
|
import crm_send_email
|
||||||
import crm_forward_to_partner
|
import crm_forward_to_partner
|
||||||
import crm_email_add_cc
|
|
||||||
|
|
||||||
import crm_lead_to_partner
|
import crm_lead_to_partner
|
||||||
import crm_lead_to_opportunity
|
import crm_lead_to_opportunity
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
from osv import osv, fields
|
|
||||||
from tools.translate import _
|
|
||||||
import tools
|
|
||||||
|
|
||||||
class crm_email_add_cc_wizard(osv.osv_memory):
|
|
||||||
""" Email Add CC"""
|
|
||||||
|
|
||||||
_name = "crm.email.add.cc"
|
|
||||||
_description = "Email Add CC"
|
|
||||||
|
|
||||||
_columns = {
|
|
||||||
'name': fields.selection([('user', 'User'), ('partner', 'Partner'), \
|
|
||||||
('email', 'Email Address')], 'Send to', required=True),
|
|
||||||
'user_id': fields.many2one('res.users', "User"),
|
|
||||||
'partner_id': fields.many2one('res.partner', "Partner"),
|
|
||||||
'email': fields.char('Email', size=32),
|
|
||||||
'subject': fields.char('Subject', size=32),
|
|
||||||
}
|
|
||||||
|
|
||||||
def on_change_email(self, cr, uid, ids, user, partner):
|
|
||||||
|
|
||||||
""" @param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of Mail’s IDs
|
|
||||||
"""
|
|
||||||
|
|
||||||
if (not partner and not user):
|
|
||||||
return {'value': {'email': False}}
|
|
||||||
email = False
|
|
||||||
if partner:
|
|
||||||
addr = self.pool.get('res.partner').address_get(cr, uid, [partner], ['contact'])
|
|
||||||
if addr:
|
|
||||||
email = self.pool.get('res.partner.address').read(cr, uid, addr['contact'] , ['email'])['email']
|
|
||||||
elif user:
|
|
||||||
addr = self.pool.get('res.users').read(cr, uid, user, ['address_id'])['address_id']
|
|
||||||
if addr:
|
|
||||||
email = self.pool.get('res.partner.address').read(cr, uid, addr[0] , ['email'])['email']
|
|
||||||
return {'value': {'email': email}}
|
|
||||||
|
|
||||||
|
|
||||||
def add_cc(self, cr, uid, ids, context={}):
|
|
||||||
"""
|
|
||||||
Adds CC value in case
|
|
||||||
@param self: The object pointer
|
|
||||||
@param cr: the current row, from the database cursor,
|
|
||||||
@param uid: the current user’s ID for security checks,
|
|
||||||
@param ids: List of create menu’s IDs
|
|
||||||
@param context: A standard dictionary for contextual values
|
|
||||||
"""
|
|
||||||
|
|
||||||
data = self.read(cr, uid, ids[0])
|
|
||||||
email = data['email']
|
|
||||||
subject = data['subject']
|
|
||||||
|
|
||||||
if not context:
|
|
||||||
return {}
|
|
||||||
history_line = self.pool.get('crm.case.history').browse(cr, uid, context['active_id'])
|
|
||||||
model = history_line.log_id.model_id.model
|
|
||||||
model_pool = self.pool.get(model)
|
|
||||||
case = model_pool.browse(cr, uid, history_line.log_id.res_id)
|
|
||||||
body = history_line.description.replace('\n', '\n> ')
|
|
||||||
if not case.email_from:
|
|
||||||
raise osv.except_osv(_('Warning!'), ('No email Address defined for this case..'))
|
|
||||||
if not email:
|
|
||||||
raise osv.except_osv(_('Warning!'), ('Please Specify email address..'))
|
|
||||||
flag = tools.email_send(
|
|
||||||
case.user_id.address_id.email,
|
|
||||||
[case.email_from],
|
|
||||||
subject or '[' + str(case.id) + '] ' + case.name,
|
|
||||||
model_pool.format_body(body),
|
|
||||||
email_cc = [email],
|
|
||||||
openobject_id = str(case.id),
|
|
||||||
subtype = "html"
|
|
||||||
)
|
|
||||||
|
|
||||||
if flag:
|
|
||||||
model_pool.write(cr, uid, case.id, {'email_cc' : case.email_cc and case.email_cc + ',' + email or email})
|
|
||||||
else:
|
|
||||||
raise osv.except_osv(_('Email Fail!'), ("Lastest Email is not sent successfully"))
|
|
||||||
return {}
|
|
||||||
|
|
||||||
crm_email_add_cc_wizard()
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue