diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py
index 3b04713a1bb..e4059896783 100644
--- a/addons/account/__openerp__.py
+++ b/addons/account/__openerp__.py
@@ -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',
diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml
index 1e3f4d5d373..e1f383f4792 100644
--- a/addons/account/project/project_view.xml
+++ b/addons/account/project/project_view.xml
@@ -17,6 +17,7 @@
+
@@ -44,6 +45,7 @@
+
diff --git a/addons/account/report/__init__.py b/addons/account/report/__init__.py
index d525d3a331b..7c09027f3ca 100644
--- a/addons/account/report/__init__.py
+++ b/addons/account/report/__init__.py
@@ -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:
diff --git a/addons/account/report/account_treasury_report.py b/addons/account/report/account_treasury_report.py
new file mode 100644
index 00000000000..9b364a55777
--- /dev/null
+++ b/addons/account/report/account_treasury_report.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+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()
diff --git a/addons/account/report/account_treasury_report_view.xml b/addons/account/report/account_treasury_report_view.xml
new file mode 100644
index 00000000000..eceb1e5c760
--- /dev/null
+++ b/addons/account/report/account_treasury_report_view.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ account.treasury.report.tree
+ account.treasury.report
+ tree
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ account.treasury.report.graph
+ account.treasury.report
+ graph
+
+
+
+
+
+
+
+
+ account.treasury.report.search
+ account.treasury.report
+ search
+
+
+
+
+
+
+
+
+
+
+
+
+ Treasury Analysis
+ account.treasury.report
+ form
+ tree,graph
+
+
+ {'group_by':[], 'group_by_no_leaf':0}
+ From this view, have an analysis of your treasury. It sums the balance of every accounting entries made on liquidity accounts per period.
+
+
+
+
diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv
index 1ab94806b63..50e8dced7e6 100644
--- a/addons/account/security/ir.model.access.csv
+++ b/addons/account/security/ir.model.access.csv
@@ -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
+
diff --git a/addons/account_analytic_plans/account_analytic_plans.py b/addons/account_analytic_plans/account_analytic_plans.py
index b25f6994394..990f53e91ef 100644
--- a/addons/account_analytic_plans/account_analytic_plans.py
+++ b/addons/account_analytic_plans/account_analytic_plans.py
@@ -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
diff --git a/addons/base_action_rule/i18n/tr.po b/addons/base_action_rule/i18n/tr.po
new file mode 100644
index 00000000000..c7120a9b549
--- /dev/null
+++ b/addons/base_action_rule/i18n/tr.po
@@ -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 , 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: FULL NAME \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 \n"
+"Language-Team: Turkish \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"
diff --git a/addons/base_calendar/__openerp__.py b/addons/base_calendar/__openerp__.py
index 5688c0e05b5..a01cf0df8cf 100644
--- a/addons/base_calendar/__openerp__.py
+++ b/addons/base_calendar/__openerp__.py
@@ -1,11 +1,55 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
{
- "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:
diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py
index bfcccb3dfe0..b0c7fa2ca7e 100644
--- a/addons/base_calendar/base_calendar.py
+++ b/addons/base_calendar/base_calendar.py
@@ -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)
diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml
index d5525e48e74..451152b2b58 100644
--- a/addons/base_calendar/base_calendar_view.xml
+++ b/addons/base_calendar/base_calendar_view.xml
@@ -242,8 +242,7 @@
widget="selection" />
-
+
diff --git a/addons/base_calendar/test/base_calendar_test.yml b/addons/base_calendar/test/base_calendar_test.yml
index 4c0f3fc126f..5512839e635 100644
--- a/addons/base_calendar/test/base_calendar_test.yml
+++ b/addons/base_calendar/test/base_calendar_test.yml
@@ -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
diff --git a/addons/base_calendar/wizard/__init__.py b/addons/base_calendar/wizard/__init__.py
index 8b45dc18a17..cd0690b1dc0 100644
--- a/addons/base_calendar/wizard/__init__.py
+++ b/addons/base_calendar/wizard/__init__.py
@@ -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:
diff --git a/addons/base_calendar/wizard/base_calendar_set_exrule.py b/addons/base_calendar/wizard/base_calendar_set_exrule.py
deleted file mode 100644
index 80fe1e44720..00000000000
--- a/addons/base_calendar/wizard/base_calendar_set_exrule.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# 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 .
-#
-##############################################################################
-
-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:
\ No newline at end of file
diff --git a/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml b/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml
deleted file mode 100644
index 2a2d6412d4b..00000000000
--- a/addons/base_calendar/wizard/base_calendar_set_exrule_view.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
- base.calendar.set.exrule.form
- base.calendar.set.exrule
- form
-
-
-
-
-
-
- Set Exclude range
- base.calendar.set.exrule
- form
- form,tree
- new
-
-
-
-
diff --git a/addons/base_calendar/wizard/calendar_event_edit_all.py b/addons/base_calendar/wizard/calendar_event_edit_all.py
deleted file mode 100644
index 23f67f579f2..00000000000
--- a/addons/base_calendar/wizard/calendar_event_edit_all.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# 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 .
-#
-##############################################################################
-
-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:
diff --git a/addons/base_calendar/wizard/calendar_event_edit_all_view.xml b/addons/base_calendar/wizard/calendar_event_edit_all_view.xml
deleted file mode 100644
index 82f403e9254..00000000000
--- a/addons/base_calendar/wizard/calendar_event_edit_all_view.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
- calendar.event.edit.all.form
- calendar.event.edit.all
- form
-
-
-
-
-
-
- Edit all events
- calendar.event.edit.all
- form
- tree,form
-
- new
-
-
-
-
diff --git a/addons/base_vat/base_vat.py b/addons/base_vat/base_vat.py
index 69b312ee62f..6393c0a6182 100644
--- a/addons/base_vat/base_vat.py
+++ b/addons/base_vat/base_vat.py
@@ -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[A-Za-z\xd1\xf1&]{3,4})" \
+ r"[ \-_]?" \
+ r"(?P[0-9]{2})(?P[01][0-9])(?P[0-3][0-9])" \
+ r"[ \-_]?" \
+ r"(?P[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:
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 903bf5f0b68..1d0ece2dce1 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -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,
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index d7b67879a7a..19d1198c37c 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -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)
diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py
index 57ce46b52cc..6d3d68ef881 100644
--- a/addons/crm/crm_meeting.py
+++ b/addons/crm/crm_meeting.py
@@ -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)]}),
diff --git a/addons/crm/crm_meeting_menu.xml b/addons/crm/crm_meeting_menu.xml
index 61ef9942464..aabd4e8592f 100644
--- a/addons/crm/crm_meeting_menu.xml
+++ b/addons/crm/crm_meeting_menu.xml
@@ -51,7 +51,7 @@
crm.meeting
calendar,tree,form,gantt
- {"search_default_user_id":uid, 'search_default_section_id': section_id}
+ {"search_default_user_id":uid, 'search_default_section_id': section_id, 'virtual_id' : True}
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.
diff --git a/addons/crm/crm_meeting_view.xml b/addons/crm/crm_meeting_view.xml
index a2d66040fa9..9ece8cafaae 100644
--- a/addons/crm/crm_meeting_view.xml
+++ b/addons/crm/crm_meeting_view.xml
@@ -45,8 +45,7 @@
widget="selection" />
-
+
diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py
index 6a4e7bfd7c7..cce0d72012c 100644
--- a/addons/crm/crm_phonecall.py
+++ b/addons/crm/crm_phonecall.py
@@ -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'),
diff --git a/addons/crm/test/test_crm_meeting.yml b/addons/crm/test/test_crm_meeting.yml
index ae52a747bf2..9b89fff697b 100644
--- a/addons/crm/test/test_crm_meeting.yml
+++ b/addons/crm/test/test_crm_meeting.yml
@@ -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
- |
diff --git a/addons/crm/test/test_crm_recurrent_meeting_case2.yml b/addons/crm/test/test_crm_recurrent_meeting_case2.yml
new file mode 100644
index 00000000000..585c6af73ae
--- /dev/null
+++ b/addons/crm/test/test_crm_recurrent_meeting_case2.yml
@@ -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 !'
+
+
\ No newline at end of file
diff --git a/addons/crm/wizard/crm_merge_opportunities.py b/addons/crm/wizard/crm_merge_opportunities.py
index 740bc7691c3..adbdcfb5940 100644
--- a/addons/crm/wizard/crm_merge_opportunities.py
+++ b/addons/crm/wizard/crm_merge_opportunities.py
@@ -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
diff --git a/addons/google_base_account/wizard/google_login.py b/addons/google_base_account/wizard/google_login.py
index f71fd4e5962..d32e2701569 100644
--- a/addons/google_base_account/wizard/google_login.py
+++ b/addons/google_base_account/wizard/google_login.py
@@ -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):
diff --git a/addons/hr/hr.py b/addons/hr/hr.py
index 59e4129fec7..8a49fd876ba 100644
--- a/addons/hr/hr.py
+++ b/addons/hr/hr.py
@@ -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'),
diff --git a/addons/procurement/security/procurement_security.xml b/addons/procurement/security/procurement_security.xml
index 2e250db461c..b6b15d565c6 100644
--- a/addons/procurement/security/procurement_security.xml
+++ b/addons/procurement/security/procurement_security.xml
@@ -6,14 +6,14 @@
procurement multi-company
- ['|','|',('company_id','child_of',[user.company_id.id]),('company_id','=',False),('company_id.child_ids','child_of',[user.company_id.id])]
+ ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]
stock_warehouse.orderpoint multi-company
- ['|','|',('company_id','child_of',[user.company_id.id]),('company_id','=',False),('company_id.child_ids','child_of',[user.company_id.id])]
+ ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]
diff --git a/addons/product/product.py b/addons/product/product.py
index 38ee641507c..fa6796b3953 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -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:
diff --git a/addons/sync_google_calendar/__init__.py b/addons/sync_google_calendar/__init__.py
new file mode 100644
index 00000000000..0827cfd7278
--- /dev/null
+++ b/addons/sync_google_calendar/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+import sync_google_calendar
+import wizard
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
diff --git a/addons/sync_google_calendar/__openerp__.py b/addons/sync_google_calendar/__openerp__.py
new file mode 100644
index 00000000000..33509f599dd
--- /dev/null
+++ b/addons/sync_google_calendar/__openerp__.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+
+{
+ '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:
diff --git a/addons/sync_google_calendar/sync_google_calendar.py b/addons/sync_google_calendar/sync_google_calendar.py
new file mode 100644
index 00000000000..f61ac409fe6
--- /dev/null
+++ b/addons/sync_google_calendar/sync_google_calendar.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+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:
diff --git a/addons/sync_google_calendar/sync_google_calendar_view.xml b/addons/sync_google_calendar/sync_google_calendar_view.xml
new file mode 100644
index 00000000000..58e1e55efd0
--- /dev/null
+++ b/addons/sync_google_calendar/sync_google_calendar_view.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ CRM - Meetings Form (Inherited)
+ crm.meeting
+ form
+
+
+
+
+
+
+
+
+
+ CRM - Meetings Tree (Inherited)
+ crm.meeting
+ tree
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/sync_google_calendar/test/test_sync_google_calendar.yml b/addons/sync_google_calendar/test/test_sync_google_calendar.yml
new file mode 100644
index 00000000000..eb3ed03fcfc
--- /dev/null
+++ b/addons/sync_google_calendar/test/test_sync_google_calendar.yml
@@ -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 !'
+
+
\ No newline at end of file
diff --git a/addons/sync_google_calendar/wizard/__init__.py b/addons/sync_google_calendar/wizard/__init__.py
new file mode 100644
index 00000000000..a781e38c1af
--- /dev/null
+++ b/addons/sync_google_calendar/wizard/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+import wizard_import_calendar_events
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/sync_google_calendar/wizard/wizard_import_calendar_events.py b/addons/sync_google_calendar/wizard/wizard_import_calendar_events.py
new file mode 100644
index 00000000000..df3804cc60b
--- /dev/null
+++ b/addons/sync_google_calendar/wizard/wizard_import_calendar_events.py
@@ -0,0 +1,304 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+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:
diff --git a/addons/sync_google_calendar/wizard/wizard_import_calendar_events_view.xml b/addons/sync_google_calendar/wizard/wizard_import_calendar_events_view.xml
new file mode 100644
index 00000000000..f50cfda5d3b
--- /dev/null
+++ b/addons/sync_google_calendar/wizard/wizard_import_calendar_events_view.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ synchronize.google.calendar.form
+ synchronize.google.calendar
+ form
+
+
+
+
+
+
+ Import Google Calendar
+ ir.actions.act_window
+ google.login
+ form
+ form
+ new
+
+
+
+
+