[MERGE] Merge with lp:~openerp-dev/openobject-addons/trunk-calendar

bzr revid: sbh@tinyerp.com-20110601111119-7nggxba11p0bp5wb
This commit is contained in:
Bhumika (OpenERP)
2011-06-01 16:41:19 +05:30
39 changed files with 1887 additions and 862 deletions
+1
View File
@@ -59,6 +59,7 @@ module named account_voucher.
'account_menuitem.xml',
'report/account_invoice_report_view.xml',
'report/account_entries_report_view.xml',
'report/account_treasury_report_view.xml',
'report/account_report_view.xml',
'report/account_analytic_entries_report_view.xml',
'wizard/account_move_bank_reconcile_view.xml',
+2
View File
@@ -17,6 +17,7 @@
<field name="user_id" invisible="1"/>
<field name="parent_id" invisible="1"/>
<field name="partner_id" invisible="1"/>
<field name="state" invisible="1"/>
</tree>
</field>
</record>
@@ -44,6 +45,7 @@
<filter string="Associated Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
<separator orientation="vertical"/>
<filter string="Parent" icon="terp-folder-orange" domain="[]" context="{'group_by':'parent_id'}"/>
<filter string="State" icon="terp-folder-green" domain="[]" context="{'group_by':'state'}"/>
</group>
</search>
</field>
+1
View File
@@ -41,6 +41,7 @@ import account_entries_report
import account_analytic_entries_report
import account_balance_sheet
import account_profit_loss
import account_treasury_report
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -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/>.
#
##############################################################################
import tools
from osv import fields,osv
import decimal_precision as dp
class account_treasury_report(osv.osv):
_name = "account.treasury.report"
_description = "Treasury Analysis"
_auto = False
def _compute_balances(self, cr, uid, ids, field_names, arg=None, context=None,
query='', query_params=()):
all_treasury_lines = self.search(cr, uid, [], context=context)
current_sum = 0
res = dict((id, dict((fn, 0.0) for fn in field_names)) for id in all_treasury_lines)
for record in self.browse(cr, uid, all_treasury_lines, context=context):
res[record.id]['starting_balance'] = current_sum
current_sum += record.balance
res[record.id]['ending_balance'] = current_sum
return res
_columns = {
'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscalyear', readonly=True),
'period_id': fields.many2one('account.period', 'Period', readonly=True),
'debit': fields.float('Debit', readonly=True),
'credit': fields.float('Credit', readonly=True),
'balance': fields.float('Balance', readonly=True),
'date': fields.date('Beginning of Period Date', readonly=True),
'starting_balance': fields.function(_compute_balances, digits_compute=dp.get_precision('Account'), method=True, string='Starting Balance', multi='balance'),
'ending_balance': fields.function(_compute_balances, digits_compute=dp.get_precision('Account'), method=True, string='Ending Balance', multi='balance'),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
}
_order = 'date asc'
def init(self, cr):
tools.drop_view_if_exists(cr, 'account_treasury_report')
cr.execute("""
create or replace view account_treasury_report as (
select
p.id as id,
p.fiscalyear_id as fiscalyear_id,
p.id as period_id,
sum(l.debit) as debit,
sum(l.credit) as credit,
sum(l.debit-l.credit) as balance,
p.date_start as date,
am.company_id as company_id
from
account_move_line l
left join account_account a on (l.account_id = a.id)
left join account_move am on (am.id=l.move_id)
left join account_period p on (am.period_id=p.id)
where l.state != 'draft'
and a.type = 'liquidity'
group by p.id, p.fiscalyear_id, p.date_start, am.company_id
)
""")
account_treasury_report()
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_account_treasury_report_tree" model="ir.ui.view">
<field name="name">account.treasury.report.tree</field>
<field name="model">account.treasury.report</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Treasury Analysis">
<field name="date" invisible="1"/>
<field name="fiscalyear_id"/>
<field name="period_id"/>
<field name="debit"/>
<field name="credit"/>
<field name="balance"/>
<field name="starting_balance"/>
<field name="ending_balance"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="view_account_treasury_report_graph" model="ir.ui.view">
<field name="name">account.treasury.report.graph</field>
<field name="model">account.treasury.report</field>
<field name="type">graph</field>
<field name="arch" type="xml">
<graph string="Treasury Analysis" type="bar">
<field name="period_id"/>
<field name="balance" operator="+"/>
</graph>
</field>
</record>
<record id="view_account_treasury_report_search" model="ir.ui.view">
<field name="name">account.treasury.report.search</field>
<field name="model">account.treasury.report</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Treasury Analysis">
<group colspan="10" col="12">
<field name="fiscalyear_id"/>
<field name="period_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<newline/>
</search>
</field>
</record>
<record id="action_account_treasury_report_all" model="ir.actions.act_window">
<field name="name">Treasury Analysis</field>
<field name="res_model">account.treasury.report</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="search_view_id" ref="view_account_treasury_report_search"/>
<field name="view_id" ref="view_account_treasury_report_tree"/>
<field name="context">{'group_by':[], 'group_by_no_leaf':0}</field>
<field name="help">From this view, have an analysis of your treasury. It sums the balance of every accounting entries made on liquidity accounts per period.</field>
</record>
<menuitem action="action_account_treasury_report_all" id="menu_action_account_treasury_report_all"
parent="account.menu_finance_statistic_report_statement"
groups="group_account_manager"
sequence="2"/>
</data>
</openerp>
@@ -125,3 +125,5 @@
"access_account_invoice_tax_sale_manager","account.invoice.tax sale manager","model_account_invoice_tax","base.group_sale_salesman",1,0,0,0
"access_account_sequence_fiscal_year_sale_user","account.sequence.fiscalyear.sale.user","model_account_sequence_fiscalyear","base.group_sale_salesman",1,1,1,0
"access_account_sequence_fiscal_year_sale_manager","account.sequence.fiscalyear.sale.manager","model_account_sequence_fiscalyear","base.group_sale_manager",1,1,1,1
"access_account_treasury_report_manager","account.treasury.report.manager","model_account_treasury_report","account.group_account_manager",1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
125 access_account_invoice_tax_sale_manager account.invoice.tax sale manager model_account_invoice_tax base.group_sale_salesman 1 0 0 0
126 access_account_sequence_fiscal_year_sale_user account.sequence.fiscalyear.sale.user model_account_sequence_fiscalyear base.group_sale_salesman 1 1 1 0
127 access_account_sequence_fiscal_year_sale_manager account.sequence.fiscalyear.sale.manager model_account_sequence_fiscalyear base.group_sale_manager 1 1 1 1
128 access_account_treasury_report_manager account.treasury.report.manager model_account_treasury_report account.group_account_manager 1 0 0 0
129
@@ -47,6 +47,26 @@ class one2many_mod2(fields.one2many):
res[r[self._fields_id]].append( r['id'] )
return res
class account_analytic_line(osv.osv):
_inherit = 'account.analytic.line'
_description = 'Analytic Line'
def _get_amount(self, cr, uid, ids, name, args, context=None):
res = {}
for id in ids:
res.setdefault(id, 0.0)
for line in self.browse(cr, uid, ids, context=context):
amount = line.move_id and line.move_id.amount_currency * (line.percentage / 100) or 0.0
res[line.id] = amount
return res
_columns = {
'amount_currency': fields.function(_get_amount, string="Amount Currency", type="float", method=True, store=True, help="The amount expressed in the related account currency if not equal to the company one.", readonly=True),
'percentage': fields.float('Percentage')
}
account_analytic_line()
class account_analytic_plan(osv.osv):
_name = "account.analytic.plan"
_description = "Analytic Plan"
@@ -338,6 +358,7 @@ class account_move_line(osv.osv):
'move_id': line.id,
'journal_id': line.journal_id.analytic_journal_id.id,
'ref': line.ref,
'percentage': line2.rate
}
analytic_line_obj.create(cr, uid, al_vals, context=context)
return True
+535
View File
@@ -0,0 +1,535 @@
# Turkish translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
"PO-Revision-Date: 2011-05-31 13:20+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Turkish <tr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-06-01 04:38+0000\n"
"X-Generator: Launchpad (build 12959)\n"
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_user:0
msgid ""
"Check this if you want the rule to send an email to the responsible person."
msgstr ""
"Sorumlu kişiye eposta gönderme kuralını kullanmak istiyorsanız bunu "
"işaretleyin."
#. module: base_action_rule
#: field:base.action.rule,act_remind_partner:0
msgid "Remind Partner"
msgstr "Paydaşa Anımsat"
#. module: base_action_rule
#: field:base.action.rule,trg_partner_categ_id:0
msgid "Partner Category"
msgstr "Paydaş Kategorisi"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_watchers:0
msgid "Mail to Watchers (CC)"
msgstr "İzleyicilere Postala (CC)"
#. module: base_action_rule
#: field:base.action.rule,trg_state_to:0
msgid "Button Pressed"
msgstr "Düğmeye Basıldı"
#. module: base_action_rule
#: field:base.action.rule,model_id:0
msgid "Object"
msgstr "Nesne"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_email:0
msgid "Mail to these Emails"
msgstr "Bu Epostalara yolla"
#. module: base_action_rule
#: field:base.action.rule,act_state:0
msgid "Set State to"
msgstr "Durumu şuna Ayarla"
#. module: base_action_rule
#: field:base.action.rule,act_email_from:0
msgid "Email From"
msgstr "Gelen Eposta"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Body"
msgstr "Eposta Gövdesi"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Days"
msgstr "Günler"
#. module: base_action_rule
#: field:base.action.rule,last_run:0
msgid "Last Run"
msgstr "Son Çalıştırma"
#. module: base_action_rule
#: code:addons/base_action_rule/base_action_rule.py:313
#, python-format
msgid "Error!"
msgstr "Hata!"
#. module: base_action_rule
#: field:base.action.rule,act_reply_to:0
msgid "Reply-To"
msgstr "Yanıtla"
#. module: base_action_rule
#: help:base.action.rule,act_email_cc:0
msgid ""
"These people will receive a copy of the future communication between partner "
"and users by email"
msgstr ""
"Bu kişiler paydaşla kullanıcılar arasındaki yapılacak iletişimin bir "
"kopyasını eposta yoluyla alacaklardır."
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Minutes"
msgstr "Dakikalar"
#. module: base_action_rule
#: field:base.action.rule,name:0
msgid "Rule Name"
msgstr "Kural Adı"
#. module: base_action_rule
#: help:base.action.rule,act_remind_partner:0
msgid ""
"Check this if you want the rule to send a reminder by email to the partner."
msgstr ""
"Kuralın ortağa hatırlatma E-Postası göndermesini istiyorsanız bunu seçiniz."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Model Partner"
msgstr "Model Paydaş için Koşullar"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Deadline"
msgstr "Bitiş tarihi"
#. module: base_action_rule
#: field:base.action.rule,trg_partner_id:0
msgid "Partner"
msgstr "Paydaş"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_subject)s = Object subject"
msgstr "%(nesne_konu)lar = Nesne konu"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Reminders"
msgstr "Eposta Anımsatıcıları"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Special Keywords to Be Used in The Body"
msgstr "Gövdede Kullanılacak Özel Anahtar Kelimeler"
#. module: base_action_rule
#: field:base.action.rule,trg_state_from:0
msgid "State"
msgstr "Durum"
#. module: base_action_rule
#: model:ir.actions.act_window,help:base_action_rule.base_action_rule_act
msgid ""
"Use automated actions to automatically trigger actions for various screens. "
"Example: a lead created by a specific user may be automatically set to a "
"specific sales team, or an opportunity which still has status pending after "
"14 days might trigger an automatic reminder email."
msgstr ""
"Değişik ekranlar için kullanılacak otomatik tetikleme eylemleri için "
"otomatize eylemleri kullan. Örnek: belirli bir kullanıcı tarafından "
"oluşturulan paydaş otomatik olarak bir satış takımına atanabilir ya da halen "
"14 günlük bekleme durumunda olan bir fırsat otomatik anımsatma epostası ile "
"tetiklenebilir."
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_email:0
msgid "Email-id of the persons whom mail is to be sent"
msgstr "Eposta gönderilecek kişinin Eposta kimliği"
#. module: base_action_rule
#: view:base.action.rule:0
#: model:ir.module.module,shortdesc:base_action_rule.module_meta_information
msgid "Action Rule"
msgstr "Eylem Kuralı"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Fields to Change"
msgstr "Değiştirilecek Alanlar"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Creation Date"
msgstr "Oluşturulma Tarihi"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Last Action Date"
msgstr "Son Eylem Tarihi"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Hours"
msgstr "Saatler"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_id)s = Object ID"
msgstr "%(nesne_no)s = Nesne NO"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Delay After Trigger Date"
msgstr "Tetikleme Tarihi sonrası Gecikme"
#. module: base_action_rule
#: field:base.action.rule,act_remind_attach:0
msgid "Remind with Attachment"
msgstr "Ek ile Anımsat"
#. module: base_action_rule
#: constraint:ir.cron:0
msgid "Invalid arguments"
msgstr "Geçersiz parametreler"
#. module: base_action_rule
#: field:base.action.rule,act_user_id:0
msgid "Set Responsible to"
msgstr "Sorumluyu şuna Ayarla"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "None"
msgstr "Hiçbiri"
#. module: base_action_rule
#: help:base.action.rule,act_email_to:0
msgid ""
"Use a python expression to specify the right field on which one than we will "
"use for the 'To' field of the header"
msgstr ""
"Başlıkta 'Kime' için kullanılacak doğru alanı belirlemek için bir python "
"ifadesi kullanın"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user_phone)s = Responsible phone"
msgstr "%(nesne_kullanıcı_telefon)lar = Sorumlu telefonu"
#. module: base_action_rule
#: view:base.action.rule:0
msgid ""
"The rule uses the AND operator. The model must match all non-empty fields so "
"that the rule executes the action described in the 'Actions' tab."
msgstr ""
"Bu kural VE operatörünü kullanır. Model tüm boş olmayan alanlara uymalıdır "
"ki; kural 'Eylemler' sekmesinde belirtilen eylemi uygulasın."
#. module: base_action_rule
#: field:base.action.rule,trg_date_range_type:0
msgid "Delay type"
msgstr "Gecikme türü"
#. module: base_action_rule
#: help:base.action.rule,regex_name:0
msgid ""
"Regular expression for matching name of the resource\n"
"e.g.: 'urgent.*' will search for records having name starting with the "
"string 'urgent'\n"
"Note: This is case sensitive search."
msgstr ""
"Kaynak adına uygun kural ifadesi\n"
"ör.: 'acil.*' 'acil' dizesi ile başlayan bütün kayıtları arıyacaktır\n"
"Not: Harf durumu duyarlı bir aramadır."
#. module: base_action_rule
#: field:base.action.rule,act_method:0
msgid "Call Object Method"
msgstr "Nesne Arama Yöntemi"
#. module: base_action_rule
#: field:base.action.rule,act_email_to:0
msgid "Email To"
msgstr "Şuna Eposta Gönder"
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_watchers:0
msgid ""
"Check this if you want the rule to mark CC(mail to any other person defined "
"in actions)."
msgstr ""
"Kuralın CC yi işaretlemesini isterseniz bunu işaretleyin (eylemlerde "
"tanımlanan herhangi bir kişiye postalama)"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(partner)s = Partner name"
msgstr "%(paydaş)lar = Paydaş Adı"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Note"
msgstr "Not"
#. module: base_action_rule
#: help:base.action.rule,act_email_from:0
msgid ""
"Use a python expression to specify the right field on which one than we will "
"use for the 'From' field of the header"
msgstr ""
"Başlıkta 'Kime' için kullanılacak doğru alanı belirlemek için bir python "
"ifadesi kullanın"
#. module: base_action_rule
#: field:base.action.rule,trg_date_range:0
msgid "Delay after trigger date"
msgstr "Tetikleme Tarihi sonrası Gecikme"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions"
msgstr "Koşullar"
#. module: base_action_rule
#: help:base.action.rule,trg_date_range:0
msgid ""
"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."
msgstr ""
"Tetikleme Sonrası Gecikme, burada negatif sayı kullanabilirsiniz. Eğer "
"tetikleme tarihinden önce, örneğin toplantıdan 15 dakika önce anımsatma "
"gönder gibi, bir gecikmeye gerek duyarsanız."
#. module: base_action_rule
#: field:base.action.rule,active:0
msgid "Active"
msgstr "Etkin"
#. module: base_action_rule
#: code:addons/base_action_rule/base_action_rule.py:314
#, python-format
msgid "No E-Mail ID Found for your Company address!"
msgstr "Firma adresinizde bir Eposta Kimliği yok!"
#. module: base_action_rule
#: field:base.action.rule,act_remind_user:0
msgid "Remind Responsible"
msgstr "Sorumluya Hatırlat"
#. module: base_action_rule
#: model:ir.module.module,description:base_action_rule.module_meta_information
msgid "This module allows to implement action rules for any object."
msgstr ""
"Bu modül herhangi bir nesne için eylem kurallaru uygulamanızı sağlar."
#. module: base_action_rule
#: help:base.action.rule,sequence:0
msgid "Gives the sequence order when displaying a list of rules."
msgstr "Kural listesini görüntülerken diziliş sırasını verir."
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Months"
msgstr "Aylar"
#. module: base_action_rule
#: field:base.action.rule,filter_id:0
msgid "Filter"
msgstr "Süzgeç"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Date"
msgstr "Tarih"
#. module: base_action_rule
#: help:base.action.rule,server_action_id:0
msgid ""
"Describes the action name.\n"
"eg:on which object which action to be taken on basis of which condition"
msgstr ""
"Eylem adını belirtir.\n"
"ör:koşula uygun olarak hangi nesne için hangi eylem ele alınır"
#. module: base_action_rule
#: model:ir.model,name:base_action_rule.model_ir_cron
msgid "ir.cron"
msgstr "ir.cron"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_description)s = Object description"
msgstr "%(nesne_tanım)lar = Nesne açıklama"
#. module: base_action_rule
#: constraint:base.action.rule:0
msgid "Error: The mail is not well formated"
msgstr "Hata: Mail düzgün bir formatta değil"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Actions"
msgstr "Epsota Eylemleri"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Information"
msgstr "Eposta Bilgisi"
#. module: base_action_rule
#: model:ir.model,name:base_action_rule.model_base_action_rule
msgid "Action Rules"
msgstr "Eylem Kuralları"
#. module: base_action_rule
#: help:base.action.rule,act_mail_body:0
msgid "Content of mail"
msgstr "Posta içeriği"
#. module: base_action_rule
#: field:base.action.rule,trg_user_id:0
msgid "Responsible"
msgstr "Sorumlu"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(partner_email)s = Partner Email"
msgstr "%(paydas_eposta)lar = Paydaş Eposta"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_date)s = Creation date"
msgstr "%(nesne_tarih)ler = Oluşturma tarihi"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user_email)s = Responsible Email"
msgstr "%(nesne_kullanıcı_eposta)lar = Sorumlu Eposta"
#. module: base_action_rule
#: field:base.action.rule,act_mail_body:0
msgid "Mail body"
msgstr "Posta gövdesi"
#. module: base_action_rule
#: help:base.action.rule,act_remind_user:0
msgid ""
"Check this if you want the rule to send a reminder by email to the user."
msgstr ""
"Kullanıcıya eposta ile anımsatma gönderme kuralını kullanmak için bunu "
"işaretleyin."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Server Action to be Triggered"
msgstr "Tetiklenecek Sunucu Eylemi"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_user:0
msgid "Mail to Responsible"
msgstr "Sorumluya Posta"
#. module: base_action_rule
#: field:base.action.rule,act_email_cc:0
msgid "Add Watchers (Cc)"
msgstr "İzleyici Ekle (Cc)"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Model Fields"
msgstr "Model Alanları Koşulları"
#. module: base_action_rule
#: model:ir.actions.act_window,name:base_action_rule.base_action_rule_act
#: model:ir.ui.menu,name:base_action_rule.menu_base_action_rule_form
msgid "Automated Actions"
msgstr "Otomatik Eylemler"
#. module: base_action_rule
#: field:base.action.rule,server_action_id:0
msgid "Server Action"
msgstr "Sunucu Eylemi"
#. module: base_action_rule
#: field:base.action.rule,regex_name:0
msgid "Regex on Resource Name"
msgstr "Kaynak Adı için Kurallı İfade"
#. module: base_action_rule
#: help:base.action.rule,act_remind_attach:0
msgid ""
"Check this if you want that all documents attached to the object be attached "
"to the reminder email sent."
msgstr ""
"Nesneye iliştirilmiş bütün belgelerin gönderilen anımsatma epostasına "
"eklenmesini isterseniz bunu işaretleyin."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Timing"
msgstr "Zamanlama Koşulları"
#. module: base_action_rule
#: field:base.action.rule,sequence:0
msgid "Sequence"
msgstr "Diziliş"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Actions"
msgstr "Eylemler"
#. module: base_action_rule
#: help:base.action.rule,active:0
msgid ""
"If the active field is set to False, it will allow you to hide the rule "
"without removing it."
msgstr ""
"Eğer etkin alanı Yanlış olarak işaretli ise, kuralı silmeden gizlemenizi "
"sağlar."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user)s = Responsible name"
msgstr "%(nesne_kullanıcı)lar = Sorumlu adı"
#. module: base_action_rule
#: field:base.action.rule,create_date:0
msgid "Create Date"
msgstr "Tarih Oluştur"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on States"
msgstr "Durum Koşulları"
#. module: base_action_rule
#: field:base.action.rule,trg_date_type:0
msgid "Trigger Date"
msgstr "Tetikleme Tarihi"
+52 -8
View File
@@ -1,11 +1,55 @@
# -*- 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/>.
#
##############################################################################
{
"name": "Base calendar",
"version": "2.0",
"depends": ['base'],
"js": [
'static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.js',
'static/src/js/calendar.js'
"name" : "Basic Calendar Functionality",
"version" : "1.0",
"depends" : ["base"],
'description': """
This is a full-featured calendar system.
========================================
It supports:
- Calendar of events
- Alerts (create requests)
- Recurring events
- Invitations to people""",
"author" : "OpenERP SA",
'category': 'Tools',
'website': 'http://www.openerp.com',
"init_xml" : [
'base_calendar_data.xml'
],
"css": ['static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.css'],
'active': True
"demo_xml" : [],
"update_xml" : [
'security/calendar_security.xml',
'security/ir.model.access.csv',
'wizard/base_calendar_invite_attendee_view.xml',
'base_calendar_view.xml'
],
"test" : ['test/base_calendar_test.yml'],
"installable" : True,
"active" : False,
"certificate" : "00694071962960352821",
'images': ['images/base_calendar1.jpeg','images/base_calendar2.jpeg','images/base_calendar3.jpeg','images/base_calendar4.jpeg',],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+182 -338
View File
@@ -22,6 +22,7 @@
from datetime import datetime, timedelta, date
from dateutil import parser
from dateutil import rrule
from dateutil.relativedelta import relativedelta
from osv import fields, osv
from service import web_services
from tools.translate import _
@@ -47,20 +48,22 @@ def get_recurrent_dates(rrulestring, exdate, startdate=None, exrule=None):
def todate(date):
val = parser.parse(''.join((re.compile('\d')).findall(date)))
return val
if not startdate:
startdate = datetime.now()
if not exdate:
exdate = []
rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True)
for date in exdate:
datetime_obj = todate(date)
rset1._exdate.append(datetime_obj)
if exrule:
rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate))
return list(rset1._iter())
return list(rset1)
def base_calendar_id2real_id(base_calendar_id=None, with_date=False):
"""
@@ -373,7 +376,8 @@ property or property parameter."),
multi='event_end_date'),
'ref': fields.reference('Event Ref', selection=_links_get, size=128),
'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"),
}
}
_defaults = {
'state': 'needs-action',
'role': 'req-participant',
@@ -411,7 +415,7 @@ property or property parameter."),
cal = vobject.iCalendar()
event = cal.add('vevent')
if not event_obj.date_deadline or not event_obj.date:
raise osv.except_osv(_('Warning !'),_("Couldn't Invite because date is not specified!"))
raise osv.except_osv(_('Warning !'),_("Couldn't Invite because date is not specified!"))
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)
@@ -456,7 +460,7 @@ property or property parameter."),
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)]
@@ -674,7 +678,7 @@ true, it will allow you to hide the event alarm information without removing it.
new_res_alarm = alarm_ids[0]
cr.execute('UPDATE %s ' % model_obj._table + \
' SET base_calendar_alarm_id=%s, alarm_id=%s ' \
' WHERE id=%s',
' WHERE id=%s',
(cal_alarm.id, new_res_alarm, data.id))
self.do_alarm_unlink(cr, uid, [data.id], model)
@@ -914,22 +918,6 @@ class calendar_event(osv.osv):
def _tz_get(self, cr, uid, context=None):
return [(x.lower(), x) for x in pytz.all_timezones]
def onchange_allday(self, cr, uid, ids, allday, context=None):
"""Sets duration as 24 Hours if event is selected for all day
@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 calendar event’s IDs.
@param allday: Value of allday boolean
@param context: A standard dictionary for contextual values
"""
if not allday or not ids:
return {}
value = {
'duration': 24
}
return {'value': value}
def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None):
"""Returns duration and/or end date based on values passed
@param self: The object pointer
@@ -952,8 +940,13 @@ class calendar_event(osv.osv):
value['duration'] = duration
if allday: # For all day event
value = {'duration': 24}
value = {'duration': 24.0}
duration = 24.0
if start_date:
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
start_date = datetime.strftime(datetime(start.year, start.month, start.day, 0,0,0), "%Y-%m-%d %H:%M:%S")
value['date'] = start_date
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
if end_date and not duration:
@@ -966,7 +959,7 @@ class calendar_event(osv.osv):
value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S")
elif end_date and duration and not allday:
# we have both, keep them synchronized:
# set duration based on end_date (arbitrary decision: this avoid
# set duration based on end_date (arbitrary decision: this avoid
# getting dates like 06:31:48 instead of 06:32:00)
end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S")
diff = end - start
@@ -982,88 +975,10 @@ class calendar_event(osv.osv):
"""
if context is None:
context = {}
cr.execute('select id from '+self._table+' where recurrent_uid in %s', (tuple(ids),))
r_ids = map(lambda x: x[0], cr.fetchall())
self.unlink(cr, uid, r_ids, context=context)
return True
def _set_rrulestring(self, cr, uid, id, name, value, arg, context=None):
"""
Sets values of fields that defines event recurrence from the value of rrule string
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param id: List of calendar event's ids.
@param context: A standard dictionary for contextual values
@return: dictionary of rrule value.
"""
if context is None:
context = {}
cr.execute("UPDATE %s set freq='None',interval=0,count=0,end_date=Null,\
mo=False,tu=False,we=False,th=False,fr=False,sa=False,su=False,\
day=0,select1='date',month_list=Null ,byday=Null where id=%%s" % (self._table), (id,))
if not value:
cr.execute("UPDATE %s set rrule_type='none' where id=%%s" % self._table,(id,))
return True
val = {}
for part in value.split(';'):
if part.lower().__contains__('freq') and len(value.split(';')) <=2:
rrule_type = part.lower()[5:]
break
else:
rrule_type = 'custom'
break
ans = value.split(';')
for i in ans:
val[i.split('=')[0].lower()] = i.split('=')[1].lower()
if not val.get('interval'):
rrule_type = 'custom'
elif int(val.get('interval')) > 1: #If interval is other than 1 rule is custom
rrule_type = 'custom'
qry = "UPDATE \"%s\" set rrule_type=%%s " % self._table
qry_args = [ rrule_type, ]
new_val = val.copy()
for k, v in val.items():
if val['freq'] == 'weekly' and val.get('byday'):
for day in val['byday'].split(','):
new_val[day] = True
val.pop('byday')
if val.get('until'):
until = parser.parse(''.join((re.compile('\d')).findall(val.get('until'))))
new_val['end_date'] = until.strftime('%Y-%m-%d')
val.pop('until')
new_val.pop('until')
if val.get('bymonthday'):
new_val['day'] = val.get('bymonthday')
val.pop('bymonthday')
new_val['select1'] = 'date'
new_val.pop('bymonthday')
if val.get('byday'):
d = val.get('byday')
if '-' in d:
new_val['byday'] = d[:2]
new_val['week_list'] = d[2:4].upper()
else:
new_val['byday'] = d[:1]
new_val['week_list'] = d[1:3].upper()
new_val['select1'] = 'day'
if val.get('bymonth'):
new_val['month_list'] = val.get('bymonth')
val.pop('bymonth')
new_val.pop('bymonth')
for k, v in new_val.items():
qry += ", %s=%%s" % k
qry_args.append(v)
qry = qry + " where id=%s"
qry_args.append(id)
cr.execute(qry, qry_args)
for event_id in ids:
cr.execute("select id from %s where recurrent_uid=%%s" % (self._table), (event_id,))
r_ids = map(lambda x: x[0], cr.fetchall())
self.unlink(cr, uid, r_ids, context=context)
return True
def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
@@ -1075,28 +990,15 @@ class calendar_event(osv.osv):
@param context: A standard dictionary for contextual values
@return: dictionary of rrule value.
"""
result = {}
for datas in self.read(cr, uid, ids, context=context):
for datas in self.read(cr, uid, ids, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'select1', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'exrule', 'day', 'week_list' ], context=context):
event = datas['id']
if datas.get('rrule_type'):
if datas.get('rrule_type') == 'none':
result[event] = False
cr.execute("UPDATE %s set exrule=Null where id=%%s" % self._table,( event,))
if datas.get('rrule_type') :
if datas.get('interval', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Interval can not be Negative'))
if datas.get('count', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Count can not be Negative'))
rrule_custom = self.compute_rule_string(cr, uid, datas, \
context=context)
result[event] = rrule_custom
else:
result[event] = self.compute_rule_string(cr, uid, {'freq': datas.get('rrule_type').upper(), 'interval': 1}, context=context)
for id, myrule in result.items():
#Remove the events generated from recurrent event
if not myrule:
self.unlink_events(cr, uid, [id], context=context)
if datas.get('interval', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Interval can not be Negative'))
if datas.get('count', 0) < 0:
raise osv.except_osv(_('Warning!'), _('Count can not be Negative'))
result[event] = self.compute_rule_string(datas)
return result
_columns = {
@@ -1122,14 +1024,10 @@ defines the list of date/time exceptions for a recurring calendar component."),
'exrule': fields.char('Exception Rule', size=352, help="Defines a \
rule or repeating pattern of time to exclude from the recurring rule."),
'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, \
string='Recurrent Rule', store=True, \
fnct_inv=_set_rrulestring, help='Defines a\
rule or repeating pattern for recurring events\n\
e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=-1SU'),
string='Recurrent Rule'),
'rrule_type': fields.selection([('none', ''), ('daily', 'Daily'), \
('weekly', 'Weekly'), ('monthly', 'Monthly'), \
('yearly', 'Yearly'),],
('yearly', 'Yearly'),],
'Recurrency', states={'done': [('readonly', True)]},
help="Let the event automatically repeat at that interval"),
'alarm_id': fields.many2one('res.alarm', 'Alarm', states={'done': [('readonly', True)]},
@@ -1141,14 +1039,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'organizer': fields.char("Organizer", size=256, states={'done': [('readonly', True)]}), # Map with Organizer Attribure of VEvent.
'organizer_id': fields.many2one('res.users', 'Organizer', states={'done': [('readonly', True)]}),
'freq': fields.selection([('None', 'No Repeat'),
('hourly', 'Hours'),
('daily', 'Days'),
('weekly', 'Weeks'),
('monthly', 'Months'),
('yearly', 'Years'), ], 'Frequency'),
'end_type' : fields.selection([('forever', 'Forever'), ('count', 'Fix amout of times'), ('end_date','End date')], 'Way to end reccurency'),
'end_type' : fields.selection([('count', 'Fix amout of times'), ('end_date','End date')], 'Way to end reccurency'),
'interval': fields.integer('Repeat every', help="Repeat every (Days/Week/Month/Year)"),
'count': fields.integer('Repeat', help="Repeat x times"),
'mo': fields.boolean('Mon'),
@@ -1158,7 +1049,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'fr': fields.boolean('Fri'),
'sa': fields.boolean('Sat'),
'su': fields.boolean('Sun'),
'select1': fields.selection([('date', 'Date of month'),
'select1': fields.selection([('date', 'Date of month'),
('day', 'Day of month')], 'Option'),
'day': fields.integer('Date of month'),
'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
@@ -1175,8 +1066,8 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'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."),
'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
'edit_all': fields.boolean('Edit All', help="Edit all Occurrences of recurrent Meeting."),
'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"),
'edit_all': fields.boolean('Edit All', help="Edit all Occurrences of recurrent Meeting."),
}
def default_organizer(self, cr, uid, context=None):
user_pool = self.pool.get('res.users')
@@ -1187,11 +1078,12 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
return res
_defaults = {
'end_type' : 'forever',
'end_type' : 'count',
'count' : 1,
'rrule_type' : 'none',
'state': 'tentative',
'class': 'public',
'show_as': 'busy',
'freq': 'None',
'select1': 'date',
'interval': 1,
'active': 1,
@@ -1200,43 +1092,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'edit_all' : False,
}
def onchange_edit_all(self, cr, uid, ids, rrule_type,edit_all, context=None):
if not context:
context = {}
value = {}
if edit_all and rrule_type:
for id in ids:
base_calendar_id2real_id(id)
return value
def modify_all(self, cr, uid, event_ids, defaults, context=None, *args):
"""
Modifies the recurring event
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param event_ids: List of crm meeting’s IDs.
@param context: A standard dictionary for contextual values
@return: True
"""
for event_id in event_ids:
event_id = base_calendar_id2real_id(event_id)
defaults.pop('id')
defaults.update({'table': self._table})
qry = "UPDATE %(table)s set name = '%(name)s', \
date = '%(date)s', date_deadline = '%(date_deadline)s'"
if defaults.get('alarm_id'):
qry += ", alarm_id = %(alarm_id)s"
if defaults.get('location'):
qry += ", location = '%(location)s'"
qry += "WHERE id = %s" % (event_id)
cr.execute(qry, defaults)
return True
def get_recurrent_ids(self, cr, uid, select, base_start_date, base_until_date, limit=100):
def get_recurrent_ids(self, cr, uid, select, base_start_date, base_until_date, limit=100, context=None):
"""Gives virtual event ids for recurring events based on value of Recurrence Rule
This method gives ids of dates that comes between start date and end date of calendar views
@param self: The object pointer
@@ -1245,46 +1101,33 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
@param base_start_date: Get Start Date
@param base_until_date: Get End Date
@param limit: The Number of Results to Return """
if not context:
context = {}
virtual_id = context and context.get('virtual_id', False) or False
if not limit:
limit = 100
if isinstance(select, (str, int, long)):
ids = [select]
else:
ids = select
result = []
recur_dict = []
if ids and (base_start_date or base_until_date):
cr.execute("select m.id, m.rrule, m.date, m.date_deadline, m.duration, \
m.exdate, m.exrule, m.recurrent_id, m.recurrent_uid from " + self._table + \
" m where m.id = ANY(%s)", (ids,) )
count = 0
for data in cr.dictfetchall():
if ids and virtual_id:
for data in super(calendar_event, self).read(cr, uid, ids, context=context):
start_date = base_start_date and datetime.strptime(base_start_date[:10]+ ' 00:00:00' , "%Y-%m-%d %H:%M:%S") or False
until_date = base_until_date and datetime.strptime(base_until_date[:10]+ ' 23:59:59', "%Y-%m-%d %H:%M:%S") or False
if count > limit:
break
event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S")
# 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 not data['rrule']:
if start_date and (event_date < start_date):
continue
if until_date and (event_date > until_date):
continue
idval = real_id2base_calendar_id(data['id'], data['date'])
if not data['recurrent_id']:
result.append(idval)
count += 1
else:
ex_id = real_id2base_calendar_id(data['recurrent_uid'], data['recurrent_id'])
ls = base_calendar_id2real_id(ex_id, with_date=data and data.get('duration', 0) or 0)
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
if ls[1] == data['recurrent_id']:
result.append(idval)
recur_dict.append(ex_id)
idval = data['id']
result.append(idval)
else:
start_date = event_date
exdate = data['exdate'] and data['exdate'].split(',') or []
rrule_str = data['rrule']
new_rrule_str = []
@@ -1300,6 +1143,8 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
until_date = rrule_until_date
if until_date:
value = until_date.strftime("%Y%m%d%H%M%S")
else:
value = value.strftime("%Y%m%d%H%M%S")
new_rule = '%s=%s' % (name, value)
new_rrule_str.append(new_rule)
if not is_until and until_date:
@@ -1309,6 +1154,7 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
new_rrule_str.append(new_rule)
new_rrule_str = ';'.join(new_rrule_str)
rdates = get_recurrent_dates(str(new_rrule_str), exdate, start_date, data['exrule'])
for r_date in rdates:
if start_date and r_date < start_date:
continue
@@ -1316,77 +1162,75 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
continue
idval = real_id2base_calendar_id(data['id'], r_date.strftime("%Y-%m-%d %H:%M:%S"))
result.append(idval)
count += 1
if result:
ids = list(set(result)-set(recur_dict))
ids = list(set(result))
if isinstance(select, (str, int, long)):
return ids and ids[0] or False
return ids
def compute_rule_string(self, cr, uid, datas, context=None, *args):
def compute_rule_string(self, datas):
"""
Compute rule string according to value type RECUR of iCalendar from the values given.
@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 datas: dictionary of freq and interval value.
@param context: A standard dictionary for contextual values
@return: String value of the format RECUR of iCalendar
"""
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
weekstring = ''
monthstring = ''
yearstring = ''
freq=datas.get('rrule_type')
if freq == 'none':
def get_week_string(freq, datas):
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
if byday:
return ';BYDAY=' + ','.join(byday)
return ''
def get_month_string(freq, datas):
if freq == 'monthly':
if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
if datas.get('select1')=='day':
return ';BYDAY=' + datas.get('byday') + datas.get('week_list')
elif datas.get('select1')=='date':
return ';BYMONTHDAY=' + str(datas.get('day'))
return ''
def get_end_date(datas):
if datas.get('end_date'):
datas['end_date_new'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z'
interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
if byday:
weekstring = ';BYDAY=' + ','.join(byday)
elif freq == 'monthly':
if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
if datas.get('select1')=='day':
monthstring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
elif datas.get('select1')=='date':
monthstring = ';BYMONTHDAY=' + str(datas.get('day'))
return (datas.get('end_type') == 'count' and (';COUNT=' + str(datas.get('count'))) or '') +\
((datas.get('end_date_new') and datas.get('end_type') == 'end_date' and (';UNTIL=' + datas.get('end_date_new'))) or '')
if datas.get('end_date'):
datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + 'T235959Z'
enddate = (datas.get('count') and (';COUNT=' + str(datas.get('count'))) or '') +\
((datas.get('end_date') and (';UNTIL=' + datas.get('end_date'))) or '')
freq=datas.get('rrule_type')
if freq == 'none':
return ''
interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
return 'FREQ=' + freq.upper() + get_week_string(freq, datas) + interval_srting + get_end_date(datas) + get_month_string(freq, datas)
rrule_string = 'FREQ=' + freq.upper() + weekstring + interval_srting \
+ enddate + monthstring + yearstring
return rrule_string
def search(self, cr, uid, args, offset=0, limit=100, order=None,
def remove_virtual_id(self, ids):
if isinstance(ids, (str, int)):
return base_calendar_id2real_id(ids)
if isinstance(ids, (list, tuple)):
res = []
for id in ids:
res.append(base_calendar_id2real_id(id))
return res
def search(self, cr, uid, args, offset=0, limit=0, order=None,
context=None, count=False):
"""
Overrides orm search method.
@param cr: the current row, from the database cursor,
@param user: the current user’s ID for security checks,
@param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
@param offset: The Number of Results to Pass
@param limit: The Number of Results to Return
@param context: A standard dictionary for contextual values
@param count: If its True the method returns number of records instead of ids
@return: List of id
"""
args_without_date = []
start_date = False
until_date = False
for arg in args:
if arg[0] not in ('date', unicode('date'), 'date_deadline', unicode('date_deadline')):
if arg[0] == "id":
new_id = self.remove_virtual_id(arg[2])
new_arg = (arg[0], arg[1], new_id)
args_without_date.append(new_arg)
elif arg[0] not in ('date', unicode('date'), 'date_deadline', unicode('date_deadline')):
args_without_date.append(arg)
else:
if arg[1] in ('>', '>='):
@@ -1397,12 +1241,17 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
if until_date:
continue
until_date = arg[2]
res = super(calendar_event, self).search(cr, uid, args_without_date, \
offset, limit, order, context, count)
0, 0, order, context, count=False)
res = self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit, context=context)
if count:
return len(res)
elif limit:
return res[offset:offset+limit]
else:
return res
res = self.get_recurrent_ids(cr, uid, res, start_date, until_date, limit)
return res
def get_edit_all(self, cr, uid, id, vals=None):
"""
@@ -1413,39 +1262,49 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
if(vals and 'edit_all' in vals): #we jsut check edit_all
return vals['edit_all']
else: #it's a recurrent event and edit_all is already check
return meeting['recurrency'] and meeting['edit_all']
return meeting['recurrency'] and meeting['edit_all']
def _get_data(self, cr, uid, id, context=None):
res = self.read(cr, uid, [id],['date', 'date_deadline'])
return res[0]
def need_to_update(self, event_id, vals):
split_id = str(event_id).split("-")
if len(split_id) < 2:
return False
else:
date_start = vals.get('date', '')
try:
date_start = datetime.strptime(date_start, '%Y-%m-%d %H:%M:%S').strftime("%Y%m%d%H%M%S")
return date_start == split_id[1]
except Exception:
return True
def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
"""
Overrides orm write 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 crm meeting's ids
@param vals: Dictionary of field value.
@param context: A standard dictionary for contextual values
@return: True
"""
if context is None:
context = {}
if isinstance(ids, (str, int, long)):
select = [ids]
else:
select = ids
new_ids = []
res = False
for event_id in select:
real_event_id = base_calendar_id2real_id(event_id)
if(self.get_edit_all(cr, uid, event_id, vals=vals)):
edit_all = self.get_edit_all(cr, uid, event_id, vals=vals)
if edit_all:
if self.need_to_update(event_id, vals):
res = self._get_data(cr, uid, real_event_id, context=context)
vals.update(res)
event_id = real_event_id
if len(str(event_id).split('-')) > 1:
#if edit one instance of a reccurrent id
if len(str(event_id).split('-')) > 1 and not edit_all:
data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
'rrule', 'duration', 'exdate'])
if data.get('rrule'):
@@ -1458,15 +1317,15 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
'edit_all': False,
'recurrency' : False,
})
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
date_new = event_id.split('-')[1]
date_new = time.strftime("%Y%m%dT%H%M%S", \
time.strptime(date_new, "%Y%m%d%H%M%S"))
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
res = self.write(cr, uid, [real_event_id], {'exdate': exdate})
context.update({'active_id': new_id, 'active_ids': [new_id]})
continue
if not real_event_id in new_ids:
@@ -1483,8 +1342,6 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
context=context)
vals.update(updated_vals.get('value', {}))
if not 'edit_all' in vals:
vals['edit_all'] = False
if new_ids:
res = super(calendar_event, self).write(cr, uid, new_ids, vals, context=context)
@@ -1494,18 +1351,9 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
# change alarm details
alarm_obj = self.pool.get('res.alarm')
alarm_obj.do_alarm_create(cr, uid, new_ids, self._name, 'date', context=context)
return res
return res or True and False
def browse(self, cr, uid, ids, context=None, list_class=None, fields_process=None):
"""
Overrides orm browse 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 crm meeting's ids
@param context: A standard dictionary for contextual values
@return: the object list.
"""
if isinstance(ids, (str, int, long)):
select = [ids]
else:
@@ -1517,20 +1365,32 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
return res and res[0] or False
return res
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
if not context:
context = {}
if 'date' in groupby:
raise osv.except_osv(_('Warning !'), _('Group by date not supported, use the calendar view instead'))
virtual_id = context.get('virtual_id', False)
context.update({'virtual_id': False})
res = super(calendar_event, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
for re in res:
#remove the count, since the value is not consistent with the result of the search when expand the group
for groupname in groupby:
if re.get(groupname + "_count"):
del re[groupname + "_count"]
re.get('__context').update({'virtual_id' : virtual_id})
return res
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
"""
Overrides orm Read method.Read List of fields for calendar event.
@param cr: the current row, from the database cursor,
@param user: the current user’s ID for security checks,
@param ids: List of calendar event's id.
@param fields: List of fields.
@param context: A standard dictionary for contextual values
@return: List of Dictionary of form [{‘name_of_the_field’: value, ...}, ...]
"""
# FIXME This whole id mangling has to go!
if context is None:
context = {}
if isinstance(ids, (str, int, long)):
select = [ids]
@@ -1544,9 +1404,10 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
fields.append('duration')
for base_calendar_id, real_id in select:
for base_calendar_id, real_id in select:
#REVET: Revision ID: olt@tinyerp.com-20100924131709-cqsd1ut234ni6txn
res = super(calendar_event, self).read(cr, uid, real_id, fields=fields, context=context, load=load)
if not res :
continue
ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
@@ -1558,41 +1419,31 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
result.append(res)
if isinstance(ids, (str, int, long)):
return result and result[0] or False
return result
def copy(self, cr, uid, id, default=None, context=None):
"""
Duplicate record on specified id.
@param self: the object pointer.
@param cr: the current row, from the database cursor,
@param id: id of record from which we duplicated.
@param context: A standard dictionary for contextual values
@return: Duplicate record id.
"""
if context is None:
context = {}
res = super(calendar_event, self).copy(cr, uid, base_calendar_id2real_id(id), default, context)
alarm_obj = self.pool.get('res.alarm')
alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
return res
def unlink(self, cr, uid, ids, context=None):
"""
Deletes records specified in ids.
@param self: the object pointer.
@param cr: the current row, from the database cursor,
@param id: List of calendar event's id.
@param context: A standard dictionary for contextual values
@return: True
"""
res = False
for event_datas in self.read(cr, uid, ids, ['date', 'rrule', 'exdate'], context=context):
event_id = event_datas['id']
if not isinstance(ids, list):
ids = [ids]
res = False
for id in ids:
event_datas = self.read(cr, uid, [id], ['date', 'rrule', 'exdate'], context=context)[0]
event_id = event_datas['id']
if self.get_edit_all(cr, uid, event_id, vals=None):
event_id = base_calendar_id2real_id(event_id)
if isinstance(event_id, (int, long)):
res = super(calendar_event, self).unlink(cr, uid, event_id, context=context)
self.pool.get('res.alarm').do_alarm_unlink(cr, uid, [event_id], self._name)
@@ -1613,15 +1464,6 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
return res
def create(self, cr, uid, vals, context=None):
"""
Create new record.
@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 vals: dictionary of every field value.
@param context: A standard dictionary for contextual values
@return: new created record id.
"""
if context is None:
context = {}
@@ -1635,11 +1477,13 @@ e.g.: Every other month on the last Sunday of the month for 10 occurrences:\
vals.get('allday', False),
context=context)
vals.update(updated_vals.get('value', {}))
res = super(calendar_event, self).create(cr, uid, vals, context)
alarm_obj = self.pool.get('res.alarm')
alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
return res
def do_tentative(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@@ -1710,7 +1554,7 @@ class calendar_todo(osv.osv):
@param args: list of tuples of form [(‘name_of_the_field’, ‘operator’, value), ...].
@param context: A standard dictionary for contextual values
"""
assert name == 'date'
return self.write(cr, uid, id, { 'date_start': value }, context=context)
@@ -1742,9 +1586,9 @@ class ir_attachment(osv.osv):
for arg in args:
args1.append(map(lambda x:str(x).split('-')[0], arg))
return super(ir_attachment, self).search_count(cr, user, args1, context)
def create(self, cr, uid, vals, context=None):
if context:
id = context.get('default_res_id', False)
+1 -2
View File
@@ -242,8 +242,7 @@
widget="selection" />
<group colspan="2" col="4" attrs="{'readonly': [('state','=','done')]}">
<field name="recurrency"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}"
on_change="onchange_edit_all(rrule_type,edit_all)"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}" />
</group>
</group>
<notebook colspan="4">
@@ -15,7 +15,8 @@
Now I will set recurrence for this event to occur monday and friday of week
-
!python {model: calendar.event}: |
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly'})
data = {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly', 'end_type': 'end_date', 'end_date': '2011-05-31 00:00:00'}
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], data)
- |
In order to check that recurrent events are views successfully in calendar view,
I will open calendar view of events
@@ -27,7 +28,7 @@
I will search for one of the recurrent event and count the number of events
-
!python {model: calendar.event}: |
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')] )
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
assert len(ids) == 9
- |
Now I will make All day event and test it
-2
View File
@@ -19,9 +19,7 @@
#
##############################################################################
import calendar_event_edit_all
import base_calendar_invite_attendee
import base_calendar_set_exrule
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,163 +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/>.
#
##############################################################################
from base_calendar import base_calendar
from osv import fields, osv
from tools.translate import _
import tools
import re
months = {
1: "January", 2: "February", 3: "March", 4: "April", \
5: "May", 6: "June", 7: "July", 8: "August", 9: "September", \
10: "October", 11: "November", 12: "December"
}
class base_calendar_set_exrule(osv.osv_memory):
"""
Set Exrule.
"""
_name = "base.calendar.set.exrule"
_description = "Set Exrule"
_columns = {
'freq': fields.selection([('None', 'No Repeat'), \
('secondly', 'Secondly'), \
('minutely', 'Minutely'), \
('hourly', 'Hourly'), \
('daily', 'Daily'), \
('weekly', 'Weekly'), \
('monthly', 'Monthly'), \
('yearly', 'Yearly')], 'Frequency',required=True),
'interval': fields.integer('Interval'),
'count': fields.integer('Count'),
'mo': fields.boolean('Mon'),
'tu': fields.boolean('Tue'),
'we': fields.boolean('Wed'),
'th': fields.boolean('Thu'),
'fr': fields.boolean('Fri'),
'sa': fields.boolean('Sat'),
'su': fields.boolean('Sun'),
'select1': fields.selection([('date', 'Date of month'), \
('day', 'Day of month')], 'Option'),
'day': fields.integer('Date of month'),
'week_list': fields.selection([('MO', 'Monday'), ('TU', 'Tuesday'), \
('WE', 'Wednesday'), ('TH', 'Thursday'), \
('FR', 'Friday'), ('SA', 'Saturday'), \
('SU', 'Sunday')], 'Weekday'),
'byday': fields.selection([('1', 'First'), ('2', 'Second'), \
('3', 'Third'), ('4', 'Fourth'), \
('5', 'Fifth'), ('-1', 'Last')], 'By day'),
'month_list': fields.selection(months.items(),'Month'),
'end_date': fields.date('Repeat Until'),
}
def view_init(self, cr, uid, fields, context=None):
"""
This function checks for precondition before wizard executes
@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 fields: List of fields for default value
@param context: A standard dictionary for contextual values
"""
if context is None:
context = {}
event_obj = self.pool.get(context.get('active_model'))
for event in event_obj.browse(cr, uid, context.get('active_ids', []), context=context):
if not event.rrule:
raise osv.except_osv(_("Warning !"), _("Please Apply Recurrency before applying Exception Rule."))
return False
def compute_exrule_string(self, cr, uid, ids, context=None):
"""
Compute rule string.
@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 datas: dictionary of freq and interval value.
@return: string value which compute FREQILY;INTERVAL
"""
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
weekstring = ''
monthstring = ''
yearstring = ''
if context is None:
context = {}
ex_id = base_calendar.base_calendar_id2real_id(context.get('active_id', False))
model = context.get('model', False)
model_obj = self.pool.get(model)
for datas in self.read(cr, uid, ids, context=context):
freq = datas.get('freq')
if freq == 'None':
model_obj.write(cr, uid, ex_id,{'exrule': ''})
return{}
interval_srting = datas.get('interval') and (';INTERVAL=' + str(datas.get('interval'))) or ''
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: datas.get(x) and x in weekdays, datas))
if byday:
weekstring = ';BYDAY=' + ','.join(byday)
elif freq == 'monthly':
if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
if datas.get('select1')=='day':
monthstring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
elif datas.get('select1')=='date':
monthstring = ';BYMONTHDAY=' + str(datas.get('day'))
elif freq == 'yearly':
if datas.get('select1')=='date' and (datas.get('day') < 1 or datas.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select proper Day of month"))
bymonth = ';BYMONTH=' + str(datas.get('month_list'))
if datas.get('select1')=='day':
bystring = ';BYDAY=' + datas.get('byday') + datas.get('week_list')
elif datas.get('select1')=='date':
bystring = ';BYMONTHDAY=' + str(datas.get('day'))
yearstring = bymonth + bystring
if datas.get('end_date'):
datas['end_date'] = ''.join((re.compile('\d')).findall(datas.get('end_date'))) + '235959Z'
enddate = (datas.get('count') and (';COUNT=' + str(datas.get('count'))) or '') +\
((datas.get('end_date') and (';UNTIL=' + datas.get('end_date'))) or '')
exrule_string = 'FREQ=' + freq.upper() + weekstring + interval_srting \
+ enddate + monthstring + yearstring
model_obj.write(cr, uid, ex_id,{'exrule': exrule_string})
return {'type': 'ir.actions.act_window_close'}
_defaults = {
'freq': lambda *x: 'None',
'select1': lambda *x: 'date',
'interval': lambda *x: 1,
}
base_calendar_set_exrule()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_base_calendar_set_exrule" model="ir.ui.view">
<field name="name">base.calendar.set.exrule.form</field>
<field name="model">base.calendar.set.exrule</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Set Exrule">
<separator string="Select range to Exclude" colspan="8"/>
<group col="8" colspan="6">
<field name="interval" />
<field name="freq" />
<field name="count" />
<field name="end_date" />
</group>
<group col="8" colspan="8" name="Select weekdays"
attrs="{'invisible' : [('freq','!=','weekly')]}">
<field name="mo" colspan="1" />
<field name="tu" colspan="1" />
<field name="we" colspan="1" />
<field name="th" colspan="1" />
<field name="fr" colspan="1" />
<field name="sa" colspan="1" />
<field name="su" colspan="1" />
<newline />
</group>
<group col="8" colspan="6"
attrs="{'invisible' : [('freq','!=','monthly'), ('freq','!=','yearly')]}">
<group col="2" colspan="1">
<field name="select1" />
</group>
<group col="2" colspan="1"
attrs="{'invisible' : [('select1','=','day')]}">
<field name="day"
attrs="{'required' : [('select1','=','date')]}"/>
</group>
<group col="3" colspan="1"
attrs="{'invisible' : [('select1','=','date')]}">
<field name="byday" string="The"
attrs="{'required' : [('select1','=','day')]}"/>
<field name="week_list" nolabel="1"
attrs="{'required' : [('select1','=','day')]}"/>
</group>
<group col="1" colspan="1"
attrs="{'invisible' : [('freq','!=','yearly')]}">
<field name="month_list" string="of"
colspan="1"
attrs="{'required' : [('freq','=','yearly')]}"/>
</group>
</group>
<group colspan="4" col="6">
<separator string="" colspan="8"/>
<label string="" colspan="2"/>
<button icon="gtk-cancel" special="cancel" string="_Cancel"/>
<button icon="gtk-ok" string="_Ok" name="compute_exrule_string" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_base_calendar_set_exrule" model="ir.actions.act_window">
<field name="name">Set Exclude range</field>
<field name="res_model">base.calendar.set.exrule</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree</field>
<field name="target">new</field>
</record>
</data>
</openerp>
@@ -1,98 +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/>.
#
##############################################################################
from osv import osv
from osv import fields
class calendar_event_edit_all(osv.osv_memory):
def _default_values(self, cr, uid, context=None):
""" Get Default value for Start 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 context: A standard dictionary for contextual values
@Return: Get Default value for Start Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date'):
return context.get('date')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date']
def _default_deadline(self, cr, uid, context=None):
""" Get Default value for End 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 context: A standard dictionary for contextual values
@return: Get Default value for End Date
"""
context_id = context and context.get('active_id', False) or False
if context_id:
if context.get('date_deadline'):
return context.get('date_deadline')
else:
model = context.get('model', False)
model_obj = self.pool.get(model)
event = model_obj.read(cr, uid, context_id, ['name', 'location', 'alarm_id'])
return event['date_deadline']
def modify_this(self, cr, uid, ids, context=None):
"""
Modify All event for Crm Meeting.
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param ids: List of calendar event edit all’s IDs
@return: dictionary {}
"""
if context is None:
context = {}
context_id = context and context.get('active_id', False) or False
if context_id:
for datas in self.read(cr, uid, ids):
model = context.get('model', False)
model_obj = self.pool.get(model)
model_obj.modify_all(cr, uid, [context_id], datas, context=context)
return {'type': 'ir.actions.act_window_close'}
_name = "calendar.event.edit.all"
_description = "Calendar Edit all event"
_columns = {
'name': fields.char('Title', size=64, required=True),
'date': fields.datetime('Start Date', required=True),
'date_deadline': fields.datetime('End Date', required=True),
'location': fields.char('Location', size=124),
'alarm_id': fields.many2one('res.alarm', 'Reminder'),
}
_defaults = {
'date': _default_values,
'date_deadline': _default_deadline
}
calendar_event_edit_all()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_calendar_event_edit_all" model="ir.ui.view">
<field name="name">calendar.event.edit.all.form</field>
<field name="model">calendar.event.edit.all</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Edit all Occurrences">
<group colspan="4" >
<separator string="" colspan="4" />
<newline/>
<field name='name' colspan="4" />
<newline />
<field name='location' colspan="4" />
<newline />
<field name='date' />
<field name='date_deadline' />
<newline />
<field name='alarm_id'/>
</group>
<separator string="" colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-save" string="_Save" name="modify_this" type="object"/>
</group>
</form>
</field>
</record>
<record id="action_calendar_event_edit_all" model="ir.actions.act_window">
<field name="name">Edit all events</field>
<field name="res_model">calendar.event.edit.all</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_calendar_event_edit_all"/>
<field name="target">new</field>
</record>
</data>
</openerp>
+29 -7
View File
@@ -23,6 +23,9 @@ import string
from osv import osv, fields
from tools.translate import _
from tools.misc import ustr
import re
import datetime
_ref_vat = {
'be': 'BE0477472701', 'at': 'ATU12345675',
@@ -39,7 +42,7 @@ _ref_vat = {
'pt': 'PT123456789', 'ro': 'RO1234567897',
'se': 'SE123456789701', 'si': 'SI12345679',
'sk': 'SK0012345675', 'el': 'EL12345670',
'mx': 'MXABC123456T1B'
'mx': 'MXABCD831230T1B',
}
@@ -1072,17 +1075,36 @@ class res_partner(osv.osv):
return False
return True
__check_vat_mx_re = re.compile(r"(?P<primeras>[A-Za-z\xd1\xf1&]{3,4})" \
r"[ \-_]?" \
r"(?P<ano>[0-9]{2})(?P<mes>[01][0-9])(?P<dia>[0-3][0-9])" \
r"[ \-_]?" \
r"(?P<code>[A-Za-z0-9&\xd1\xf1]{3})$")
def check_vat_mx(self, vat):
''' Mexican VAT verification
Verificar RFC México
'''
Verificar RFC méxico
'''
if not 12 <= len(vat) <= 13:
# we convert to 8-bit encoding, to help the regex parse only bytes
vat = ustr(vat).encode('iso8859-1')
m = self.__check_vat_mx_re.match(vat)
if not m:
#No valid format
return False
elif len(vat)==12 and not vat[:3].isalpha() | vat[3:9].isdigit() | vat[-3:].isalnum():
return False
elif len(vat)==13 and not vat[:4].isalpha() | vat[4:10].isdigit() | vat[-3:].isalnum():
try:
ano = int(m.group('ano'))
if ano > 30:
ano = 1900 + ano
else:
ano = 2000 + ano
datetime.date(ano, int(m.group('mes')), int(m.group('dia')))
except ValueError:
return False
#Valid format and valid date
return True
res_partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+1
View File
@@ -129,6 +129,7 @@ Creates a dashboard for CRM that includes:
'test/test_crm_phonecall.yml',
'test/test_crm_recurrent_meeting.yml',
'test/test_crm_stage_changes.yml',
'test/test_crm_recurrent_meeting_case2.yml',
],
'installable': True,
'active': False,
+222 -104
View File
@@ -44,8 +44,221 @@ AVAILABLE_PRIORITIES = [
('5', 'Lowest'),
]
class crm_case(object):
"""A simple python class to be used for common functions """
class crm_base(object):
"""
Base classe for crm object,
Object that inherit from this class should have
date_open
date_closed
user_id
partner_id
partner_address_id
as field to be compatible with this class
"""
def _get_default_partner_address(self, cr, uid, context=None):
"""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 context is None:
context = {}
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=None):
"""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 context is None:
context = {}
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if not user.address_id:
return False
return user.address_id.partner_id.id
def _get_default_email(self, cr, uid, context=None):
"""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=context)
if not user.address_id:
return False
return user.address_id.email
def _get_default_user(self, cr, uid, context=None):
"""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 and context.get('portal', False):
return False
return uid
def _get_section(self, cr, uid, context=None):
"""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 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)
if address.email:
return {'value': {'email_from': address.email, 'phone': address.phone}}
else:
return {'value': {'phone': address.phone}}
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
"""
data={}
if part:
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 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)
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.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_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.write(cr, uid, ids, {'state': 'cancel',
'active': True})
self._action(cr, uid, cases, 'cancel')
for case in cases:
message = _("The case '%s' has been cancelled.") % (case.name,)
self.log(cr, uid, case.id, message)
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.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
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
class crm_case(crm_base):
"""
A simple python class to be used for common functions
Object that inherit from this class should inherit from mailgate.thread
And need a stage_id field
And object that inherit (orm inheritance) from a class the overwrite copy
"""
def _find_lost_stage(self, cr, uid, type, section_id):
return self._find_percent_stage(cr, uid, 0.0, type, section_id)
@@ -105,35 +318,7 @@ class crm_case(object):
return {'value':{}}
return {'value':{'probability': stage.probability}}
def _get_default_partner_address(self, cr, uid, context=None):
"""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 context is None:
context = {}
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=None):
"""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 context is None:
context = {}
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if not user.address_id:
return False
return user.address_id.partner_id.id
def copy(self, cr, uid, id, default=None, context=None):
"""
@@ -145,6 +330,7 @@ class crm_case(object):
@param default: Dictionary of default values for copy.
@param context: A standard dictionary for contextual values
"""
if context is None:
context = {}
if default is None:
@@ -164,40 +350,9 @@ class crm_case(object):
})
return super(osv.osv, self).copy(cr, uid, id, default, context=context)
def _get_default_email(self, cr, uid, context=None):
"""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=context)
if not user.address_id:
return False
return user.address_id.email
def _get_default_user(self, cr, uid, context=None):
"""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 and context.get('portal', False):
return False
return uid
def _get_section(self, cr, uid, context=None):
"""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 _find_next_stage(self, cr, uid, stage_list, index, current_seq, stage_pool, context=None):
if index + 1 == len(stage_list):
@@ -274,38 +429,9 @@ class crm_case(object):
@param context: A standard dictionary for contextual values"""
return self.stage_change(cr, uid, ids, context=context, order='sequence desc')
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
"""
data={}
if part:
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)
if address.email:
return {'value': {'email_from': address.email, 'phone': address.phone}}
else:
return {'value': {'phone': address.phone}}
def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context=None):
mailgate_pool = self.pool.get('mailgate.thread')
@@ -521,15 +647,7 @@ class crm_case(object):
cases = self.browse(cr, uid, ids2, context=context)
return self._action(cr, uid, cases, False, context=context)
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
def format_body(self, body):
return self.pool.get('base.action.rule').format_body(body)
+3 -3
View File
@@ -20,7 +20,7 @@
##############################################################################
from base_calendar import base_calendar
from crm import crm_case
from crm import crm_base, crm_case
from osv import fields, osv
from tools.translate import _
import logging
@@ -36,13 +36,13 @@ class crm_phonecall(crm_case, osv.osv):
crm_phonecall()
class crm_meeting(crm_case, osv.osv):
class crm_meeting(crm_base, osv.osv):
""" CRM Meeting Cases """
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
_inherit = ['mailgate.thread',"calendar.event"]
_inherit = "calendar.event"
_columns = {
# From crm.case
'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}),
+1 -1
View File
@@ -51,7 +51,7 @@
<field name="res_model">crm.meeting</field>
<field name="view_mode">calendar,tree,form,gantt</field>
<field name="view_id" ref="crm_case_calendar_view_meet"/>
<field name="context">{"search_default_user_id":uid, 'search_default_section_id': section_id}</field>
<field name="context">{"search_default_user_id":uid, 'search_default_section_id': section_id, 'virtual_id' : True}</field>
<field name="search_view_id" ref="view_crm_case_meetings_filter"/>
<field name="help">The meeting calendar is shared between the sales teams and fully integrated with other applications such as the employee holidays or the business opportunities. You can also synchronize meetings with your mobile phone using the caldav interface.
</field>
+1 -2
View File
@@ -45,8 +45,7 @@
widget="selection" />
<group colspan="2" col="4" attrs="{'readonly': ['|', ('recurrent_uid','!=',False), ('state','=','done')]}">
<field name="recurrency"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}"
on_change="onchange_edit_all(rrule_type,edit_all)"/>
<field name="edit_all" attrs="{'invisible':[('recurrency','=', False)]}" />
</group>
</group>
<notebook colspan="4">
+2 -3
View File
@@ -19,20 +19,19 @@
#
##############################################################################
from crm import crm_case
from crm import crm_base
from osv import fields, osv
from tools.translate import _
import crm
import time
from datetime import datetime
class crm_phonecall(crm_case, osv.osv):
class crm_phonecall(crm_base, osv.osv):
""" Phonecall Cases """
_name = "crm.phonecall"
_description = "Phonecall"
_order = "id desc"
_inherit = ['mailgate.thread']
_columns = {
# From crm.case
'id': fields.integer('ID'),
+1 -1
View File
@@ -57,7 +57,7 @@
!python {model: crm.meeting}: |
import time
from datetime import datetime, date, timedelta
ids = self.search(cr, uid, [('date', '>=', time.strftime('%Y-%m-%d 00:00:00')), ('date', '<=', (datetime.now()+timedelta(31)).strftime('%Y-%m-%d 00:00:00')), ('name', '=', 'Regarding Presentation')] )
ids = self.search(cr, uid, [('date', '>=', time.strftime('%Y-%m-%d 00:00:00')), ('date', '<=', (datetime.now()+timedelta(31)).strftime('%Y-%m-%d 00:00:00')), ('name', '=', 'Regarding Presentation')], context={'virtual_id': True})
assert len(ids) == 10
- |
@@ -0,0 +1,98 @@
-
In order to test recurrent meetings in OpenERP, I create meetings with different recurrency using different test cases.
-
I create a recurrent meeting with daily recurrency and fixed amount of time.
-
!record {model: crm.meeting, id: crm_meeting_testmeeting0}:
count: 5
date: '2011-04-13 11:04:00'
date_deadline: '2011-04-13 12:04:00'
day: 0.0
duration: 1.0
name: Test Meeting
recurrency: true
recurrent_uid: 0.0
rrule_type: daily
sequence: 0.0
-
I search for all the recurrent meetings.
-
!python {model: crm.meeting}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('crm_meeting_testmeeting0')]),('date','>=','2011-03-13'), ('date_deadline', '<=', '2011-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 5, 'Recurrent daily meetings are not created !'
-
Now I create a weekly meeting till a particular end date.
-
!record {model: crm.meeting, id: crm_meeting_reviewcodewithprogrammer0}:
date: '2011-04-18 11:47:00'
date_deadline: '2011-04-18 12:47:00'
day: 0.0
duration: 1.0
end_date: '2011-04-30'
end_type: end_date
fr: true
mo: true
th: true
tu: true
we: true
name: Review code with programmer
recurrency: true
recurrent_uid: 0.0
rrule_type: weekly
sequence: 0.0
-
I search for all the recurrent weekly meetings.
-
!python {model: crm.meeting}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('crm_meeting_reviewcodewithprogrammer0')]),('date','>=','2011-03-13'), ('date_deadline', '<=', '2011-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 10, 'Recurrent weekly meetings are not created !'
-
I want to schedule a meeting every month for Sprint review.
-
!record {model: crm.meeting, id: crm_meeting_sprintreview0}:
count: 12
date: '2011-04-01 12:01:00'
date_deadline: '2011-04-01 13:01:00'
day: 1
duration: 1.0
name: Sprint Review
recurrency: true
recurrent_uid: 0.0
rrule_type: monthly
sequence: 0.0
-
I search for all the recurrent monthly meetings.
-
!python {model: crm.meeting}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('crm_meeting_sprintreview0')]),('date','>=','2011-03-01'), ('date_deadline', '<=', '2012-05-13')], context={'virtual_id': True})
assert len(meeting_ids) == 12, 'Recurrent monthly meetings are not created !'
-
I change name of my monthly Sprint Review meeting.
-
!python {model: crm.meeting}: |
from base_calendar import base_calendar
base_cal_id = base_calendar.real_id2base_calendar_id(ref('crm_meeting_sprintreview0'), '2011-09-01 13:01:00')
self.write(cr, uid, [base_cal_id], {'name': 'Sprint Review for google modules', 'edit_all': True})
-
I check whether all the records are edited or not.
-
!python {model: crm.meeting}: |
meeting_ids = self.search(cr, uid, [('id', 'in', [ref('crm_meeting_sprintreview0')]),('date','>=','2011-03-01'), ('date_deadline', '<=', '2012-05-13')], context={'virtual_id': True})
meetings = self.browse(cr, uid, meeting_ids, context)
for meeting in meetings:
assert meeting.name == 'Sprint Review for google modules', 'Name not changed for id: %s' %meeting.id
-
I change description of my weekly meeting Review code with programmer.
-
!python {model: crm.meeting}: |
from base_calendar import base_calendar
base_cal_id = base_calendar.real_id2base_calendar_id(ref('crm_meeting_reviewcodewithprogrammer0'), '2011-04-25 12:47:00')
self.write(cr, uid, [base_cal_id], {'description': 'Review code of the module: sync_google_calendar.'})
-
I check whether the record is edited perfectly or not.
-
!python {model: crm.meeting}: |
meeting_ids = self.search(cr, uid, [('recurrent_uid', '=', ref('crm_meeting_reviewcodewithprogrammer0')), ('recurrent_id','=','2011-04-25 12:47:00')], context)
assert meeting_ids, 'Meeting is not edited !'
+2 -3
View File
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
@@ -139,7 +138,7 @@ class crm_merge_opportunity(osv.osv_memory):
attach_ids = self.get_attachments(cr, uid, opp, context=context)
self.set_attachements_res_id(cr, uid, first_opportunity.id, attach_ids)
for history in opp.message_ids:
new_history = message_obj.write(cr, uid, history.id, {'res_id': first_opportunity.id, 'name' : _("From %s : %s") % (opp.name, history.name) }, context=context)
message_obj.write(cr, uid, history.id, {'res_id': first_opportunity.id, 'name' : _("From %s : %s") % (opp.name, history.name) }, context=context)
#Notification about loss of information
details = []
@@ -225,7 +224,7 @@ class crm_merge_opportunity(osv.osv_memory):
This function gets default values
@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 uid: the current users ID for security checks,
@param fields: List of fields for default value
@param context: A standard dictionary for contextual values
@@ -34,12 +34,19 @@ class google_login(osv.osv_memory):
'password': fields.char('Google Password', size=64),
}
def google_login(self, user, password, type='group', context=None):
gd_client = gdata.contacts.service.ContactsService()
def google_login(self,cr,uid,user,password,type='',context=None):
gd_client=False
if type == 'group':
gd_client = gdata.contacts.client.ContactsClient(source='OpenERP')
if type == 'contact' :
gd_client = gdata.contacts.service.ContactsService()
if type == 'calendar':
gd_client = gdata.calendar.service.CalendarService()
try:
gd_client.ClientLogin(user, password,gd_client.source)
except Exception:
return False
except Exception, e:
raise osv.except_osv(_('Error'), _(e))
return gd_client
@@ -51,12 +58,19 @@ class google_login(osv.osv_memory):
if 'password' in fields:
res.update({'password': user_obj.gmail_password})
return res
def login(self, cr, uid, ids, context=None):
def check_login(self, cr, uid, ids, context=None):
if context == None:
context = {}
data = self.read(cr, uid, ids)[0]
user = data['user']
password = data['password']
if self.google_login(user, password):
gd_client = gdata.contacts.service.ContactsService()
gd_client.email = user
gd_client.password = password
gd_client.source = 'OpenERP'
try:
gd_client.ProgrammaticLogin()
res = {
'gmail_user': user,
'gmail_password': password
@@ -65,6 +79,7 @@ class google_login(osv.osv_memory):
else:
raise osv.except_osv(_('Error'), _("Authentication fail check the user and password !"))
return self._get_next_action(cr, uid, context=context)
def _get_next_action(self, cr, uid, context=None):
+2 -2
View File
@@ -92,8 +92,8 @@ class hr_job(osv.osv):
_description = "Job Description"
_columns = {
'name': fields.char('Job Name', size=128, required=True, select=True),
'expected_employees': fields.function(_no_of_employee, method=True, string='Expected Employees', help='Required number of Employees in total for that job.', multi="no_of_employee"),
'no_of_employee': fields.function(_no_of_employee, method=True, string="No of Employee", help='Number of employee with that job.', multi="no_of_employee"),
'expected_employees': fields.function(_no_of_employee, method=True, string='Expected Employees', help='Required number of Employees in total for that job.', multi="no_of_employee", store=True),
'no_of_employee': fields.function(_no_of_employee, method=True, string="No of Employee", help='Number of employee with that job.', multi="no_of_employee", store=True),
'no_of_recruitment': fields.float('Expected in Recruitment'),
'employee_ids': fields.one2many('hr.employee', 'job_id', 'Employees'),
'description': fields.text('Job Description'),
@@ -6,14 +6,14 @@
<field name="name">procurement multi-company</field>
<field name="model_id" search="[('model','=','procurement.order')]" model="ir.model"/>
<field name="global" eval="True"/>
<field name="domain_force">['|','|',('company_id','child_of',[user.company_id.id]),('company_id','=',False),('company_id.child_ids','child_of',[user.company_id.id])]</field>
<field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
<record model="ir.rule" id="stock_warehouse_orderpoint_rule">
<field name="name">stock_warehouse.orderpoint multi-company</field>
<field name="model_id" search="[('model','=','stock.warehouse.orderpoint')]" model="ir.model"/>
<field name="global" eval="True"/>
<field name="domain_force">['|','|',('company_id','child_of',[user.company_id.id]),('company_id','=',False),('company_id.child_ids','child_of',[user.company_id.id])]</field>
<field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
</record>
</data>
+6 -2
View File
@@ -317,8 +317,12 @@ class product_template(osv.osv):
if 'categ_id' in context and context['categ_id']:
return context['categ_id']
md = self.pool.get('ir.model.data')
res = md.get_object_reference(cr, uid, 'product', 'cat0') or False
return res and res[1] or False
res = False
try:
res = md.get_object_reference(cr, uid, 'product', 'cat0')[1]
except ValueError:
res = False
return res
def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id):
if uom_id:
+26
View File
@@ -0,0 +1,26 @@
# -*- 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/>.
#
##############################################################################
import sync_google_calendar
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,39 @@
# -*- 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/>.
#
##############################################################################
{
'name': 'Google Calendar',
'version': '1.0',
'category': 'Generic Modules/Others',
'description': """The module adds google calendar events to meetings.""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base','google_base_account', 'crm'],
'init_xml': [],
'update_xml': ['wizard/wizard_import_calendar_events_view.xml', 'sync_google_calendar_view.xml'],
'test': ['test/test_sync_google_calendar.yml'],
'demo_xml': [],
'installable': True,
'active': False,
'certificate': '',
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,50 @@
# -*- 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
class crm_meeting(osv.osv):
_inherit = "crm.meeting"
def unlink(self, cr, uid, ids, context=None):
ids = self.web_client_unfucking_timebomb(ids)
res = super(crm_meeting, self).unlink(cr, uid, ids, context=context)
ids_real = self.remove_virtual_id(ids)
model_obj = self.pool.get('ir.model.data')
remain_ids = self.search(cr, uid, [('id','in',ids_real)])
ids_to_remove = list(set(ids_real) - set(remain_ids))
model_ids = model_obj.search(cr, uid, [('res_id','in',ids_to_remove),('model','=','crm.meeting'),('module','=','sync_google_calendar')], context=context)
model_obj.unlink(cr, uid, model_ids, context=context)
return res
crm_meeting()
class crm_case_categ(osv.osv):
""" Category of Case """
_inherit = "crm.case.categ"
_columns = {
'user_id': fields.many2one('res.users', 'User')
}
crm_case_categ()
# vim:expandtab:smartindent:toabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="crm_case_form_view_meet_inherit">
<field name="name">CRM - Meetings Form (Inherited)</field>
<field name="model">crm.meeting</field>
<field name="type">form</field>
<field name="inherit_id" ref="crm.crm_case_form_view_meet"/>
<field name="arch" type="xml">
<field name="categ_id" position="replace">
<field name="categ_id" widget="selection"
string="Meeting Type" groups="base.group_extended"
domain="[('object_id.model', '=', 'crm.meeting'),('user_id','in',[uid, False])]" />
</field>
</field>
</record>
<record model="ir.ui.view" id="crm_case_tree_view_meet_inherit">
<field name="name">CRM - Meetings Tree (Inherited)</field>
<field name="model">crm.meeting</field>
<field name="type">tree</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_meet"/>
<field name="arch" type="xml">
<field name="categ_id" position="replace">
<field name="categ_id" widget="selection"
string="Meeting Type" groups="base.group_extended"
domain="[('object_id.model', '=', 'crm.meeting'),('user_id','in',[uid, False])]" />
</field>
</field>
</record>
</data>
</openerp>
@@ -0,0 +1,36 @@
-
In order to test google calendar feature with OpenERP, I import events from
a google account and store them in Meetings.
- |
I create a record for the gmail account for which I want to import the contacts.
-
!record {model: google.login, id: google_login_contact_id0}:
user: testmail.openerp
password: openerptiny
- |
I login into that account.
-
!python {model: google.login}: |
self.check_login(cr, uid, [ref('google_login_contact_id0')], context)
- |
Now I want to import all the events from all the calendars in the user account.
- |
I select all calendars.
-
!record {model: synchronize.google.calendar, id: synchronize_google_calendar_id0}:
calendar_name: all
- |
I import the events from the google calendar.
-
!python {model: synchronize.google.calendar}: |
self.import_calendar_events(cr, uid, [ref('synchronize_google_calendar_id0')], context)
- |
Now I check my meetings are created or not.
-
!python {model: crm.meeting}: |
model_obj = self.pool.get('ir.model.data')
meeting_ids = self.search(cr, uid, [])
model_ids = model_obj.search(cr, uid, [('res_id','in',meeting_ids),('model','=','crm.meeting'),('module','=','sync_google_calendar')])
assert model_ids, 'Meetings not created !'
@@ -0,0 +1,24 @@
# -*- 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/>.
#
##############################################################################
import wizard_import_calendar_events
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,304 @@
# -*- 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 osv
from tools.translate import _
import tools
import time
import datetime
import dateutil
from dateutil.tz import *
from dateutil.parser import *
from dateutil import parser
from pytz import timezone
import re
import urllib
try:
import gdata
import gdata.calendar.service
import gdata.calendar
except ImportError:
raise osv.except_osv(_('Google Contacts Import Error!'), _('Please install gdata-python-client from http://code.google.com/p/gdata-python-client/downloads/list'))
def _get_tinydates(self, stime, etime):
stime = dateutil.parser.parse(stime)
etime = dateutil.parser.parse(etime)
try:
au_dt = au_tz.normalize(stime.astimezone(au_tz))
timestring = datetime.datetime(*au_dt.timetuple()[:6]).strftime('%Y-%m-%d %H:%M:%S')
au_dt = au_tz.normalize(etime.astimezone(au_tz))
timestring_end = datetime.datetime(*au_dt.timetuple()[:6]).strftime('%Y-%m-%d %H:%M:%S')
except:
timestring = datetime.datetime(*stime.timetuple()[:6]).strftime('%Y-%m-%d %H:%M:%S')
timestring_end = datetime.datetime(*etime.timetuple()[:6]).strftime('%Y-%m-%d %H:%M:%S')
return (timestring, timestring_end)
def _get_rules(self, datas):
new_val = {}
if datas['FREQ'] == 'WEEKLY' and datas.get('BYDAY'):
for day in datas['BYDAY'].split(','):
new_val[day.lower()] = True
datas.pop('BYDAY')
if datas.get('UNTIL'):
until = parser.parse(''.join((re.compile('\d')).findall(datas.get('UNTIL'))))
new_val['end_date'] = until.strftime('%Y-%m-%d')
new_val['end_type'] = 'end_date'
datas.pop('UNTIL')
if datas.get('COUNT'):
new_val['count'] = datas.get('COUNT')
new_val['end_type'] = 'count'
datas.pop('COUNT')
if datas.get('INTERVAL'):
new_val['interval'] = datas.get('INTERVAL')
else:
new_val['interval'] = 1
if datas.get('BYMONTHDAY'):
new_val['day'] = datas.get('BYMONTHDAY')
datas.pop('BYMONTHDAY')
new_val['select1'] = 'date'
if datas.get('BYDAY'):
d = datas.get('BYDAY')
if '-' in d:
new_val['byday'] = d[:2]
new_val['week_list'] = d[2:4].upper()
else:
new_val['byday'] = d[:1]
new_val['week_list'] = d[1:3].upper()
new_val['select1'] = 'day'
if datas.get('BYMONTH'):
new_val['month_list'] = datas.get('BYMONTH')
datas.pop('bymonth')
return new_val
def _get_repeat_status(self, str_google):
rrule = str_google[str_google.find('FREQ'):str_google.find('\nBEGIN')]
status = {}
for rule in rrule.split(';'):
status[rule.split('=')[0]] = rule.split('=')[-1:] and rule.split('=')[-1:][0] or ''
rules = _get_rules(self, status)
if status.get('FREQ') == 'WEEKLY':
status.update({'rrule_type': 'weekly'})
status.pop('FREQ')
elif status.get('FREQ') == 'DAILY':
status.update({'rrule_type': 'daily'})
status.pop('FREQ')
elif status.get('FREQ') == 'MONTHLY':
status.update({'rrule_type': 'monthly'})
status.pop('FREQ')
elif status.get('FREQ') == 'YEARLY':
status.update({'rrule_type': 'yearly'})
status.pop('FREQ')
status.update(rules)
return status
def _get_repeat_dates(self, x):
if len(x) > 4:
if x[3].startswith('BY'):
zone_time = x[4].split('+')[-1:][0].split(':')[0][:4]
else:
zone_time = x[3].split('+')[-1:][0].split(':')[0][:4]
else:
zone_time = x[2].split('+')[-1:][0].split(':')[0][:4]
tz_format = zone_time[:2]+':'+zone_time[2:]
repeat_start = x[1].split('\n')[0].split(':')[1]
repeat_end = x[2].split('\n')[0].split(':')[1]
o = repeat_start.split('T')
repeat_start = str(o[0][:4]) + '-' + str(o[0][4:6]) + '-' + str(o[0][6:8])
if len(o) == 2:
repeat_start += ' ' + str(o[1][:2]) + ':' + str(o[1][2:4]) + ':' + str(o[1][4:6])
else:
repeat_start += ' ' + '00' + ':' + '00' + ':' + '00'
p = repeat_end.split('T')
repeat_end = str(p[0][:4]) + '-' + str(p[0][4:6]) + '-' + str(p[0][6:8])
if len(p) == 2:
repeat_end += ' ' + str(p[1][:2]) + ':' + str(p[1][2:4]) + ':' + str(p[1][4:6])
else:
repeat_end += ' ' + '00' + ':' + '00' + ':' + '00'
return (repeat_start, repeat_end, tz_format)
class google_login(osv.osv_memory):
_inherit = 'google.login'
_name = 'google.login'
def _get_next_action(self, cr, uid, context=None):
data_obj = self.pool.get('ir.model.data')
data_id = data_obj._get_id(cr, uid, 'sync_google_calendar', 'view_synchronize_google_calendar_import_form')
view_id = False
if data_id:
view_id = data_obj.browse(cr, uid, data_id, context=context).res_id
value = {
'name': _('Import Events'),
'view_type': 'form',
'view_mode': 'form,tree',
'res_model': 'synchronize.google.calendar',
'view_id': False,
'context': context,
'views': [(view_id, 'form')],
'type': 'ir.actions.act_window',
'target': 'new',
}
return value
google_login()
class synchronize_google_calendar_events(osv.osv_memory):
_name = 'synchronize.google.calendar'
def _get_calendars(self, cr, uid, context=None):
user_obj = self.pool.get('res.users').browse(cr, uid, uid)
google = self.pool.get('google.login')
res = []
try:
gd_client = google.google_login(cr, uid, user_obj.gmail_user, user_obj.gmail_password, type='calendar')
calendars = gd_client.GetAllCalendarsFeed()
for cal in calendars.entry:
res.append((cal.id.text, cal.title.text))
except Exception, e:
raise osv.except_osv('Error !', e.args[0].get('body'))
res.append(('all', 'All Calendars'))
return res
_columns = {
'calendar_name': fields.selection(_get_calendars, "Calendar Name", size=32),
}
_defaults = {
'calendar_name': 'all',
}
def get_events(self, cr, uid, event_feed, context=None):
meeting_obj = self.pool.get('crm.meeting')
categ_obj = self.pool.get('crm.case.categ')
model_obj = self.pool.get('ir.model.data')
object_id = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'crm.meeting')])
meeting_ids = []
categ_id = categ_obj.search(cr, uid, [('name', '=', event_feed.title.text), ('object_id', '=', object_id and object_id[0]), ('user_id', '=', uid)])
if not categ_id:
categ_id.append(categ_obj.create(cr, uid, {'name': event_feed.title.text,
'object_id': object_id and object_id[0],
'user_id': uid}))
if 'tz' in context and context['tz']:
time_zone = context['tz']
else:
time_zone = tools.get_server_timezone()
au_tz = timezone(time_zone)
for feed in event_feed.entry:
google_id = feed.id.text
model_data = {
'name': google_id,
'model': 'crm.meeting',
'module': 'sync_google_calendar',
'noupdate': True,
}
vals = {
'name': feed.title.text or '(No title)',
'description': feed.content.text,
'categ_id': categ_id and categ_id[0],
}
if feed.when:
timestring, timestring_end = _get_tinydates(self, feed.when[0].start_time, feed.when[0].end_time)
else:
x = feed.recurrence.text.split(';')
repeat_status = _get_repeat_status(self, feed.recurrence.text)
repeat_start, repeat_end, zone_time = _get_repeat_dates(self, x)
timestring = time.strftime('%Y-%m-%d %H:%M:%S', time.strptime(repeat_start, "%Y-%m-%d %H:%M:%S"))
timestring_end = time.strftime('%Y-%m-%d %H:%M:%S', time.strptime(repeat_end, "%Y-%m-%d %H:%M:%S"))
if repeat_status:
repeat_status.update({'recurrency': True})
vals.update(repeat_status)
vals.update({'date': timestring, 'date_deadline': timestring_end})
data_ids = model_obj.search(cr, uid, [('model', '=', 'crm.meeting'), ('name', '=', google_id)])
if data_ids:
res_id = model_obj.browse(cr, uid, data_ids[0], context=context).res_id
meeting = meeting_obj.browse(cr, uid, res_id, context=context)
google_updated = feed.updated.text
utime = dateutil.parser.parse(google_updated)
au_dt = au_tz.normalize(utime.astimezone(au_tz))
updated_dt = datetime.datetime(*au_dt.timetuple()[:6]).strftime('%Y-%m-%d %H:%M:%S')
if meeting.write_date < updated_dt:
meeting_ids.append(res_id)
meeting_obj.write(cr, uid, [res_id], vals, context=context)
else:
res_id = meeting_obj.create(cr, uid, vals, context=context)
meeting_ids.append(res_id)
model_data.update({'res_id': res_id})
model_obj.create(cr, uid, model_data, context=context)
return meeting_ids
def import_calendar_events(self, cr, uid, ids, context=None):
obj = self.browse(cr, uid, ids, context=context)[0]
if not ids:
return {'type': 'ir.actions.act_window_close'}
user_obj = self.pool.get('res.users').browse(cr, uid, uid)
gmail_user = user_obj.gmail_user
gamil_pwd = user_obj.gmail_password
google = self.pool.get('google.login')
gd_client = google.google_login(cr, uid, gmail_user, gamil_pwd, type='calendar')
if not gmail_user or not gamil_pwd:
raise osv.except_osv(_('Error'), _("Please specify the user and password !"))
meetings = []
if obj.calendar_name != 'all':
events_query = gdata.calendar.service.CalendarEventQuery(user=urllib.unquote(obj.calendar_name.split('/')[~0]))
events_query.start_index = 1
events_query.max_results = 1000
event_feed = gd_client.GetCalendarEventFeed(events_query.ToUri())
meetings.append(self.get_events(cr, uid, event_feed, context=context))
else:
calendars = map(lambda x: x[0], [cal for cal in self._get_calendars(cr, uid, context) if cal[0] != 'all'])
for cal in calendars:
events_query = gdata.calendar.service.CalendarEventQuery(user=urllib.unquote(cal.split('/')[~0]))
events_query.start_index = 1
events_query.max_results = 1000
event_feed = gd_client.GetCalendarEventFeed(events_query.ToUri())
meetings.append(self.get_events(cr, uid, event_feed, context=context))
meeting_ids = []
for meeting in meetings:
meeting_ids += meeting
return {
'type': 'ir.actions.act_window_close',
}
synchronize_google_calendar_events()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -0,0 +1,41 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="view_synchronize_google_calendar_import_form">
<field name="name">synchronize.google.calendar.form</field>
<field name="model">synchronize.google.calendar</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Import Google Calendar Events">
<group colspan="4" col="4">
<field name="calendar_name" />
</group>
<separator string="" colspan="4"/>
<group colspan="4" col="4">
<group colspan="2" col="2"/>
<group colspan="2" col="2">
<button special="cancel" string="_Cancel" icon="gtk-cancel"/>
<button name="import_calendar_events" string="_Import Events" type="object" icon="terp-calendar"/>
</group>
</group>
</form>
</field>
</record>
<!--
Login Action
-->
<record model="ir.actions.act_window" id="act_google_login_form">
<field name="name">Import Google Calendar</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">google.login</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="google_base_account.view_google_login_form" />
</record>
<menuitem id="menu_sync_contact"
parent="crm.menu_meeting_sale"
action="act_google_login_form"
sequence="20" />
</data>
</openerp>