[MERGE] from trunk
bzr revid: fva@openerp.com-20140224172822-ehn3xeb7rb1xro07
This commit is contained in:
commit
0368f23b1e
|
@ -114,7 +114,7 @@ class account_invoice(osv.osv):
|
||||||
#we check if the invoice is partially reconciled and if there are other invoices
|
#we check if the invoice is partially reconciled and if there are other invoices
|
||||||
#involved in this partial reconciliation (and we sum these invoices)
|
#involved in this partial reconciliation (and we sum these invoices)
|
||||||
for line in aml.reconcile_partial_id.line_partial_ids:
|
for line in aml.reconcile_partial_id.line_partial_ids:
|
||||||
if line.invoice:
|
if line.invoice and invoice.type == line.invoice.type:
|
||||||
nb_inv_in_partial_rec += 1
|
nb_inv_in_partial_rec += 1
|
||||||
#store the max invoice id as for this invoice we will make a balance instead of a simple division
|
#store the max invoice id as for this invoice we will make a balance instead of a simple division
|
||||||
max_invoice_id = max(max_invoice_id, line.invoice.id)
|
max_invoice_id = max(max_invoice_id, line.invoice.id)
|
||||||
|
|
|
@ -1451,7 +1451,7 @@
|
||||||
<act_window
|
<act_window
|
||||||
id="act_account_move_to_account_move_line_open"
|
id="act_account_move_to_account_move_line_open"
|
||||||
name="Journal Items"
|
name="Journal Items"
|
||||||
context="{'search_default_journal_id': active_id, 'default_journal_id': active_id}"
|
context="{'search_default_move_id': active_id, 'default_move_id': active_id}"
|
||||||
res_model="account.move.line"
|
res_model="account.move.line"
|
||||||
src_model="account.move"/>
|
src_model="account.move"/>
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,6 +22,7 @@ openerp.account.quickadd = function (instance) {
|
||||||
start:function(){
|
start:function(){
|
||||||
var tmp = this._super.apply(this, arguments);
|
var tmp = this._super.apply(this, arguments);
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var defs = [];
|
||||||
this.$el.parent().prepend(QWeb.render("AccountMoveLineQuickAdd", {widget: this}));
|
this.$el.parent().prepend(QWeb.render("AccountMoveLineQuickAdd", {widget: this}));
|
||||||
|
|
||||||
this.$el.parent().find('.oe_account_select_journal').change(function() {
|
this.$el.parent().find('.oe_account_select_journal').change(function() {
|
||||||
|
@ -41,11 +42,17 @@ openerp.account.quickadd = function (instance) {
|
||||||
self.$el.parent().find('.oe_account_select_period').removeAttr('disabled');
|
self.$el.parent().find('.oe_account_select_period').removeAttr('disabled');
|
||||||
});
|
});
|
||||||
var mod = new instance.web.Model("account.move.line", self.dataset.context, self.dataset.domain);
|
var mod = new instance.web.Model("account.move.line", self.dataset.context, self.dataset.domain);
|
||||||
mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) {
|
defs.push(mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) {
|
||||||
self.current_period = result['period_id'];
|
self.current_period = result['period_id'];
|
||||||
self.current_journal = result['journal_id'];
|
self.current_journal = result['journal_id'];
|
||||||
});
|
}));
|
||||||
return tmp;
|
defs.push(mod.call("list_journals", []).then(function(result) {
|
||||||
|
self.journals = result;
|
||||||
|
}));
|
||||||
|
defs.push(mod.call("list_periods", []).then(function(result) {
|
||||||
|
self.periods = result;
|
||||||
|
}));
|
||||||
|
return $.when(tmp, defs);
|
||||||
},
|
},
|
||||||
do_search: function(domain, context, group_by) {
|
do_search: function(domain, context, group_by) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -53,34 +60,27 @@ openerp.account.quickadd = function (instance) {
|
||||||
this.last_context = context;
|
this.last_context = context;
|
||||||
this.last_group_by = group_by;
|
this.last_group_by = group_by;
|
||||||
this.old_search = _.bind(this._super, this);
|
this.old_search = _.bind(this._super, this);
|
||||||
var mod = new instance.web.Model("account.move.line", context, domain);
|
var o;
|
||||||
return $.when(mod.call("list_journals", []).then(function(result) {
|
self.$el.parent().find('.oe_account_select_journal').children().remove().end();
|
||||||
self.journals = result;
|
self.$el.parent().find('.oe_account_select_journal').append(new Option('', ''));
|
||||||
}),mod.call("list_periods", []).then(function(result) {
|
for (var i = 0;i < self.journals.length;i++){
|
||||||
self.periods = result;
|
o = new Option(self.journals[i][1], self.journals[i][0]);
|
||||||
})).then(function () {
|
if (self.journals[i][0] === self.current_journal){
|
||||||
var o;
|
self.current_journal_type = self.journals[i][2];
|
||||||
self.$el.parent().find('.oe_account_select_journal').children().remove().end();
|
self.current_journal_currency = self.journals[i][3];
|
||||||
self.$el.parent().find('.oe_account_select_journal').append(new Option('', ''));
|
self.current_journal_analytic = self.journals[i][4];
|
||||||
for (var i = 0;i < self.journals.length;i++){
|
$(o).attr('selected',true);
|
||||||
o = new Option(self.journals[i][1], self.journals[i][0]);
|
|
||||||
if (self.journals[i][0] === self.current_journal){
|
|
||||||
self.current_journal_type = self.journals[i][2];
|
|
||||||
self.current_journal_currency = self.journals[i][3];
|
|
||||||
self.current_journal_analytic = self.journals[i][4];
|
|
||||||
$(o).attr('selected',true);
|
|
||||||
}
|
|
||||||
self.$el.parent().find('.oe_account_select_journal').append(o);
|
|
||||||
}
|
}
|
||||||
self.$el.parent().find('.oe_account_select_period').children().remove().end();
|
self.$el.parent().find('.oe_account_select_journal').append(o);
|
||||||
self.$el.parent().find('.oe_account_select_period').append(new Option('', ''));
|
}
|
||||||
for (var i = 0;i < self.periods.length;i++){
|
self.$el.parent().find('.oe_account_select_period').children().remove().end();
|
||||||
o = new Option(self.periods[i][1], self.periods[i][0]);
|
self.$el.parent().find('.oe_account_select_period').append(new Option('', ''));
|
||||||
self.$el.parent().find('.oe_account_select_period').append(o);
|
for (var i = 0;i < self.periods.length;i++){
|
||||||
}
|
o = new Option(self.periods[i][1], self.periods[i][0]);
|
||||||
self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true);
|
self.$el.parent().find('.oe_account_select_period').append(o);
|
||||||
return self.search_by_journal_period();
|
}
|
||||||
});
|
self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true);
|
||||||
|
return self.search_by_journal_period();
|
||||||
},
|
},
|
||||||
search_by_journal_period: function() {
|
search_by_journal_period: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -93,7 +93,9 @@ openerp.account.quickadd = function (instance) {
|
||||||
self.last_context["journal_type"] = self.current_journal_type;
|
self.last_context["journal_type"] = self.current_journal_type;
|
||||||
self.last_context["currency"] = self.current_journal_currency;
|
self.last_context["currency"] = self.current_journal_currency;
|
||||||
self.last_context["analytic_journal_id"] = self.current_journal_analytic;
|
self.last_context["analytic_journal_id"] = self.current_journal_analytic;
|
||||||
return self.old_search(new instance.web.CompoundDomain(self.last_domain, domain), self.last_context, self.last_group_by);
|
var compound_domain = new instance.web.CompoundDomain(self.last_domain, domain);
|
||||||
|
self.dataset.domain = compound_domain.eval();
|
||||||
|
return self.old_search(compound_domain, self.last_context, self.last_group_by);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -664,13 +664,20 @@ class account_analytic_account(osv.osv):
|
||||||
|
|
||||||
partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False
|
partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False
|
||||||
|
|
||||||
|
currency_id = False
|
||||||
|
if contract.pricelist_id:
|
||||||
|
currency_id = contract.pricelist_id.currency_id.id
|
||||||
|
elif contract.partner_id.property_product_pricelist:
|
||||||
|
currency_id = contract.partner_id.property_product_pricelist.currency_id.id
|
||||||
|
elif contract.company_id:
|
||||||
|
currency_id = contract.company_id.currency_id.id
|
||||||
|
|
||||||
inv_data = {
|
inv_data = {
|
||||||
'reference': contract.code or False,
|
'reference': contract.code or False,
|
||||||
'account_id': contract.partner_id.property_account_receivable.id,
|
'account_id': contract.partner_id.property_account_receivable.id,
|
||||||
'type': 'out_invoice',
|
'type': 'out_invoice',
|
||||||
'partner_id': contract.partner_id.id,
|
'partner_id': contract.partner_id.id,
|
||||||
'currency_id': contract.partner_id.property_product_pricelist.id or False,
|
'currency_id': currency_id,
|
||||||
'journal_id': len(journal_ids) and journal_ids[0] or False,
|
'journal_id': len(journal_ids) and journal_ids[0] or False,
|
||||||
'date_invoice': contract.recurring_next_date,
|
'date_invoice': contract.recurring_next_date,
|
||||||
'origin': contract.name,
|
'origin': contract.name,
|
||||||
|
|
|
@ -8,19 +8,19 @@ msgstr ""
|
||||||
"Project-Id-Version: openobject-addons\n"
|
"Project-Id-Version: openobject-addons\n"
|
||||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||||
"PO-Revision-Date: 2010-09-29 11:28+0000\n"
|
"PO-Revision-Date: 2014-02-18 16:35+0000\n"
|
||||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
"Last-Translator: Boško Stojaković <bluesoft83@gmail.com>\n"
|
||||||
"Language-Team: Bosnian <bs@li.org>\n"
|
"Language-Team: Bosnian <bs@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2014-01-28 06:28+0000\n"
|
"X-Launchpad-Export-Date: 2014-02-19 05:23+0000\n"
|
||||||
"X-Generator: Launchpad (build 16914)\n"
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
#. module: account_cancel
|
#. module: account_cancel
|
||||||
#: view:account.invoice:0
|
#: view:account.invoice:0
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr "Otkaži"
|
||||||
|
|
||||||
#~ msgid "Account Cancel"
|
#~ msgid "Account Cancel"
|
||||||
#~ msgstr "Računovodstvo - povrat"
|
#~ msgstr "Računovodstvo - povrat"
|
||||||
|
|
|
@ -911,9 +911,10 @@ class account_voucher(osv.osv):
|
||||||
if context.get('payment_expected_currency') and currency_id != context.get('payment_expected_currency'):
|
if context.get('payment_expected_currency') and currency_id != context.get('payment_expected_currency'):
|
||||||
vals['value']['amount'] = 0
|
vals['value']['amount'] = 0
|
||||||
amount = 0
|
amount = 0
|
||||||
res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context)
|
if partner_id:
|
||||||
for key in res.keys():
|
res = self.onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context)
|
||||||
vals[key].update(res[key])
|
for key in res.keys():
|
||||||
|
vals[key].update(res[key])
|
||||||
return vals
|
return vals
|
||||||
|
|
||||||
def button_proforma_voucher(self, cr, uid, ids, context=None):
|
def button_proforma_voucher(self, cr, uid, ids, context=None):
|
||||||
|
@ -965,7 +966,7 @@ class account_voucher(osv.osv):
|
||||||
res = {}
|
res = {}
|
||||||
if not partner_id:
|
if not partner_id:
|
||||||
return res
|
return res
|
||||||
res = {'account_id':False}
|
res = {}
|
||||||
partner_pool = self.pool.get('res.partner')
|
partner_pool = self.pool.get('res.partner')
|
||||||
journal_pool = self.pool.get('account.journal')
|
journal_pool = self.pool.get('account.journal')
|
||||||
if pay_now == 'pay_later':
|
if pay_now == 'pay_later':
|
||||||
|
@ -977,7 +978,8 @@ class account_voucher(osv.osv):
|
||||||
account_id = partner.property_account_payable.id
|
account_id = partner.property_account_payable.id
|
||||||
else:
|
else:
|
||||||
account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
|
account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id
|
||||||
res['account_id'] = account_id
|
if account_id:
|
||||||
|
res['account_id'] = account_id
|
||||||
return {'value':res}
|
return {'value':res}
|
||||||
|
|
||||||
def _sel_context(self, cr, uid, voucher_id, context=None):
|
def _sel_context(self, cr, uid, voucher_id, context=None):
|
||||||
|
@ -1366,6 +1368,7 @@ class account_voucher(osv.osv):
|
||||||
move_pool = self.pool.get('account.move')
|
move_pool = self.pool.get('account.move')
|
||||||
move_line_pool = self.pool.get('account.move.line')
|
move_line_pool = self.pool.get('account.move.line')
|
||||||
for voucher in self.browse(cr, uid, ids, context=context):
|
for voucher in self.browse(cr, uid, ids, context=context):
|
||||||
|
local_context = dict(context, force_company=voucher.journal_id.company_id.id)
|
||||||
if voucher.move_id:
|
if voucher.move_id:
|
||||||
continue
|
continue
|
||||||
company_currency = self._get_company_currency(cr, uid, voucher.id, context)
|
company_currency = self._get_company_currency(cr, uid, voucher.id, context)
|
||||||
|
@ -1380,7 +1383,7 @@ class account_voucher(osv.osv):
|
||||||
# Get the name of the account_move just created
|
# Get the name of the account_move just created
|
||||||
name = move_pool.browse(cr, uid, move_id, context=context).name
|
name = move_pool.browse(cr, uid, move_id, context=context).name
|
||||||
# Create the first line of the voucher
|
# Create the first line of the voucher
|
||||||
move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, context), context)
|
move_line_id = move_line_pool.create(cr, uid, self.first_move_line_get(cr,uid,voucher.id, move_id, company_currency, current_currency, local_context), local_context)
|
||||||
move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context)
|
move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context)
|
||||||
line_total = move_line_brw.debit - move_line_brw.credit
|
line_total = move_line_brw.debit - move_line_brw.credit
|
||||||
rec_list_ids = []
|
rec_list_ids = []
|
||||||
|
@ -1392,9 +1395,9 @@ class account_voucher(osv.osv):
|
||||||
line_total, rec_list_ids = self.voucher_move_line_create(cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context)
|
line_total, rec_list_ids = self.voucher_move_line_create(cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context)
|
||||||
|
|
||||||
# Create the writeoff line if needed
|
# Create the writeoff line if needed
|
||||||
ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, context)
|
ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, local_context)
|
||||||
if ml_writeoff:
|
if ml_writeoff:
|
||||||
move_line_pool.create(cr, uid, ml_writeoff, context)
|
move_line_pool.create(cr, uid, ml_writeoff, local_context)
|
||||||
# We post the voucher.
|
# We post the voucher.
|
||||||
self.write(cr, uid, [voucher.id], {
|
self.write(cr, uid, [voucher.id], {
|
||||||
'move_id': move_id,
|
'move_id': move_id,
|
||||||
|
@ -1605,7 +1608,11 @@ class account_bank_statement(osv.osv):
|
||||||
bank_st_line_obj = self.pool.get('account.bank.statement.line')
|
bank_st_line_obj = self.pool.get('account.bank.statement.line')
|
||||||
st_line = bank_st_line_obj.browse(cr, uid, st_line_id, context=context)
|
st_line = bank_st_line_obj.browse(cr, uid, st_line_id, context=context)
|
||||||
if st_line.voucher_id:
|
if st_line.voucher_id:
|
||||||
voucher_obj.write(cr, uid, [st_line.voucher_id.id], {'number': next_number}, context=context)
|
voucher_obj.write(cr, uid, [st_line.voucher_id.id],
|
||||||
|
{'number': next_number,
|
||||||
|
'date': st_line.date,
|
||||||
|
'period_id': st_line.statement_id.period_id.id},
|
||||||
|
context=context)
|
||||||
if st_line.voucher_id.state == 'cancel':
|
if st_line.voucher_id.state == 'cancel':
|
||||||
voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context)
|
voucher_obj.action_cancel_draft(cr, uid, [st_line.voucher_id.id], context=context)
|
||||||
voucher_obj.signal_proforma_voucher(cr, uid, [st_line.voucher_id.id])
|
voucher_obj.signal_proforma_voucher(cr, uid, [st_line.voucher_id.id])
|
||||||
|
|
|
@ -73,8 +73,14 @@
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="type" invisible="True"/>
|
<field name="type" invisible="True"/>
|
||||||
<field name="partner_id" domain="[('customer','=',True)]" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" string="Customer" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
|
||||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||||
|
<field name="partner_id" domain="[('customer','=',True)]" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" string="Customer" context="{'search_default_customer':1, 'show_address': 1}" options='{"always_reload": True}'/>
|
||||||
|
<field name="account_id"
|
||||||
|
domain="[('type','in', ['liquidity', 'receivable'])]"/>
|
||||||
|
<field name="pay_now" on_change="onchange_payment(pay_now, journal_id, partner_id)" required="1"/>
|
||||||
|
<field name="date_due" attrs="{'invisible':[('pay_now','=','pay_now')]}"/>
|
||||||
|
<field name="reference"
|
||||||
|
attrs="{'invisible':[('pay_now','!=','pay_now')]}"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="journal_id" domain="[('type','in',['sale','sale_refund'])]" widget="selection" on_change="onchange_journal(journal_id, line_cr_ids, tax_id, partner_id, date, amount, type, company_id, context)" groups="account.group_account_user"/>
|
<field name="journal_id" domain="[('type','in',['sale','sale_refund'])]" widget="selection" on_change="onchange_journal(journal_id, line_cr_ids, tax_id, partner_id, date, amount, type, company_id, context)" groups="account.group_account_user"/>
|
||||||
|
@ -112,15 +118,6 @@
|
||||||
</div>
|
</div>
|
||||||
<field name="amount" class="oe_subtotal_footer_separator" nolabel="1"/>
|
<field name="amount" class="oe_subtotal_footer_separator" nolabel="1"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
|
||||||
<field name="pay_now" on_change="onchange_payment(pay_now, journal_id, partner_id)" required="1"/>
|
|
||||||
<field name="date_due" attrs="{'invisible':[('pay_now','=','pay_now')]}"/>
|
|
||||||
<field name="account_id"
|
|
||||||
attrs="{'invisible':[('pay_now','!=','pay_now')]}"
|
|
||||||
domain="[('type','=','liquidity')]"/>
|
|
||||||
<field name="reference"
|
|
||||||
attrs="{'invisible':[('pay_now','!=','pay_now')]}"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="Journal Items" attrs="{'invisible': [('state','!=','posted')]}">
|
<page string="Journal Items" attrs="{'invisible': [('state','!=','posted')]}">
|
||||||
|
@ -224,11 +221,11 @@
|
||||||
<h1><label for="number" string="Purchase Receipt"/> <field name="number" class="oe_inline" readonly="1"/></h1>
|
<h1><label for="number" string="Purchase Receipt"/> <field name="number" class="oe_inline" readonly="1"/></h1>
|
||||||
|
|
||||||
<field name="pay_now" invisible="1"/>
|
<field name="pay_now" invisible="1"/>
|
||||||
<field name="account_id" domain="[('type','=','other')]" invisible="True"/>
|
|
||||||
<field name="type" invisible="True"/>
|
<field name="type" invisible="True"/>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="partner_id" domain="[('supplier','=',True)]" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" />
|
<field name="partner_id" domain="[('supplier','=',True)]" string="Supplier" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1}" />
|
||||||
|
<field name="account_id" domain="[('type', 'in', ['liquidity', 'payable'])]"/>
|
||||||
<field name="name" colspan="2"/>
|
<field name="name" colspan="2"/>
|
||||||
<field name="reference"/>
|
<field name="reference"/>
|
||||||
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
|
||||||
|
|
|
@ -94,7 +94,7 @@ class account_statement_from_invoice_lines(osv.osv_memory):
|
||||||
'account_id': result['value'].get('account_id', statement.journal_id.default_credit_account_id.id),
|
'account_id': result['value'].get('account_id', statement.journal_id.default_credit_account_id.id),
|
||||||
'company_id': statement.company_id.id,
|
'company_id': statement.company_id.id,
|
||||||
'currency_id': statement.currency.id,
|
'currency_id': statement.currency.id,
|
||||||
'date': line.date,
|
'date': statement.date,
|
||||||
'amount': sign*amount,
|
'amount': sign*amount,
|
||||||
'payment_rate': result['value']['payment_rate'],
|
'payment_rate': result['value']['payment_rate'],
|
||||||
'payment_rate_currency_id': result['value']['payment_rate_currency_id'],
|
'payment_rate_currency_id': result['value']['payment_rate_currency_id'],
|
||||||
|
@ -119,7 +119,7 @@ class account_statement_from_invoice_lines(osv.osv_memory):
|
||||||
'statement_id': statement_id,
|
'statement_id': statement_id,
|
||||||
'ref': line.ref,
|
'ref': line.ref,
|
||||||
'voucher_id': voucher_id,
|
'voucher_id': voucher_id,
|
||||||
'date': time.strftime('%Y-%m-%d'),
|
'date': statement.date,
|
||||||
}, context=context)
|
}, context=context)
|
||||||
return {'type': 'ir.actions.act_window_close'}
|
return {'type': 'ir.actions.act_window_close'}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Japanese translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-21 02:29+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Japanese <ja@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-22 06:39+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16926)\n"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: view:account.analytic.account:0
|
||||||
|
msgid "or view"
|
||||||
|
msgstr "または参照"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: view:account.analytic.account:0
|
||||||
|
msgid "Nothing to invoice, create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: view:account.analytic.account:0
|
||||||
|
msgid "expenses"
|
||||||
|
msgstr "経費"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: model:ir.model,name:analytic_contract_hr_expense.model_account_analytic_account
|
||||||
|
msgid "Analytic Account"
|
||||||
|
msgstr "分析勘定"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: code:addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py:144
|
||||||
|
#, python-format
|
||||||
|
msgid "Expenses to Invoice of %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: code:addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py:136
|
||||||
|
#, python-format
|
||||||
|
msgid "Expenses of %s"
|
||||||
|
msgstr "%sの経費"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: field:account.analytic.account,expense_invoiced:0
|
||||||
|
#: field:account.analytic.account,expense_to_invoice:0
|
||||||
|
#: field:account.analytic.account,remaining_expense:0
|
||||||
|
msgid "unknown"
|
||||||
|
msgstr "不明"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: field:account.analytic.account,est_expenses:0
|
||||||
|
msgid "Estimation of Expenses to Invoice"
|
||||||
|
msgstr "請求対象経費見込"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: field:account.analytic.account,charge_expenses:0
|
||||||
|
msgid "Charge Expenses"
|
||||||
|
msgstr "経費請求"
|
||||||
|
|
||||||
|
#. module: analytic_contract_hr_expense
|
||||||
|
#: view:account.analytic.account:0
|
||||||
|
msgid "⇒ Invoice"
|
||||||
|
msgstr "⇒ 請求"
|
|
@ -268,10 +268,14 @@ def log_fct(cr, uid_orig, model, method, fct_src, *args, **kw):
|
||||||
new_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
new_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
||||||
elif method == 'read':
|
elif method == 'read':
|
||||||
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
||||||
|
if isinstance(res, dict):
|
||||||
|
records = [res]
|
||||||
|
else:
|
||||||
|
records = res
|
||||||
# build the res_ids and the old_values dict. Here we don't use get_data() to
|
# build the res_ids and the old_values dict. Here we don't use get_data() to
|
||||||
# avoid performing an additional read()
|
# avoid performing an additional read()
|
||||||
res_ids = []
|
res_ids = []
|
||||||
for record in res:
|
for record in records:
|
||||||
res_ids.append(record['id'])
|
res_ids.append(record['id'])
|
||||||
old_values[(model.id, record['id'])] = {'value': record, 'text': record}
|
old_values[(model.id, record['id'])] = {'value': record, 'text': record}
|
||||||
# log only the fields read
|
# log only the fields read
|
||||||
|
@ -279,7 +283,9 @@ def log_fct(cr, uid_orig, model, method, fct_src, *args, **kw):
|
||||||
elif method == 'unlink':
|
elif method == 'unlink':
|
||||||
res_ids = args[0]
|
res_ids = args[0]
|
||||||
old_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
old_values = get_data(cr, uid_orig, pool, res_ids, model, method)
|
||||||
res = fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
# process_data first as fct_src will unlink the record
|
||||||
|
self.process_data(cr, uid_orig, pool, res_ids, model, method, old_values, new_values, field_list)
|
||||||
|
return fct_src(cr, uid_orig, model.model, method, *args, **kw)
|
||||||
else: # method is write, action or workflow action
|
else: # method is write, action or workflow action
|
||||||
res_ids = []
|
res_ids = []
|
||||||
if args:
|
if args:
|
||||||
|
@ -322,7 +328,7 @@ def get_data(cr, uid, pool, res_ids, model, method):
|
||||||
data = {}
|
data = {}
|
||||||
resource_pool = pool[model.model]
|
resource_pool = pool[model.model]
|
||||||
# read all the fields of the given resources in super admin mode
|
# read all the fields of the given resources in super admin mode
|
||||||
for resource in resource_pool.read(cr, SUPERUSER_ID, res_ids):
|
for resource in resource_pool.read(cr, SUPERUSER_ID, res_ids, resource_pool._all_columns):
|
||||||
values = {}
|
values = {}
|
||||||
values_text = {}
|
values_text = {}
|
||||||
resource_id = resource['id']
|
resource_id = resource['id']
|
||||||
|
@ -456,7 +462,9 @@ def process_data(cr, uid, pool, res_ids, model, method, old_values=None, new_val
|
||||||
|
|
||||||
# if at least one modification has been found
|
# if at least one modification has been found
|
||||||
for model_id, resource_id in lines:
|
for model_id, resource_id in lines:
|
||||||
name = pool[model.model].name_get(cr, uid, [resource_id])[0][1]
|
line_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, model_id).model
|
||||||
|
name = pool.get(line_model).name_get(cr, uid, [resource_id])[0][1]
|
||||||
|
|
||||||
vals = {
|
vals = {
|
||||||
'method': method,
|
'method': method,
|
||||||
'object_id': model_id,
|
'object_id': model_id,
|
||||||
|
|
|
@ -25,7 +25,7 @@ def fragment_to_query_string(func):
|
||||||
return """<html><head><script>
|
return """<html><head><script>
|
||||||
var l = window.location;
|
var l = window.location;
|
||||||
var q = l.hash.substring(1);
|
var q = l.hash.substring(1);
|
||||||
var r = '/' + l.search;
|
var r = l.pathname + l.search;
|
||||||
if(q.length !== 0) {
|
if(q.length !== 0) {
|
||||||
var s = l.search ? (l.search === '?' ? '' : '&') : '?';
|
var s = l.search ? (l.search === '?' ? '' : '&') : '?';
|
||||||
r = l.pathname + l.search + s + q;
|
r = l.pathname + l.search + s + q;
|
||||||
|
|
|
@ -36,7 +36,10 @@ class AuthSignup(openerp.addons.web.controllers.main.Home):
|
||||||
def web_login(self, *args, **kw):
|
def web_login(self, *args, **kw):
|
||||||
mode = request.params.get('mode')
|
mode = request.params.get('mode')
|
||||||
qcontext = request.params.copy()
|
qcontext = request.params.copy()
|
||||||
super_response = super(AuthSignup, self).web_login(*args, **kw)
|
super_response = None
|
||||||
|
if request.httprequest.method != 'POST' or mode not in ('reset', 'signup'):
|
||||||
|
# Default behavior is to try to login, which in reset or signup mode in a non-sense.
|
||||||
|
super_response = super(AuthSignup, self).web_login(*args, **kw)
|
||||||
response = webmain.render_bootstrap_template(request.session.db, 'auth_signup.signup', qcontext, lazy=True)
|
response = webmain.render_bootstrap_template(request.session.db, 'auth_signup.signup', qcontext, lazy=True)
|
||||||
if isinstance(super_response, LazyResponse):
|
if isinstance(super_response, LazyResponse):
|
||||||
response.params['values'].update(super_response.params['values'])
|
response.params['values'].update(super_response.params['values'])
|
||||||
|
@ -62,7 +65,7 @@ class AuthSignup(openerp.addons.web.controllers.main.Home):
|
||||||
}
|
}
|
||||||
qcontext.update(config)
|
qcontext.update(config)
|
||||||
|
|
||||||
if 'error' in qcontext or mode not in ('reset', 'signup') or (not token and not config[mode]):
|
if 'error' in request.params or mode not in ('reset', 'signup') or (not token and not config[mode]):
|
||||||
if isinstance(super_response, LazyResponse):
|
if isinstance(super_response, LazyResponse):
|
||||||
super_response.params['values'].update(config)
|
super_response.params['values'].update(config)
|
||||||
return super_response
|
return super_response
|
||||||
|
@ -78,22 +81,28 @@ class AuthSignup(openerp.addons.web.controllers.main.Home):
|
||||||
res_users.reset_password(request.cr, openerp.SUPERUSER_ID, login)
|
res_users.reset_password(request.cr, openerp.SUPERUSER_ID, login)
|
||||||
qcontext['message'] = _("An email has been sent with credentials to reset your password")
|
qcontext['message'] = _("An email has been sent with credentials to reset your password")
|
||||||
response.params['template'] = 'web.login'
|
response.params['template'] = 'web.login'
|
||||||
except Exception:
|
except Exception, e:
|
||||||
qcontext['error'] = _("Could not reset your password")
|
qcontext['error'] = exception_to_unicode(e) or _("Could not reset your password")
|
||||||
_logger.exception('error when resetting password')
|
_logger.exception('error when resetting password')
|
||||||
else:
|
else:
|
||||||
values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password'))
|
values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password'))
|
||||||
try:
|
try:
|
||||||
self._signup_with_values(token, values)
|
self._signup_with_values(token, values)
|
||||||
request.cr.commit()
|
redirect = request.params.get('redirect')
|
||||||
|
if not redirect:
|
||||||
|
redirect = '/web?' + request.httprequest.query_string
|
||||||
|
return http.redirect_with_hash(redirect)
|
||||||
except SignupError, e:
|
except SignupError, e:
|
||||||
qcontext['error'] = exception_to_unicode(e)
|
qcontext['error'] = exception_to_unicode(e)
|
||||||
return super(AuthSignup, self).web_login(*args, **kw)
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _signup_with_values(self, token, values):
|
def _signup_with_values(self, token, values):
|
||||||
request.registry['res.users'].signup(request.cr, openerp.SUPERUSER_ID, values, token)
|
db, login, password = request.registry['res.users'].signup(request.cr, openerp.SUPERUSER_ID, values, token)
|
||||||
|
request.cr.commit() # as authenticate will use its own cursor we need to commit the current transaction
|
||||||
|
uid = request.session.authenticate(db, login, password)
|
||||||
|
if not uid:
|
||||||
|
raise SignupError(_('Authentification Failed.'))
|
||||||
|
|
||||||
|
|
||||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -25,7 +25,7 @@ from urlparse import urljoin
|
||||||
|
|
||||||
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException
|
from openerp.addons.base.ir.ir_mail_server import MailDeliveryException
|
||||||
from openerp.osv import osv, fields
|
from openerp.osv import osv, fields
|
||||||
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
|
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, ustr
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
|
|
||||||
|
@ -236,7 +236,12 @@ class res_users(osv.Model):
|
||||||
# create a copy of the template user (attached to a specific partner_id if given)
|
# create a copy of the template user (attached to a specific partner_id if given)
|
||||||
values['active'] = True
|
values['active'] = True
|
||||||
context = dict(context or {}, no_reset_password=True)
|
context = dict(context or {}, no_reset_password=True)
|
||||||
return self.copy(cr, uid, template_user_id, values, context=context)
|
try:
|
||||||
|
with cr.savepoint():
|
||||||
|
return self.copy(cr, uid, template_user_id, values, context=context)
|
||||||
|
except Exception, e:
|
||||||
|
# copy may failed if asked login is not available.
|
||||||
|
raise SignupError(ustr(e))
|
||||||
|
|
||||||
def reset_password(self, cr, uid, login, context=None):
|
def reset_password(self, cr, uid, login, context=None):
|
||||||
""" retrieve the user corresponding to login (login or email),
|
""" retrieve the user corresponding to login (login or email),
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
<div class="form-group field-login">
|
<div class="form-group field-login">
|
||||||
<label for="login" class="control-label">Your Email</label>
|
<label for="login" class="control-label">Your Email</label>
|
||||||
<input type="text" name="login" t-att-value="login" id="login" class="form-control"
|
<input type="email" name="login" t-att-value="login" id="login" class="form-control"
|
||||||
t-att-autofocus="'autofocus' if reset_without_token else None"
|
t-att-autofocus="'autofocus' if reset_without_token else None"
|
||||||
required="required" t-att-disabled="'disabled' if mode == 'reset' and token else None"/>
|
required="required" t-att-disabled="'disabled' if mode == 'reset' and token else None"/>
|
||||||
<input type="hidden" name="login" t-att-value="login" t-if="mode == 'reset' and token"/>
|
<input type="hidden" name="login" t-att-value="login" t-if="mode == 'reset' and token"/>
|
||||||
|
|
|
@ -275,6 +275,13 @@ class base_action_rule(osv.osv):
|
||||||
if action.filter_id:
|
if action.filter_id:
|
||||||
domain = eval(action.filter_id.domain)
|
domain = eval(action.filter_id.domain)
|
||||||
ctx.update(eval(action.filter_id.context))
|
ctx.update(eval(action.filter_id.context))
|
||||||
|
if 'lang' not in ctx:
|
||||||
|
# Filters might be language-sensitive, attempt to reuse creator lang
|
||||||
|
# as we are usually running this as super-user in background
|
||||||
|
[filter_meta] = action.filter_id.perm_read()
|
||||||
|
user_id = filter_meta['write_uid'] and filter_meta['write_uid'][0] or \
|
||||||
|
filter_meta['create_uid'][0]
|
||||||
|
ctx['lang'] = self.pool['res.users'].browse(cr, uid, user_id).lang
|
||||||
record_ids = model.search(cr, uid, domain, context=ctx)
|
record_ids = model.search(cr, uid, domain, context=ctx)
|
||||||
|
|
||||||
# determine when action should occur for the records
|
# determine when action should occur for the records
|
||||||
|
|
|
@ -41,7 +41,9 @@ class base_config_settings(osv.osv_memory):
|
||||||
help="""This installs the module google_docs."""),
|
help="""This installs the module google_docs."""),
|
||||||
'module_google_calendar': fields.boolean('Allow the users to synchronize their calendar with Google Calendar',
|
'module_google_calendar': fields.boolean('Allow the users to synchronize their calendar with Google Calendar',
|
||||||
help="""This installs the module google_calendar."""),
|
help="""This installs the module google_calendar."""),
|
||||||
'font': fields.many2one('res.font', string="Report Font", help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
|
'font': fields.many2one('res.font', string="Report Font", domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
|
||||||
|
help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults= {
|
_defaults= {
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
<label for="font" />
|
<label for="font" />
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<field name="font" class="oe_inline" domain="[('mode', 'in', ('normal', 'regular', 'all', 'book'))]" />
|
<field name="font" class="oe_inline" />
|
||||||
<button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link"/>
|
<button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -54,7 +54,7 @@ _ref_vat = {
|
||||||
'gr': 'GR12345670',
|
'gr': 'GR12345670',
|
||||||
'hu': 'HU12345676',
|
'hu': 'HU12345676',
|
||||||
'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson
|
'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson
|
||||||
'ie': 'IE1234567T',
|
'ie': 'IE1234567FA',
|
||||||
'it': 'IT12345670017',
|
'it': 'IT12345670017',
|
||||||
'lt': 'LT123456715',
|
'lt': 'LT123456715',
|
||||||
'lu': 'LU12345613',
|
'lu': 'LU12345613',
|
||||||
|
@ -190,6 +190,34 @@ class res_partner(osv.osv):
|
||||||
return check == int(num[8])
|
return check == int(num[8])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _ie_check_char(self, vat):
|
||||||
|
vat = vat.zfill(8)
|
||||||
|
extra = 0
|
||||||
|
if vat[7] not in ' W':
|
||||||
|
if vat[7].isalpha():
|
||||||
|
extra = 9 * (ord(vat[7]) - 64)
|
||||||
|
else:
|
||||||
|
# invalid
|
||||||
|
return -1
|
||||||
|
checksum = extra + sum((8-i) * int(x) for i, x in enumerate(vat[:7]))
|
||||||
|
return 'WABCDEFGHIJKLMNOPQRSTUV'[checksum % 23]
|
||||||
|
|
||||||
|
def check_vat_ie(self, vat):
|
||||||
|
""" Temporary Ireland VAT validation to support the new format
|
||||||
|
introduced in January 2013 in Ireland, until upstream is fixed.
|
||||||
|
TODO: remove when fixed upstream"""
|
||||||
|
if len(vat) not in (8, 9) or not vat[2:7].isdigit():
|
||||||
|
return False
|
||||||
|
if len(vat) == 8:
|
||||||
|
# Normalize pre-2013 numbers: final space or 'W' not significant
|
||||||
|
vat += ' '
|
||||||
|
if vat[:7].isdigit():
|
||||||
|
return vat[7] == self._ie_check_char(vat[:7] + vat[8])
|
||||||
|
elif vat[1] in (string.ascii_uppercase + '+*'):
|
||||||
|
# Deprecated format
|
||||||
|
# See http://www.revenue.ie/en/online/third-party-reporting/reporting-payment-details/faqs.html#section3
|
||||||
|
return vat[7] == self._ie_check_char(vat[2:7] + vat[0] + vat[8])
|
||||||
|
return False
|
||||||
|
|
||||||
# Mexican VAT verification, contributed by <moylop260@hotmail.com>
|
# Mexican VAT verification, contributed by <moylop260@hotmail.com>
|
||||||
# and Panos Christeas <p_christ@hol.gr>
|
# and Panos Christeas <p_christ@hol.gr>
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import pytz
|
import pytz
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import openerp
|
import openerp
|
||||||
import openerp.service.report
|
import openerp.service.report
|
||||||
import uuid
|
import uuid
|
||||||
|
from werkzeug.exceptions import BadRequest
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from dateutil import rrule
|
from dateutil import rrule
|
||||||
|
@ -34,10 +34,11 @@ from openerp import tools, SUPERUSER_ID
|
||||||
from openerp.osv import fields, osv
|
from openerp.osv import fields, osv
|
||||||
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
from openerp import http
|
|
||||||
from openerp.http import request
|
from openerp.http import request
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
|
from werkzeug.exceptions import BadRequest
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -776,7 +777,7 @@ class calendar_event(osv.Model):
|
||||||
else:
|
else:
|
||||||
result[event] = ""
|
result[event] = ""
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _rrule_write(self, cr, uid, ids, field_name, field_value, args, context=None):
|
def _rrule_write(self, cr, uid, ids, field_name, field_value, args, context=None):
|
||||||
if not isinstance(ids, list):
|
if not isinstance(ids, list):
|
||||||
ids = [ids]
|
ids = [ids]
|
||||||
|
@ -789,7 +790,7 @@ class calendar_event(osv.Model):
|
||||||
data.update(update_data)
|
data.update(update_data)
|
||||||
self.write(cr, uid, ids, data, context=context)
|
self.write(cr, uid, ids, data, context=context)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _tz_get(self, cr, uid, context=None):
|
def _tz_get(self, cr, uid, context=None):
|
||||||
return [(x.lower(), x) for x in pytz.all_timezones]
|
return [(x.lower(), x) for x in pytz.all_timezones]
|
||||||
|
|
||||||
|
@ -802,7 +803,7 @@ class calendar_event(osv.Model):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_columns = {
|
_columns = {
|
||||||
'id': fields.integer('ID', readonly=True),
|
'id': fields.integer('ID', readonly=True),
|
||||||
'state': fields.selection([('draft', 'Unconfirmed'), ('open', 'Confirmed')], string='Status', readonly=True, track_visibility='onchange'),
|
'state': fields.selection([('draft', 'Unconfirmed'), ('open', 'Confirmed')], string='Status', readonly=True, track_visibility='onchange'),
|
||||||
'name': fields.char('Meeting Subject', required=True, states={'done': [('readonly', True)]}),
|
'name': fields.char('Meeting Subject', required=True, states={'done': [('readonly', True)]}),
|
||||||
'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
|
'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
|
||||||
|
@ -844,6 +845,7 @@ class calendar_event(osv.Model):
|
||||||
'attendee_ids': fields.one2many('calendar.attendee', 'event_id', 'Attendees', ondelete='cascade'),
|
'attendee_ids': fields.one2many('calendar.attendee', 'event_id', 'Attendees', ondelete='cascade'),
|
||||||
'partner_ids': fields.many2many('res.partner', string='Attendees', states={'done': [('readonly', True)]}),
|
'partner_ids': fields.many2many('res.partner', string='Attendees', states={'done': [('readonly', True)]}),
|
||||||
'alarm_ids': fields.many2many('calendar.alarm', string='Reminders', ondelete="restrict"),
|
'alarm_ids': fields.many2many('calendar.alarm', string='Reminders', ondelete="restrict"),
|
||||||
|
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'end_type': 'count',
|
'end_type': 'count',
|
||||||
|
@ -870,11 +872,9 @@ class calendar_event(osv.Model):
|
||||||
]
|
]
|
||||||
|
|
||||||
def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None):
|
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
|
"""Returns duration and/or end date based on values passed
|
||||||
@param ids: List of calendar event's IDs.
|
@param ids: List of calendar event's IDs.
|
||||||
@param start_date: Starting date
|
|
||||||
@param duration: Duration between start date and end date
|
|
||||||
@param end_date: Ending Datee
|
|
||||||
"""
|
"""
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
@ -888,14 +888,14 @@ class calendar_event(osv.Model):
|
||||||
value['duration'] = duration
|
value['duration'] = duration
|
||||||
|
|
||||||
if allday: # For all day event
|
if allday: # For all day event
|
||||||
start = datetime.strptime(start_date.split(' ')[0].split('T')[0], "%Y-%m-%d")
|
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
|
||||||
duration = 24.0
|
|
||||||
value['duration'] = duration
|
|
||||||
user = self.pool['res.users'].browse(cr, uid, uid)
|
user = self.pool['res.users'].browse(cr, uid, uid)
|
||||||
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
|
tz = pytz.timezone(user.tz) if user.tz else pytz.utc
|
||||||
start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone
|
start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone
|
||||||
start = start.astimezone(pytz.utc) # convert start back to utc
|
start = start.astimezone(pytz.utc) # convert start back to utc
|
||||||
value['date'] = start.strftime("%Y-%m-%d") + ' 00:00:00'
|
|
||||||
|
value['duration'] = 24.0
|
||||||
|
value['date'] = datetime.strftime(start, "%Y-%m-%d %H:%M:%S")
|
||||||
else:
|
else:
|
||||||
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
|
start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
@ -1353,15 +1353,55 @@ class calendar_event(osv.Model):
|
||||||
res = super(calendar_event, self).copy(cr, uid, calendar_id2real_id(id), default, context)
|
res = super(calendar_event, self).copy(cr, uid, calendar_id2real_id(id), default, context)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def _detach_one_event(self, cr, uid, id, values=dict(), context=None):
|
||||||
|
real_event_id = calendar_id2real_id(id)
|
||||||
|
data = self.read(cr, uid, id, ['date', 'date_deadline', 'rrule', 'duration'])
|
||||||
|
|
||||||
|
if data.get('rrule'):
|
||||||
|
data.update(
|
||||||
|
values,
|
||||||
|
recurrent_id=real_event_id,
|
||||||
|
recurrent_id_date=data.get('date'),
|
||||||
|
rrule_type=False,
|
||||||
|
rrule='',
|
||||||
|
recurrency=False,
|
||||||
|
end_date = datetime.strptime(values.get('date', False) or data.get('date'),"%Y-%m-%d %H:%M:%S")
|
||||||
|
+ timedelta(hours=values.get('duration', False) or data.get('duration'))
|
||||||
|
)
|
||||||
|
|
||||||
|
#do not copy the id
|
||||||
|
if data.get('id'):
|
||||||
|
del(data['id'])
|
||||||
|
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
|
||||||
|
return new_id
|
||||||
|
|
||||||
|
def open_after_detach_event(self, cr, uid, ids, context=None):
|
||||||
|
if context is None:
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
new_id = self._detach_one_event(cr, uid, ids[0], context=context)
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'calendar.event',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': new_id,
|
||||||
|
'target': 'current',
|
||||||
|
'flags': {'form': {'action_buttons': True, 'options' : { 'mode' : 'edit' } } }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def write(self, cr, uid, ids, values, context=None):
|
def write(self, cr, uid, ids, values, context=None):
|
||||||
def _only_changes_to_apply_on_real_ids(field_names):
|
def _only_changes_to_apply_on_real_ids(field_names):
|
||||||
''' return True if changes are only to be made on the real ids'''
|
''' return True if changes are only to be made on the real ids'''
|
||||||
for field in field_names:
|
for field in field_names:
|
||||||
if field not in ['name', 'message_follower_ids','oe_update_date']:
|
if field in ['date','active']:
|
||||||
return False
|
return True
|
||||||
return True
|
return False
|
||||||
|
|
||||||
context = context or {}
|
context = context or {}
|
||||||
|
|
||||||
|
|
||||||
if isinstance(ids, (str,int, long)):
|
if isinstance(ids, (str,int, long)):
|
||||||
if len(str(ids).split('-')) == 1:
|
if len(str(ids).split('-')) == 1:
|
||||||
|
@ -1383,31 +1423,14 @@ class calendar_event(osv.Model):
|
||||||
# if we are setting the recurrency flag to False or if we are only changing fields that
|
# if we are setting the recurrency flag to False or if we are only changing fields that
|
||||||
# should be only updated on the real ID and not on the virtual (like message_follower_ids):
|
# should be only updated on the real ID and not on the virtual (like message_follower_ids):
|
||||||
# then set real ids to be updated.
|
# then set real ids to be updated.
|
||||||
if not values.get('recurrency', True) or _only_changes_to_apply_on_real_ids(values.keys()):
|
if not values.get('recurrency', True) or not _only_changes_to_apply_on_real_ids(values.keys()):
|
||||||
ids.append(real_event_id)
|
ids.append(real_event_id)
|
||||||
continue
|
continue
|
||||||
|
else:
|
||||||
#if edit one instance of a reccurrent id
|
data = self.read(cr, uid, event_id, ['date', 'date_deadline', 'rrule', 'duration'])
|
||||||
data = self.read(cr, uid, event_id, ['date', 'date_deadline', 'rrule', 'duration'])
|
if data.get('rrule'):
|
||||||
if data.get('rrule'):
|
new_id = self._detach_one_event(cr, uid, event_id, values, context=None)
|
||||||
data.update(
|
|
||||||
values,
|
|
||||||
recurrent_id=real_event_id,
|
|
||||||
recurrent_id_date=data.get('date'),
|
|
||||||
rrule_type=False,
|
|
||||||
rrule='',
|
|
||||||
recurrency=False,
|
|
||||||
end_date = datetime.strptime(values.get('date', False) or data.get('date'),"%Y-%m-%d %H:%M:%S")
|
|
||||||
+ timedelta(hours=values.get('duration', False) or data.get('duration'))
|
|
||||||
)
|
|
||||||
|
|
||||||
#do not copy the id
|
|
||||||
if data.get('id'):
|
|
||||||
del(data['id'])
|
|
||||||
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
|
|
||||||
context.update({'active_id': new_id, 'active_ids': [new_id]})
|
|
||||||
continue
|
|
||||||
|
|
||||||
res = super(calendar_event, self).write(cr, uid, ids, values, context=context)
|
res = super(calendar_event, self).write(cr, uid, ids, values, context=context)
|
||||||
|
|
||||||
# set end_date for calendar searching
|
# set end_date for calendar searching
|
||||||
|
@ -1473,7 +1496,6 @@ class calendar_event(osv.Model):
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
fields2 = fields and fields[:] or None
|
fields2 = fields and fields[:] or None
|
||||||
|
|
||||||
EXTRAFIELDS = ('class', 'user_id', 'duration', 'date', 'rrule', 'vtimezone')
|
EXTRAFIELDS = ('class', 'user_id', 'duration', 'date', 'rrule', 'vtimezone')
|
||||||
for f in EXTRAFIELDS:
|
for f in EXTRAFIELDS:
|
||||||
if fields and (f not in fields):
|
if fields and (f not in fields):
|
||||||
|
@ -1518,6 +1540,7 @@ class calendar_event(osv.Model):
|
||||||
for k in EXTRAFIELDS:
|
for k in EXTRAFIELDS:
|
||||||
if (k in r) and (fields and (k not in fields)):
|
if (k in r) and (fields and (k not in fields)):
|
||||||
del r[k]
|
del r[k]
|
||||||
|
|
||||||
if isinstance(ids, (str, int, long)):
|
if isinstance(ids, (str, int, long)):
|
||||||
return result and result[0] or False
|
return result and result[0] or False
|
||||||
return result
|
return result
|
||||||
|
@ -1531,7 +1554,7 @@ class calendar_event(osv.Model):
|
||||||
ids_to_unlink = []
|
ids_to_unlink = []
|
||||||
|
|
||||||
# One time moved to google_Calendar, we can specify, if not in google, and not rec or get_inst = 0, we delete it
|
# One time moved to google_Calendar, we can specify, if not in google, and not rec or get_inst = 0, we delete it
|
||||||
for event_id in ids:
|
for event_id in ids:
|
||||||
if unlink_level == 1 and len(str(event_id).split('-')) == 1: # if ID REAL
|
if unlink_level == 1 and len(str(event_id).split('-')) == 1: # if ID REAL
|
||||||
if self.browse(cr, uid, event_id).recurrent_id:
|
if self.browse(cr, uid, event_id).recurrent_id:
|
||||||
ids_to_exclure.append(event_id)
|
ids_to_exclure.append(event_id)
|
||||||
|
|
|
@ -6,20 +6,7 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--For Meetings -->
|
<!--For Meetings -->
|
||||||
<record id="res_partner_another" model="res.partner">
|
<record id="cal_contact_1" model="calendar.contacts">
|
||||||
<field name="name">Arshaw</field>
|
|
||||||
<field name="company_id" ref="base.main_company"/>
|
|
||||||
<field name="customer" eval="False"/>
|
|
||||||
<field name="email">fullcalendar@example.com</field>
|
|
||||||
</record>
|
|
||||||
<record id="res_user_another" model="res.users">
|
|
||||||
<field name="name" >Second Demo User</field>
|
|
||||||
<field name="login" >Second Demo User</field>
|
|
||||||
<field name="partner_id" ref="res_partner_another"/>
|
|
||||||
<field name="company_id" ref="base.main_company" />
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="cal_contact_1" model="calendar.contacts">
|
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field name="partner_id" ref="base.res_partner_1"/>
|
<field name="partner_id" ref="base.res_partner_1"/>
|
||||||
|
@ -105,8 +92,8 @@
|
||||||
|
|
||||||
<record id="calendar_event_7" model="calendar.event">
|
<record id="calendar_event_7" model="calendar.event">
|
||||||
<field eval="1" name="active"/>
|
<field eval="1" name="active"/>
|
||||||
<field name="user_id" ref="res_user_another"/>
|
<field name="user_id" ref="base.user_demo"/>
|
||||||
<field name="partner_ids" eval="[(6,0,[ref('res_partner_another'),ref('base.res_partner_8')])]"/>
|
<field name="partner_ids" eval="[(6,0,[ref('base.res_partner_7')])]"/>
|
||||||
<field name="name">Presentation of the new Calendar</field>
|
<field name="name">Presentation of the new Calendar</field>
|
||||||
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
|
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
|
||||||
<field eval="time.strftime('%Y-%m-16 6:00:00')" name="date"/>
|
<field eval="time.strftime('%Y-%m-16 6:00:00')" name="date"/>
|
||||||
|
@ -114,18 +101,7 @@
|
||||||
<field eval="8.5" name="duration"/>
|
<field eval="8.5" name="duration"/>
|
||||||
<field name="state">draft</field>
|
<field name="state">draft</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="calendar_event_8" model="calendar.event">
|
|
||||||
<field eval="1" name="active"/>
|
|
||||||
<field name="user_id" ref="res_user_another"/>
|
|
||||||
<field name="partner_ids" eval="[(6,0,[ref('res_partner_another'),ref('base.partner_root')])]"/>
|
|
||||||
<field name="name">Discuss about the module : Calendar </field>
|
|
||||||
<field name="categ_ids" eval="[(6,0,[ref('categ_meet1'), ref('categ_meet2')])]"/>
|
|
||||||
<field eval="time.strftime('%Y-%m-16 6:00:00')" name="date"/>
|
|
||||||
<field eval="time.strftime('%Y-%m-16 18:30:00')" name="date_deadline"/>
|
|
||||||
<field eval="8.5" name="duration"/>
|
|
||||||
<field name="state">draft</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -51,21 +51,26 @@
|
||||||
</div>
|
</div>
|
||||||
<notebook>
|
<notebook>
|
||||||
<page string="Meeting Details">
|
<page string="Meeting Details">
|
||||||
|
<group attrs="{'invisible': [('recurrency','==',False)]}" class="oe_edit_only ">
|
||||||
|
<p class='alert alert-warning'> This event is linked to a recurrence...<br/>
|
||||||
|
<button type="object" name="open_after_detach_event" string="Update only this instance" help="Click here to update only this instance and not all recurrences. " class="oe_link"/>
|
||||||
|
</p>
|
||||||
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="date" string="Starting at" on_change="onchange_dates(date, duration, False, allday)"/>
|
<field name="date" string="Starting at" on_change="onchange_dates(date, duration, False, allday)"/>
|
||||||
<label for="duration"/>
|
<label for="duration"/>
|
||||||
<div>
|
<div>
|
||||||
<field name="duration" widget="float_time"
|
<field name="duration" widget="float_time"
|
||||||
on_change="onchange_dates(date,duration,False,allday)"
|
on_change="onchange_dates(date, duration, False, allday)"
|
||||||
class="oe_inline" attrs="{'invisible': [('allday','=',True)]}"/>
|
class="oe_inline" attrs="{'invisible': [('allday','=',True)]}"/>
|
||||||
<label string="hours" attrs="{'invisible': [('allday','=',True)]}"/>
|
<label string="hours" attrs="{'invisible': [('allday','=',True)]}"/>
|
||||||
(<field name="allday" on_change="onchange_dates(date,False,False,allday)" class="oe_inline"/>
|
(<field name="allday" class="oe_inline"/>
|
||||||
<label for="allday" string="All Day?"/>)
|
<label for="allday" string="All Day?"/>)
|
||||||
</div>
|
</div>
|
||||||
<field name="date_deadline" groups="base.group_no_one"
|
<field name="date_deadline" groups="base.group_no_one"
|
||||||
attrs="{'invisible': ['|', ('allday','=',True), ('duration','<', 24)]}"
|
attrs="{'invisible': ['|', ('allday','=',True), ('duration','<', 24)]}"
|
||||||
on_change="onchange_dates(date,False,date_deadline)"/>
|
/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="categ_ids" widget="many2many_tags"/>
|
<field name="categ_ids" widget="many2many_tags"/>
|
||||||
|
@ -121,10 +126,10 @@
|
||||||
</div>
|
</div>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="class"/>
|
<field name="class"/>
|
||||||
<field name="show_as"/>
|
<field name="show_as"/>
|
||||||
<field name="rrule" invisible="1" readonly="0" />
|
<field name="rrule" invisible="1" readonly="0" />
|
||||||
<field name="recurrent_id" invisible="1" />
|
<field name="recurrent_id" invisible="1" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
@ -136,7 +141,7 @@
|
||||||
<tree string="Invitation details" editable="top" create="false" delete="false">
|
<tree string="Invitation details" editable="top" create="false" delete="false">
|
||||||
<field name="partner_id" />
|
<field name="partner_id" />
|
||||||
<field name="state" />
|
<field name="state" />
|
||||||
<field name="email" widget="email"/>
|
<field name="email" widget="email"/>
|
||||||
|
|
||||||
<button name="do_tentative" states="needsAction,declined,accepted" string="Uncertain" type="object" icon="terp-crm" />
|
<button name="do_tentative" states="needsAction,declined,accepted" string="Uncertain" type="object" icon="terp-crm" />
|
||||||
<button name="do_accept" string="Accept" states="needsAction,tentative,declined" type="object" icon="gtk-apply"/>
|
<button name="do_accept" string="Accept" states="needsAction,tentative,declined" type="object" icon="gtk-apply"/>
|
||||||
|
@ -176,7 +181,7 @@
|
||||||
<field name="date" string="Start" />
|
<field name="date" string="Start" />
|
||||||
<field name="duration" string="Duration" widget="float_time" attrs="{'invisible': [('allday','=',True)]}"/>
|
<field name="duration" string="Duration" widget="float_time" attrs="{'invisible': [('allday','=',True)]}"/>
|
||||||
<field name="allday" class="oe_inline" attrs="{'invisible': [('allday','=',False)]}"/>
|
<field name="allday" class="oe_inline" attrs="{'invisible': [('allday','=',False)]}"/>
|
||||||
<field name="partner_ids" widget="many2manyattendee" string="Attendees"/>
|
<field name="partner_ids" widget="many2manyattendee" string="Attendees"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="categ_ids" widget="many2many_tags"/>
|
<field name="categ_ids" widget="many2many_tags"/>
|
||||||
|
@ -213,7 +218,7 @@
|
||||||
<field name="name">Meetings Calendar</field>
|
<field name="name">Meetings Calendar</field>
|
||||||
<field name="model">calendar.event</field>
|
<field name="model">calendar.event</field>
|
||||||
<field name="priority" eval="2"/>
|
<field name="priority" eval="2"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<calendar string="Meetings" date_start="date" date_stop="date_deadline" date_delay="duration" all_day="allday"
|
<calendar string="Meetings" date_start="date" date_stop="date_deadline" date_delay="duration" all_day="allday"
|
||||||
display="[name]" color="color_partner_id" attendee="partner_ids" avatar_model="res.partner"
|
display="[name]" color="color_partner_id" attendee="partner_ids" avatar_model="res.partner"
|
||||||
use_contacts="True" event_open_popup="%(calendar.view_calendar_event_form_popup)s">
|
use_contacts="True" event_open_popup="%(calendar.view_calendar_event_form_popup)s">
|
||||||
|
@ -248,14 +253,14 @@
|
||||||
<field name="user_id"/>
|
<field name="user_id"/>
|
||||||
<field name="show_as"/>
|
<field name="show_as"/>
|
||||||
<field name="class" string="Privacy"/>
|
<field name="class" string="Privacy"/>
|
||||||
<filter icon="terp-go-today" string="My Events" domain="[('user_id','=',uid)]" help="My Events"/>
|
<filter icon="terp-go-today" string="My Events" domain="[('user_id','=',uid)]" help="My Events"/>
|
||||||
<filter string="My Meetings" help="My Meetings" name="mymeetings" context='{"mymeetings": 1}'/>
|
<filter string="My Meetings" help="My Meetings" name="mymeetings" context='{"mymeetings": 1}'/>
|
||||||
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<group expand="0" string="Group By...">
|
<group expand="0" string="Group By...">
|
||||||
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
|
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
|
||||||
<filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'show_as'}"/>
|
<filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'show_as'}"/>
|
||||||
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
|
<filter string="Privacy" icon="terp-locked" domain="[]" context="{'group_by':'class'}"/>
|
||||||
<filter string="Event Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Start Date of Event by Month"/>
|
<filter string="Event Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Start Date of Event by Month"/>
|
||||||
</group>
|
</group>
|
||||||
</search>
|
</search>
|
||||||
|
@ -267,8 +272,7 @@
|
||||||
<field name="res_model">calendar.event</field>
|
<field name="res_model">calendar.event</field>
|
||||||
<field name="view_mode">calendar,tree,form,gantt</field>
|
<field name="view_mode">calendar,tree,form,gantt</field>
|
||||||
<field name="view_id" ref="view_calendar_event_calendar"/>
|
<field name="view_id" ref="view_calendar_event_calendar"/>
|
||||||
<field name="search_view_id" ref="view_calendar_event_search"/>
|
<field name="search_view_id" ref="view_calendar_event_search"/>
|
||||||
<field name="context">{"search_default_mymeetings": 1}</field>
|
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="oe_view_nocontent_create">
|
<p class="oe_view_nocontent_create">
|
||||||
Click to schedule a new meeting.
|
Click to schedule a new meeting.
|
||||||
|
@ -313,7 +317,7 @@
|
||||||
<field name="name">Meetings</field>
|
<field name="name">Meetings</field>
|
||||||
<field name="res_model">calendar.event</field>
|
<field name="res_model">calendar.event</field>
|
||||||
<field name="view_mode">form,calendar,tree,gantt</field>
|
<field name="view_mode">form,calendar,tree,gantt</field>
|
||||||
<field name="view_id" ref="view_calendar_event_form"/>
|
<field name="view_id" ref="view_calendar_event_form"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class calendar_contacts(osv.osv):
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'user_id': fields.many2one('res.users','Me'),
|
'user_id': fields.many2one('res.users','Me'),
|
||||||
'partner_id': fields.many2one('res.partner','Employee',required=True, domain=[('customer','=',True)]),
|
'partner_id': fields.many2one('res.partner','Employee',required=True, domain=[]),
|
||||||
'active':fields.boolean('active'),
|
'active':fields.boolean('active'),
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<field name="res_model">calendar.contacts</field>
|
<field name="res_model">calendar.contacts</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="domain">[('user_id','=',uid)]</field>
|
||||||
<field name="view_id" ref="view_calendar_contacts" />
|
<field name="view_id" ref="view_calendar_contacts" />
|
||||||
<field name="help" type="html">
|
<field name="help" type="html">
|
||||||
<p class="oe_view_nocontent_create">
|
<p class="oe_view_nocontent_create">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
openerp.calendar = function(instance) {
|
openerp.calendar = function(instance) {
|
||||||
var _t = instance.web._t;
|
var _t = instance.web._t;
|
||||||
var QWeb = instance.web.qweb;
|
var QWeb = instance.web.qweb;
|
||||||
instance.calendar = {}
|
instance.calendar = {};
|
||||||
|
|
||||||
|
|
||||||
instance.web.WebClient = instance.web.WebClient.extend({
|
instance.web.WebClient = instance.web.WebClient.extend({
|
||||||
|
@ -11,8 +11,8 @@ openerp.calendar = function(instance) {
|
||||||
get_next_notif: function() {
|
get_next_notif: function() {
|
||||||
var self= this;
|
var self= this;
|
||||||
this.rpc("/calendar/notify")
|
this.rpc("/calendar/notify")
|
||||||
.then(
|
.then(
|
||||||
function(result) {
|
function(result) {
|
||||||
_.each(result, function(res) {
|
_.each(result, function(res) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
//If notification not already displayed, we add button and action on it
|
//If notification not already displayed, we add button and action on it
|
||||||
|
@ -21,25 +21,25 @@ openerp.calendar = function(instance) {
|
||||||
res.message += QWeb.render("notify_footer");
|
res.message += QWeb.render("notify_footer");
|
||||||
a = self.do_notify(res.title,res.message,true);
|
a = self.do_notify(res.title,res.message,true);
|
||||||
|
|
||||||
$(".link2event").on('click', function() {
|
$(".link2event").on('click', function() {
|
||||||
self.rpc("/web/action/load", {
|
self.rpc("/web/action/load", {
|
||||||
action_id: "calendar.action_calendar_event_notify",
|
action_id: "calendar.action_calendar_event_notify",
|
||||||
}).then( function(r) {
|
}).then( function(r) {
|
||||||
r.res_id = res.event_id;
|
r.res_id = res.event_id;
|
||||||
return self.action_manager.do_action(r);
|
return self.action_manager.do_action(r);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
a.element.find(".link2recall").on('click',function() {
|
a.element.find(".link2recall").on('click',function() {
|
||||||
self.get_notif_box(this).find('.ui-notify-close').trigger("click");
|
self.get_notif_box(this).find('.ui-notify-close').trigger("click");
|
||||||
});
|
});
|
||||||
a.element.find(".link2showed").on('click',function() {
|
a.element.find(".link2showed").on('click',function() {
|
||||||
self.get_notif_box(this).find('.ui-notify-close').trigger("click");
|
self.get_notif_box(this).find('.ui-notify-close').trigger("click");
|
||||||
self.rpc("/calendar/notify_ack");
|
self.rpc("/calendar/notify_ack");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//If notification already displayed in the past, we remove the css attribute which hide this notification
|
//If notification already displayed in the past, we remove the css attribute which hide this notification
|
||||||
else if (self.get_notif_box($.find(".eid_"+res.event_id)).attr("style") !== ""){
|
else if (self.get_notif_box($.find(".eid_"+res.event_id)).attr("style") !== ""){
|
||||||
self.get_notif_box($.find(".eid_"+res.event_id)).attr("style","");
|
self.get_notif_box($.find(".eid_"+res.event_id)).attr("style","");
|
||||||
}
|
}
|
||||||
},res.timer * 1000);
|
},res.timer * 1000);
|
||||||
});
|
});
|
||||||
|
@ -48,7 +48,7 @@ openerp.calendar = function(instance) {
|
||||||
},
|
},
|
||||||
check_notifications: function() {
|
check_notifications: function() {
|
||||||
var self= this;
|
var self= this;
|
||||||
self.get_next_notif();
|
self.get_next_notif();
|
||||||
setInterval(function(){
|
setInterval(function(){
|
||||||
self.get_next_notif();
|
self.get_next_notif();
|
||||||
}, 5 * 60 * 1000 );
|
}, 5 * 60 * 1000 );
|
||||||
|
@ -58,7 +58,7 @@ openerp.calendar = function(instance) {
|
||||||
show_application: function() {
|
show_application: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.check_notifications();
|
this.check_notifications();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,7 +77,6 @@ openerp.calendar = function(instance) {
|
||||||
if(instance.session.session_is_valid(self.db) && instance.session.username != "anonymous") {
|
if(instance.session.session_is_valid(self.db) && instance.session.username != "anonymous") {
|
||||||
self.redirect_meeting_view(self.db,self.action,self.id,self.view);
|
self.redirect_meeting_view(self.db,self.action,self.id,self.view);
|
||||||
} else {
|
} else {
|
||||||
alert('in anonymous or null ');
|
|
||||||
self.open_invitation_form(self.attendee_data);
|
self.open_invitation_form(self.attendee_data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -92,9 +91,8 @@ openerp.calendar = function(instance) {
|
||||||
|
|
||||||
var reload_page = function(){
|
var reload_page = function(){
|
||||||
return location.replace(action_url);
|
return location.replace(action_url);
|
||||||
}
|
};
|
||||||
reload_page();
|
reload_page();
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -108,7 +106,7 @@ openerp.calendar = function(instance) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
map_tag: function(value){
|
map_tag: function(value){
|
||||||
return _.map(value, function(el) {return {name: el[1], id:el[0], state: el[2]};})
|
return _.map(value, function(el) {return {name: el[1], id:el[0], state: el[2]};});
|
||||||
},
|
},
|
||||||
get_render_data: function(ids){
|
get_render_data: function(ids){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -121,7 +119,7 @@ openerp.calendar = function(instance) {
|
||||||
if (! self.get("effective_readonly")) {
|
if (! self.get("effective_readonly")) {
|
||||||
var tag_element = self.tags.tagElements();
|
var tag_element = self.tags.tagElements();
|
||||||
_.each(data,function(value, key){
|
_.each(data,function(value, key){
|
||||||
$(tag_element[key]).find(".custom-edit").addClass(data[key][2])
|
$(tag_element[key]).find(".custom-edit").addClass(data[key][2]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +132,7 @@ openerp.calendar = function(instance) {
|
||||||
instance.session.session_bind(instance.session.origin).done(function () {
|
instance.session.session_bind(instance.session.origin).done(function () {
|
||||||
new instance.calendar.invitation(null,db,action,id,view,attendee_data).appendTo($("body").addClass('openerp'));
|
new instance.calendar.invitation(null,db,action,id,view,attendee_data).appendTo($("body").addClass('openerp'));
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,14 @@
|
||||||
!python {model: calendar.event}: |
|
!python {model: calendar.event}: |
|
||||||
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
|
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
|
||||||
before = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
|
before = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
|
||||||
self.write(cr, uid,[ids[1]], {'name':'New Name','recurrency' : True}, context={'virtual_id': True})
|
# We start by detach the event
|
||||||
|
newid = self._detach_one_event(cr, uid,ids[1])
|
||||||
|
self.write(cr, uid,[newid], {'name':'New Name','recurrency' : True}, context={'virtual_id': True})
|
||||||
after = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
|
after = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': False})
|
||||||
assert len(after) == len(before)+1, 'Wrong number of events found, after to have moved a virtual event'
|
assert len(after) == len(before)+1, 'Wrong number of events found, after to have moved a virtual event'
|
||||||
new_id = list(set(after)-set(before))[0]
|
new_id = list(set(after)-set(before))[0]
|
||||||
new_event = self.browse(cr,uid,new_id,context=context)
|
new_event = self.browse(cr,uid,new_id,context=context)
|
||||||
assert new_event.recurrent_id == before[0], 'Recurrent_id not correctly passed to the new event'
|
assert new_event.recurrent_id == before[0], 'Recurrent_id not correctly passed to the new event'
|
||||||
-
|
-
|
||||||
Now I will make All day event and test it
|
Now I will make All day event and test it
|
||||||
-
|
-
|
||||||
|
@ -58,7 +60,6 @@
|
||||||
duration: 1
|
duration: 1
|
||||||
interval: days
|
interval: days
|
||||||
type: notification
|
type: notification
|
||||||
|
|
||||||
-
|
-
|
||||||
Now I will assign this reminder to all day event
|
Now I will assign this reminder to all day event
|
||||||
-
|
-
|
||||||
|
|
|
@ -84,9 +84,12 @@
|
||||||
idval = '%d-%s' % (ref('calendar_event_sprintreview0'), '20110425124700')
|
idval = '%d-%s' % (ref('calendar_event_sprintreview0'), '20110425124700')
|
||||||
self.write(cr, uid, [idval], {'description': 'Review code of the module: sync_google_calendar.'})
|
self.write(cr, uid, [idval], {'description': 'Review code of the module: sync_google_calendar.'})
|
||||||
-
|
-
|
||||||
I check whether the record is edited perfectly or not.
|
I check whether that all the records of this recurrence has been edited.
|
||||||
-
|
-
|
||||||
!python {model: calendar.event}: |
|
!python {model: calendar.event}: |
|
||||||
meeting_ids = self.search(cr, uid, [('recurrent_id', '=', ref('calendar_event_sprintreview0')), ('recurrent_id_date','=','2011-04-25 12:47:00')], context)
|
meeting_ids = self.search(cr, uid, [('recurrent_id', '=', ref('calendar_event_sprintreview0'))], context)
|
||||||
assert meeting_ids, 'Meeting is not edited !'
|
meetings = self.browse(cr, uid, meeting_ids, context)
|
||||||
|
for meeting in meetings:
|
||||||
|
assert meeting.description == 'Review code of the module: sync_google_calendar.', 'Description not changed for id: %s' %meeting.id
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -120,14 +120,14 @@ class crm_case_section(osv.osv):
|
||||||
month_begin = date.today().replace(day=1)
|
month_begin = date.today().replace(day=1)
|
||||||
section_result = [{
|
section_result = [{
|
||||||
'value': 0,
|
'value': 0,
|
||||||
'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B'),
|
'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B %Y'),
|
||||||
} for i in range(self._period_number - 1, -1, -1)]
|
} for i in range(self._period_number - 1, -1, -1)]
|
||||||
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
|
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
|
||||||
pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
|
pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
for group in group_obj:
|
for group in group_obj:
|
||||||
group_begin_date = datetime.strptime(group['__domain'][0][2], pattern)
|
group_begin_date = datetime.strptime(group['__domain'][0][2], pattern)
|
||||||
month_delta = relativedelta.relativedelta(month_begin, group_begin_date)
|
month_delta = relativedelta.relativedelta(month_begin, group_begin_date)
|
||||||
section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group_begin_date.strftime('%B')}
|
section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field, 0)}
|
||||||
return section_result
|
return section_result
|
||||||
|
|
||||||
def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None):
|
def _get_opportunities_data(self, cr, uid, ids, field_name, arg, context=None):
|
||||||
|
@ -140,13 +140,18 @@ class crm_case_section(osv.osv):
|
||||||
month_begin = date.today().replace(day=1)
|
month_begin = date.today().replace(day=1)
|
||||||
date_begin = month_begin - relativedelta.relativedelta(months=self._period_number - 1)
|
date_begin = month_begin - relativedelta.relativedelta(months=self._period_number - 1)
|
||||||
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1])
|
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1])
|
||||||
date_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT))]
|
lead_pre_domain = [('create_date', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||||
|
('create_date', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)),
|
||||||
|
('type', '=', 'lead')]
|
||||||
|
opp_pre_domain = [('date_deadline', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||||
|
('date_deadline', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||||
|
('type', '=', 'opportunity')]
|
||||||
for id in ids:
|
for id in ids:
|
||||||
res[id] = dict()
|
res[id] = dict()
|
||||||
lead_domain = date_domain + [('type', '=', 'lead'), ('section_id', '=', id)]
|
lead_domain = lead_pre_domain + [('section_id', '=', id)]
|
||||||
|
opp_domain = opp_pre_domain + [('section_id', '=', id)]
|
||||||
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
|
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
|
||||||
opp_domain = date_domain + [('type', '=', 'opportunity'), ('section_id', '=', id)]
|
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context)
|
||||||
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'create_date'], 'planned_revenue', 'create_date', context=context)
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
|
|
|
@ -63,9 +63,9 @@
|
||||||
<record id="action_report_crm_lead_salesteam" model="ir.actions.act_window">
|
<record id="action_report_crm_lead_salesteam" model="ir.actions.act_window">
|
||||||
<field name="name">Leads Analysis</field>
|
<field name="name">Leads Analysis</field>
|
||||||
<field name="res_model">crm.lead.report</field>
|
<field name="res_model">crm.lead.report</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="context">{"search_default_month":1}</field>
|
<field name="context">{"search_default_month":1}</field>
|
||||||
<field name="view_mode">tree,graph</field>
|
<field name="view_mode">graph</field>
|
||||||
|
<field name="view_id" ref="crm.view_report_crm_lead_graph_two"/>
|
||||||
<field name="domain">[('type','=', 'lead'),('section_id', '=', active_id)]</field>
|
<field name="domain">[('type','=', 'lead'),('section_id', '=', active_id)]</field>
|
||||||
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
|
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -73,9 +73,8 @@
|
||||||
<record id="action_report_crm_opportunity_salesteam" model="ir.actions.act_window">
|
<record id="action_report_crm_opportunity_salesteam" model="ir.actions.act_window">
|
||||||
<field name="name">Opportunities Analysis</field>
|
<field name="name">Opportunities Analysis</field>
|
||||||
<field name="res_model">crm.lead.report</field>
|
<field name="res_model">crm.lead.report</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_mode">graph</field>
|
||||||
<field name="context">{"search_default_month":1}</field>
|
<field name="view_id" ref="crm.view_report_crm_opportunity_graph"/>
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="domain">[('type','=', 'opportunity'), ('section_id', '=', active_id)]</field>
|
<field name="domain">[('type','=', 'opportunity'), ('section_id', '=', active_id)]</field>
|
||||||
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
|
<field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the teams of the sales pipeline.</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -117,14 +116,14 @@
|
||||||
<a name="%(crm_case_form_view_salesteams_lead)d" type="action">Leads</a>
|
<a name="%(crm_case_form_view_salesteams_lead)d" type="action">Leads</a>
|
||||||
<a name="%(action_report_crm_lead_salesteam)d" type="action" class="oe_sparkline_bar_link">
|
<a name="%(action_report_crm_lead_salesteam)d" type="action" class="oe_sparkline_bar_link">
|
||||||
<field name="monthly_open_leads" widget="sparkline_bar"
|
<field name="monthly_open_leads" widget="sparkline_bar"
|
||||||
options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': 'Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
|
options="{'height': '20px', 'barWidth': 4, 'barSpacing': 1, 'delayIn': '3000', 'tooltip_suffix': ' Leads'}">Open Leads per Month<br/>Click to see a detailed analysis of leads.</field>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_salesteams_opportunities">
|
<div class="oe_salesteams_opportunities">
|
||||||
<a name="%(crm_case_form_view_salesteams_opportunity)d" type="action">Opportunities</a>
|
<a name="%(crm_case_form_view_salesteams_opportunity)d" type="action">Opportunities</a>
|
||||||
<a name="%(action_report_crm_opportunity_salesteam)d" type="action">
|
<a name="%(action_report_crm_opportunity_salesteam)d" type="action">
|
||||||
<field name="monthly_planned_revenue" widget="sparkline_bar"
|
<field name="monthly_planned_revenue" widget="sparkline_bar"
|
||||||
options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': 'Opportunities'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
|
options="{'height': '20px', 'barWidth': '4', 'barSpacing': '1', 'delayIn': '3000', 'tooltip_suffix': ' (Planned Revenue)'}">Planned Revenue per Month<br/>Click to see a detailed analysis of opportunities.</field>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -330,6 +330,7 @@ class crm_lead(format_address, osv.osv):
|
||||||
'phone': partner.phone,
|
'phone': partner.phone,
|
||||||
'mobile': partner.mobile,
|
'mobile': partner.mobile,
|
||||||
'fax': partner.fax,
|
'fax': partner.fax,
|
||||||
|
'zip': partner.zip,
|
||||||
}
|
}
|
||||||
return {'value': values}
|
return {'value': values}
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,7 @@
|
||||||
<!-- CRM-related subtypes for messaging / Chatter -->
|
<!-- CRM-related subtypes for messaging / Chatter -->
|
||||||
<record id="mt_lead_create" model="mail.message.subtype">
|
<record id="mt_lead_create" model="mail.message.subtype">
|
||||||
<field name="name">Lead Created</field>
|
<field name="name">Lead Created</field>
|
||||||
|
<field name="hidden" eval="True"/>
|
||||||
<field name="res_model">crm.lead</field>
|
<field name="res_model">crm.lead</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="description">Lead created</field>
|
<field name="description">Lead created</field>
|
||||||
|
@ -171,6 +172,7 @@
|
||||||
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
||||||
<record id="mt_salesteam_lead" model="mail.message.subtype">
|
<record id="mt_salesteam_lead" model="mail.message.subtype">
|
||||||
<field name="name">Lead Created</field>
|
<field name="name">Lead Created</field>
|
||||||
|
<field name="sequence">10</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_lead_create')"/>
|
<field name="parent_id" eval="ref('mt_lead_create')"/>
|
||||||
|
@ -178,18 +180,21 @@
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_salesteam_lead_stage" model="mail.message.subtype">
|
<record id="mt_salesteam_lead_stage" model="mail.message.subtype">
|
||||||
<field name="name">Opportunity Stage Changed</field>
|
<field name="name">Opportunity Stage Changed</field>
|
||||||
|
<field name="sequence">11</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="parent_id" eval="ref('mt_lead_stage')"/>
|
<field name="parent_id" eval="ref('mt_lead_stage')"/>
|
||||||
<field name="relation_field">section_id</field>
|
<field name="relation_field">section_id</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_salesteam_lead_won" model="mail.message.subtype">
|
<record id="mt_salesteam_lead_won" model="mail.message.subtype">
|
||||||
<field name="name">Opportunity Won</field>
|
<field name="name">Opportunity Won</field>
|
||||||
|
<field name="sequence">12</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="parent_id" eval="ref('mt_lead_won')"/>
|
<field name="parent_id" eval="ref('mt_lead_won')"/>
|
||||||
<field name="relation_field">section_id</field>
|
<field name="relation_field">section_id</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_salesteam_lead_lost" model="mail.message.subtype">
|
<record id="mt_salesteam_lead_lost" model="mail.message.subtype">
|
||||||
<field name="name">Opportunity Lost</field>
|
<field name="name">Opportunity Lost</field>
|
||||||
|
<field name="sequence">13</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_lead_lost')"/>
|
<field name="parent_id" eval="ref('mt_lead_lost')"/>
|
||||||
|
|
|
@ -343,6 +343,7 @@
|
||||||
help="Leads that are assigned to any sales teams I am member of"/>
|
help="Leads that are assigned to any sales teams I am member of"/>
|
||||||
<filter string="Dead" name="dead"
|
<filter string="Dead" name="dead"
|
||||||
domain="[('probability', '=', '0'), ('stage_id.fold', '=', True)]"/>
|
domain="[('probability', '=', '0'), ('stage_id.fold', '=', True)]"/>
|
||||||
|
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||||
<separator />
|
<separator />
|
||||||
<filter string="Available for mass mailing"
|
<filter string="Available for mass mailing"
|
||||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||||
|
@ -562,6 +563,7 @@
|
||||||
<filter string="My Team(s)"
|
<filter string="My Team(s)"
|
||||||
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
|
domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}"
|
||||||
help="Opportunities that are assigned to any sales teams I am member of"/>
|
help="Opportunities that are assigned to any sales teams I am member of"/>
|
||||||
|
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<group expand="0" string="Group By..." colspan="16">
|
<group expand="0" string="Group By..." colspan="16">
|
||||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||||
|
|
|
@ -171,6 +171,7 @@
|
||||||
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/>
|
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter string="Unassigned Phonecalls" icon="terp-personal-" domain="[('user_id','=',False)]" help="Unassigned Phonecalls"/>
|
<filter string="Unassigned Phonecalls" icon="terp-personal-" domain="[('user_id','=',False)]" help="Unassigned Phonecalls"/>
|
||||||
|
<filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter string="Phone Calls Assigned to Me or My Team(s)" icon="terp-personal+" domain="['|', ('section_id.user_id','=',uid), ('user_id', '=', uid)]"
|
<filter string="Phone Calls Assigned to Me or My Team(s)" icon="terp-personal+" domain="['|', ('section_id.user_id','=',uid), ('user_id', '=', uid)]"
|
||||||
help="Phone Calls Assigned to the current user or with a team having the current user as team leader"/>
|
help="Phone Calls Assigned to the current user or with a team having the current user as team leader"/>
|
||||||
|
|
|
@ -89,10 +89,6 @@ class crm_lead_report(osv.osv):
|
||||||
id,
|
id,
|
||||||
c.date_deadline,
|
c.date_deadline,
|
||||||
|
|
||||||
to_char(c.create_date, 'YYYY') as creation_year,
|
|
||||||
to_char(c.create_date, 'MM') as creation_month,
|
|
||||||
to_char(c.create_date, 'YYYY-MM-DD') as creation_day,
|
|
||||||
|
|
||||||
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
|
to_char(c.date_open, 'YYYY-MM-DD') as opening_date,
|
||||||
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
|
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,17 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_report_crm_lead_graph_two" model="ir.ui.view">
|
||||||
|
<field name="name">crm.lead.report.graph.two</field>
|
||||||
|
<field name="model">crm.lead.report</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<graph string="Leads Analysis" type="pivot" stacked="True">
|
||||||
|
<field name="create_date" type="row"/>
|
||||||
|
<field name="user_id" type="col"/>
|
||||||
|
</graph>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="view_report_crm_opportunity_graph" model="ir.ui.view">
|
<record id="view_report_crm_opportunity_graph" model="ir.ui.view">
|
||||||
<field name="name">crm.opportunity.report.graph</field>
|
<field name="name">crm.opportunity.report.graph</field>
|
||||||
<field name="model">crm.lead.report</field>
|
<field name="model">crm.lead.report</field>
|
||||||
|
@ -23,7 +34,7 @@
|
||||||
<field name="date_deadline" type="row"/>
|
<field name="date_deadline" type="row"/>
|
||||||
<field name="user_id" type="col"/>
|
<field name="user_id" type="col"/>
|
||||||
<field name="stage_id" type="col"/>
|
<field name="stage_id" type="col"/>
|
||||||
<field name="probable_revenue" type="measure"/>
|
<field name="planned_revenue" type="measure"/>
|
||||||
</graph>
|
</graph>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
@ -84,7 +95,7 @@
|
||||||
<filter string="Creation date (week)" icon="terp-go-year"
|
<filter string="Creation date (week)" icon="terp-go-year"
|
||||||
domain="[]" context="{'group_by':'create_date:week'}"/>
|
domain="[]" context="{'group_by':'create_date:week'}"/>
|
||||||
<filter string="Creation date (month)" icon="terp-go-year"
|
<filter string="Creation date (month)" icon="terp-go-year"
|
||||||
domain="[]" context="{'group_by':'create_date:month'}"/>
|
domain="[]" context="{'group_by':'create_date:month'}" name="month"/>
|
||||||
<filter string="Creation date (year)" icon="terp-go-year"
|
<filter string="Creation date (year)" icon="terp-go-year"
|
||||||
domain="[]" context="{'group_by':'create_date:year'}"/>
|
domain="[]" context="{'group_by':'create_date:year'}"/>
|
||||||
<separator orientation="vertical" />
|
<separator orientation="vertical" />
|
||||||
|
|
|
@ -140,8 +140,8 @@
|
||||||
</group>
|
</group>
|
||||||
<group col="2">
|
<group col="2">
|
||||||
<separator string="Categorization" colspan="2"/>
|
<separator string="Categorization" colspan="2"/>
|
||||||
<field name="type_id" widget="selection" readonly="1"/>
|
<field name="type_id" readonly="1"/>
|
||||||
<field name="channel_id" widget="selection" readonly="1"/>
|
<field name="channel_id" readonly="1"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<separator string="Details" />
|
<separator string="Details" />
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<h3>Body</h3>
|
<h3>Body</h3>
|
||||||
<field name="body_html" width="250" height="450"
|
<field name="body_html" width="250" height="450"
|
||||||
placeholder="Rich-text/HTML content of the message (placeholders may be used here)"/>
|
placeholder="Rich-text/HTML content of the message (placeholders may be used here)"/>
|
||||||
<field name="attachment_ids" nolabel="1" widget="many2many_binary"/>
|
<field name="attachment_ids" widget="many2many_binary"/>
|
||||||
</page>
|
</page>
|
||||||
<page string="Advanced Settings">
|
<page string="Advanced Settings">
|
||||||
<group>
|
<group>
|
||||||
|
|
|
@ -219,6 +219,7 @@ class event_event(osv.osv):
|
||||||
]
|
]
|
||||||
|
|
||||||
def onchange_event_type(self, cr, uid, ids, type_event, context=None):
|
def onchange_event_type(self, cr, uid, ids, type_event, context=None):
|
||||||
|
values = {}
|
||||||
if type_event:
|
if type_event:
|
||||||
type_info = self.pool.get('event.type').browse(cr,uid,type_event,context)
|
type_info = self.pool.get('event.type').browse(cr,uid,type_event,context)
|
||||||
dic ={
|
dic ={
|
||||||
|
@ -228,7 +229,8 @@ class event_event(osv.osv):
|
||||||
'seats_min': type_info.default_registration_min,
|
'seats_min': type_info.default_registration_min,
|
||||||
'seats_max': type_info.default_registration_max,
|
'seats_max': type_info.default_registration_max,
|
||||||
}
|
}
|
||||||
return {'value': dic}
|
values.update(dic)
|
||||||
|
return values
|
||||||
|
|
||||||
def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
|
def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
|
||||||
res = {'value':{}}
|
res = {'value':{}}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Finnish translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-18 20:10+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Finnish <fi@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-19 05:23+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: model:ir.model,name:event_sale.model_product_product
|
||||||
|
msgid "Product"
|
||||||
|
msgstr "Tuote"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: help:product.product,event_ok:0
|
||||||
|
msgid ""
|
||||||
|
"Determine if a product needs to create automatically an event registration "
|
||||||
|
"at the confirmation of a sales order line."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: help:sale.order.line,event_id:0
|
||||||
|
msgid ""
|
||||||
|
"Choose an event and it will automatically create a registration for this "
|
||||||
|
"event."
|
||||||
|
msgstr ""
|
||||||
|
"Valitse tapahtuma ja se luo automaattisesti rekisteröinnin kyseiseen "
|
||||||
|
"tapahtumaan."
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: model:event.event,name:event_sale.event_technical_training
|
||||||
|
msgid "Technical training in Grand-Rosiere"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: help:product.product,event_type_id:0
|
||||||
|
msgid ""
|
||||||
|
"Select event types so when we use this product in sales order lines, it will "
|
||||||
|
"filter events of this type only."
|
||||||
|
msgstr ""
|
||||||
|
"Valitse tapahtumatyypit, joiden avulla myyntitilausrivillä voidaan suodattaa "
|
||||||
|
"vain tämän tyyppiset tapahtumat."
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: field:product.product,event_type_id:0
|
||||||
|
msgid "Type of Event"
|
||||||
|
msgstr "Tapahtumatyyppi"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: field:sale.order.line,event_ok:0
|
||||||
|
msgid "event_ok"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: field:product.product,event_ok:0
|
||||||
|
msgid "Event Subscription"
|
||||||
|
msgstr "Tapahtumaan rekisteröityminen"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: field:sale.order.line,event_type_id:0
|
||||||
|
msgid "Event Type"
|
||||||
|
msgstr "Tapahtumatyyppi"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: model:product.template,name:event_sale.event_product_product_template
|
||||||
|
msgid "Technical Training"
|
||||||
|
msgstr "Tekninen koulutus"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: code:addons/event_sale/event_sale.py:88
|
||||||
|
#, python-format
|
||||||
|
msgid "The registration %s has been created from the Sales Order %s."
|
||||||
|
msgstr "Rekisteröinti %s on luotu myyntitilaukselta %s."
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: field:sale.order.line,event_id:0
|
||||||
|
msgid "Event"
|
||||||
|
msgstr "Tapahtuma"
|
||||||
|
|
||||||
|
#. module: event_sale
|
||||||
|
#: model:ir.model,name:event_sale.model_sale_order_line
|
||||||
|
msgid "Sales Order Line"
|
||||||
|
msgstr "Myyntitilausrivi"
|
|
@ -187,7 +187,7 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
|
||||||
for server in self.browse(cr, uid, ids, context=context):
|
for server in self.browse(cr, uid, ids, context=context):
|
||||||
_logger.info('start checking for new emails on %s server %s', server.type, server.name)
|
_logger.info('start checking for new emails on %s server %s', server.type, server.name)
|
||||||
context.update({'fetchmail_server_id': server.id, 'server_type': server.type})
|
context.update({'fetchmail_server_id': server.id, 'server_type': server.type})
|
||||||
count = 0
|
count, failed = 0, 0
|
||||||
imap_server = False
|
imap_server = False
|
||||||
pop_server = False
|
pop_server = False
|
||||||
if server.type == 'imap':
|
if server.type == 'imap':
|
||||||
|
@ -196,20 +196,26 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
|
||||||
imap_server.select()
|
imap_server.select()
|
||||||
result, data = imap_server.search(None, '(UNSEEN)')
|
result, data = imap_server.search(None, '(UNSEEN)')
|
||||||
for num in data[0].split():
|
for num in data[0].split():
|
||||||
|
res_id = None
|
||||||
result, data = imap_server.fetch(num, '(RFC822)')
|
result, data = imap_server.fetch(num, '(RFC822)')
|
||||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
imap_server.store(num, '-FLAGS', '\\Seen')
|
||||||
data[0][1],
|
try:
|
||||||
save_original=server.original,
|
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||||
strip_attachments=(not server.attach),
|
data[0][1],
|
||||||
context=context)
|
save_original=server.original,
|
||||||
|
strip_attachments=(not server.attach),
|
||||||
|
context=context)
|
||||||
|
except Exception:
|
||||||
|
_logger.exception('Failed to process mail from %s server %s.', server.type, server.name)
|
||||||
|
failed += 1
|
||||||
if res_id and server.action_id:
|
if res_id and server.action_id:
|
||||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
||||||
imap_server.store(num, '+FLAGS', '\\Seen')
|
imap_server.store(num, '+FLAGS', '\\Seen')
|
||||||
cr.commit()
|
cr.commit()
|
||||||
count += 1
|
count += 1
|
||||||
_logger.info("fetched/processed %s email(s) on %s server %s", count, server.type, server.name)
|
_logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", count, server.type, server.name, (count - failed), failed)
|
||||||
except Exception:
|
except Exception:
|
||||||
_logger.exception("Failed to fetch mail from %s server %s.", server.type, server.name)
|
_logger.exception("General failure when trying to fetch mail from %s server %s.", server.type, server.name)
|
||||||
finally:
|
finally:
|
||||||
if imap_server:
|
if imap_server:
|
||||||
imap_server.close()
|
imap_server.close()
|
||||||
|
@ -222,18 +228,23 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
|
||||||
for num in range(1, numMsgs + 1):
|
for num in range(1, numMsgs + 1):
|
||||||
(header, msges, octets) = pop_server.retr(num)
|
(header, msges, octets) = pop_server.retr(num)
|
||||||
msg = '\n'.join(msges)
|
msg = '\n'.join(msges)
|
||||||
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
res_id = None
|
||||||
msg,
|
try:
|
||||||
save_original=server.original,
|
res_id = mail_thread.message_process(cr, uid, server.object_id.model,
|
||||||
strip_attachments=(not server.attach),
|
msg,
|
||||||
context=context)
|
save_original=server.original,
|
||||||
|
strip_attachments=(not server.attach),
|
||||||
|
context=context)
|
||||||
|
except Exception:
|
||||||
|
_logger.exception('Failed to process mail from %s server %s.', server.type, server.name)
|
||||||
|
failed += 1
|
||||||
if res_id and server.action_id:
|
if res_id and server.action_id:
|
||||||
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
|
||||||
pop_server.dele(num)
|
pop_server.dele(num)
|
||||||
cr.commit()
|
cr.commit()
|
||||||
_logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name)
|
_logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", numMsgs, server.type, server.name, (numMsgs - failed), failed)
|
||||||
except Exception:
|
except Exception:
|
||||||
_logger.exception("Failed to fetch mail from %s server %s.", server.type, server.name)
|
_logger.exception("General failure when trying to fetch mail from %s server %s.", server.type, server.name)
|
||||||
finally:
|
finally:
|
||||||
if pop_server:
|
if pop_server:
|
||||||
pop_server.quit()
|
pop_server.quit()
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
<section class="oe_container">
|
|
||||||
<div class="oe_row oe_spaced">
|
|
||||||
<div class="oe_span12">
|
|
||||||
<h2 class="oe_slogan">Drive Engagement with Gamification</h2>
|
|
||||||
<h3 class="oe_slogan">Leverage natural desire for competition</h3>
|
|
||||||
<p class="oe_mt32">
|
|
||||||
Reinforce good habits and improve win rates with real-time recognition and rewards inspired by <a href="http://en.wikipedia.org/wiki/Gamification">game mechanics</a>. Align teams around clear business objectives with challenges, personal objectives and team leader boards.
|
|
||||||
</p>
|
|
||||||
<div class="oe_span4 oe_centered">
|
|
||||||
<h3>Leaderboards</h3>
|
|
||||||
<div class="oe_row_img oe_centered">
|
|
||||||
<img class="oe_picture" src="crm_game_01.png">
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Promote leaders and competition amongst sales team with performance ratios.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="oe_span4 oe_centered">
|
|
||||||
<h3>Personnal Objectives</h3>
|
|
||||||
<div class="oe_row_img">
|
|
||||||
<img class="oe_picture" src="crm_game_02.png">
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Assign clear goals to users to align them with the company objectives.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="oe_span4 oe_centered">
|
|
||||||
<h3>Visual Information</h3>
|
|
||||||
<div class="oe_row_img oe_centered">
|
|
||||||
<img class="oe_picture" src="crm_game_03.png">
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
See in an glance the progress of each user.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
<section class="oe_container oe_dark">
|
|
||||||
<div class="oe_row oe_spaced">
|
|
||||||
<h2 class="oe_slogan">Create custom Challenges</h2>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<p class="oe_mt32">
|
|
||||||
Use predefined goals to generate easily your own challenges. Assign it to a team or individual users. Receive feedback as often as needed: daily, weekly... Repeat it automatically to compare progresses through time.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<div class="oe_row_img oe_centered">
|
|
||||||
<img class="oe_picture oe_screenshot" src="crm_sc_05.png">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="oe_container">
|
|
||||||
<div class="oe_row oe_spaced">
|
|
||||||
<h2 class="oe_slogan">Motivate with Badges</h2>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<div class="oe_row_img oe_centered">
|
|
||||||
<img class="oe_picture" src="crm_linkedin.png">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<p class="oe_mt32">
|
|
||||||
Inspire achievement with recognition of coworker's good work by rewarding badges. These can be deserved manually or upon completion of challenges. Add fun to the competition with rare badges.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="oe_container oe_dark">
|
|
||||||
<div class="oe_row oe_spaced">
|
|
||||||
<h2 class="oe_slogan">Adapt to any module</h2>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<p class="oe_mt32">
|
|
||||||
Create goals linked to any module. The evaluation system is very flexible and can be used for many different tasks : sales evaluation, creation of events, project completion or even helping new users to complete their profile.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="oe_span6">
|
|
||||||
<div class="oe_row_img oe_centered">
|
|
||||||
<img class="oe_picture oe_screenshot" src="crm_sc_02.png">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -4,10 +4,10 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: OpenERP Server 8.0alpha1\n"
|
"Project-Id-Version: OpenERP Server 7.saas~3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2014-02-13 15:08+0000\n"
|
"POT-Creation-Date: 2014-02-13 15:03+0000\n"
|
||||||
"PO-Revision-Date: 2014-02-13 15:08+0000\n"
|
"PO-Revision-Date: 2014-02-13 15:03+0000\n"
|
||||||
"Last-Translator: <>\n"
|
"Last-Translator: <>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
|
|
@ -433,7 +433,7 @@ class gamification_challenge(osv.Model):
|
||||||
##### JS utilities #####
|
##### JS utilities #####
|
||||||
|
|
||||||
def _get_serialized_challenge_lines(self, cr, uid, challenge, user_id=False, restrict_goal_ids=False, restrict_top=False, context=None):
|
def _get_serialized_challenge_lines(self, cr, uid, challenge, user_id=False, restrict_goal_ids=False, restrict_top=False, context=None):
|
||||||
"""Return a serialised version of the goals information
|
"""Return a serialised version of the goals information if the user has not completed every goal
|
||||||
|
|
||||||
:challenge: browse record of challenge to compute
|
:challenge: browse record of challenge to compute
|
||||||
:user_id: res.users id of the user retrieving progress (False if no distinction, only for ranking challenges)
|
:user_id: res.users id of the user retrieving progress (False if no distinction, only for ranking challenges)
|
||||||
|
@ -487,6 +487,7 @@ class gamification_challenge(osv.Model):
|
||||||
(start_date, end_date) = start_end_date_for_period(challenge.period)
|
(start_date, end_date) = start_end_date_for_period(challenge.period)
|
||||||
|
|
||||||
res_lines = []
|
res_lines = []
|
||||||
|
all_reached = True
|
||||||
for line in challenge.line_ids:
|
for line in challenge.line_ids:
|
||||||
line_data = {
|
line_data = {
|
||||||
'name': line.definition_id.name,
|
'name': line.definition_id.name,
|
||||||
|
@ -537,6 +538,8 @@ class gamification_challenge(osv.Model):
|
||||||
'completeness': goal.completeness,
|
'completeness': goal.completeness,
|
||||||
'state': goal.state,
|
'state': goal.state,
|
||||||
})
|
})
|
||||||
|
if goal.state != 'reached':
|
||||||
|
all_reached = False
|
||||||
else:
|
else:
|
||||||
ranking += 1
|
ranking += 1
|
||||||
if user_id and goal.user_id.id == user_id:
|
if user_id and goal.user_id.id == user_id:
|
||||||
|
@ -554,8 +557,12 @@ class gamification_challenge(osv.Model):
|
||||||
'completeness': goal.completeness,
|
'completeness': goal.completeness,
|
||||||
'state': goal.state,
|
'state': goal.state,
|
||||||
})
|
})
|
||||||
|
if goal.state != 'reached':
|
||||||
|
all_reached = False
|
||||||
if goal_ids:
|
if goal_ids:
|
||||||
res_lines.append(line_data)
|
res_lines.append(line_data)
|
||||||
|
if all_reached:
|
||||||
|
return []
|
||||||
return res_lines
|
return res_lines
|
||||||
|
|
||||||
##### Reporting #####
|
##### Reporting #####
|
||||||
|
@ -620,22 +627,28 @@ class gamification_challenge(osv.Model):
|
||||||
return self.write(cr, uid, challenge.id, {'last_report_date': fields.date.today()}, context=context)
|
return self.write(cr, uid, challenge.id, {'last_report_date': fields.date.today()}, context=context)
|
||||||
|
|
||||||
##### Challenges #####
|
##### Challenges #####
|
||||||
|
# TODO in trunk, remove unused parameter user_id
|
||||||
def accept_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
|
def accept_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
|
||||||
"""The user accept the suggested challenge"""
|
"""The user accept the suggested challenge"""
|
||||||
user_id = user_id or uid
|
return self._accept_challenge(cr, uid, uid, challenge_ids, context=context)
|
||||||
|
|
||||||
|
def _accept_challenge(self, cr, uid, user_id, challenge_ids, context=None):
|
||||||
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
|
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
|
||||||
message = "%s has joined the challenge" % user.name
|
message = "%s has joined the challenge" % user.name
|
||||||
self.message_post(cr, uid, challenge_ids, body=message, context=context)
|
self.message_post(cr, SUPERUSER_ID, challenge_ids, body=message, context=context)
|
||||||
self.write(cr, SUPERUSER_ID, challenge_ids, {'invited_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context)
|
self.write(cr, SUPERUSER_ID, challenge_ids, {'invited_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context)
|
||||||
return self.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
|
return self.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||||
|
|
||||||
|
# TODO in trunk, remove unused parameter user_id
|
||||||
def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
|
def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
|
||||||
"""The user discard the suggested challenge"""
|
"""The user discard the suggested challenge"""
|
||||||
user_id = user_id or uid
|
return self._discard_challenge(cr, uid, uid, challenge_ids, context=context)
|
||||||
|
|
||||||
|
def _discard_challenge(self, cr, uid, user_id, challenge_ids, context=None):
|
||||||
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
|
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
|
||||||
message = "%s has refused the challenge" % user.name
|
message = "%s has refused the challenge" % user.name
|
||||||
self.message_post(cr, SUPERUSER_ID, challenge_ids, body=message, context=context)
|
self.message_post(cr, SUPERUSER_ID, challenge_ids, body=message, context=context)
|
||||||
return self.write(cr, uid, challenge_ids, {'invited_user_ids': (3, user_id)}, context=context)
|
return self.write(cr, SUPERUSER_ID, challenge_ids, {'invited_user_ids': (3, user_id)}, context=context)
|
||||||
|
|
||||||
def reply_challenge_wizard(self, cr, uid, challenge_id, context=None):
|
def reply_challenge_wizard(self, cr, uid, challenge_id, context=None):
|
||||||
result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'challenge_wizard')
|
result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'challenge_wizard')
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
from openerp import SUPERUSER_ID
|
||||||
from openerp.osv import osv
|
from openerp.osv import osv
|
||||||
from challenge import MAX_VISIBILITY_RANKING
|
from challenge import MAX_VISIBILITY_RANKING
|
||||||
|
|
||||||
|
@ -39,10 +40,10 @@ class res_users_gamification_group(osv.Model):
|
||||||
user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]
|
user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]
|
||||||
|
|
||||||
challenge_obj = self.pool.get('gamification.challenge')
|
challenge_obj = self.pool.get('gamification.challenge')
|
||||||
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
|
challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', user_group_ids)], context=context)
|
||||||
if challenge_ids:
|
if challenge_ids:
|
||||||
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
|
challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
|
||||||
challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
|
challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||||
return write_res
|
return write_res
|
||||||
|
|
||||||
def create(self, cr, uid, vals, context=None):
|
def create(self, cr, uid, vals, context=None):
|
||||||
|
@ -54,10 +55,10 @@ class res_users_gamification_group(osv.Model):
|
||||||
user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]
|
user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]
|
||||||
|
|
||||||
challenge_obj = self.pool.get('gamification.challenge')
|
challenge_obj = self.pool.get('gamification.challenge')
|
||||||
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
|
challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', user_group_ids)], context=context)
|
||||||
if challenge_ids:
|
if challenge_ids:
|
||||||
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, write_res)]}, context=context)
|
challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, write_res)]}, context=context)
|
||||||
challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
|
challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||||
return write_res
|
return write_res
|
||||||
|
|
||||||
# def get_goals_todo_info(self, cr, uid, context=None):
|
# def get_goals_todo_info(self, cr, uid, context=None):
|
||||||
|
@ -130,8 +131,8 @@ class res_groups_gamification_group(osv.Model):
|
||||||
user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]]
|
user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]]
|
||||||
|
|
||||||
challenge_obj = self.pool.get('gamification.challenge')
|
challenge_obj = self.pool.get('gamification.challenge')
|
||||||
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context)
|
challenge_ids = challenge_obj.search(cr, SUPERUSER_ID, [('autojoin_group_id', 'in', ids)], context=context)
|
||||||
if challenge_ids:
|
if challenge_ids:
|
||||||
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
|
challenge_obj.write(cr, SUPERUSER_ID, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
|
||||||
challenge_obj.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
|
challenge_obj.generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context)
|
||||||
return write_res
|
return write_res
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
# License, or (at your option) any later version.
|
# License, or (at your option) any later version.
|
||||||
|
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
@ -36,21 +37,185 @@ from dateutil import parser
|
||||||
import pytz
|
import pytz
|
||||||
from openerp.osv import fields, osv
|
from openerp.osv import fields, osv
|
||||||
from openerp.osv import osv
|
from openerp.osv import osv
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta(type):
|
||||||
|
""" This Meta class allow to define class as a structure, and so instancied variable
|
||||||
|
in __init__ to avoid to have side effect alike 'static' variable """
|
||||||
|
def __new__(typ, name, parents, attrs):
|
||||||
|
methods = dict((k, v) for k, v in attrs.iteritems()
|
||||||
|
if callable(v))
|
||||||
|
attrs = dict((k, v) for k, v in attrs.iteritems()
|
||||||
|
if not callable(v))
|
||||||
|
|
||||||
|
def init(self, **kw):
|
||||||
|
for k, v in attrs.iteritems():
|
||||||
|
setattr(self, k, v)
|
||||||
|
for k, v in kw.iteritems():
|
||||||
|
assert k in attrs
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
methods['__init__'] = init
|
||||||
|
methods['__getitem__'] = getattr
|
||||||
|
return type.__new__(typ, name, parents, methods)
|
||||||
|
|
||||||
|
class Struct(object):
|
||||||
|
__metaclass__ = Meta
|
||||||
|
|
||||||
|
class OpenerpEvent(Struct):
|
||||||
|
event = False
|
||||||
|
found = False
|
||||||
|
event_id = False
|
||||||
|
isRecurrence = False
|
||||||
|
isInstance = False
|
||||||
|
update = False
|
||||||
|
status = False
|
||||||
|
attendee_id = False
|
||||||
|
synchro = False
|
||||||
|
|
||||||
|
class GmailEvent(Struct):
|
||||||
|
event = False
|
||||||
|
found = False
|
||||||
|
isRecurrence = False
|
||||||
|
isInstance = False
|
||||||
|
update = False
|
||||||
|
status = False
|
||||||
|
|
||||||
|
class SyncEvent(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.OE = OpenerpEvent()
|
||||||
|
self.GG = GmailEvent()
|
||||||
|
self.OP = None
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return getattr(self,key)
|
||||||
|
|
||||||
|
def compute_OP(self):
|
||||||
|
#If event are already in Gmail and in OpenERP
|
||||||
|
if self.OE.found and self.GG.found:
|
||||||
|
#If the event has been deleted from one side, we delete on other side !
|
||||||
|
if self.OE.status != self.GG.status:
|
||||||
|
self.OP = Delete((self.OE.status and "OE") or (self.GG.status and "GG"),
|
||||||
|
'The event has been deleted from one side, we delete on other side !' )
|
||||||
|
#If event is not deleted !
|
||||||
|
elif self.OE.status and self.GG.status:
|
||||||
|
if self.OE.update.split('.')[0] != self.GG.update.split('.')[0]:
|
||||||
|
if self.OE.update < self.GG.update:
|
||||||
|
tmpSrc = 'GG'
|
||||||
|
elif self.OE.update > self.GG.update:
|
||||||
|
tmpSrc = 'OE'
|
||||||
|
assert tmpSrc in ['GG','OE']
|
||||||
|
|
||||||
|
|
||||||
|
#if self.OP.action == None:
|
||||||
|
if self[tmpSrc].isRecurrence:
|
||||||
|
if self[tmpSrc].status:
|
||||||
|
self.OP = Update(tmpSrc, 'Only need to update, because i\'m active')
|
||||||
|
else:
|
||||||
|
self.OP = Exclude(tmpSrc, 'Need to Exclude (Me = First event from recurrence) from recurrence')
|
||||||
|
|
||||||
|
elif self[tmpSrc].isInstance:
|
||||||
|
self.OP= Update(tmpSrc, 'Only need to update, because already an exclu');
|
||||||
|
else:
|
||||||
|
self.OP = Update(tmpSrc, 'Simply Update... I\'m a single event');
|
||||||
|
#end-if self.OP.action == None:
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not self.OE.synchro or self.OE.synchro.split('.')[0] < self.OE.update.split('.')[0]:
|
||||||
|
self.OP = Update('OE','Event already updated by another user, but not synchro with my google calendar')
|
||||||
|
#import ipdb; ipdb.set_trace();
|
||||||
|
else:
|
||||||
|
self.OP = NothingToDo("",'Not update needed')
|
||||||
|
else:
|
||||||
|
self.OP = NothingToDo("", "Both are already deleted");
|
||||||
|
|
||||||
|
# New in openERP... Create on create_events of synchronize function
|
||||||
|
elif self.OE.found and not self.GG.found:
|
||||||
|
#Has been deleted from gmail
|
||||||
|
if self.OE.status:
|
||||||
|
self.OP = Delete('OE', 'Removed from GOOGLE')
|
||||||
|
else:
|
||||||
|
self.OP = NothingToDo("","Already Deleted in gmail and unlinked in OpenERP")
|
||||||
|
elif self.GG.found and not self.OE.found:
|
||||||
|
tmpSrc = 'GG'
|
||||||
|
if not self.GG.status and not self.GG.isInstance:
|
||||||
|
# don't need to make something... because event has been created and deleted before the synchronization
|
||||||
|
self.OP = NothingToDo("", 'Nothing to do... Create and Delete directly')
|
||||||
|
else:
|
||||||
|
if self.GG.isInstance:
|
||||||
|
if self[tmpSrc].status:
|
||||||
|
self.OP = Exclude(tmpSrc, 'Need to create the new exclu')
|
||||||
|
else:
|
||||||
|
self.OP = Exclude(tmpSrc, 'Need to copy and Exclude')
|
||||||
|
else:
|
||||||
|
self.OP = Create(tmpSrc, 'New EVENT CREATE from GMAIL')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__repr__()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
myPrint = "---- A SYNC EVENT ---"
|
||||||
|
myPrint += "\n ID OE: %s " % (self.OE.event and self.OE.event.id)
|
||||||
|
myPrint += "\n ID GG: %s " % (self.GG.event and self.GG.event.get('id', False))
|
||||||
|
myPrint += "\n Name OE: %s " % (self.OE.event and self.OE.event.name)
|
||||||
|
myPrint += "\n Name GG: %s " % (self.GG.event and self.GG.event.get('summary', False))
|
||||||
|
myPrint += "\n Found OE:%5s vs GG: %5s" % (self.OE.found, self.GG.found)
|
||||||
|
myPrint += "\n Recurrence OE:%5s vs GG: %5s" % (self.OE.isRecurrence, self.GG.isRecurrence)
|
||||||
|
myPrint += "\n Instance OE:%5s vs GG: %5s" % (self.OE.isInstance, self.GG.isInstance)
|
||||||
|
myPrint += "\n Synchro OE: %10s " % (self.OE.synchro)
|
||||||
|
myPrint += "\n Update OE: %10s " % (self.OE.update)
|
||||||
|
myPrint += "\n Update GG: %10s " % (self.GG.update)
|
||||||
|
myPrint += "\n Status OE:%5s vs GG: %5s" % (self.OE.status, self.GG.status)
|
||||||
|
if (self.OP is None):
|
||||||
|
myPrint += "\n Action %s" % "---!!!---NONE---!!!---"
|
||||||
|
else:
|
||||||
|
myPrint += "\n Action %s" % type(self.OP).__name__
|
||||||
|
myPrint += "\n Source %s" % (self.OP.src)
|
||||||
|
myPrint += "\n comment %s" % (self.OP.info)
|
||||||
|
return myPrint
|
||||||
|
|
||||||
|
|
||||||
|
class SyncOperation(object):
|
||||||
|
def __init__(self, src,info, **kw):
|
||||||
|
self.src = src
|
||||||
|
self.info = info
|
||||||
|
for k,v in kw.items():
|
||||||
|
setattr(self,k,v)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'in__STR__'
|
||||||
|
|
||||||
|
class Create(SyncOperation):
|
||||||
|
pass
|
||||||
|
class Update(SyncOperation):
|
||||||
|
pass
|
||||||
|
class Delete(SyncOperation):
|
||||||
|
pass
|
||||||
|
class NothingToDo(SyncOperation):
|
||||||
|
pass
|
||||||
|
class Exclude(SyncOperation):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class google_calendar(osv.AbstractModel):
|
class google_calendar(osv.AbstractModel):
|
||||||
STR_SERVICE = 'calendar'
|
STR_SERVICE = 'calendar'
|
||||||
_name = 'google.%s' % STR_SERVICE
|
_name = 'google.%s' % STR_SERVICE
|
||||||
|
|
||||||
def generate_data(self, cr, uid, event, context=None):
|
def generate_data(self, cr, uid, event, context=None):
|
||||||
if event.allday:
|
if event.allday:
|
||||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) , context=context).isoformat('T').split('T')[0]
|
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) , context=context).isoformat('T').split('T')[0]
|
||||||
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration), context=context).isoformat('T').split('T')[0]
|
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(hours=event.duration), context=context).isoformat('T').split('T')[0]
|
||||||
type = 'date'
|
type = 'date'
|
||||||
|
vstype = 'dateTime'
|
||||||
else:
|
else:
|
||||||
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
start_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||||
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
end_date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(event.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context).isoformat('T')
|
||||||
type = 'dateTime'
|
type = 'dateTime'
|
||||||
|
vstype = 'date'
|
||||||
attendee_list = []
|
attendee_list = []
|
||||||
|
|
||||||
for attendee in event.attendee_ids:
|
for attendee in event.attendee_ids:
|
||||||
|
@ -64,10 +229,12 @@ class google_calendar(osv.AbstractModel):
|
||||||
"description": event.description or '',
|
"description": event.description or '',
|
||||||
"start":{
|
"start":{
|
||||||
type:start_date,
|
type:start_date,
|
||||||
|
vstype:None,
|
||||||
'timeZone':'UTC'
|
'timeZone':'UTC'
|
||||||
},
|
},
|
||||||
"end":{
|
"end":{
|
||||||
type:end_date,
|
type:end_date,
|
||||||
|
vstype:None,
|
||||||
'timeZone':'UTC'
|
'timeZone':'UTC'
|
||||||
},
|
},
|
||||||
"attendees":attendee_list,
|
"attendees":attendee_list,
|
||||||
|
@ -80,10 +247,13 @@ class google_calendar(osv.AbstractModel):
|
||||||
if not event.active:
|
if not event.active:
|
||||||
data["state"] = "cancelled"
|
data["state"] = "cancelled"
|
||||||
|
|
||||||
|
if not self.get_need_synchro_attendee(cr,uid,context=context):
|
||||||
|
data.pop("attendees")
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def create_an_event(self, cr, uid,event, context=None):
|
def create_an_event(self, cr, uid,event, context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
data = self.generate_data(cr, uid,event, context=context)
|
data = self.generate_data(cr, uid,event, context=context)
|
||||||
|
|
||||||
|
@ -94,7 +264,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
return gs_pool._do_request(cr, uid, url, data_json, headers, type='POST', context=context)
|
return gs_pool._do_request(cr, uid, url, data_json, headers, type='POST', context=context)
|
||||||
|
|
||||||
def delete_an_event(self, cr, uid,event_id, context=None):
|
def delete_an_event(self, cr, uid,event_id, context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'access_token' : self.get_token(cr,uid,context)
|
'access_token' : self.get_token(cr,uid,context)
|
||||||
|
@ -108,12 +278,14 @@ class google_calendar(osv.AbstractModel):
|
||||||
if not token:
|
if not token:
|
||||||
token = self.get_token(cr,uid,context)
|
token = self.get_token(cr,uid,context)
|
||||||
|
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'fields': 'items,nextPageToken',
|
'fields': 'items,nextPageToken',
|
||||||
'access_token' : token,
|
'access_token' : token,
|
||||||
'maxResults':1000
|
'maxResults':1000,
|
||||||
|
'timeMin': self.get_start_time_to_synchro(cr,uid,context=context).strftime("%Y-%m-%dT%H:%M:%S.%fz"),
|
||||||
|
|
||||||
}
|
}
|
||||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||||
|
|
||||||
|
@ -134,7 +306,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
|
|
||||||
def update_to_google(self, cr, uid, oe_event, google_event, context):
|
def update_to_google(self, cr, uid, oe_event, google_event, context):
|
||||||
calendar_event = self.pool['calendar.event']
|
calendar_event = self.pool['calendar.event']
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
url = "/calendar/v3/calendars/%s/events/%s?fields=%s&access_token=%s" % ('primary', google_event['id'],'id,updated', self.get_token(cr,uid,context))
|
url = "/calendar/v3/calendars/%s/events/%s?fields=%s&access_token=%s" % ('primary', google_event['id'],'id,updated', self.get_token(cr,uid,context))
|
||||||
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||||
|
@ -148,10 +320,10 @@ class google_calendar(osv.AbstractModel):
|
||||||
calendar_event.write(cr, uid, [oe_event.id], {'oe_update_date':update_date})
|
calendar_event.write(cr, uid, [oe_event.id], {'oe_update_date':update_date})
|
||||||
|
|
||||||
if context['curr_attendee']:
|
if context['curr_attendee']:
|
||||||
self.pool.get('calendar.attendee').write(cr,uid,[context['curr_attendee']], {'oe_synchro_date':update_date},context)
|
self.pool['calendar.attendee'].write(cr,uid,[context['curr_attendee']], {'oe_synchro_date':update_date},context)
|
||||||
|
|
||||||
def update_an_event(self, cr, uid,event, context=None):
|
def update_an_event(self, cr, uid,event, context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
data = self.generate_data(cr, uid,event, context=context)
|
data = self.generate_data(cr, uid,event, context=context)
|
||||||
|
|
||||||
|
@ -164,7 +336,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def update_recurrent_event_exclu(self, cr, uid,instance_id,event_ori_google_id,event_new, context=None):
|
def update_recurrent_event_exclu(self, cr, uid,instance_id,event_ori_google_id,event_new, context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
data = self.generate_data(cr, uid,event_new, context=context)
|
data = self.generate_data(cr, uid,event_new, context=context)
|
||||||
|
|
||||||
|
@ -186,7 +358,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
calendar_event = self.pool['calendar.event']
|
calendar_event = self.pool['calendar.event']
|
||||||
res_partner_obj = self.pool['res.partner']
|
res_partner_obj = self.pool['res.partner']
|
||||||
calendar_attendee_obj = self.pool['calendar.attendee']
|
calendar_attendee_obj = self.pool['calendar.attendee']
|
||||||
user_obj = self.pool.get('res.users')
|
user_obj = self.pool['res.users']
|
||||||
myPartnerID = user_obj.browse(cr,uid,uid,context).partner_id.id
|
myPartnerID = user_obj.browse(cr,uid,uid,context).partner_id.id
|
||||||
attendee_record = []
|
attendee_record = []
|
||||||
partner_record = [(4,myPartnerID)]
|
partner_record = [(4,myPartnerID)]
|
||||||
|
@ -203,14 +375,16 @@ class google_calendar(osv.AbstractModel):
|
||||||
|
|
||||||
if google_attendee.get('found',False):
|
if google_attendee.get('found',False):
|
||||||
continue
|
continue
|
||||||
attendee_id = res_partner_obj.search(cr, uid,[('email', '=', google_attendee['email'])], context=context)
|
if self.get_need_synchro_attendee(cr,uid,context=context):
|
||||||
if not attendee_id:
|
attendee_id = res_partner_obj.search(cr, uid,[('email', '=', google_attendee['email'])], context=context)
|
||||||
attendee_id = [res_partner_obj.create(cr, uid,{'email': google_attendee['email'],'Customer': False, 'name': google_attendee.get("displayName",False) or google_attendee['email'] }, context=context)]
|
if not attendee_id:
|
||||||
attendee = res_partner_obj.read(cr, uid, attendee_id[0], ['email'], context=context)
|
attendee_id = [res_partner_obj.create(cr, uid,{'email': google_attendee['email'],'customer': False, 'name': google_attendee.get("displayName",False) or google_attendee['email'] }, context=context)]
|
||||||
partner_record.append((4, attendee.get('id')))
|
attendee = res_partner_obj.read(cr, uid, attendee_id[0], ['email'], context=context)
|
||||||
attendee['partner_id'] = attendee.pop('id')
|
partner_record.append((4, attendee.get('id')))
|
||||||
attendee['state'] = google_attendee['responseStatus']
|
attendee['partner_id'] = attendee.pop('id')
|
||||||
attendee_record.append((0, 0, attendee))
|
attendee['state'] = google_attendee['responseStatus']
|
||||||
|
attendee_record.append((0, 0, attendee))
|
||||||
|
|
||||||
UTC = pytz.timezone('UTC')
|
UTC = pytz.timezone('UTC')
|
||||||
if single_event_dict.get('start') and single_event_dict.get('end'): # If not cancelled
|
if single_event_dict.get('start') and single_event_dict.get('end'): # If not cancelled
|
||||||
if single_event_dict['start'].get('dateTime',False) and single_event_dict['end'].get('dateTime',False):
|
if single_event_dict['start'].get('dateTime',False) and single_event_dict['end'].get('dateTime',False):
|
||||||
|
@ -244,7 +418,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
'location':single_event_dict.get('location',False),
|
'location':single_event_dict.get('location',False),
|
||||||
'class':single_event_dict.get('visibility','public'),
|
'class':single_event_dict.get('visibility','public'),
|
||||||
'oe_update_date':update_date,
|
'oe_update_date':update_date,
|
||||||
# 'google_internal_event_id': single_event_dict.get('id',False),
|
# 'google_internal_event_id': single_event_dict.get('id',False),
|
||||||
})
|
})
|
||||||
|
|
||||||
if single_event_dict.get("recurrence",False):
|
if single_event_dict.get("recurrence",False):
|
||||||
|
@ -261,16 +435,17 @@ class google_calendar(osv.AbstractModel):
|
||||||
res = calendar_event.create(cr, uid, result, context=context)
|
res = calendar_event.create(cr, uid, result, context=context)
|
||||||
|
|
||||||
if context['curr_attendee']:
|
if context['curr_attendee']:
|
||||||
self.pool.get('calendar.attendee').write(cr,uid,[context['curr_attendee']], {'oe_synchro_date':update_date,'google_internal_event_id': single_event_dict.get('id',False)},context)
|
self.pool['calendar.attendee'].write(cr,uid,[context['curr_attendee']], {'oe_synchro_date':update_date,'google_internal_event_id': single_event_dict.get('id',False)},context)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def synchronize_events(self, cr, uid, ids, context=None):
|
def synchronize_events(self, cr, uid, ids, context=None):
|
||||||
gc_obj = self.pool.get('google.calendar')
|
gc_obj = self.pool['google.calendar']
|
||||||
|
|
||||||
|
# Create all new events from OpenERP into Gmail, if that is not recurrent event
|
||||||
self.create_new_events(cr, uid, context=context)
|
self.create_new_events(cr, uid, context=context)
|
||||||
|
|
||||||
self.bind_recurring_events_to_google(cr, uid, context)
|
self.bind_recurring_events_to_google(cr, uid, context)
|
||||||
cr.commit()
|
|
||||||
|
|
||||||
res = self.update_events(cr, uid, context)
|
res = self.update_events(cr, uid, context)
|
||||||
|
|
||||||
|
@ -279,10 +454,9 @@ class google_calendar(osv.AbstractModel):
|
||||||
"url" : ''
|
"url" : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_new_events(self, cr, uid, context):
|
def create_new_events(self, cr, uid, context=None):
|
||||||
gc_pool = self.pool.get('google.calendar')
|
gc_pool = self.pool['google.calendar']
|
||||||
|
ev_obj = self.pool['calendar.event']
|
||||||
calendar_event = self.pool['calendar.event']
|
|
||||||
att_obj = self.pool['calendar.attendee']
|
att_obj = self.pool['calendar.attendee']
|
||||||
user_obj = self.pool['res.users']
|
user_obj = self.pool['res.users']
|
||||||
myPartnerID = user_obj.browse(cr,uid,uid,context=context).partner_id.id
|
myPartnerID = user_obj.browse(cr,uid,uid,context=context).partner_id.id
|
||||||
|
@ -290,75 +464,73 @@ class google_calendar(osv.AbstractModel):
|
||||||
context_norecurrent = context.copy()
|
context_norecurrent = context.copy()
|
||||||
context_norecurrent['virtual_id'] = False
|
context_norecurrent['virtual_id'] = False
|
||||||
|
|
||||||
my_att_ids = att_obj.search(cr, uid,[('partner_id', '=', myPartnerID),('google_internal_event_id', '=', False)], context=context_norecurrent)
|
my_att_ids = att_obj.search(cr, uid,[ ('partner_id', '=', myPartnerID),
|
||||||
|
('google_internal_event_id', '=', False),
|
||||||
|
'|',
|
||||||
|
('event_id.date_deadline','>',self.get_start_time_to_synchro(cr,uid,context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
|
('event_id.end_date','>',self.get_start_time_to_synchro(cr,uid,context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
|
], context=context_norecurrent)
|
||||||
|
|
||||||
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
||||||
if not att.event_id.recurrent_id or att.event_id.recurrent_id == 0:
|
if not att.event_id.recurrent_id or att.event_id.recurrent_id == 0:
|
||||||
response = self.create_an_event(cr,uid,att.event_id,context=context)
|
response = self.create_an_event(cr,uid,att.event_id,context=context)
|
||||||
update_date = datetime.strptime(response['updated'],"%Y-%m-%dT%H:%M:%S.%fz")
|
update_date = datetime.strptime(response['updated'],"%Y-%m-%dT%H:%M:%S.%fz")
|
||||||
calendar_event.write(cr, uid, att.event_id.id, {'oe_update_date':update_date})
|
ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date':update_date})
|
||||||
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date':update_date})
|
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date':update_date})
|
||||||
cr.commit()
|
cr.commit()
|
||||||
return True
|
|
||||||
|
|
||||||
def get_empty_synchro_summarize(self) :
|
def bind_recurring_events_to_google(self, cr, uid, context):
|
||||||
return {
|
ev_obj = self.pool['calendar.event']
|
||||||
#OPENERP
|
att_obj = self.pool['calendar.attendee']
|
||||||
'OE_event' : False,
|
user_obj = self.pool['res.users']
|
||||||
'OE_found' : False,
|
myPartnerID = user_obj.browse(cr,uid,uid,context=context).partner_id.id
|
||||||
'OE_event_id' : False,
|
|
||||||
'OE_isRecurrence':False,
|
|
||||||
'OE_isInstance':False,
|
|
||||||
'OE_update':False,
|
|
||||||
'OE_status':False,
|
|
||||||
'OE_attendee_id': False,
|
|
||||||
'OE_synchro':False,
|
|
||||||
|
|
||||||
#GOOGLE
|
context_norecurrent = context.copy()
|
||||||
'GG_event' : False,
|
context_norecurrent['virtual_id'] = False
|
||||||
'GG_found' : False,
|
context_norecurrent['active_test'] = False
|
||||||
'GG_isRecurrence':False,
|
|
||||||
'GG_isInstance':False,
|
|
||||||
'GG_update':False,
|
|
||||||
'GG_status':False,
|
|
||||||
|
|
||||||
#TO_DO_IN_GOOGLE
|
my_att_ids = att_obj.search(cr, uid,[('partner_id', '=', myPartnerID),('google_internal_event_id', '=', False)], context=context_norecurrent)
|
||||||
'td_action':'', # create, update, delete, None
|
|
||||||
#If 'td_action' in (create , update),
|
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
||||||
# If td_source == OE
|
if att.event_id.recurrent_id and att.event_id.recurrent_id > 0:
|
||||||
# We create in google the event based on OpenERP
|
new_google_internal_event_id = False
|
||||||
# If td_source == GG
|
source_event_record = ev_obj.browse(cr, uid, att.event_id.recurrent_id, context)
|
||||||
# We create in OpenERP the event based on Gmail
|
source_attendee_record_id = att_obj.search(cr, uid, [('partner_id','=', myPartnerID), ('event_id','=',source_event_record.id)], context=context)
|
||||||
#
|
source_attendee_record = att_obj.browse(cr, uid, source_attendee_record_id, context)[0]
|
||||||
#If 'td_action' in (delete),
|
|
||||||
# If td_source == OE
|
if att.event_id.recurrent_id_date and source_event_record.allday and source_attendee_record.google_internal_event_id:
|
||||||
# We delete in OpenERP the event
|
new_google_internal_event_id = source_attendee_record.google_internal_event_id +'_'+ att.event_id.recurrent_id_date.split(' ')[0].replace('-','')
|
||||||
# If td_source == GG
|
elif att.event_id.recurrent_id_date and source_attendee_record.google_internal_event_id:
|
||||||
# We delete in Gmail the event
|
new_google_internal_event_id = source_attendee_record.google_internal_event_id +'_'+ att.event_id.recurrent_id_date.replace('-','').replace(' ','T').replace(':','') + 'Z'
|
||||||
# If td_source == ALL
|
|
||||||
# We delete in openERP AND in Gmail the event
|
|
||||||
'td_source': '', # OE, GG, ALL
|
|
||||||
'td_comment':''
|
|
||||||
|
|
||||||
}
|
if new_google_internal_event_id:
|
||||||
|
#TODO WARNING, NEED TO CHECK THAT EVENT and ALL instance NOT DELETE IN GMAIL BEFORE !
|
||||||
|
res = self.update_recurrent_event_exclu(cr, uid,new_google_internal_event_id, source_attendee_record.google_internal_event_id,att.event_id, context=context)
|
||||||
|
att_obj.write(cr, uid, [att.id], {'google_internal_event_id': new_google_internal_event_id}, context=context)
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
def update_events(self, cr, uid, context):
|
def update_events(self, cr, uid, context=None):
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
calendar_event = self.pool['calendar.event']
|
calendar_event = self.pool['calendar.event']
|
||||||
user_obj = self.pool['res.users']
|
user_obj = self.pool['res.users']
|
||||||
att_obj = self.pool['calendar.attendee']
|
att_obj = self.pool['calendar.attendee']
|
||||||
myPartnerID = user_obj.browse(cr,uid,uid,context=context).partner_id.id
|
myPartnerID = user_obj.browse(cr, uid, uid, context=context).partner_id.id
|
||||||
|
|
||||||
context_novirtual = context.copy()
|
context_novirtual = context.copy()
|
||||||
context_novirtual['virtual_id'] = False
|
context_novirtual['virtual_id'] = False
|
||||||
context_novirtual['active_test'] = False
|
context_novirtual['active_test'] = False
|
||||||
|
|
||||||
all_event_from_google = self.get_event_dict(cr,uid,context=context)
|
all_event_from_google = self.get_event_dict(cr, uid, context=context)
|
||||||
all_new_event_from_google = all_event_from_google.copy()
|
|
||||||
|
|
||||||
# Select all events from OpenERP which have been already synchronized in gmail
|
# Select all events from OpenERP which have been already synchronized in gmail
|
||||||
my_att_ids = att_obj.search(cr, uid,[('partner_id', '=', myPartnerID),('google_internal_event_id', '!=', False)], context=context_novirtual)
|
my_att_ids = att_obj.search(cr, uid,[ ('partner_id', '=', myPartnerID),
|
||||||
|
('google_internal_event_id', '!=', False),
|
||||||
|
'|',
|
||||||
|
('event_id.date_deadline','>',self.get_start_time_to_synchro(cr,uid,context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
|
('event_id.end_date','>',self.get_start_time_to_synchro(cr,uid,context).strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
|
], context=context_novirtual)
|
||||||
event_to_synchronize = {}
|
event_to_synchronize = {}
|
||||||
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
||||||
event = att.event_id
|
event = att.event_id
|
||||||
|
@ -369,18 +541,19 @@ class google_calendar(osv.AbstractModel):
|
||||||
event_to_synchronize[base_event_id] = {}
|
event_to_synchronize[base_event_id] = {}
|
||||||
|
|
||||||
if att.google_internal_event_id not in event_to_synchronize[base_event_id]:
|
if att.google_internal_event_id not in event_to_synchronize[base_event_id]:
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id] = self.get_empty_synchro_summarize()
|
event_to_synchronize[base_event_id][att.google_internal_event_id] = SyncEvent()
|
||||||
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_attendee_id'] = att.id
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_event'] = event
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_found'] = True
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_event_id'] = event.id
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_isRecurrence'] = event.recurrency
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_isInstance'] = bool(event.recurrent_id and event.recurrent_id > 0)
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_update'] = event.oe_update_date
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_status'] = event.active
|
|
||||||
event_to_synchronize[base_event_id][att.google_internal_event_id]['OE_synchro'] = att.oe_synchro_date
|
|
||||||
|
|
||||||
|
ev_to_sync = event_to_synchronize[base_event_id][att.google_internal_event_id]
|
||||||
|
|
||||||
|
ev_to_sync.OE.attendee_id = att.id
|
||||||
|
ev_to_sync.OE.event = event
|
||||||
|
ev_to_sync.OE.found = True
|
||||||
|
ev_to_sync.OE.event_id = event.id
|
||||||
|
ev_to_sync.OE.isRecurrence = event.recurrency
|
||||||
|
ev_to_sync.OE.isInstance = bool(event.recurrent_id and event.recurrent_id > 0)
|
||||||
|
ev_to_sync.OE.update = event.oe_update_date
|
||||||
|
ev_to_sync.OE.status = event.active
|
||||||
|
ev_to_sync.OE.synchro = att.oe_synchro_date
|
||||||
|
|
||||||
for event in all_event_from_google.values():
|
for event in all_event_from_google.values():
|
||||||
event_id = event.get('id')
|
event_id = event.get('id')
|
||||||
|
@ -390,98 +563,28 @@ class google_calendar(osv.AbstractModel):
|
||||||
event_to_synchronize[base_event_id] = {}
|
event_to_synchronize[base_event_id] = {}
|
||||||
|
|
||||||
if event_id not in event_to_synchronize[base_event_id]:
|
if event_id not in event_to_synchronize[base_event_id]:
|
||||||
event_to_synchronize[base_event_id][event_id] = self.get_empty_synchro_summarize()
|
event_to_synchronize[base_event_id][event_id] = SyncEvent()
|
||||||
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_event'] = event
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_found'] = True
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_isRecurrence'] = bool(event.get('recurrence',''))
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_isInstance'] = bool(event.get('recurringEventId',0))
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_update'] = event.get('updated',None) # if deleted, no date without browse event
|
|
||||||
if event_to_synchronize[base_event_id][event_id]['GG_update']:
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_update'] =event_to_synchronize[base_event_id][event_id]['GG_update'].replace('T',' ').replace('Z','')
|
|
||||||
event_to_synchronize[base_event_id][event_id]['GG_status'] = (event.get('status') != 'cancelled')
|
|
||||||
|
|
||||||
|
|
||||||
|
ev_to_sync = event_to_synchronize[base_event_id][event_id]
|
||||||
|
|
||||||
|
ev_to_sync.GG.event = event
|
||||||
|
ev_to_sync.GG.found = True
|
||||||
|
ev_to_sync.GG.isRecurrence = bool(event.get('recurrence',''))
|
||||||
|
ev_to_sync.GG.isInstance = bool(event.get('recurringEventId',0))
|
||||||
|
ev_to_sync.GG.update = event.get('updated',None) # if deleted, no date without browse event
|
||||||
|
if ev_to_sync.GG.update:
|
||||||
|
ev_to_sync.GG.update = ev_to_sync.GG.update.replace('T',' ').replace('Z','')
|
||||||
|
ev_to_sync.GG.status = (event.get('status') != 'cancelled')
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# PRE-PROCESSING #
|
# PRE-PROCESSING #
|
||||||
######################
|
######################
|
||||||
|
|
||||||
for base_event in event_to_synchronize:
|
for base_event in event_to_synchronize:
|
||||||
for current_event in event_to_synchronize[base_event]:
|
for current_event in event_to_synchronize[base_event]:
|
||||||
event = event_to_synchronize[base_event][current_event]
|
event_to_synchronize[base_event][current_event].compute_OP()
|
||||||
|
#print event_to_synchronize[base_event]
|
||||||
#If event are already in Gmail and in OpenERP
|
#print "========================================================"
|
||||||
if event['OE_found'] and event['GG_found']:
|
|
||||||
#If the event has been deleted from one side, we delete on other side !
|
|
||||||
if event['OE_status'] != event['GG_status']:
|
|
||||||
event['td_action'] = "DELETE"
|
|
||||||
event['td_source'] = (event['OE_status'] and "OE") or (event['GG_status'] and "GG")
|
|
||||||
#If event is not deleted !
|
|
||||||
elif event['OE_status'] and event['GG_status']:
|
|
||||||
if event['OE_update'].split('.')[0] != event['GG_update'].split('.')[0]:
|
|
||||||
if event['OE_update'] < event['GG_update']:
|
|
||||||
event['td_source'] = 'GG'
|
|
||||||
elif event['OE_update'] > event['GG_update']:
|
|
||||||
event['td_source'] = 'OE'
|
|
||||||
|
|
||||||
|
|
||||||
if event['td_action'] != "None":
|
|
||||||
if event['%s_isRecurrence' % event['td_source']]:
|
|
||||||
if event['%s_status' % event['td_source']]:
|
|
||||||
event['td_action'] = "UPDATE"
|
|
||||||
event['td_comment'] = 'Only need to update, because i\'m active'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "EXCLUDE"
|
|
||||||
event['td_comment'] = 'Need to Exclude (Me = First event from recurrence) from recurrence'
|
|
||||||
|
|
||||||
elif event['%s_isInstance' % event['td_source']]:
|
|
||||||
event['td_action'] = "UPDATE"
|
|
||||||
event['td_comment'] = 'Only need to update, because already an exclu'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "UPDATE"
|
|
||||||
event['td_comment'] = 'Simply Update... I\'m a single event'
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not event['OE_synchro'] or event['OE_synchro'].split('.')[0] < event['OE_update'].split('.')[0]:
|
|
||||||
event['td_source'] = 'OE'
|
|
||||||
event['td_action'] = "UPDATE"
|
|
||||||
event['td_comment'] = 'Event already updated by another user, but not synchro with my google calendar'
|
|
||||||
|
|
||||||
else:
|
|
||||||
event['td_action'] = "None"
|
|
||||||
event['td_comment'] = 'Not update needed'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "None"
|
|
||||||
event['td_comment'] = "Both are already deleted"
|
|
||||||
# New in openERP... Create on create_events of synchronize function
|
|
||||||
elif event['OE_found'] and not event['GG_found']:
|
|
||||||
#Has been deleted from gmail
|
|
||||||
if event['OE_status']:
|
|
||||||
event['td_source'] = 'OE'
|
|
||||||
event['td_action'] = 'DELETE'
|
|
||||||
event['td_comment'] = 'Removed from GOOGLE ?'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "None"
|
|
||||||
event['td_comment'] = "Already Deleted in gmail and unlinked in OpenERP"
|
|
||||||
elif event['GG_found'] and not event['OE_found']:
|
|
||||||
event['td_source'] = 'GG'
|
|
||||||
if not event['GG_status'] and not event['GG_isInstance']:
|
|
||||||
# don't need to make something... because event has been created and deleted before the synchronization
|
|
||||||
event['td_action'] = 'None'
|
|
||||||
event['td_comment'] = 'Nothing to do... Create and Delete directly'
|
|
||||||
|
|
||||||
else:
|
|
||||||
if event['GG_isInstance']:
|
|
||||||
if event['%s_status' % event['td_source']]:
|
|
||||||
event['td_action'] = "EXCLUDE"
|
|
||||||
event['td_comment'] = 'Need to create the new exclu'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "EXCLUDE"
|
|
||||||
event['td_comment'] = 'Need to copy and Exclude'
|
|
||||||
else:
|
|
||||||
event['td_action'] = "CREATE"
|
|
||||||
event['td_comment'] = 'New EVENT CREATE from GMAIL'
|
|
||||||
|
|
||||||
######################
|
######################
|
||||||
# DO ACTION #
|
# DO ACTION #
|
||||||
######################
|
######################
|
||||||
|
@ -489,105 +592,62 @@ class google_calendar(osv.AbstractModel):
|
||||||
event_to_synchronize[base_event] = sorted(event_to_synchronize[base_event].iteritems(),key=operator.itemgetter(0))
|
event_to_synchronize[base_event] = sorted(event_to_synchronize[base_event].iteritems(),key=operator.itemgetter(0))
|
||||||
for current_event in event_to_synchronize[base_event]:
|
for current_event in event_to_synchronize[base_event]:
|
||||||
cr.commit()
|
cr.commit()
|
||||||
event = current_event[1]
|
event = current_event[1] # event is an Sync Event !
|
||||||
#############
|
|
||||||
### DEBUG ###
|
actToDo = event.OP
|
||||||
#############
|
actSrc = event.OP.src
|
||||||
# if event['td_action'] and event['td_action'] != 'None':
|
|
||||||
# print " Real Event %s (%s)" % (current_event[0],event['OE_event_id'])
|
|
||||||
# print " Found OE:%5s vs GG: %5s" % (event['OE_found'],event['GG_found'])
|
|
||||||
# print " Recurrence OE:%5s vs GG: %5s" % (event['OE_isRecurrence'],event['GG_isRecurrence'])
|
|
||||||
# print " Instance OE:%5s vs GG: %5s" % (event['OE_isInstance'],event['GG_isInstance'])
|
|
||||||
# print " Synchro OE: %10s " % (event['OE_synchro'])
|
|
||||||
# print " Update OE: %10s " % (event['OE_update'])
|
|
||||||
# print " Update GG: %10s " % (event['GG_update'])
|
|
||||||
# print " Status OE:%5s vs GG: %5s" % (event['OE_status'],event['GG_status'])
|
|
||||||
# print " Action %s" % (event['td_action'])
|
|
||||||
# print " Source %s" % (event['td_source'])
|
|
||||||
# print " comment %s" % (event['td_comment'])
|
|
||||||
|
|
||||||
context['curr_attendee'] = event.get('OE_attendee_id',False)
|
# if not isinstance(actToDo, NothingToDo):
|
||||||
|
# print event
|
||||||
|
|
||||||
|
context['curr_attendee'] = event.OE.attendee_id
|
||||||
|
|
||||||
actToDo = event['td_action']
|
if isinstance(actToDo, NothingToDo):
|
||||||
actSrc = event['td_source']
|
continue
|
||||||
if not actToDo:
|
elif isinstance(actToDo, Create):
|
||||||
raise ("#!? WHAT I NEED TO DO ????")
|
context_tmp = context.copy()
|
||||||
else:
|
context_tmp['NewMeeting'] = True
|
||||||
if actToDo == 'None':
|
if actSrc == 'GG':
|
||||||
continue
|
res = self.update_from_google(cr, uid, False, event.GG.event, "create", context=context_tmp)
|
||||||
elif actToDo == 'CREATE':
|
event.OE.event_id = res
|
||||||
context_tmp = context.copy()
|
meeting = calendar_event.browse(cr,uid,res,context=context)
|
||||||
context_tmp['NewMeeting'] = True
|
attendee_record_id = att_obj.search(cr, uid, [('partner_id','=', myPartnerID), ('event_id','=',res)], context=context)
|
||||||
if actSrc == 'GG':
|
self.pool['calendar.attendee'].write(cr, uid, attendee_record_id, {'oe_synchro_date':meeting.oe_update_date, 'google_internal_event_id':event.GG.event['id']}, context=context_tmp)
|
||||||
res = self.update_from_google(cr, uid, False, event['GG_event'], "create", context=context_tmp)
|
elif actSrc == 'OE':
|
||||||
event['OE_event_id'] = res
|
raise "Should be never here, creation for OE is done before update !"
|
||||||
meeting = calendar_event.browse(cr,uid,res,context=context)
|
#TODO Add to batch
|
||||||
attendee_record_id = att_obj.search(cr, uid, [('partner_id','=', myPartnerID), ('event_id','=',res)], context=context)
|
elif isinstance(actToDo, Update):
|
||||||
self.pool.get('calendar.attendee').write(cr,uid,attendee_record_id, {'oe_synchro_date':meeting.oe_update_date,'google_internal_event_id': event['GG_event']['id']},context=context_tmp)
|
if actSrc == 'GG':
|
||||||
elif actSrc == 'OE':
|
self.update_from_google(cr, uid, event.OE.event, event.GG.event, 'write', context)
|
||||||
raise "Should be never here, creation for OE is done before update !"
|
elif actSrc == 'OE':
|
||||||
#TODO Add to batch
|
self.update_to_google(cr, uid, event.OE.event, event.GG.event, context)
|
||||||
elif actToDo == 'UPDATE':
|
elif isinstance(actToDo, Exclude):
|
||||||
if actSrc == 'GG':
|
if actSrc == 'OE':
|
||||||
self.update_from_google(cr, uid, event['OE_event'], event['GG_event'], 'write', context)
|
self.delete_an_event(cr,uid,current_event[0],context=context)
|
||||||
elif actSrc == 'OE':
|
elif actSrc == 'GG':
|
||||||
self.update_to_google(cr, uid, event['OE_event'], event['GG_event'], context)
|
new_google_event_id = event.GG.event['id'].split('_')[1]
|
||||||
elif actToDo == 'EXCLUDE' :
|
if 'T' in new_google_event_id:
|
||||||
if actSrc == 'OE':
|
new_google_event_id = new_google_event_id.replace('T','')[:-1]
|
||||||
self.delete_an_event(cr,uid,current_event[0],context=context)
|
else:
|
||||||
elif actSrc == 'GG':
|
new_google_event_id = new_google_event_id + "000000"
|
||||||
new_google_event_id = event['GG_event']['id'].split('_')[1]
|
|
||||||
if 'T' in new_google_event_id:
|
if event.GG.status:
|
||||||
new_google_event_id = new_google_event_id.replace('T','')[:-1]
|
parent_event = {}
|
||||||
|
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id , new_google_event_id)
|
||||||
|
res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context)
|
||||||
else:
|
else:
|
||||||
new_google_event_id = new_google_event_id + "000000"
|
if event_to_synchronize[base_event][0][1].OE.event_id:
|
||||||
|
parent_oe_id = event_to_synchronize[base_event][0][1].OE.event_id
|
||||||
if event['GG_status']:
|
calendar_event.unlink(cr,uid,"%s-%s" % (parent_oe_id,new_google_event_id),unlink_level=1,context=context)
|
||||||
parent_event = {}
|
|
||||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].get('OE_event_id') , new_google_event_id)
|
|
||||||
res = self.update_from_google(cr, uid, parent_event, event['GG_event'], "copy", context)
|
|
||||||
else:
|
|
||||||
if event_to_synchronize[base_event][0][1].get('OE_event_id'):
|
|
||||||
parent_oe_id = event_to_synchronize[base_event][0][1].get('OE_event_id')
|
|
||||||
calendar_event.unlink(cr,uid,"%s-%s" % (parent_oe_id,new_google_event_id),unlink_level=1,context=context)
|
|
||||||
|
|
||||||
elif actToDo == 'DELETE':
|
elif isinstance(actToDo, Delete):
|
||||||
if actSrc == 'GG':
|
if actSrc == 'GG':
|
||||||
self.delete_an_event(cr,uid,current_event[0],context=context)
|
self.delete_an_event(cr,uid,current_event[0],context=context)
|
||||||
elif actSrc == 'OE':
|
elif actSrc == 'OE':
|
||||||
calendar_event.unlink(cr,uid,event['OE_event_id'],unlink_level=0,context=context)
|
calendar_event.unlink(cr,uid,event.OE.event_id,unlink_level=0,context=context)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def bind_recurring_events_to_google(self, cr, uid, context):
|
|
||||||
calendar_event = self.pool['calendar.event']
|
|
||||||
att_obj = self.pool.get('calendar.attendee')
|
|
||||||
user_obj = self.pool['res.users']
|
|
||||||
myPartnerID = user_obj.browse(cr,uid,uid,context=context).partner_id.id
|
|
||||||
|
|
||||||
context_norecurrent = context.copy()
|
|
||||||
context_norecurrent['virtual_id'] = False
|
|
||||||
context_norecurrent['active_test'] = False
|
|
||||||
|
|
||||||
my_att_ids = att_obj.search(cr, uid,[('partner_id', '=', myPartnerID),('google_internal_event_id', '=', False)], context=context_norecurrent)
|
|
||||||
for att in att_obj.browse(cr,uid,my_att_ids,context=context):
|
|
||||||
if att.event_id.recurrent_id and att.event_id.recurrent_id > 0:
|
|
||||||
new_google_internal_event_id = False
|
|
||||||
source_event_record = calendar_event.browse(cr, uid, att.event_id.recurrent_id, context)
|
|
||||||
source_attendee_record_id = att_obj.search(cr, uid, [('partner_id','=', myPartnerID), ('event_id','=',source_event_record.id)], context=context)
|
|
||||||
source_attendee_record = att_obj.browse(cr, uid, source_attendee_record_id, context)
|
|
||||||
if source_attendee_record:
|
|
||||||
source_attendee_record = source_attendee_record[0]
|
|
||||||
|
|
||||||
if att.event_id.recurrent_id_date and source_event_record.allday and source_attendee_record.google_internal_event_id:
|
|
||||||
new_google_internal_event_id = source_attendee_record.google_internal_event_id +'_'+ att.event_id.recurrent_id_date.split(' ')[0].replace('-','')
|
|
||||||
elif event.recurrent_id_date and source_attendee_record.google_internal_event_id:
|
|
||||||
new_google_internal_event_id = source_attendee_record.google_internal_event_id +'_'+ att.event_id.recurrent_id_date.replace('-','').replace(' ','T').replace(':','') + 'Z'
|
|
||||||
|
|
||||||
if new_google_internal_event_id:
|
|
||||||
#TODO WARNING, NEED TO CHECK THAT EVENT and ALL instance NOT DELETE IN GMAIL BEFORE !
|
|
||||||
res = self.update_recurrent_event_exclu(cr,uid,new_google_internal_event_id,source_attendee_record.google_internal_event_id,att.event_id,context=context)
|
|
||||||
att_obj.write(cr, uid, [att.event_id.id], {'google_internal_event_id': new_google_internal_event_id})
|
|
||||||
|
|
||||||
def check_and_sync(self, cr, uid, oe_event, google_event, context):
|
def check_and_sync(self, cr, uid, oe_event, google_event, context):
|
||||||
if datetime.strptime(oe_event.oe_update_date,"%Y-%m-%d %H:%M:%S.%f") > datetime.strptime(google_event['updated'],"%Y-%m-%dT%H:%M:%S.%fz"):
|
if datetime.strptime(oe_event.oe_update_date,"%Y-%m-%d %H:%M:%S.%f") > datetime.strptime(google_event['updated'],"%Y-%m-%dT%H:%M:%S.%fz"):
|
||||||
self.update_to_google(cr, uid, oe_event, google_event, context)
|
self.update_to_google(cr, uid, oe_event, google_event, context)
|
||||||
|
@ -595,7 +655,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
self.update_from_google(cr, uid, oe_event, google_event, 'write', context)
|
self.update_from_google(cr, uid, oe_event, google_event, 'write', context)
|
||||||
|
|
||||||
def get_sequence(self,cr,uid,instance_id,context=None):
|
def get_sequence(self,cr,uid,instance_id,context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
'fields': 'sequence',
|
'fields': 'sequence',
|
||||||
|
@ -613,7 +673,7 @@ class google_calendar(osv.AbstractModel):
|
||||||
#################################
|
#################################
|
||||||
|
|
||||||
def get_token(self,cr,uid,context=None):
|
def get_token(self,cr,uid,context=None):
|
||||||
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
|
current_user = self.pool['res.users'].browse(cr,uid,uid,context=context)
|
||||||
|
|
||||||
if datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], "%Y-%m-%d %H:%M:%S") < (datetime.now() + timedelta(minutes=1)):
|
if datetime.strptime(current_user.google_calendar_token_validity.split('.')[0], "%Y-%m-%d %H:%M:%S") < (datetime.now() + timedelta(minutes=1)):
|
||||||
self.do_refresh_token(cr,uid,context=context)
|
self.do_refresh_token(cr,uid,context=context)
|
||||||
|
@ -622,20 +682,20 @@ class google_calendar(osv.AbstractModel):
|
||||||
return current_user.google_calendar_token
|
return current_user.google_calendar_token
|
||||||
|
|
||||||
def do_refresh_token(self,cr,uid,context=None):
|
def do_refresh_token(self,cr,uid,context=None):
|
||||||
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
|
current_user = self.pool['res.users'].browse(cr,uid,uid,context=context)
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
|
|
||||||
refresh = current_user.google_calendar_rtoken
|
refresh = current_user.google_calendar_rtoken
|
||||||
all_token = gs_pool._refresh_google_token_json(cr, uid, current_user.google_calendar_rtoken,self.STR_SERVICE,context=context)
|
all_token = gs_pool._refresh_google_token_json(cr, uid, current_user.google_calendar_rtoken, self.STR_SERVICE, context=context)
|
||||||
|
|
||||||
vals = {}
|
vals = {}
|
||||||
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in'))
|
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in'))
|
||||||
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
|
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
|
||||||
|
|
||||||
self.pool.get('res.users').write(cr,SUPERUSER_ID,uid,vals,context=context)
|
self.pool['res.users'].write(cr,SUPERUSER_ID,uid,vals,context=context)
|
||||||
|
|
||||||
def need_authorize(self,cr,uid,context=None):
|
def need_authorize(self,cr,uid,context=None):
|
||||||
current_user = self.pool.get('res.users').browse(cr,uid,uid,context=context)
|
current_user = self.pool['res.users'].browse(cr,uid,uid,context=context)
|
||||||
return current_user.google_calendar_rtoken == False
|
return current_user.google_calendar_rtoken == False
|
||||||
|
|
||||||
def get_calendar_scope(self,RO=False):
|
def get_calendar_scope(self,RO=False):
|
||||||
|
@ -643,22 +703,30 @@ class google_calendar(osv.AbstractModel):
|
||||||
return 'https://www.googleapis.com/auth/calendar%s' % (readonly)
|
return 'https://www.googleapis.com/auth/calendar%s' % (readonly)
|
||||||
|
|
||||||
def authorize_google_uri(self,cr,uid,from_url='http://www.openerp.com',context=None):
|
def authorize_google_uri(self,cr,uid,from_url='http://www.openerp.com',context=None):
|
||||||
url = self.pool.get('google.service')._get_authorize_uri(cr,uid,from_url,self.STR_SERVICE,scope=self.get_calendar_scope(),context=context)
|
url = self.pool['google.service']._get_authorize_uri(cr,uid,from_url,self.STR_SERVICE,scope=self.get_calendar_scope(),context=context)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def can_authorize_google(self,cr,uid,context=None):
|
def can_authorize_google(self,cr,uid,context=None):
|
||||||
return self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager')
|
return self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager')
|
||||||
|
|
||||||
def set_all_tokens(self,cr,uid,authorization_code,context=None):
|
def set_all_tokens(self,cr,uid,authorization_code,context=None):
|
||||||
gs_pool = self.pool.get('google.service')
|
gs_pool = self.pool['google.service']
|
||||||
all_token = gs_pool._get_google_token_json(cr, uid, authorization_code,self.STR_SERVICE,context=context)
|
all_token = gs_pool._get_google_token_json(cr, uid, authorization_code,self.STR_SERVICE,context=context)
|
||||||
|
|
||||||
vals = {}
|
vals = {}
|
||||||
vals['google_%s_rtoken' % self.STR_SERVICE] = all_token.get('refresh_token')
|
vals['google_%s_rtoken' % self.STR_SERVICE] = all_token.get('refresh_token')
|
||||||
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in'))
|
vals['google_%s_token_validity' % self.STR_SERVICE] = datetime.now() + timedelta(seconds=all_token.get('expires_in'))
|
||||||
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
|
vals['google_%s_token' % self.STR_SERVICE] = all_token.get('access_token')
|
||||||
self.pool.get('res.users').write(cr,SUPERUSER_ID,uid,vals,context=context)
|
self.pool['res.users'].write(cr,SUPERUSER_ID,uid,vals,context=context)
|
||||||
|
|
||||||
|
def get_start_time_to_synchro(self, cr, uid, context=None) :
|
||||||
|
# WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4
|
||||||
|
number_of_week = 13
|
||||||
|
return datetime.now()-timedelta(weeks=number_of_week)
|
||||||
|
|
||||||
|
def get_need_synchro_attendee(self, cr, uid, context=None):
|
||||||
|
# WILL BE AN IR CONFIG PARAMETER - beginning from SAAS4
|
||||||
|
return True
|
||||||
|
|
||||||
class res_users(osv.Model):
|
class res_users(osv.Model):
|
||||||
_inherit = 'res.users'
|
_inherit = 'res.users'
|
||||||
|
@ -718,7 +786,6 @@ class calendar_attendee(osv.Model):
|
||||||
# If attendees are updated, we need to specify that next synchro need an action
|
# If attendees are updated, we need to specify that next synchro need an action
|
||||||
# Except if it come from an update_from_google
|
# Except if it come from an update_from_google
|
||||||
if not context.get('curr_attendee', False) and not context.get('NewMeeting', False):
|
if not context.get('curr_attendee', False) and not context.get('NewMeeting', False):
|
||||||
self.pool.get('calendar.event').write(cr, uid, ref, {'oe_update_date':datetime.now()},context)
|
self.pool['calendar.event'].write(cr, uid, ref, {'oe_update_date':datetime.now()},context)
|
||||||
|
|
||||||
return super(calendar_attendee, self).write(cr, uid, ids, vals, context=context)
|
|
||||||
|
|
||||||
|
return super(calendar_attendee, self).write(cr, uid, ids, vals, context=context)
|
|
@ -74,8 +74,8 @@
|
||||||
<label for="number_of_days_temp" string="Duration"/>
|
<label for="number_of_days_temp" string="Duration"/>
|
||||||
<div>
|
<div>
|
||||||
<group col="3" attrs="{'invisible': [('type', '=', 'add')]}">
|
<group col="3" attrs="{'invisible': [('type', '=', 'add')]}">
|
||||||
<field name="date_from" nolabel="1" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/><label string="-" class="oe_inline"/>
|
<field name="date_from" nolabel="1" on_change="onchange_date_from(date_to, date_from)" attrs="{'required':[('type', '=', 'remove')]}" class="oe_inline"/><label string="-" class="oe_inline"/>
|
||||||
<field name="date_to" nolabel="1" on_change="onchange_date_to(date_to, date_from)" required="1" class="oe_inline"/>
|
<field name="date_to" nolabel="1" on_change="onchange_date_to(date_to, date_from)" attrs="{'required':[('type', '=', 'remove')]}" class="oe_inline"/>
|
||||||
</group>
|
</group>
|
||||||
<div>
|
<div>
|
||||||
<field name="number_of_days_temp" class="oe_inline"/> days
|
<field name="number_of_days_temp" class="oe_inline"/> days
|
||||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
||||||
"Project-Id-Version: openobject-addons\n"
|
"Project-Id-Version: openobject-addons\n"
|
||||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
||||||
"PO-Revision-Date: 2012-02-11 15:26+0000\n"
|
"PO-Revision-Date: 2014-02-20 15:53+0000\n"
|
||||||
"Last-Translator: 开阖软件 Jeff Wang <jeff@osbzr.com>\n"
|
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
|
||||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2014-01-28 06:29+0000\n"
|
"X-Launchpad-Export-Date: 2014-02-21 05:48+0000\n"
|
||||||
"X-Generator: Launchpad (build 16914)\n"
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: help:hr.applicant,active:0
|
#: help:hr.applicant,active:0
|
||||||
|
@ -33,24 +33,24 @@ msgstr "必备条件"
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
msgid "Application Summary"
|
msgid "Application Summary"
|
||||||
msgstr ""
|
msgstr "申请摘要"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
msgid "Start Interview"
|
msgid "Start Interview"
|
||||||
msgstr ""
|
msgstr "开始面试"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
msgid "Mobile:"
|
msgid "Mobile:"
|
||||||
msgstr ""
|
msgstr "手机:"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: help:hr.recruitment.stage,fold:0
|
#: help:hr.recruitment.stage,fold:0
|
||||||
msgid ""
|
msgid ""
|
||||||
"This stage is not visible, for example in status bar or kanban view, when "
|
"This stage is not visible, for example in status bar or kanban view, when "
|
||||||
"there are no records in that stage to display."
|
"there are no records in that stage to display."
|
||||||
msgstr ""
|
msgstr "此阶段是不可见的。例如:状态栏或看板视图中,在该阶段中 不存在可显示的记录。"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: model:hr.recruitment.degree,name:hr_recruitment.degree_graduate
|
#: model:hr.recruitment.degree,name:hr_recruitment.degree_graduate
|
||||||
|
@ -99,7 +99,7 @@ msgstr "暂停的职位"
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
#: field:hr.applicant,message_unread:0
|
#: field:hr.applicant,message_unread:0
|
||||||
msgid "Unread Messages"
|
msgid "Unread Messages"
|
||||||
msgstr ""
|
msgstr "未读消息"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: field:hr.applicant,company_id:0
|
#: field:hr.applicant,company_id:0
|
||||||
|
@ -157,7 +157,7 @@ msgstr ""
|
||||||
#: model:ir.actions.act_window,name:hr_recruitment.crm_case_categ0_act_job
|
#: model:ir.actions.act_window,name:hr_recruitment.crm_case_categ0_act_job
|
||||||
#: model:ir.ui.menu,name:hr_recruitment.menu_crm_case_categ0_act_job
|
#: model:ir.ui.menu,name:hr_recruitment.menu_crm_case_categ0_act_job
|
||||||
msgid "Applications"
|
msgid "Applications"
|
||||||
msgstr ""
|
msgstr "求职申请"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: field:hr.applicant,day_open:0
|
#: field:hr.applicant,day_open:0
|
||||||
|
@ -167,12 +167,12 @@ msgstr "开启天数"
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: field:hr.applicant,emp_id:0
|
#: field:hr.applicant,emp_id:0
|
||||||
msgid "employee"
|
msgid "employee"
|
||||||
msgstr ""
|
msgstr "雇员"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: field:hr.config.settings,fetchmail_applicants:0
|
#: field:hr.config.settings,fetchmail_applicants:0
|
||||||
msgid "Create applicants from an incoming email account"
|
msgid "Create applicants from an incoming email account"
|
||||||
msgstr ""
|
msgstr "从电子邮件帐号接收的邮件创建求职申请"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.recruitment.report:0
|
#: view:hr.recruitment.report:0
|
||||||
|
@ -184,7 +184,7 @@ msgstr "天数"
|
||||||
#: view:hr.recruitment.partner.create:0
|
#: view:hr.recruitment.partner.create:0
|
||||||
#: model:ir.actions.act_window,name:hr_recruitment.action_hr_recruitment_partner_create
|
#: model:ir.actions.act_window,name:hr_recruitment.action_hr_recruitment_partner_create
|
||||||
msgid "Create Contact"
|
msgid "Create Contact"
|
||||||
msgstr ""
|
msgstr "新建联系人"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
|
@ -808,7 +808,7 @@ msgstr "生效"
|
||||||
#: view:hr.recruitment.report:0
|
#: view:hr.recruitment.report:0
|
||||||
#: field:hr.recruitment.report,nbr:0
|
#: field:hr.recruitment.report,nbr:0
|
||||||
msgid "# of Applications"
|
msgid "# of Applications"
|
||||||
msgstr ""
|
msgstr "个申请"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: model:ir.actions.act_window,help:hr_recruitment.hr_recruitment_stage_act
|
#: model:ir.actions.act_window,help:hr_recruitment.hr_recruitment_stage_act
|
||||||
|
@ -1251,7 +1251,7 @@ msgstr "开始"
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:board.board:0
|
#: view:board.board:0
|
||||||
msgid "Applications to be Processed"
|
msgid "Applications to be Processed"
|
||||||
msgstr ""
|
msgstr "待处理的申请"
|
||||||
|
|
||||||
#. module: hr_recruitment
|
#. module: hr_recruitment
|
||||||
#: view:hr.applicant:0
|
#: view:hr.applicant:0
|
||||||
|
|
|
@ -7,14 +7,14 @@ msgstr ""
|
||||||
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
"Project-Id-Version: OpenERP Server 6.0dev\n"
|
||||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||||
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
||||||
"PO-Revision-Date: 2012-11-01 08:13+0000\n"
|
"PO-Revision-Date: 2014-02-20 15:57+0000\n"
|
||||||
"Last-Translator: 盈通 ccdos <ccdos@intoerp.com>\n"
|
"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2014-01-28 06:13+0000\n"
|
"X-Launchpad-Export-Date: 2014-02-21 05:48+0000\n"
|
||||||
"X-Generator: Launchpad (build 16914)\n"
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: field:hr.analytic.timesheet,sheet_id:0
|
#: field:hr.analytic.timesheet,sheet_id:0
|
||||||
|
@ -33,7 +33,7 @@ msgstr "服务"
|
||||||
#: field:hr.timesheet.report,quantity:0
|
#: field:hr.timesheet.report,quantity:0
|
||||||
#: field:timesheet.report,quantity:0
|
#: field:timesheet.report,quantity:0
|
||||||
msgid "Time"
|
msgid "Time"
|
||||||
msgstr ""
|
msgstr "时间"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: help:hr.config.settings,timesheet_max_difference:0
|
#: help:hr.config.settings,timesheet_max_difference:0
|
||||||
|
@ -92,7 +92,7 @@ msgstr "# 成本"
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: field:hr_timesheet_sheet.sheet,message_unread:0
|
#: field:hr_timesheet_sheet.sheet,message_unread:0
|
||||||
msgid "Unread Messages"
|
msgid "Unread Messages"
|
||||||
msgstr ""
|
msgstr "未读消息"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: view:hr.timesheet.report:0
|
#: view:hr.timesheet.report:0
|
||||||
|
@ -133,7 +133,7 @@ msgstr "日期到"
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: view:hr_timesheet_sheet.sheet:0
|
#: view:hr_timesheet_sheet.sheet:0
|
||||||
msgid "to"
|
msgid "to"
|
||||||
msgstr ""
|
msgstr "到"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: model:process.node,note:hr_timesheet_sheet.process_node_invoiceonwork0
|
#: model:process.node,note:hr_timesheet_sheet.process_node_invoiceonwork0
|
||||||
|
@ -145,7 +145,7 @@ msgstr "根据计工单"
|
||||||
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:398
|
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:398
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You cannot modify an entry in a confirmed timesheet."
|
msgid "You cannot modify an entry in a confirmed timesheet."
|
||||||
msgstr ""
|
msgstr "你不能修改已确认的时间表条目"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: view:hr.timesheet.report:0
|
#: view:hr.timesheet.report:0
|
||||||
|
@ -188,13 +188,13 @@ msgstr "拒绝"
|
||||||
#: view:hr_timesheet_sheet.sheet:0
|
#: view:hr_timesheet_sheet.sheet:0
|
||||||
#: model:ir.actions.act_window,name:hr_timesheet_sheet.act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet
|
#: model:ir.actions.act_window,name:hr_timesheet_sheet.act_hr_timesheet_sheet_sheet_2_hr_analytic_timesheet
|
||||||
msgid "Timesheet Activities"
|
msgid "Timesheet Activities"
|
||||||
msgstr ""
|
msgstr "时间表活动"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38
|
#: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Please create an employee and associate it with this user."
|
msgid "Please create an employee and associate it with this user."
|
||||||
msgstr ""
|
msgstr "请创建员工信息并关联到此用户"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:402
|
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:402
|
||||||
|
@ -202,7 +202,7 @@ msgstr ""
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"You cannot enter an attendance date outside the current timesheet dates."
|
"You cannot enter an attendance date outside the current timesheet dates."
|
||||||
msgstr ""
|
msgstr "无法录入当前时间表以外的出勤日期"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:205
|
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:205
|
||||||
|
@ -226,7 +226,7 @@ msgstr ""
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: field:hr_timesheet_sheet.sheet,message_ids:0
|
#: field:hr_timesheet_sheet.sheet,message_ids:0
|
||||||
msgid "Messages"
|
msgid "Messages"
|
||||||
msgstr ""
|
msgstr "消息"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: help:hr_timesheet_sheet.sheet,state:0
|
#: help:hr_timesheet_sheet.sheet,state:0
|
||||||
|
@ -252,7 +252,7 @@ msgstr ""
|
||||||
#: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38
|
#: code:addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py:38
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Error!"
|
msgid "Error!"
|
||||||
msgstr ""
|
msgstr "错误!"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: field:hr.config.settings,timesheet_max_difference:0
|
#: field:hr.config.settings,timesheet_max_difference:0
|
||||||
|
@ -307,7 +307,7 @@ msgstr "员工计工单"
|
||||||
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:215
|
#: code:addons/hr_timesheet_sheet/hr_timesheet_sheet.py:215
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Invalid Action!"
|
msgid "Invalid Action!"
|
||||||
msgstr ""
|
msgstr "无效的动作!"
|
||||||
|
|
||||||
#. module: hr_timesheet_sheet
|
#. module: hr_timesheet_sheet
|
||||||
#: view:hr.timesheet.report:0
|
#: view:hr.timesheet.report:0
|
||||||
|
|
|
@ -95,6 +95,9 @@ class EscposDriver(Thread):
|
||||||
_logger.warning('ESC/POS Device Disconnected: '+message)
|
_logger.warning('ESC/POS Device Disconnected: '+message)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if not escpos:
|
||||||
|
_logger.error('ESC/POS cannot initialize, please verify system dependencies.')
|
||||||
|
return
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
timestamp, task, data = self.queue.get(True)
|
timestamp, task, data = self.queue.get(True)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
access_im_message,im.message,model_im_message,,1,0,1,0
|
access_im_message,im.message,model_im_message,,1,0,1,0
|
||||||
access_im_user,im.user,model_im_user,,1,1,1,0
|
access_im_user,im.user,model_im_user,,1,1,1,0
|
||||||
access_im_session,im.session,model_im_session,,1,0,0,0
|
access_im_session,im.session,model_im_session,,1,1,1,0
|
|
|
@ -54,6 +54,7 @@
|
||||||
<!-- Discussion subtype for messaging / Chatter -->
|
<!-- Discussion subtype for messaging / Chatter -->
|
||||||
<record id="mt_comment" model="mail.message.subtype">
|
<record id="mt_comment" model="mail.message.subtype">
|
||||||
<field name="name">Discussions</field>
|
<field name="name">Discussions</field>
|
||||||
|
<field name="sequence" eval="0"/>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
|
@ -29,6 +29,8 @@ class mail_message_subtype(osv.osv):
|
||||||
on the Wall. """
|
on the Wall. """
|
||||||
_name = 'mail.message.subtype'
|
_name = 'mail.message.subtype'
|
||||||
_description = 'Message subtypes'
|
_description = 'Message subtypes'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Message Type', required=True, translate=True,
|
'name': fields.char('Message Type', required=True, translate=True,
|
||||||
help='Message subtype gives a more precise type on the message, '\
|
help='Message subtype gives a more precise type on the message, '\
|
||||||
|
@ -50,7 +52,11 @@ class mail_message_subtype(osv.osv):
|
||||||
help="Model the subtype applies to. If False, this subtype applies to all models."),
|
help="Model the subtype applies to. If False, this subtype applies to all models."),
|
||||||
'default': fields.boolean('Default',
|
'default': fields.boolean('Default',
|
||||||
help="Activated by default when subscribing."),
|
help="Activated by default when subscribing."),
|
||||||
|
'sequence': fields.integer('Sequence', help="Used to order subtypes."),
|
||||||
|
'hidden': fields.boolean('Hidden', help="Hide the subtype in the follower options")
|
||||||
}
|
}
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'default': True,
|
'default': True,
|
||||||
|
'sequence': 1,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Subtype">
|
<tree string="Subtype">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
|
<field name="sequence"/>
|
||||||
<field name="res_model"/>
|
<field name="res_model"/>
|
||||||
<field name="default"/>
|
<field name="default"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
@ -22,12 +23,18 @@
|
||||||
<form string="Email message" version="7.0">
|
<form string="Email message" version="7.0">
|
||||||
<sheet>
|
<sheet>
|
||||||
<group>
|
<group>
|
||||||
<field name="name"/>
|
<group string='Description'>
|
||||||
<field name="res_model"/>
|
<field name="name"/>
|
||||||
<field name="default"/>
|
<field name="sequence"/>
|
||||||
<field name="description"/>
|
<field name="res_model"/>
|
||||||
<field name="parent_id"/>
|
<field name="description"/>
|
||||||
<field name="relation_field"/>
|
<field name="default"/>
|
||||||
|
<field name="hidden"/>
|
||||||
|
</group>
|
||||||
|
<group string='Auto subscription'>
|
||||||
|
<field name="parent_id"/>
|
||||||
|
<field name="relation_field"/>
|
||||||
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
from collections import OrderedDict
|
||||||
import datetime
|
import datetime
|
||||||
import dateutil
|
import dateutil
|
||||||
import email
|
import email
|
||||||
|
@ -185,8 +186,17 @@ class mail_thread(osv.AbstractModel):
|
||||||
|
|
||||||
# find current model subtypes, add them to a dictionary
|
# find current model subtypes, add them to a dictionary
|
||||||
subtype_obj = self.pool.get('mail.message.subtype')
|
subtype_obj = self.pool.get('mail.message.subtype')
|
||||||
subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
|
subtype_ids = subtype_obj.search(
|
||||||
subtype_dict = dict((subtype.name, dict(default=subtype.default, followed=False, id=subtype.id)) for subtype in subtype_obj.browse(cr, uid, subtype_ids, context=context))
|
cr, uid, [
|
||||||
|
'&', ('hidden', '=', False), '|', ('res_model', '=', self._name), ('res_model', '=', False)
|
||||||
|
], context=context)
|
||||||
|
subtype_dict = OrderedDict(
|
||||||
|
(subtype.name, {
|
||||||
|
'default': subtype.default,
|
||||||
|
'followed': False,
|
||||||
|
'parent_model': subtype.parent_id and subtype.parent_id.res_model or self._name,
|
||||||
|
'id': subtype.id}
|
||||||
|
) for subtype in subtype_obj.browse(cr, uid, subtype_ids, context=context))
|
||||||
for id in ids:
|
for id in ids:
|
||||||
res[id]['message_subtype_data'] = subtype_dict.copy()
|
res[id]['message_subtype_data'] = subtype_dict.copy()
|
||||||
|
|
||||||
|
@ -348,11 +358,7 @@ class mail_thread(osv.AbstractModel):
|
||||||
message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False
|
message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False
|
||||||
message_follower_ids.append([4, pid])
|
message_follower_ids.append([4, pid])
|
||||||
values['message_follower_ids'] = message_follower_ids
|
values['message_follower_ids'] = message_follower_ids
|
||||||
# add operation to ignore access rule checking for subscription
|
thread_id = super(mail_thread, self).create(cr, uid, values, context=context)
|
||||||
context_operation = dict(context, operation='create')
|
|
||||||
else:
|
|
||||||
context_operation = context
|
|
||||||
thread_id = super(mail_thread, self).create(cr, uid, values, context=context_operation)
|
|
||||||
|
|
||||||
# automatic logging unless asked not to (mainly for various testing purpose)
|
# automatic logging unless asked not to (mainly for various testing purpose)
|
||||||
if not context.get('mail_create_nolog'):
|
if not context.get('mail_create_nolog'):
|
||||||
|
@ -369,10 +375,11 @@ class mail_thread(osv.AbstractModel):
|
||||||
track_ctx = dict(context)
|
track_ctx = dict(context)
|
||||||
if 'lang' not in track_ctx:
|
if 'lang' not in track_ctx:
|
||||||
track_ctx['lang'] = self.pool.get('res.users').browse(cr, uid, uid, context=context).lang
|
track_ctx['lang'] = self.pool.get('res.users').browse(cr, uid, uid, context=context).lang
|
||||||
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
|
if not context.get('mail_notrack'):
|
||||||
if tracked_fields:
|
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
|
||||||
initial_values = {thread_id: dict((item, False) for item in tracked_fields)}
|
if tracked_fields:
|
||||||
self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=track_ctx)
|
initial_values = {thread_id: dict((item, False) for item in tracked_fields)}
|
||||||
|
self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=track_ctx)
|
||||||
return thread_id
|
return thread_id
|
||||||
|
|
||||||
def write(self, cr, uid, ids, values, context=None):
|
def write(self, cr, uid, ids, values, context=None):
|
||||||
|
@ -396,7 +403,11 @@ class mail_thread(osv.AbstractModel):
|
||||||
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
|
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
|
||||||
self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
|
self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
|
||||||
|
|
||||||
# Perform the tracking
|
if not context.get('mail_notrack'):
|
||||||
|
# Perform the tracking
|
||||||
|
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
|
||||||
|
else:
|
||||||
|
tracked_fields = None
|
||||||
if tracked_fields:
|
if tracked_fields:
|
||||||
self.message_track(cr, uid, ids, tracked_fields, initial_values, context=track_ctx)
|
self.message_track(cr, uid, ids, tracked_fields, initial_values, context=track_ctx)
|
||||||
return result
|
return result
|
||||||
|
@ -417,6 +428,9 @@ class mail_thread(osv.AbstractModel):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def copy(self, cr, uid, id, default=None, context=None):
|
def copy(self, cr, uid, id, default=None, context=None):
|
||||||
|
# avoid tracking multiple temporary changes during copy
|
||||||
|
context = dict(context or {}, mail_notrack=True)
|
||||||
|
|
||||||
default = default or {}
|
default = default or {}
|
||||||
default['message_ids'] = []
|
default['message_ids'] = []
|
||||||
default['message_follower_ids'] = []
|
default['message_follower_ids'] = []
|
||||||
|
@ -728,13 +742,15 @@ class mail_thread(osv.AbstractModel):
|
||||||
# Private message: should not contain any thread_id
|
# Private message: should not contain any thread_id
|
||||||
if not model and thread_id:
|
if not model and thread_id:
|
||||||
if assert_model:
|
if assert_model:
|
||||||
assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message), received %s.' % thread_id
|
if thread_id:
|
||||||
_warn('posting a message without model should be with a null res_id (private message), received %s, resetting thread_id' % thread_id)
|
raise ValueError('Routing: posting a message without model should be with a null res_id (private message), received %s.' % thread_id)
|
||||||
|
_warn('posting a message without model should be with a null res_id (private message), received %s resetting thread_id' % thread_id)
|
||||||
thread_id = 0
|
thread_id = 0
|
||||||
# Private message: should have a parent_id (only answers)
|
# Private message: should have a parent_id (only answers)
|
||||||
if not model and not message_dict.get('parent_id'):
|
if not model and not message_dict.get('parent_id'):
|
||||||
if assert_model:
|
if assert_model:
|
||||||
assert message_dict.get('parent_id'), 'Routing: posting a message without model should be with a parent_id (private mesage).'
|
if not message_dict.get('parent_id'):
|
||||||
|
raise ValueError('Routing: posting a message without model should be with a parent_id (private mesage).')
|
||||||
_warn('posting a message without model should be with a parent_id (private mesage), skipping')
|
_warn('posting a message without model should be with a parent_id (private mesage), skipping')
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
@ -763,7 +779,10 @@ class mail_thread(osv.AbstractModel):
|
||||||
# New Document: check model accepts the mailgateway
|
# New Document: check model accepts the mailgateway
|
||||||
if not thread_id and model and not hasattr(model_pool, 'message_new'):
|
if not thread_id and model and not hasattr(model_pool, 'message_new'):
|
||||||
if assert_model:
|
if assert_model:
|
||||||
assert hasattr(model_pool, 'message_new'), 'Model %s does not accept document creation, crashing' % model
|
if not hasattr(model_pool, 'message_new'):
|
||||||
|
raise ValueError(
|
||||||
|
'Model %s does not accept document creation, crashing' % model
|
||||||
|
)
|
||||||
_warn('model %s does not accept document creation, skipping' % model)
|
_warn('model %s does not accept document creation, skipping' % model)
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
@ -824,8 +843,11 @@ class mail_thread(osv.AbstractModel):
|
||||||
to which this mail should be attached. Only used if the message
|
to which this mail should be attached. Only used if the message
|
||||||
does not reply to an existing thread and does not match any mail alias.
|
does not reply to an existing thread and does not match any mail alias.
|
||||||
:return: list of [model, thread_id, custom_values, user_id, alias]
|
:return: list of [model, thread_id, custom_values, user_id, alias]
|
||||||
|
|
||||||
|
:raises: ValueError, TypeError
|
||||||
"""
|
"""
|
||||||
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
|
if not isinstance(message, Message):
|
||||||
|
raise TypeError('message must be an email.message.Message at this point')
|
||||||
mail_msg_obj = self.pool['mail.message']
|
mail_msg_obj = self.pool['mail.message']
|
||||||
fallback_model = model
|
fallback_model = model
|
||||||
|
|
||||||
|
@ -943,9 +965,11 @@ class mail_thread(osv.AbstractModel):
|
||||||
return [route]
|
return [route]
|
||||||
|
|
||||||
# AssertionError if no routes found and if no bounce occured
|
# AssertionError if no routes found and if no bounce occured
|
||||||
assert False, \
|
raise ValueError(
|
||||||
"No possible route found for incoming message from %s to %s (Message-Id %s:)." \
|
'No possible route found for incoming message from %s to %s (Message-Id %s:). '
|
||||||
"Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
|
'Create an appropriate mail.alias or force the destination model.' %
|
||||||
|
(email_from, email_to, message_id)
|
||||||
|
)
|
||||||
|
|
||||||
def message_route_process(self, cr, uid, message, message_dict, routes, context=None):
|
def message_route_process(self, cr, uid, message, message_dict, routes, context=None):
|
||||||
# postpone setting message_dict.partner_ids after message_post, to avoid double notifications
|
# postpone setting message_dict.partner_ids after message_post, to avoid double notifications
|
||||||
|
@ -956,9 +980,11 @@ class mail_thread(osv.AbstractModel):
|
||||||
context.update({'thread_model': model})
|
context.update({'thread_model': model})
|
||||||
if model:
|
if model:
|
||||||
model_pool = self.pool[model]
|
model_pool = self.pool[model]
|
||||||
assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
|
if not (thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new')):
|
||||||
"Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
|
raise ValueError(
|
||||||
(message_dict['message_id'], model)
|
"Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" %
|
||||||
|
(message_dict['message_id'], model)
|
||||||
|
)
|
||||||
|
|
||||||
# disabled subscriptions during message_new/update to avoid having the system user running the
|
# disabled subscriptions during message_new/update to avoid having the system user running the
|
||||||
# email gateway become a follower of all inbound messages
|
# email gateway become a follower of all inbound messages
|
||||||
|
@ -968,7 +994,8 @@ class mail_thread(osv.AbstractModel):
|
||||||
else:
|
else:
|
||||||
thread_id = model_pool.message_new(cr, user_id, message_dict, custom_values, context=nosub_ctx)
|
thread_id = model_pool.message_new(cr, user_id, message_dict, custom_values, context=nosub_ctx)
|
||||||
else:
|
else:
|
||||||
assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
|
if thread_id:
|
||||||
|
raise ValueError("Posting a message without model should be with a null res_id, to create a private message.")
|
||||||
model_pool = self.pool.get('mail.thread')
|
model_pool = self.pool.get('mail.thread')
|
||||||
if not hasattr(model_pool, 'message_post'):
|
if not hasattr(model_pool, 'message_post'):
|
||||||
context['thread_model'] = model
|
context['thread_model'] = model
|
||||||
|
@ -1552,18 +1579,20 @@ class mail_thread(osv.AbstractModel):
|
||||||
""" Add partners to the records followers. """
|
""" Add partners to the records followers. """
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
# not necessary for computation, but saves an access right check
|
||||||
|
if not partner_ids:
|
||||||
|
return True
|
||||||
|
|
||||||
mail_followers_obj = self.pool.get('mail.followers')
|
mail_followers_obj = self.pool.get('mail.followers')
|
||||||
subtype_obj = self.pool.get('mail.message.subtype')
|
subtype_obj = self.pool.get('mail.message.subtype')
|
||||||
|
|
||||||
user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
|
user_pid = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id
|
||||||
if set(partner_ids) == set([user_pid]):
|
if set(partner_ids) == set([user_pid]):
|
||||||
if context.get('operation', '') != 'create':
|
try:
|
||||||
try:
|
self.check_access_rights(cr, uid, 'read')
|
||||||
self.check_access_rights(cr, uid, 'read')
|
self.check_access_rule(cr, uid, ids, 'read')
|
||||||
self.check_access_rule(cr, uid, ids, 'read')
|
except (osv.except_osv, orm.except_orm):
|
||||||
except (osv.except_osv, orm.except_orm):
|
return False
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
self.check_access_rights(cr, uid, 'write')
|
self.check_access_rights(cr, uid, 'write')
|
||||||
self.check_access_rule(cr, uid, ids, 'write')
|
self.check_access_rule(cr, uid, ids, 'write')
|
||||||
|
@ -1608,6 +1637,9 @@ class mail_thread(osv.AbstractModel):
|
||||||
|
|
||||||
def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):
|
def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):
|
||||||
""" Remove partners from the records followers. """
|
""" Remove partners from the records followers. """
|
||||||
|
# not necessary for computation, but saves an access right check
|
||||||
|
if not partner_ids:
|
||||||
|
return True
|
||||||
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
|
||||||
if set(partner_ids) == set([user_pid]):
|
if set(partner_ids) == set([user_pid]):
|
||||||
self.check_access_rights(cr, uid, 'read')
|
self.check_access_rights(cr, uid, 'read')
|
||||||
|
|
|
@ -115,6 +115,9 @@
|
||||||
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p {
|
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body * {
|
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body * {
|
||||||
text-overflow:ellipsis;
|
text-overflow:ellipsis;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
@ -757,4 +760,8 @@
|
||||||
.openerp .oe_sidebar_suggestion .oe_suggest_items:hover a.oe_suggestion_remove_item {
|
.openerp .oe_sidebar_suggestion .oe_suggest_items:hover a.oe_suggestion_remove_item {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
.subtype-border {
|
||||||
|
border-bottom: 1px solid #dddddd;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -925,20 +925,12 @@ openerp.mail = function (session) {
|
||||||
},
|
},
|
||||||
|
|
||||||
on_record_clicked: function (event) {
|
on_record_clicked: function (event) {
|
||||||
event.stopPropagation();
|
|
||||||
var state = {
|
var state = {
|
||||||
'model': this.model,
|
'model': this.model,
|
||||||
'id': this.res_id,
|
'id': this.res_id,
|
||||||
'title': this.record_name
|
'title': this.record_name
|
||||||
};
|
};
|
||||||
session.webclient.action_manager.do_push_state(state);
|
session.webclient.action_manager.do_push_state(state);
|
||||||
this.do_action({
|
|
||||||
res_model: state.model,
|
|
||||||
res_id: state.id,
|
|
||||||
type: 'ir.actions.act_window',
|
|
||||||
views: [[false, 'form']]
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Call the on_compose_message on the thread of this message. */
|
/* Call the on_compose_message on the thread of this message. */
|
||||||
|
|
|
@ -197,6 +197,7 @@ openerp_mail_followers = function(session, mail) {
|
||||||
// clean and display title
|
// clean and display title
|
||||||
var node_user_list = this.$('.oe_follower_list').empty();
|
var node_user_list = this.$('.oe_follower_list').empty();
|
||||||
this.$('.oe_follower_title').html(this._format_followers(this.followers.length));
|
this.$('.oe_follower_title').html(this._format_followers(this.followers.length));
|
||||||
|
self.message_is_follower = _.indexOf(this.followers.map(function (rec) { return rec[2]['is_uid']}), true) != -1;
|
||||||
// truncate number of displayed followers
|
// truncate number of displayed followers
|
||||||
var truncated = this.followers.slice(0, this.displayed_nb);
|
var truncated = this.followers.slice(0, this.displayed_nb);
|
||||||
_(truncated).each(function (record) {
|
_(truncated).each(function (record) {
|
||||||
|
@ -207,9 +208,6 @@ openerp_mail_followers = function(session, mail) {
|
||||||
'is_editable': record[2]['is_editable'],
|
'is_editable': record[2]['is_editable'],
|
||||||
'avatar_url': mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record[0]),
|
'avatar_url': mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record[0]),
|
||||||
}
|
}
|
||||||
if (partner.is_uid) {
|
|
||||||
self.message_is_follower = partner.is_uid;
|
|
||||||
}
|
|
||||||
$(session.web.qweb.render('mail.followers.partner', {'record': partner, 'widget': self})).appendTo(node_user_list);
|
$(session.web.qweb.render('mail.followers.partner', {'record': partner, 'widget': self})).appendTo(node_user_list);
|
||||||
// On mouse-enter it will show the edit_subtype pencil.
|
// On mouse-enter it will show the edit_subtype pencil.
|
||||||
if (partner.is_editable) {
|
if (partner.is_editable) {
|
||||||
|
@ -267,7 +265,15 @@ openerp_mail_followers = function(session, mail) {
|
||||||
var records = data[id].message_subtype_data;
|
var records = data[id].message_subtype_data;
|
||||||
this.records_length = $.map(records, function(value, index) { return index; }).length;
|
this.records_length = $.map(records, function(value, index) { return index; }).length;
|
||||||
if (this.records_length > 1) { self.display_followers(); }
|
if (this.records_length > 1) { self.display_followers(); }
|
||||||
|
var old_model = '';
|
||||||
_(records).each(function (record, record_name) {
|
_(records).each(function (record, record_name) {
|
||||||
|
if (old_model != record.parent_model){
|
||||||
|
if (old_model != ''){
|
||||||
|
var index = $($list).find('.oe_subtype').length;
|
||||||
|
$($($list).find('.oe_subtype')[index-1]).addClass('subtype-border');
|
||||||
|
}
|
||||||
|
old_model = record.parent_model;
|
||||||
|
}
|
||||||
record.name = record_name;
|
record.name = record_name;
|
||||||
record.followed = record.followed || undefined;
|
record.followed = record.followed || undefined;
|
||||||
$(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo($list);
|
$(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo($list);
|
||||||
|
|
|
@ -135,14 +135,14 @@
|
||||||
<t t-if="widget.is_private or (widget.user_pid != partner[0])">
|
<t t-if="widget.is_private or (widget.user_pid != partner[0])">
|
||||||
<t t-if="!widget.is_private and inc==0"> and </t>
|
<t t-if="!widget.is_private and inc==0"> and </t>
|
||||||
<span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/>
|
<span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/>
|
||||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a>
|
||||||
<t t-if="!widget.options.show_link" t-raw="partner[1]"/>
|
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
|
||||||
</span>
|
</span>
|
||||||
<t t-set="inc" t-value="inc+1"/>
|
<t t-set="inc" t-value="inc+1"/>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.partner_ids.length > 3">
|
<t t-if="widget.partner_ids.length > 3">
|
||||||
<span class="oe_more">, <a><t t-raw="widget.partner_ids.length - 3"/> others...</a></span>
|
<span class="oe_more">, <a><t t-esc="widget.partner_ids.length - 3"/> others...</a></span>
|
||||||
<a class="oe_more_hidden"><<<</a>
|
<a class="oe_more_hidden"><<<</a>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
@ -150,8 +150,8 @@
|
||||||
<t t-foreach='widget.recipients' t-as='recipient'>
|
<t t-foreach='widget.recipients' t-as='recipient'>
|
||||||
<label t-attf-title="Add as recipient and follower (reason: #{recipient.reason})">
|
<label t-attf-title="Add as recipient and follower (reason: #{recipient.reason})">
|
||||||
<input type="checkbox" t-att-checked="recipient.checked ? 'checked' : undefined" t-att-data="recipient.full_name"/>
|
<input type="checkbox" t-att-checked="recipient.checked ? 'checked' : undefined" t-att-data="recipient.full_name"/>
|
||||||
<t t-raw="recipient.name"/>
|
<t t-esc="recipient.name"/>
|
||||||
<t t-if="recipient.email_address">(<t t-raw="recipient.email_address"/>)</t>
|
<t t-if="recipient.email_address">(<t t-esc="recipient.email_address"/>)</t>
|
||||||
<t t-if="!recipient.email_address">(no email address)</t>
|
<t t-if="!recipient.email_address">(no email address)</t>
|
||||||
</label>
|
</label>
|
||||||
</t>
|
</t>
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<h2 class="oe_view_title">
|
<h2 class="oe_view_title">
|
||||||
<span class="oe_view_title_text">
|
<span class="oe_view_title_text">
|
||||||
<t t-raw="widget.action.name"/>
|
<t t-esc="widget.action.name"/>
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<t t-if="widget.action.params.header_description">
|
<t t-if="widget.action.params.header_description">
|
||||||
|
@ -262,12 +262,13 @@
|
||||||
<!-- message itself -->
|
<!-- message itself -->
|
||||||
<div class="oe_msg_content">
|
<div class="oe_msg_content">
|
||||||
<h1 t-if="(widget.show_record_name or widget.subject) and !widget.thread_level" class="oe_msg_title">
|
<h1 t-if="(widget.show_record_name or widget.subject) and !widget.thread_level" class="oe_msg_title">
|
||||||
<a t-if="widget.options.show_link and widget.show_record_name" class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&id=#{widget.res_id}">
|
<a t-if="widget.options.show_link and widget.show_record_name" class="oe_mail_action_model"
|
||||||
<t t-raw="widget.record_name"/>
|
t-attf-href="#action=mail.action_mail_redirect&model=#{widget.model}&res_id=#{widget.res_id}">
|
||||||
|
<t t-esc="widget.record_name"/>
|
||||||
</a>
|
</a>
|
||||||
<span t-if="!widget.options.show_link and widget.show_record_name"><t t-raw="widget.record_name"/></span>
|
<span t-if="!widget.options.show_link and widget.show_record_name"><t t-esc="widget.record_name"/></span>
|
||||||
<t t-if="widget.show_record_name and widget.subject">: </t>
|
<t t-if="widget.show_record_name and widget.subject">: </t>
|
||||||
<t t-if="widget.subject" t-raw="widget.subject"/>
|
<t t-if="widget.subject" t-esc="widget.subject"/>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="oe_msg_body">
|
<div class="oe_msg_body">
|
||||||
<t t-if="widget.body_short">
|
<t t-if="widget.body_short">
|
||||||
|
@ -283,8 +284,8 @@
|
||||||
<t t-if="widget.attachment_ids.length > 0">
|
<t t-if="widget.attachment_ids.length > 0">
|
||||||
<div class="oe_msg_attachment_list"></div>
|
<div class="oe_msg_attachment_list"></div>
|
||||||
</t>
|
</t>
|
||||||
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-raw="widget.author_id[2]"/></a>
|
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-esc="widget.author_id[2]"/></a>
|
||||||
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-raw="widget.author_id[2]"/></span>
|
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-esc="widget.author_id[2]"/></span>
|
||||||
<t t-if="widget.type == 'notification'">
|
<t t-if="widget.type == 'notification'">
|
||||||
updated document
|
updated document
|
||||||
<t t-if="widget.partner_ids.length > 0">
|
<t t-if="widget.partner_ids.length > 0">
|
||||||
|
@ -303,20 +304,20 @@
|
||||||
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
|
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
|
||||||
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
|
||||||
<span t-attf-class="oe_partner_follower">
|
<span t-attf-class="oe_partner_follower">
|
||||||
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-raw="partner[1]"/></a>
|
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a>
|
||||||
<t t-if="!widget.options.show_link" t-raw="partner[1]"/>
|
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
|
||||||
</span>
|
</span>
|
||||||
<t t-if="!partner_last">,</t>
|
<t t-if="!partner_last">,</t>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.partner_ids.length > 3">
|
<t t-if="widget.partner_ids.length > 3">
|
||||||
<span t-att-title="widget.extra_partners_str">and <t t-raw="widget.extra_partners_nbr"/> more</span>
|
<span t-att-title="widget.extra_partners_str">and <t t-esc="widget.extra_partners_nbr"/> more</span>
|
||||||
</t>
|
</t>
|
||||||
<t t-if="widget.type == 'notification' and widget.partner_ids.length > 0">
|
<t t-if="widget.type == 'notification' and widget.partner_ids.length > 0">
|
||||||
notified
|
notified
|
||||||
</t>
|
</t>
|
||||||
<span class='oe_subtle'>•</span>
|
<span class='oe_subtle'>•</span>
|
||||||
<span t-att-title="widget.date">
|
<span t-att-title="widget.date">
|
||||||
<t t-if="widget.timerelative" t-raw="widget.timerelative"/>
|
<t t-if="widget.timerelative" t-esc="widget.timerelative"/>
|
||||||
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
|
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
|
||||||
</span>
|
</span>
|
||||||
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
<span t-if="!widget.options.readonly" class='oe_subtle'>•</span>
|
||||||
|
@ -333,7 +334,7 @@
|
||||||
<div class='oe_separator'></div>
|
<div class='oe_separator'></div>
|
||||||
<a t-if="widget.nb_messages <= 0" class="oe_msg_fetch_more">show more message</a>
|
<a t-if="widget.nb_messages <= 0" class="oe_msg_fetch_more">show more message</a>
|
||||||
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
|
<a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
|
||||||
<a t-if="widget.nb_messages > 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
|
<a t-if="widget.nb_messages > 1" class="oe_msg_fetch_more">show <t t-esc="widget.nb_messages" /> more messages</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
@ -353,7 +354,7 @@
|
||||||
-->
|
-->
|
||||||
<span t-name="mail.thread.message.vote">
|
<span t-name="mail.thread.message.vote">
|
||||||
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
||||||
<t t-raw='widget.vote_nb' />
|
<t t-esc='widget.vote_nb' />
|
||||||
<span class='oe_e'>8</span>
|
<span class='oe_e'>8</span>
|
||||||
</span>
|
</span>
|
||||||
<a href='#' class="oe_msg_vote">
|
<a href='#' class="oe_msg_vote">
|
||||||
|
|
|
@ -43,13 +43,13 @@
|
||||||
<table class='oe_subtype'>
|
<table class='oe_subtype'>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
|
<td width="10%"><input type="checkbox" t-att-checked="record.followed" t-att-id="'input_mail_followers_subtype_'+record.id" t-att-data-id="record.id" t-att-name="record.name" class="oe_msg_subtype_check"/></td>
|
||||||
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-raw="record.name"/></label></td>
|
<td><label t-att-for="'input_mail_followers_subtype_'+record.id"><t t-esc="record.name"/></label></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-name="mail.followers.show_more">
|
<t t-name="mail.followers.show_more">
|
||||||
<div class="oe_partner oe_show_more">And <t t-raw="number"/> more.</div>
|
<div class="oe_partner oe_show_more">And <t t-esc="number"/> more.</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -394,7 +394,7 @@ class TestMailgateway(TestMail):
|
||||||
'message_process: after reply, group should have 2 followers')
|
'message_process: after reply, group should have 2 followers')
|
||||||
|
|
||||||
# Do: incoming email with ref holding model / res_id but that does not match any message in the thread: must raise since OpenERP saas-3
|
# Do: incoming email with ref holding model / res_id but that does not match any message in the thread: must raise since OpenERP saas-3
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(ValueError,
|
||||||
format_and_process,
|
format_and_process,
|
||||||
MAIL_TEMPLATE, email_from='other5@gmail.com',
|
MAIL_TEMPLATE, email_from='other5@gmail.com',
|
||||||
to='noone@example.com', subject='spam',
|
to='noone@example.com', subject='spam',
|
||||||
|
@ -490,14 +490,14 @@ class TestMailgateway(TestMail):
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
# Do: incoming email with model that does not accepts incoming emails must raise
|
# Do: incoming email with model that does not accepts incoming emails must raise
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(ValueError,
|
||||||
format_and_process,
|
format_and_process,
|
||||||
MAIL_TEMPLATE,
|
MAIL_TEMPLATE,
|
||||||
to='noone@example.com', subject='spam', extra='', model='res.country',
|
to='noone@example.com', subject='spam', extra='', model='res.country',
|
||||||
msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
|
msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
|
||||||
|
|
||||||
# Do: incoming email without model and without alias must raise
|
# Do: incoming email without model and without alias must raise
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(ValueError,
|
||||||
format_and_process,
|
format_and_process,
|
||||||
MAIL_TEMPLATE,
|
MAIL_TEMPLATE,
|
||||||
to='noone@example.com', subject='spam', extra='',
|
to='noone@example.com', subject='spam', extra='',
|
||||||
|
|
|
@ -130,7 +130,7 @@ class mail_compose_message(osv.TransientModel):
|
||||||
'wizard_id', 'attachment_id', 'Attachments'),
|
'wizard_id', 'attachment_id', 'Attachments'),
|
||||||
'filter_id': fields.many2one('ir.filters', 'Filters'),
|
'filter_id': fields.many2one('ir.filters', 'Filters'),
|
||||||
}
|
}
|
||||||
|
#TODO change same_thread to False in trunk (Require view update)
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'composition_mode': 'comment',
|
'composition_mode': 'comment',
|
||||||
'body': lambda self, cr, uid, ctx={}: '',
|
'body': lambda self, cr, uid, ctx={}: '',
|
||||||
|
@ -268,6 +268,8 @@ class mail_compose_message(osv.TransientModel):
|
||||||
'mail.message', 0,
|
'mail.message', 0,
|
||||||
context=context)
|
context=context)
|
||||||
mail_values['attachment_ids'] = m2m_attachment_ids
|
mail_values['attachment_ids'] = m2m_attachment_ids
|
||||||
|
if not mail_values.get('reply_to'):
|
||||||
|
mail_values['reply_to'] = mail_values['email_from']
|
||||||
self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)
|
self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)
|
||||||
else:
|
else:
|
||||||
subtype = 'mail.mt_comment'
|
subtype = 'mail.mt_comment'
|
||||||
|
@ -321,10 +323,10 @@ class mail_compose_message(osv.TransientModel):
|
||||||
if email_dict.get('email_from'):
|
if email_dict.get('email_from'):
|
||||||
mail_values['email_from'] = email_dict.pop('email_from')
|
mail_values['email_from'] = email_dict.pop('email_from')
|
||||||
# replies redirection: mass mailing only
|
# replies redirection: mass mailing only
|
||||||
if not wizard.same_thread:
|
if wizard.same_thread and wizard.post:
|
||||||
mail_values['reply_to'] = email_dict.pop('reply_to', None)
|
|
||||||
else:
|
|
||||||
email_dict.pop('reply_to', None)
|
email_dict.pop('reply_to', None)
|
||||||
|
else:
|
||||||
|
mail_values['reply_to'] = email_dict.pop('reply_to', None)
|
||||||
mail_values.update(email_dict)
|
mail_values.update(email_dict)
|
||||||
# mass mailing without post: mail_mail values
|
# mass mailing without post: mail_mail values
|
||||||
if mass_mail_mode and not wizard.post:
|
if mass_mail_mode and not wizard.post:
|
||||||
|
|
|
@ -47,10 +47,10 @@
|
||||||
<field name="notify"
|
<field name="notify"
|
||||||
attrs="{'invisible':['|', ('post', '!=', True), ('composition_mode', '!=', 'mass_mail')]}"/>
|
attrs="{'invisible':['|', ('post', '!=', True), ('composition_mode', '!=', 'mass_mail')]}"/>
|
||||||
<field name="same_thread"
|
<field name="same_thread"
|
||||||
attrs="{'invisible':[('composition_mode', '!=', 'mass_mail')]}"/>
|
attrs="{'invisible':['|', ('composition_mode', '!=', 'mass_mail'), ('post', '=', False)]}"/>
|
||||||
<field name="reply_to" placeholder="Email address te redirect replies..."
|
<field name="reply_to" placeholder="Email address te redirect replies..."
|
||||||
attrs="{'invisible':['|', ('same_thread', '=', True), ('composition_mode', '!=', 'mass_mail')],
|
attrs="{'invisible':['|', '&', ('same_thread', '=', True), ('post', '=', True), ('composition_mode', '!=', 'mass_mail')],
|
||||||
'required':[('same_thread', '!=', True)]}"/>
|
'required':['&', '|', ('post', '=', False), ('same_thread', '=', False), ('composition_mode', '=', 'mass_mail')]}"/>
|
||||||
</group>
|
</group>
|
||||||
<field name="body"/>
|
<field name="body"/>
|
||||||
<field name="attachment_ids" widget="many2many_binary" string="Attach a file"/>
|
<field name="attachment_ids" widget="many2many_binary" string="Attach a file"/>
|
||||||
|
|
|
@ -48,6 +48,7 @@ class MailThread(osv.Model):
|
||||||
if bounce_alias in email_to:
|
if bounce_alias in email_to:
|
||||||
bounce_match = tools.bounce_re.search(email_to)
|
bounce_match = tools.bounce_re.search(email_to)
|
||||||
if bounce_match:
|
if bounce_match:
|
||||||
|
bounced_model, bounced_thread_id = None, False
|
||||||
bounced_mail_id = bounce_match.group(1)
|
bounced_mail_id = bounce_match.group(1)
|
||||||
stat_ids = self.pool['mail.mail.statistics'].set_bounced(cr, uid, mail_mail_ids=[bounced_mail_id], context=context)
|
stat_ids = self.pool['mail.mail.statistics'].set_bounced(cr, uid, mail_mail_ids=[bounced_mail_id], context=context)
|
||||||
for stat in self.pool['mail.mail.statistics'].browse(cr, uid, stat_ids, context=context):
|
for stat in self.pool['mail.mail.statistics'].browse(cr, uid, stat_ids, context=context):
|
||||||
|
@ -55,7 +56,7 @@ class MailThread(osv.Model):
|
||||||
bounced_thread_id = stat.res_id
|
bounced_thread_id = stat.res_id
|
||||||
_logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
|
_logger.info('Routing mail from %s to %s with Message-Id %s: bounced mail from mail %s, model: %s, thread_id: %s',
|
||||||
email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
|
email_from, email_to, message_id, bounced_mail_id, bounced_model, bounced_thread_id)
|
||||||
if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce'):
|
if bounced_model and bounced_model in self.pool and hasattr(self.pool[bounced_model], 'message_receive_bounce') and bounced_thread_id:
|
||||||
self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
|
self.pool[bounced_model].message_receive_bounce(cr, uid, [bounced_thread_id], mail_id=bounced_mail_id, context=context)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -168,8 +168,10 @@ class MassMailing(osv.Model):
|
||||||
'tooltip': (date_begin + relativedelta.relativedelta(days=i)).strftime('%d %B %Y'),
|
'tooltip': (date_begin + relativedelta.relativedelta(days=i)).strftime('%d %B %Y'),
|
||||||
} for i in range(0, self._period_number)]
|
} for i in range(0, self._period_number)]
|
||||||
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
|
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
|
||||||
|
field_col_info = obj._all_columns.get(groupby_field.split(':')[0])
|
||||||
|
pattern = tools.DEFAULT_SERVER_DATE_FORMAT if field_col_info.column._type == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
for group in group_obj:
|
for group in group_obj:
|
||||||
group_begin_date = datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATE_FORMAT).date()
|
group_begin_date = datetime.strptime(group['__domain'][0][2], pattern).date()
|
||||||
timedelta = relativedelta.relativedelta(group_begin_date, date_begin)
|
timedelta = relativedelta.relativedelta(group_begin_date, date_begin)
|
||||||
section_result[timedelta.days] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field)}
|
section_result[timedelta.days] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field)}
|
||||||
return section_result
|
return section_result
|
||||||
|
|
|
@ -111,9 +111,6 @@ class procurement_order(osv.osv):
|
||||||
bom_result = production_obj.action_compute(cr, uid,
|
bom_result = production_obj.action_compute(cr, uid,
|
||||||
[produce_id], properties=[x.id for x in procurement.property_ids])
|
[produce_id], properties=[x.id for x in procurement.property_ids])
|
||||||
production_obj.signal_button_confirm(cr, uid, [produce_id])
|
production_obj.signal_button_confirm(cr, uid, [produce_id])
|
||||||
if res_id:
|
|
||||||
move_obj.write(cr, uid, [res_id],
|
|
||||||
{'location_id': procurement.location_id.id})
|
|
||||||
self.production_order_create_note(cr, uid, ids, context=context)
|
self.production_order_create_note(cr, uid, ids, context=context)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,286 @@
|
||||||
|
# Finnish translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-18 16:05+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Finnish <fi@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-19 05:23+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,memo:0
|
||||||
|
msgid "Note Content"
|
||||||
|
msgstr "Muistiinpanon sisältö"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.stage:0
|
||||||
|
msgid "Stages of Notes"
|
||||||
|
msgstr "Muistiipanojen vaiheet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:note.stage,name:note.demo_note_stage_04
|
||||||
|
#: model:note.stage,name:note.note_stage_02
|
||||||
|
msgid "This Week"
|
||||||
|
msgstr "Tämä viikko"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.model,name:note.model_base_config_settings
|
||||||
|
msgid "base.config.settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.model,name:note.model_note_tag
|
||||||
|
msgid "Note Tag"
|
||||||
|
msgstr "Muistiinpanon tunniste"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:res.groups,name:note.group_note_fancy
|
||||||
|
msgid "Notes / Fancy mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.model,name:note.model_note_note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "Note"
|
||||||
|
msgstr "Muistiinpano"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "Group By..."
|
||||||
|
msgstr "Ryhmittely..."
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,message_follower_ids:0
|
||||||
|
msgid "Followers"
|
||||||
|
msgstr "Seuraajat"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.actions.act_window,help:note.action_note_note
|
||||||
|
msgid ""
|
||||||
|
"<p class=\"oe_view_nocontent_create\">\n"
|
||||||
|
" Click to add a personal note.\n"
|
||||||
|
" </p><p>\n"
|
||||||
|
" Use notes to organize personal tasks or notes. All\n"
|
||||||
|
" notes are private; no one else will be able to see them. "
|
||||||
|
"However\n"
|
||||||
|
" you can share some notes with other people by inviting "
|
||||||
|
"followers\n"
|
||||||
|
" on the note. (Useful for meeting minutes, especially if\n"
|
||||||
|
" you activate the pad feature for collaborative writings).\n"
|
||||||
|
" </p><p>\n"
|
||||||
|
" You can customize how you process your notes/tasks by adding,\n"
|
||||||
|
" removing or modifying columns.\n"
|
||||||
|
" </p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:note.stage,name:note.demo_note_stage_01
|
||||||
|
#: model:note.stage,name:note.note_stage_01
|
||||||
|
msgid "Today"
|
||||||
|
msgstr "Tänään"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.model,name:note.model_res_users
|
||||||
|
msgid "Users"
|
||||||
|
msgstr "Käyttäjät"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "í"
|
||||||
|
msgstr "í"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.stage:0
|
||||||
|
msgid "Stage of Notes"
|
||||||
|
msgstr "Muistiinpanojenvaiheet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,message_unread:0
|
||||||
|
msgid "Unread Messages"
|
||||||
|
msgstr "Lukemattomat viestit"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,current_partner_id:0
|
||||||
|
msgid "unknown"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "By sticky note Category"
|
||||||
|
msgstr "Muistilaput ryhmittäin"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: help:note.note,message_unread:0
|
||||||
|
msgid "If checked new messages require your attention."
|
||||||
|
msgstr "Jos valittu, uudet viestit vaativat huomiosi."
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.stage,name:0
|
||||||
|
msgid "Stage Name"
|
||||||
|
msgstr "Vaiheen nimi"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,message_is_follower:0
|
||||||
|
msgid "Is a Follower"
|
||||||
|
msgstr "on seuraaja"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:note.stage,name:note.demo_note_stage_02
|
||||||
|
msgid "Tomorrow"
|
||||||
|
msgstr "Huomenna"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
#: field:note.note,open:0
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "Aktiivinen"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: help:note.stage,user_id:0
|
||||||
|
msgid "Owner of the note stage."
|
||||||
|
msgstr "Muistiinpanon vaiheen omistaja."
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.ui.menu,name:note.menu_notes_stage
|
||||||
|
msgid "Categories"
|
||||||
|
msgstr "Ryhmät"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
#: field:note.note,stage_id:0
|
||||||
|
msgid "Stage"
|
||||||
|
msgstr "Vaihe"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.tag,name:0
|
||||||
|
msgid "Tag Name"
|
||||||
|
msgstr "Tunnisteen nimi"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,message_ids:0
|
||||||
|
msgid "Messages"
|
||||||
|
msgstr "Viestit"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:base.config.settings:0
|
||||||
|
#: model:ir.actions.act_window,name:note.action_note_note
|
||||||
|
#: model:ir.ui.menu,name:note.menu_note_notes
|
||||||
|
#: view:note.note:0
|
||||||
|
#: model:note.stage,name:note.note_stage_04
|
||||||
|
msgid "Notes"
|
||||||
|
msgstr "Muistiinpanot"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:note.stage,name:note.demo_note_stage_03
|
||||||
|
#: model:note.stage,name:note.note_stage_03
|
||||||
|
msgid "Later"
|
||||||
|
msgstr "Myöhemmin"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.model,name:note.model_note_stage
|
||||||
|
msgid "Note Stage"
|
||||||
|
msgstr "Muistiinpanon vaihe"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,message_summary:0
|
||||||
|
msgid "Summary"
|
||||||
|
msgstr "Yhteenveto"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,stage_ids:0
|
||||||
|
msgid "Stages of Users"
|
||||||
|
msgstr "Käyttäjien vaiheet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,name:0
|
||||||
|
msgid "Note Summary"
|
||||||
|
msgstr "Muistiinpanon yhteenveto"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: model:ir.actions.act_window,name:note.action_note_stage
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "Stages"
|
||||||
|
msgstr "Vaiheet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: help:note.note,message_ids:0
|
||||||
|
msgid "Messages and communication history"
|
||||||
|
msgstr "Viesti- ja kommunikointihistoria"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "Poista"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,color:0
|
||||||
|
msgid "Color Index"
|
||||||
|
msgstr "Väri-indeksi"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,sequence:0
|
||||||
|
#: field:note.stage,sequence:0
|
||||||
|
msgid "Sequence"
|
||||||
|
msgstr "Järjestysluku"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
#: field:note.note,tag_ids:0
|
||||||
|
msgid "Tags"
|
||||||
|
msgstr "Tunnisteet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: view:note.note:0
|
||||||
|
msgid "Archive"
|
||||||
|
msgstr "Arkisto"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:base.config.settings,module_note_pad:0
|
||||||
|
msgid "Use collaborative pads (etherpad)"
|
||||||
|
msgstr "Käytä yhteistyöhön tauluja (Etherpad)"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: help:note.note,message_summary:0
|
||||||
|
msgid ""
|
||||||
|
"Holds the Chatter summary (number of messages, ...). This summary is "
|
||||||
|
"directly in html format in order to be inserted in kanban views."
|
||||||
|
msgstr ""
|
||||||
|
"Sisältää viestien yhteenvedon (viestien määrän,...). Tämä yhteenveto on "
|
||||||
|
"valmiiksi html-muodossa, jotta se voidaan viedä kanban näkymään."
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:base.config.settings,group_note_fancy:0
|
||||||
|
msgid "Use fancy layouts for notes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,current_partner_id:0
|
||||||
|
#: field:note.stage,user_id:0
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "Omistaja"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: help:note.stage,sequence:0
|
||||||
|
msgid "Used to order the note stages"
|
||||||
|
msgstr "Käytetään järjestämään muistiinpanon vaiheet"
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.note,date_done:0
|
||||||
|
msgid "Date done"
|
||||||
|
msgstr "Valmis pvm."
|
||||||
|
|
||||||
|
#. module: note
|
||||||
|
#: field:note.stage,fold:0
|
||||||
|
msgid "Folded by Default"
|
||||||
|
msgstr "Tyhjää ei näytetä"
|
|
@ -72,12 +72,6 @@ class note_note(osv.osv):
|
||||||
def onclick_note_not_done(self, cr, uid, ids, context=None):
|
def onclick_note_not_done(self, cr, uid, ids, context=None):
|
||||||
return self.write(cr, uid, ids, {'open': True}, context=context)
|
return self.write(cr, uid, ids, {'open': True}, context=context)
|
||||||
|
|
||||||
#used for undisplay the follower if it's the current user
|
|
||||||
def _get_my_current_partner(self, cr, uid, ids, name, args, context=None):
|
|
||||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
||||||
pid = user.partner_id and user.partner_id.id or False
|
|
||||||
return dict.fromkeys(ids, pid)
|
|
||||||
|
|
||||||
#return the default stage for the uid user
|
#return the default stage for the uid user
|
||||||
def _get_default_stage_id(self,cr,uid,context=None):
|
def _get_default_stage_id(self,cr,uid,context=None):
|
||||||
ids = self.pool.get('note.stage').search(cr,uid,[('user_id','=',uid)], context=context)
|
ids = self.pool.get('note.stage').search(cr,uid,[('user_id','=',uid)], context=context)
|
||||||
|
@ -101,6 +95,7 @@ class note_note(osv.osv):
|
||||||
'name': fields.function(_get_note_first_line,
|
'name': fields.function(_get_note_first_line,
|
||||||
string='Note Summary',
|
string='Note Summary',
|
||||||
type='text', store=True),
|
type='text', store=True),
|
||||||
|
'user_id': fields.many2one('res.users', 'Owner'),
|
||||||
'memo': fields.html('Note Content'),
|
'memo': fields.html('Note Content'),
|
||||||
'sequence': fields.integer('Sequence'),
|
'sequence': fields.integer('Sequence'),
|
||||||
'stage_id': fields.function(_get_stage_per_user,
|
'stage_id': fields.function(_get_stage_per_user,
|
||||||
|
@ -113,9 +108,9 @@ class note_note(osv.osv):
|
||||||
'date_done': fields.date('Date done'),
|
'date_done': fields.date('Date done'),
|
||||||
'color': fields.integer('Color Index'),
|
'color': fields.integer('Color Index'),
|
||||||
'tag_ids' : fields.many2many('note.tag','note_tags_rel','note_id','tag_id','Tags'),
|
'tag_ids' : fields.many2many('note.tag','note_tags_rel','note_id','tag_id','Tags'),
|
||||||
'current_partner_id' : fields.function(_get_my_current_partner, type="many2one", relation='res.partner', string="Owner"),
|
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
'user_id': lambda self, cr, uid, ctx=None: uid,
|
||||||
'open' : 1,
|
'open' : 1,
|
||||||
'stage_id' : _get_default_stage_id,
|
'stage_id' : _get_default_stage_id,
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
<field name="open"/>
|
<field name="open"/>
|
||||||
<field name="memo"/>
|
<field name="memo"/>
|
||||||
<field name="date_done"/>
|
<field name="date_done"/>
|
||||||
<field name="current_partner_id"/>
|
|
||||||
<field name="message_follower_ids"/>
|
<field name="message_follower_ids"/>
|
||||||
<field name="tag_ids"/>
|
<field name="tag_ids"/>
|
||||||
<templates>
|
<templates>
|
||||||
|
@ -82,7 +81,7 @@
|
||||||
<field name="tag_ids"/>
|
<field name="tag_ids"/>
|
||||||
<div class="oe_right">
|
<div class="oe_right">
|
||||||
<t t-foreach="record.message_follower_ids.raw_value" t-as="follower">
|
<t t-foreach="record.message_follower_ids.raw_value" t-as="follower">
|
||||||
<img t-if="record.current_partner_id.raw_value!=follower" t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar" t-att-data-member_id="follower"/>
|
<img t-att-src="kanban_image('res.partner', 'image_small', follower)" width="24" height="24" class="oe_kanban_avatar" t-att-data-member_id="follower"/>
|
||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_clear"></div>
|
<div class="oe_clear"></div>
|
||||||
|
|
|
@ -1,23 +1,30 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<openerp>
|
<openerp>
|
||||||
<data>
|
<data>
|
||||||
<record id="note_note_rule_global" model="ir.rule">
|
<record id="note_note_rule_global" model="ir.rule">
|
||||||
<field name="name">Only followers can access a sticky notes</field>
|
<field name="name">Only followers can access a sticky notes</field>
|
||||||
<field ref="model_note_note" name="model_id"/>
|
<field name="model_id" ref="model_note_note"/>
|
||||||
<field name="domain_force">[('message_follower_ids','=',user.partner_id.id)]</field>
|
<field name="domain_force">['|', ('user_id', '=', user.id), ('message_follower_ids', '=', user.partner_id.id)]</field>
|
||||||
<field eval="True" name="global"/>
|
<field name="global" eval="True"/>
|
||||||
<field eval="1" name="perm_unlink"/>
|
<field name="perm_create" eval="False"/>
|
||||||
<field eval="0" name="perm_write"/>
|
<field name="perm_unlink" eval="False"/>
|
||||||
<field eval="1" name="perm_read"/>
|
</record>
|
||||||
<field eval="0" name="perm_create"/>
|
|
||||||
</record>
|
<record id="note_note_create_unlink_global" model="ir.rule">
|
||||||
|
<field name="name">note: create / unlink: responsible</field>
|
||||||
|
<field name="model_id" ref="model_note_note"/>
|
||||||
|
<field name="domain_force">[('user_id', '=', user.id)]</field>
|
||||||
|
<field name="global" eval="True"/>
|
||||||
|
<field name="perm_write" eval="False"/>
|
||||||
|
<field name="perm_read" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="note_stage_rule_global" model="ir.rule">
|
||||||
|
<field name="name">Each user have his stage name</field>
|
||||||
|
<field name="model_id" ref="model_note_stage"/>
|
||||||
|
<field name="domain_force">['|',('user_id','=',False),('user_id','=',user.id)]</field>
|
||||||
|
<field name="global" eval="True"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="note_stage_rule_global" model="ir.rule">
|
|
||||||
<field name="name">Each user have his stage name</field>
|
|
||||||
<field ref="model_note_stage" name="model_id"/>
|
|
||||||
<field name="domain_force">['|',('user_id','=',False),('user_id','=',user.id)]</field>
|
|
||||||
<field eval="True" name="global"/>
|
|
||||||
</record>
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Module to talk to EtherpadLite API."""
|
"""Module to talk to EtherpadLite API."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import werkzeug.urls
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class EtherpadLiteClient:
|
||||||
|
|
||||||
params = arguments or {}
|
params = arguments or {}
|
||||||
params.update({'apikey': self.apiKey})
|
params.update({'apikey': self.apiKey})
|
||||||
data = werkzeug.url_encode(params, True)
|
data = urllib.urlencode(params, True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opener = urllib2.build_opener()
|
opener = urllib2.build_opener()
|
||||||
|
|
|
@ -13,13 +13,12 @@
|
||||||
border: solid 1px #ccc;
|
border: solid 1px #ccc;
|
||||||
border-radius:3px;
|
border-radius:3px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 22px;
|
line-height: 28px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
padding-top:-3px;
|
|
||||||
padding-left:-1px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.oe_pad_switch:hover{
|
.oe_pad_switch:hover{
|
||||||
|
|
|
@ -13,6 +13,8 @@ openerp.pad = function(instance) {
|
||||||
this.switch_configured();
|
this.switch_configured();
|
||||||
this.$('.oe_pad_switch').click(function() {
|
this.$('.oe_pad_switch').click(function() {
|
||||||
self.$el.toggleClass('oe_pad_fullscreen');
|
self.$el.toggleClass('oe_pad_fullscreen');
|
||||||
|
self.$el.find('.oe_pad_switch').toggleClass('fa-expand fa-compress');
|
||||||
|
self.view.$el.find('.oe_chatter').toggle();
|
||||||
});
|
});
|
||||||
this.render_value();
|
this.render_value();
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-if="! widget.get('effective_readonly')">
|
<t t-if="! widget.get('effective_readonly')">
|
||||||
<div class="oe_pad_switch_positioner oe_configured">
|
<div class="oe_pad_switch_positioner oe_configured">
|
||||||
<span class="oe_pad_switch oe_e">&Ntilde;</span>
|
<span class="fa fa-expand oe_pad_switch"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="oe_pad_content oe_editing oe_configured">
|
<div class="oe_pad_content oe_editing oe_configured">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -474,11 +474,12 @@ class pos_session(osv.osv):
|
||||||
account_move_obj = self.pool.get('account.move')
|
account_move_obj = self.pool.get('account.move')
|
||||||
pos_order_obj = self.pool.get('pos.order')
|
pos_order_obj = self.pool.get('pos.order')
|
||||||
for session in self.browse(cr, uid, ids, context=context):
|
for session in self.browse(cr, uid, ids, context=context):
|
||||||
|
local_context = dict(context or {}, force_company=session.config_id.journal_id.company_id.id)
|
||||||
order_ids = [order.id for order in session.order_ids if order.state == 'paid']
|
order_ids = [order.id for order in session.order_ids if order.state == 'paid']
|
||||||
|
|
||||||
move_id = account_move_obj.create(cr, uid, {'ref' : session.name, 'journal_id' : session.config_id.journal_id.id, }, context=context)
|
move_id = account_move_obj.create(cr, uid, {'ref' : session.name, 'journal_id' : session.config_id.journal_id.id, }, context=local_context)
|
||||||
|
|
||||||
pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=context)
|
pos_order_obj._create_account_move_line(cr, uid, order_ids, session, move_id, context=local_context)
|
||||||
|
|
||||||
for order in session.order_ids:
|
for order in session.order_ids:
|
||||||
if order.state not in ('paid', 'invoiced'):
|
if order.state not in ('paid', 'invoiced'):
|
||||||
|
@ -948,22 +949,16 @@ class pos_order(osv.osv):
|
||||||
# Tricky, via the workflow, we only have one id in the ids variable
|
# Tricky, via the workflow, we only have one id in the ids variable
|
||||||
"""Create a account move line of order grouped by products or not."""
|
"""Create a account move line of order grouped by products or not."""
|
||||||
account_move_obj = self.pool.get('account.move')
|
account_move_obj = self.pool.get('account.move')
|
||||||
account_move_line_obj = self.pool.get('account.move.line')
|
|
||||||
account_period_obj = self.pool.get('account.period')
|
account_period_obj = self.pool.get('account.period')
|
||||||
account_tax_obj = self.pool.get('account.tax')
|
account_tax_obj = self.pool.get('account.tax')
|
||||||
user_proxy = self.pool.get('res.users')
|
|
||||||
property_obj = self.pool.get('ir.property')
|
property_obj = self.pool.get('ir.property')
|
||||||
cur_obj = self.pool.get('res.currency')
|
cur_obj = self.pool.get('res.currency')
|
||||||
|
|
||||||
period = account_period_obj.find(cr, uid, context=context)[0]
|
|
||||||
|
|
||||||
#session_ids = set(order.session_id for order in self.browse(cr, uid, ids, context=context))
|
#session_ids = set(order.session_id for order in self.browse(cr, uid, ids, context=context))
|
||||||
|
|
||||||
if session and not all(session.id == order.session_id.id for order in self.browse(cr, uid, ids, context=context)):
|
if session and not all(session.id == order.session_id.id for order in self.browse(cr, uid, ids, context=context)):
|
||||||
raise osv.except_osv(_('Error!'), _('Selected orders do not have the same session!'))
|
raise osv.except_osv(_('Error!'), _('Selected orders do not have the same session!'))
|
||||||
|
|
||||||
current_company = user_proxy.browse(cr, uid, uid, context=context).company_id
|
|
||||||
|
|
||||||
grouped_data = {}
|
grouped_data = {}
|
||||||
have_to_group_by = session and session.config_id.group_by or False
|
have_to_group_by = session and session.config_id.group_by or False
|
||||||
|
|
||||||
|
@ -983,7 +978,7 @@ class pos_order(osv.osv):
|
||||||
if order.state != 'paid':
|
if order.state != 'paid':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
user_company = user_proxy.browse(cr, order.user_id.id, order.user_id.id).company_id
|
current_company = order.sale_journal.company_id
|
||||||
|
|
||||||
group_tax = {}
|
group_tax = {}
|
||||||
account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
|
account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
|
||||||
|
@ -1004,6 +999,7 @@ class pos_order(osv.osv):
|
||||||
# if have_to_group_by:
|
# if have_to_group_by:
|
||||||
|
|
||||||
sale_journal_id = order.sale_journal.id
|
sale_journal_id = order.sale_journal.id
|
||||||
|
period = account_period_obj.find(cr, uid, context=dict(context or {}, company_id=current_company.id))[0]
|
||||||
|
|
||||||
# 'quantity': line.qty,
|
# 'quantity': line.qty,
|
||||||
# 'product_id': line.product_id.id,
|
# 'product_id': line.product_id.id,
|
||||||
|
@ -1013,7 +1009,7 @@ class pos_order(osv.osv):
|
||||||
'journal_id' : sale_journal_id,
|
'journal_id' : sale_journal_id,
|
||||||
'period_id' : period,
|
'period_id' : period,
|
||||||
'move_id' : move_id,
|
'move_id' : move_id,
|
||||||
'company_id': user_company and user_company.id or False,
|
'company_id': current_company.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
if data_type == 'product':
|
if data_type == 'product':
|
||||||
|
@ -1054,7 +1050,10 @@ class pos_order(osv.osv):
|
||||||
cur = order.pricelist_id.currency_id
|
cur = order.pricelist_id.currency_id
|
||||||
for line in order.lines:
|
for line in order.lines:
|
||||||
tax_amount = 0
|
tax_amount = 0
|
||||||
taxes = [t for t in line.product_id.taxes_id]
|
taxes = []
|
||||||
|
for t in line.product_id.taxes_id:
|
||||||
|
if t.company_id.id == current_company.id:
|
||||||
|
taxes.append(t)
|
||||||
computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty)['taxes']
|
computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty)['taxes']
|
||||||
|
|
||||||
for tax in computed_taxes:
|
for tax in computed_taxes:
|
||||||
|
|
|
@ -99,13 +99,10 @@ class pos_details_summary(report_sxw.rml_parse):
|
||||||
|
|
||||||
def _get_tax_amount(self, objects):
|
def _get_tax_amount(self, objects):
|
||||||
res = {}
|
res = {}
|
||||||
list_ids = []
|
|
||||||
for order in objects:
|
for order in objects:
|
||||||
for line in order.lines:
|
for line in order.lines:
|
||||||
if len(line.product_id.taxes_id):
|
for tax in line.product_id.taxes_id:
|
||||||
tax = line.product_id.taxes_id[0]
|
res[tax.name] = res.setdefault(tax.name, 0.0) + (line.price_subtotal_incl - line.price_subtotal)
|
||||||
res[tax.name] = (line.price_unit * line.qty * (1-(line.discount or 0.0) / 100.0)) + (tax.id in list_ids and res[tax.name] or 0)
|
|
||||||
list_ids.append(tax.id)
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _get_sales_total(self, objects):
|
def _get_sales_total(self, objects):
|
||||||
|
|
|
@ -232,7 +232,7 @@
|
||||||
<blockTable colWidths="255.0,255.0" style="Table11">
|
<blockTable colWidths="255.0,255.0" style="Table11">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<para style="P2">[[ t[0] ]]</para>
|
<para style="P2">[[ t[0].name ]]</para>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<para style="terp_default_Right_9_Bold">[[ formatLang(t[1], currency_obj=company.currency_id) ]]</para>
|
<para style="terp_default_Right_9_Bold">[[ formatLang(t[1], currency_obj=company.currency_id) ]]</para>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Finnish translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:05+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-22 20:02+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Finnish <fi@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-23 07:07+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16926)\n"
|
||||||
|
|
||||||
|
#. module: portal_project
|
||||||
|
#: model:ir.actions.act_window,help:portal_project.open_view_project
|
||||||
|
msgid ""
|
||||||
|
"<p class=\"oe_view_nocontent_create\">\n"
|
||||||
|
" Click to start a new project.\n"
|
||||||
|
" </p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"<p class=\"oe_view_nocontent_create\">\n"
|
||||||
|
" Klikkaa aloittaaksesi uuden projektin.\n"
|
||||||
|
" </p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#. module: portal_project
|
||||||
|
#: model:ir.actions.act_window,name:portal_project.open_view_project
|
||||||
|
#: model:ir.ui.menu,name:portal_project.portal_services_projects
|
||||||
|
msgid "Projects"
|
||||||
|
msgstr "Projektit"
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Finnish translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-22 20:06+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Finnish <fi@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-23 07:07+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16926)\n"
|
||||||
|
|
||||||
|
#. module: portal_project_issue
|
||||||
|
#: view:project.issue:0
|
||||||
|
msgid "Creation:"
|
||||||
|
msgstr "Luonti:"
|
||||||
|
|
||||||
|
#. module: portal_project_issue
|
||||||
|
#: model:ir.actions.act_window,help:portal_project_issue.project_issue_categ_act0
|
||||||
|
msgid ""
|
||||||
|
"<p class=\"oe_view_nocontent_create\">\n"
|
||||||
|
" Click to create an issue.\n"
|
||||||
|
" </p><p>\n"
|
||||||
|
" You can track your issues from this menu and the action we\n"
|
||||||
|
" will take.\n"
|
||||||
|
" </p>\n"
|
||||||
|
" "
|
||||||
|
msgstr ""
|
||||||
|
"<p class=\"oe_view_nocontent_create\">\n"
|
||||||
|
" Klikkaa luodaksesi palautteen.\n"
|
||||||
|
" </p><p>\n"
|
||||||
|
" Voit seurata palautteitasi ja niiden\n"
|
||||||
|
" etenemistä tästä valikosta.\n"
|
||||||
|
" </p>\n"
|
||||||
|
" "
|
||||||
|
|
||||||
|
#. module: portal_project_issue
|
||||||
|
#: model:ir.actions.act_window,name:portal_project_issue.project_issue_categ_act0
|
||||||
|
msgid "Issues"
|
||||||
|
msgstr "Ilmoitukset"
|
|
@ -244,13 +244,15 @@ class product_pricelist(osv.osv):
|
||||||
for seller in product.seller_ids:
|
for seller in product.seller_ids:
|
||||||
if (not partner) or (seller.name.id<>partner):
|
if (not partner) or (seller.name.id<>partner):
|
||||||
continue
|
continue
|
||||||
product_default_uom = product.uom_id.id
|
qty_in_seller_uom = qty
|
||||||
|
from_uom = context.get('uom') or product.uom_id.id
|
||||||
seller_uom = seller.product_uom and seller.product_uom.id or False
|
seller_uom = seller.product_uom and seller.product_uom.id or False
|
||||||
if seller_uom and product_default_uom and product_default_uom != seller_uom:
|
if seller_uom and from_uom and from_uom != seller_uom:
|
||||||
|
qty_in_seller_uom = product_uom_obj._compute_qty(cr, uid, from_uom, qty, to_uom_id=seller_uom)
|
||||||
|
else:
|
||||||
uom_price_already_computed = True
|
uom_price_already_computed = True
|
||||||
qty = product_uom_obj._compute_qty(cr, uid, product_default_uom, qty, to_uom_id=seller_uom)
|
|
||||||
for line in seller.pricelist_ids:
|
for line in seller.pricelist_ids:
|
||||||
if line.min_quantity <= qty:
|
if line.min_quantity <= qty_in_seller_uom:
|
||||||
price = line.price
|
price = line.price
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -277,7 +279,6 @@ class product_pricelist(osv.osv):
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
if 'uom' in context and not uom_price_already_computed:
|
if 'uom' in context and not uom_price_already_computed:
|
||||||
product = products_dict[product.id]
|
|
||||||
uom = product.uos_id or product.uom_id
|
uom = product.uos_id or product.uom_id
|
||||||
price = product_uom_obj._compute_price(cr, uid, uom.id, price, context['uom'])
|
price = product_uom_obj._compute_price(cr, uid, uom.id, price, context['uom'])
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
# Amharic translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-21 11:53+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Amharic <am@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-22 06:39+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16926)\n"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:product.template,name:product_expiry.product_product_from_product_template
|
||||||
|
msgid "Ham"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:product.template,name:product_expiry.product_product_lait_product_template
|
||||||
|
msgid "Cow milk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:product.product,life_time:0
|
||||||
|
msgid "Product Life Time"
|
||||||
|
msgstr "የእቃው የመቆያ ግዜ"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:stock.production.lot,removal_date:0
|
||||||
|
msgid ""
|
||||||
|
"This is the date on which the goods with this Serial Number should be "
|
||||||
|
"removed from the stock."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:product.product,removal_time:0
|
||||||
|
msgid ""
|
||||||
|
"When a new a Serial Number is issued, this is the number of days before the "
|
||||||
|
"goods should be removed from the stock."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:product.product,use_time:0
|
||||||
|
msgid "Product Use Time"
|
||||||
|
msgstr "የእቃው መጠቀምያ ግዜ"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:ir.model,name:product_expiry.model_product_product
|
||||||
|
msgid "Product"
|
||||||
|
msgstr "እቃ"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:product.product,use_time:0
|
||||||
|
msgid ""
|
||||||
|
"When a new a Serial Number is issued, this is the number of days before the "
|
||||||
|
"goods starts deteriorating, without being dangerous yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:product.product,removal_time:0
|
||||||
|
msgid "Product Removal Time"
|
||||||
|
msgstr "የእቃው የሚወገድበት ግዜ"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:stock.production.lot,alert_date:0
|
||||||
|
msgid ""
|
||||||
|
"This is the date on which an alert should be notified about the goods with "
|
||||||
|
"this Serial Number."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:ir.model,name:product_expiry.model_stock_production_lot
|
||||||
|
msgid "Serial Number"
|
||||||
|
msgstr "መለያ ቁጥር"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:product.product,alert_time:0
|
||||||
|
msgid ""
|
||||||
|
"When a new a Serial Number is issued, this is the number of days before an "
|
||||||
|
"alert should be notified."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:stock.production.lot,removal_date:0
|
||||||
|
msgid "Removal Date"
|
||||||
|
msgstr "የሚወገድበት ቀን"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:product.template,name:product_expiry.product_product_pain_product_template
|
||||||
|
msgid "Bread"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: view:product.product:0
|
||||||
|
msgid "Dates"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:stock.production.lot,life_date:0
|
||||||
|
msgid "End of Life Date"
|
||||||
|
msgstr "የመጨረሻው ቀን"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:stock.production.lot,use_date:0
|
||||||
|
msgid "Best before Date"
|
||||||
|
msgstr "ያለፍ ጥሩ ቀናቶች"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: model:product.template,name:product_expiry.product_product_jambon_product_template
|
||||||
|
msgid "French cheese Camenbert"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:product.product,life_time:0
|
||||||
|
msgid ""
|
||||||
|
"When a new a Serial Number is issued, this is the number of days before the "
|
||||||
|
"goods may become dangerous and must not be consumed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:stock.production.lot,alert_date:0
|
||||||
|
msgid "Alert Date"
|
||||||
|
msgstr "ማሳወቅያ ቀን"
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:stock.production.lot,use_date:0
|
||||||
|
msgid ""
|
||||||
|
"This is the date on which the goods with this Serial Number start "
|
||||||
|
"deteriorating, without being dangerous yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: help:stock.production.lot,life_date:0
|
||||||
|
msgid ""
|
||||||
|
"This is the date on which the goods with this Serial Number may become "
|
||||||
|
"dangerous and must not be consumed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: product_expiry
|
||||||
|
#: field:product.product,alert_time:0
|
||||||
|
msgid "Product Alert Time"
|
||||||
|
msgstr "እቃውን የማሳወቅያ ግዜ"
|
|
@ -358,6 +358,11 @@ class project(osv.osv):
|
||||||
default['state'] = 'open'
|
default['state'] = 'open'
|
||||||
default['line_ids'] = []
|
default['line_ids'] = []
|
||||||
default['tasks'] = []
|
default['tasks'] = []
|
||||||
|
|
||||||
|
# Don't prepare (expensive) data to copy children (analytic accounts),
|
||||||
|
# they are discarded in analytic.copy(), and handled in duplicate_template()
|
||||||
|
default['child_ids'] = []
|
||||||
|
|
||||||
proj = self.browse(cr, uid, id, context=context)
|
proj = self.browse(cr, uid, id, context=context)
|
||||||
if not default.get('name', False):
|
if not default.get('name', False):
|
||||||
default.update(name=_("%s (copy)") % (proj.name))
|
default.update(name=_("%s (copy)") % (proj.name))
|
||||||
|
@ -690,23 +695,13 @@ class task(osv.osv):
|
||||||
return {'value': vals}
|
return {'value': vals}
|
||||||
|
|
||||||
def duplicate_task(self, cr, uid, map_ids, context=None):
|
def duplicate_task(self, cr, uid, map_ids, context=None):
|
||||||
for new in map_ids.values():
|
mapper = lambda t: map_ids.get(t.id, t.id)
|
||||||
task = self.browse(cr, uid, new, context)
|
for task in self.browse(cr, uid, map_ids.values(), context):
|
||||||
child_ids = [ ch.id for ch in task.child_ids]
|
new_child_ids = set(map(mapper, task.child_ids))
|
||||||
if task.child_ids:
|
new_parent_ids = set(map(mapper, task.parent_ids))
|
||||||
for child in task.child_ids:
|
if new_child_ids or new_parent_ids:
|
||||||
if child.id in map_ids.keys():
|
task.write({'parent_ids': [(6,0,list(new_parent_ids))],
|
||||||
child_ids.remove(child.id)
|
'child_ids': [(6,0,list(new_child_ids))]})
|
||||||
child_ids.append(map_ids[child.id])
|
|
||||||
|
|
||||||
parent_ids = [ ch.id for ch in task.parent_ids]
|
|
||||||
if task.parent_ids:
|
|
||||||
for parent in task.parent_ids:
|
|
||||||
if parent.id in map_ids.keys():
|
|
||||||
parent_ids.remove(parent.id)
|
|
||||||
parent_ids.append(map_ids[parent.id])
|
|
||||||
#FIXME why there is already the copy and the old one
|
|
||||||
self.write(cr, uid, new, {'parent_ids':[(6,0,set(parent_ids))], 'child_ids':[(6,0, set(child_ids))]})
|
|
||||||
|
|
||||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||||
if default is None:
|
if default is None:
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
<field name="name">Task Created</field>
|
<field name="name">Task Created</field>
|
||||||
<field name="res_model">project.task</field>
|
<field name="res_model">project.task</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
|
<field name="hidden" eval="True"/>
|
||||||
<field name="description">Task created</field>
|
<field name="description">Task created</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_task_assigned" model="mail.message.subtype">
|
<record id="mt_task_assigned" model="mail.message.subtype">
|
||||||
|
@ -101,6 +102,7 @@
|
||||||
<!-- Project-related subtypes for messaging / Chatter -->
|
<!-- Project-related subtypes for messaging / Chatter -->
|
||||||
<record id="mt_project_task_new" model="mail.message.subtype">
|
<record id="mt_project_task_new" model="mail.message.subtype">
|
||||||
<field name="name">Task Created</field>
|
<field name="name">Task Created</field>
|
||||||
|
<field name="sequence">10</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_task_new')"/>
|
<field name="parent_id" eval="ref('mt_task_new')"/>
|
||||||
|
@ -108,6 +110,7 @@
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_task_assigned" model="mail.message.subtype">
|
<record id="mt_project_task_assigned" model="mail.message.subtype">
|
||||||
<field name="name">Task Assigned</field>
|
<field name="name">Task Assigned</field>
|
||||||
|
<field name="sequence">11</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_task_assigned')"/>
|
<field name="parent_id" eval="ref('mt_task_assigned')"/>
|
||||||
|
@ -115,12 +118,14 @@
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_task_blocked" model="mail.message.subtype">
|
<record id="mt_project_task_blocked" model="mail.message.subtype">
|
||||||
<field name="name">Task Blocked</field>
|
<field name="name">Task Blocked</field>
|
||||||
|
<field name="sequence">12</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="parent_id" eval="ref('mt_task_blocked')"/>
|
<field name="parent_id" eval="ref('mt_task_blocked')"/>
|
||||||
<field name="relation_field">project_id</field>
|
<field name="relation_field">project_id</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_task_stage" model="mail.message.subtype">
|
<record id="mt_project_task_stage" model="mail.message.subtype">
|
||||||
<field name="name">Task Stage Changed</field>
|
<field name="name">Task Stage Changed</field>
|
||||||
|
<field name="sequence">13</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="parent_id" eval="ref('mt_task_stage')"/>
|
<field name="parent_id" eval="ref('mt_task_stage')"/>
|
||||||
<field name="relation_field">project_id</field>
|
<field name="relation_field">project_id</field>
|
||||||
|
|
|
@ -33,6 +33,7 @@ Access all issues from the top Project menu, and access the issues of a specific
|
||||||
<field name="name">Issue Created</field>
|
<field name="name">Issue Created</field>
|
||||||
<field name="res_model">project.issue</field>
|
<field name="res_model">project.issue</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
|
<field name="hidden" eval="True"/>
|
||||||
<field name="description">Issue created</field>
|
<field name="description">Issue created</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_issue_assigned" model="mail.message.subtype">
|
<record id="mt_issue_assigned" model="mail.message.subtype">
|
||||||
|
@ -56,6 +57,7 @@ Access all issues from the top Project menu, and access the issues of a specific
|
||||||
<!-- Project-related subtypes for messaging / Chatter -->
|
<!-- Project-related subtypes for messaging / Chatter -->
|
||||||
<record id="mt_project_issue_new" model="mail.message.subtype">
|
<record id="mt_project_issue_new" model="mail.message.subtype">
|
||||||
<field name="name">Issue Created</field>
|
<field name="name">Issue Created</field>
|
||||||
|
<field name="sequence">20</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_issue_new')"/>
|
<field name="parent_id" eval="ref('mt_issue_new')"/>
|
||||||
|
@ -63,6 +65,7 @@ Access all issues from the top Project menu, and access the issues of a specific
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_issue_assigned" model="mail.message.subtype">
|
<record id="mt_project_issue_assigned" model="mail.message.subtype">
|
||||||
<field name="name">Issue Assigned</field>
|
<field name="name">Issue Assigned</field>
|
||||||
|
<field name="sequence">21</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="default" eval="False"/>
|
<field name="default" eval="False"/>
|
||||||
<field name="parent_id" eval="ref('mt_issue_assigned')"/>
|
<field name="parent_id" eval="ref('mt_issue_assigned')"/>
|
||||||
|
@ -70,12 +73,14 @@ Access all issues from the top Project menu, and access the issues of a specific
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_issue_blocked" model="mail.message.subtype">
|
<record id="mt_project_issue_blocked" model="mail.message.subtype">
|
||||||
<field name="name">Issue Blocked</field>
|
<field name="name">Issue Blocked</field>
|
||||||
|
<field name="sequence">22</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="parent_id" eval="ref('mt_issue_blocked')"/>
|
<field name="parent_id" eval="ref('mt_issue_blocked')"/>
|
||||||
<field name="relation_field">project_id</field>
|
<field name="relation_field">project_id</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_project_issue_stage" model="mail.message.subtype">
|
<record id="mt_project_issue_stage" model="mail.message.subtype">
|
||||||
<field name="name">Issue Stage Changed</field>
|
<field name="name">Issue Stage Changed</field>
|
||||||
|
<field name="sequence">23</field>
|
||||||
<field name="res_model">project.project</field>
|
<field name="res_model">project.project</field>
|
||||||
<field name="description">Stage changed</field>
|
<field name="description">Stage changed</field>
|
||||||
<field name="parent_id" eval="ref('mt_issue_stage')"/>
|
<field name="parent_id" eval="ref('mt_issue_stage')"/>
|
||||||
|
|
|
@ -176,7 +176,7 @@
|
||||||
<button name="invoice_ok" states="except_invoice" string="Manually Corrected"/>
|
<button name="invoice_ok" states="except_invoice" string="Manually Corrected"/>
|
||||||
<button name="purchase_approve" states="confirmed" string="Approve Order" class="oe_highlight" groups="purchase.group_purchase_manager"/>
|
<button name="purchase_approve" states="confirmed" string="Approve Order" class="oe_highlight" groups="purchase.group_purchase_manager"/>
|
||||||
<button name="view_picking" string="Receive Products" type="object" attrs="{'invisible': ['|', ('shipped','=',True), ('state','!=', 'approved')]}" class="oe_highlight"/>
|
<button name="view_picking" string="Receive Products" type="object" attrs="{'invisible': ['|', ('shipped','=',True), ('state','!=', 'approved')]}" class="oe_highlight"/>
|
||||||
<button name="view_invoice" string="Receive Invoice" type="object" attrs="{'invisible': ['|', ('invoice_method','=','picking'), '|', ('state','!=', 'approved'), ('invoiced','=',True) ]}" class="oe_highlight"/>
|
<button name="view_invoice" string="Receive Invoice" type="object" attrs="{'invisible': ['|', ('invoice_method','in', ['picking', 'manual']), '|', ('state','!=', 'approved'), ('invoiced','=',True) ]}" class="oe_highlight"/>
|
||||||
<button name="action_cancel_draft" states="cancel,confirmed" string="Set to Draft" type="object" />
|
<button name="action_cancel_draft" states="cancel,confirmed" string="Set to Draft" type="object" />
|
||||||
<button name="purchase_cancel" states="draft,confirmed,sent" string="Cancel"/>
|
<button name="purchase_cancel" states="draft,confirmed,sent" string="Cancel"/>
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,approved,done" statusbar_colors='{"except_picking":"red","except_invoice":"red","confirmed":"blue"}' readonly="1"/>
|
<field name="state" widget="statusbar" statusbar_visible="draft,sent,approved,done" statusbar_colors='{"except_picking":"red","except_invoice":"red","confirmed":"blue"}' readonly="1"/>
|
||||||
|
|
|
@ -1014,8 +1014,8 @@ class account_invoice(osv.Model):
|
||||||
sale_order_obj = self.pool.get('sale.order')
|
sale_order_obj = self.pool.get('sale.order')
|
||||||
res = super(account_invoice, self).confirm_paid(cr, uid, ids, context=context)
|
res = super(account_invoice, self).confirm_paid(cr, uid, ids, context=context)
|
||||||
so_ids = sale_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context)
|
so_ids = sale_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context)
|
||||||
if so_ids:
|
for so_id in so_ids:
|
||||||
sale_order_obj.message_post(cr, uid, so_ids, body=_("Invoice paid"), context=context)
|
sale_order_obj.message_post(cr, uid, so_id, body=_("Invoice paid"), context=context)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def unlink(self, cr, uid, ids, context=None):
|
def unlink(self, cr, uid, ids, context=None):
|
||||||
|
|
|
@ -77,9 +77,9 @@
|
||||||
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
|
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
|
||||||
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
|
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
|
||||||
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
||||||
<button name="action_quotation_send" string="Send by Email" type="object" states="sent" groups="base.group_user"/>
|
<button name="action_quotation_send" string="Send by Email" type="object" states="sent,progress,manual" groups="base.group_user"/>
|
||||||
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
|
||||||
<button name="print_quotation" string="Print" type="object" states="sent" groups="base.group_user"/>
|
<button name="print_quotation" string="Print" type="object" states="sent,progress,manual" groups="base.group_user"/>
|
||||||
<button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
|
<button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
|
||||||
<button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
|
<button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
|
||||||
<button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
|
<button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
<button name="cancel" states="draft,sent" string="Cancel Quotation" groups="base.group_user"/>
|
<button name="cancel" states="draft,sent" string="Cancel Quotation" groups="base.group_user"/>
|
||||||
<button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
|
<button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
|
||||||
<button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
|
<button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,invoiced,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
|
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
|
||||||
</header>
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
<h1>
|
<h1>
|
||||||
|
@ -231,7 +231,7 @@
|
||||||
<field name="name">sale.order.form.editable.list</field>
|
<field name="name">sale.order.form.editable.list</field>
|
||||||
<field name="model">sale.order</field>
|
<field name="model">sale.order</field>
|
||||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
<field name="groups_id" eval="[(6, 0, [ref('product.group_uos'), ref('product.group_stock_packaging'), ref('sale.group_mrp_properties')])]"/>
|
<field name="groups_id" eval="[(4, ref('product.group_uos')), (4, ref('product.group_stock_packaging')), (4, ref('sale.group_mrp_properties'))]"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='order_line']/tree" position="attributes">
|
<xpath expr="//field[@name='order_line']/tree" position="attributes">
|
||||||
<attribute name="editable"/>
|
<attribute name="editable"/>
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
<!-- Salesteam-related subtypes for messaging / Chatter -->
|
||||||
<record id="mt_salesteam_order_sent" model="mail.message.subtype">
|
<record id="mt_salesteam_order_sent" model="mail.message.subtype">
|
||||||
<field name="name">Quotation Send</field>
|
<field name="name">Quotation Send</field>
|
||||||
|
<field name="sequence">20</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="parent_id" eval="ref('sale.mt_order_sent')"/>
|
<field name="parent_id" eval="ref('sale.mt_order_sent')"/>
|
||||||
<field name="relation_field">section_id</field>
|
<field name="relation_field">section_id</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="mt_salesteam_order_confirmed" model="mail.message.subtype">
|
<record id="mt_salesteam_order_confirmed" model="mail.message.subtype">
|
||||||
<field name="name">Sales Order Confirmed</field>
|
<field name="name">Sales Order Confirmed</field>
|
||||||
|
<field name="sequence">21</field>
|
||||||
<field name="res_model">crm.case.section</field>
|
<field name="res_model">crm.case.section</field>
|
||||||
<field name="parent_id" eval="ref('sale.mt_order_confirmed')"/>
|
<field name="parent_id" eval="ref('sale.mt_order_confirmed')"/>
|
||||||
<field name="relation_field">section_id</field>
|
<field name="relation_field">section_id</field>
|
||||||
|
|
|
@ -214,8 +214,7 @@
|
||||||
<record id="action_order_report_quotation_salesteam" model="ir.actions.act_window">
|
<record id="action_order_report_quotation_salesteam" model="ir.actions.act_window">
|
||||||
<field name="name">Quotations Analysis</field>
|
<field name="name">Quotations Analysis</field>
|
||||||
<field name="res_model">sale.report</field>
|
<field name="res_model">sale.report</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_mode">graph</field>
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="domain">[('state','=','draft'),('section_id', '=', active_id)]</field>
|
<field name="domain">[('state','=','draft'),('section_id', '=', active_id)]</field>
|
||||||
<field name="context">{'search_default_order_month':1}</field>
|
<field name="context">{'search_default_order_month':1}</field>
|
||||||
<field name="help">This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
|
<field name="help">This report performs analysis on your quotations. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
|
||||||
|
@ -224,8 +223,7 @@
|
||||||
<record id="action_order_report_so_salesteam" model="ir.actions.act_window">
|
<record id="action_order_report_so_salesteam" model="ir.actions.act_window">
|
||||||
<field name="name">Sales Analysis</field>
|
<field name="name">Sales Analysis</field>
|
||||||
<field name="res_model">sale.report</field>
|
<field name="res_model">sale.report</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_mode">graph</field>
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="domain">[('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)]</field>
|
<field name="domain">[('state','not in',('draft','sent','cancel')),('section_id', '=', active_id)]</field>
|
||||||
<field name="context">{'search_default_order_month':1}</field>
|
<field name="context">{'search_default_order_month':1}</field>
|
||||||
<field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
|
<field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field>
|
||||||
|
@ -234,8 +232,7 @@
|
||||||
<record id="action_account_invoice_report_salesteam" model="ir.actions.act_window">
|
<record id="action_account_invoice_report_salesteam" model="ir.actions.act_window">
|
||||||
<field name="name">Invoices Analysis</field>
|
<field name="name">Invoices Analysis</field>
|
||||||
<field name="res_model">account.invoice.report</field>
|
<field name="res_model">account.invoice.report</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_mode">graph</field>
|
||||||
<field name="view_mode">tree,graph</field>
|
|
||||||
<field name="domain">[('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field>
|
<field name="domain">[('section_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field>
|
||||||
<field name="context">{'search_default_month':1}</field>
|
<field name="context">{'search_default_month':1}</field>
|
||||||
<field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
|
<field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
<xpath expr="//button[@name='action_cancel']" position="after">
|
<xpath expr="//button[@name='action_cancel']" position="after">
|
||||||
<button name="ship_cancel" states="shipping_except" string="Cancel Order"/>
|
<button name="ship_cancel" states="shipping_except" string="Cancel Order"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<field name="state" position="replace">
|
<field name="state" position="attributes">
|
||||||
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,invoiced,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
|
<attribute name="statusbar_colors">{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}</attribute>
|
||||||
</field>
|
</field>
|
||||||
<field name="company_id" position="replace">
|
<field name="company_id" position="replace">
|
||||||
<field name="company_id" readonly="True"/>
|
<field name="company_id" readonly="True"/>
|
||||||
|
|
|
@ -379,8 +379,6 @@ class product_product(osv.osv):
|
||||||
"In a context with a single Warehouse, this includes "
|
"In a context with a single Warehouse, this includes "
|
||||||
"goods stored in the Stock Location of this Warehouse, or any "
|
"goods stored in the Stock Location of this Warehouse, or any "
|
||||||
"of its children.\n"
|
"of its children.\n"
|
||||||
"stored in the Stock Location of the Warehouse of this Shop, "
|
|
||||||
"or any of its children.\n"
|
|
||||||
"Otherwise, this includes goods stored in any Stock Location "
|
"Otherwise, this includes goods stored in any Stock Location "
|
||||||
"with 'internal' type."),
|
"with 'internal' type."),
|
||||||
'incoming_qty': fields.function(_product_available, multi='qty_available',
|
'incoming_qty': fields.function(_product_available, multi='qty_available',
|
||||||
|
@ -392,9 +390,6 @@ class product_product(osv.osv):
|
||||||
"In a context with a single Warehouse, this includes "
|
"In a context with a single Warehouse, this includes "
|
||||||
"goods arriving to the Stock Location of this Warehouse, or "
|
"goods arriving to the Stock Location of this Warehouse, or "
|
||||||
"any of its children.\n"
|
"any of its children.\n"
|
||||||
"In a context with a single Shop, this includes goods "
|
|
||||||
"arriving to the Stock Location of the Warehouse of this "
|
|
||||||
"Shop, or any of its children.\n"
|
|
||||||
"Otherwise, this includes goods arriving to any Stock "
|
"Otherwise, this includes goods arriving to any Stock "
|
||||||
"Location with 'internal' type."),
|
"Location with 'internal' type."),
|
||||||
'outgoing_qty': fields.function(_product_available, multi='qty_available',
|
'outgoing_qty': fields.function(_product_available, multi='qty_available',
|
||||||
|
@ -406,9 +401,6 @@ class product_product(osv.osv):
|
||||||
"In a context with a single Warehouse, this includes "
|
"In a context with a single Warehouse, this includes "
|
||||||
"goods leaving the Stock Location of this Warehouse, or "
|
"goods leaving the Stock Location of this Warehouse, or "
|
||||||
"any of its children.\n"
|
"any of its children.\n"
|
||||||
"In a context with a single Shop, this includes goods "
|
|
||||||
"leaving the Stock Location of the Warehouse of this "
|
|
||||||
"Shop, or any of its children.\n"
|
|
||||||
"Otherwise, this includes goods leaving any Stock "
|
"Otherwise, this includes goods leaving any Stock "
|
||||||
"Location with 'internal' type."),
|
"Location with 'internal' type."),
|
||||||
'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"),
|
'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"),
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
# Finnish translation for openobject-addons
|
||||||
|
# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
|
||||||
|
# This file is distributed under the same license as the openobject-addons package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: openobject-addons\n"
|
||||||
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
|
||||||
|
"PO-Revision-Date: 2014-02-19 10:26+0000\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: Finnish <fi@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Launchpad-Export-Date: 2014-02-20 05:14+0000\n"
|
||||||
|
"X-Generator: Launchpad (build 16916)\n"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "here:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: field:sale.config.settings,api_key:0
|
||||||
|
msgid "API Key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/js/linkedin.js:331
|
||||||
|
#, python-format
|
||||||
|
msgid "No results found"
|
||||||
|
msgstr "Haku ei tuottanut tuloksia."
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/js/linkedin.js:62
|
||||||
|
#, python-format
|
||||||
|
msgid "Ok"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "Log into LinkedIn."
|
||||||
|
msgstr "Kirjaudun LinkedIn:iin."
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/xml/linkedin.xml:13
|
||||||
|
#, python-format
|
||||||
|
msgid "People"
|
||||||
|
msgstr "Henkilöt"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: model:ir.model,name:web_linkedin.model_sale_config_settings
|
||||||
|
msgid "sale.config.settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: field:sale.config.settings,server_domain:0
|
||||||
|
msgid "unknown"
|
||||||
|
msgstr "tuntematon"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "https://www.linkedin.com/secure/developer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/xml/linkedin.xml:15
|
||||||
|
#, python-format
|
||||||
|
msgid "Companies"
|
||||||
|
msgstr "Yritykset"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "Copy the"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/js/linkedin.js:263
|
||||||
|
#, python-format
|
||||||
|
msgid "LinkedIn search"
|
||||||
|
msgstr "LinkedIn-haku"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/xml/linkedin.xml:31
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"LinkedIn access was not enabled on this server.\n"
|
||||||
|
" Please ask your administrator to configure it in Settings > "
|
||||||
|
"Configuration > Sales > Social Network Integration."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid ""
|
||||||
|
"To use the LinkedIn module with this database, an API Key is required. "
|
||||||
|
"Please follow this procedure:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#. openerp-web
|
||||||
|
#: code:addons/web_linkedin/static/src/js/linkedin.js:60
|
||||||
|
#, python-format
|
||||||
|
msgid "LinkedIn is not enabled"
|
||||||
|
msgstr "LinkedIn ei ole vahvistettu käyttöön"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "Add a new application and fill the form:"
|
||||||
|
msgstr "Lisää uusi hakemus ja täytä lomake:"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "Go to this URL:"
|
||||||
|
msgstr "Siirry tähän URL-osoitteeseen:"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "The programming tool is Javascript"
|
||||||
|
msgstr "Ohjelmointityökalu on JavaScript"
|
||||||
|
|
||||||
|
#. module: web_linkedin
|
||||||
|
#: view:sale.config.settings:0
|
||||||
|
msgid "JavaScript API Domain:"
|
||||||
|
msgstr "JavaScript API Toimialue:"
|
|
@ -95,8 +95,15 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||||
# Edit
|
# Edit
|
||||||
#------------------------------------------------------
|
#------------------------------------------------------
|
||||||
@http.route('/website/add/<path:path>', type='http', auth="user", website=True)
|
@http.route('/website/add/<path:path>', type='http', auth="user", website=True)
|
||||||
def pagenew(self, path, noredirect=False):
|
def pagenew(self, path, noredirect=False, add_menu=None):
|
||||||
xml_id = request.registry['website'].new_page(request.cr, request.uid, path, context=request.context)
|
xml_id = request.registry['website'].new_page(request.cr, request.uid, path, context=request.context)
|
||||||
|
if add_menu:
|
||||||
|
model, id = request.registry["ir.model.data"].get_object_reference(request.cr, request.uid, 'website', 'main_menu')
|
||||||
|
request.registry['website.menu'].create(request.cr, request.uid, {
|
||||||
|
'name': path,
|
||||||
|
'url': "/page/" + xml_id,
|
||||||
|
'parent_id': id,
|
||||||
|
}, context=request.context)
|
||||||
url = "/page/" + xml_id
|
url = "/page/" + xml_id
|
||||||
if noredirect:
|
if noredirect:
|
||||||
return werkzeug.wrappers.Response(url, mimetype='text/plain')
|
return werkzeug.wrappers.Response(url, mimetype='text/plain')
|
||||||
|
|
|
@ -148,7 +148,7 @@ class view(osv.osv):
|
||||||
qcontext.update(values)
|
qcontext.update(values)
|
||||||
|
|
||||||
# in edit mode ir.ui.view will tag nodes
|
# in edit mode ir.ui.view will tag nodes
|
||||||
context['inherit_branding'] = qcontext['editable']
|
context['inherit_branding'] = qcontext.get('editable', False)
|
||||||
|
|
||||||
view_obj = request.website.get_template(id_or_xml_id)
|
view_obj = request.website.get_template(id_or_xml_id)
|
||||||
if 'main_object' not in qcontext:
|
if 'main_object' not in qcontext:
|
||||||
|
|
|
@ -178,10 +178,8 @@ class website(osv.osv):
|
||||||
return '%s.%s' % (module, slugify(name, max_length=50))
|
return '%s.%s' % (module, slugify(name, max_length=50))
|
||||||
|
|
||||||
def page_exists(self, cr, uid, ids, name, module='website', context=None):
|
def page_exists(self, cr, uid, ids, name, module='website', context=None):
|
||||||
page = self.page_for_name(cr, uid, ids, name, module=module, context=context)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.pool["ir.model.data"].get_object_reference(cr, uid, module, name)
|
return self.pool["ir.model.data"].get_object_reference(cr, uid, module, name)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -251,7 +249,7 @@ class website(osv.osv):
|
||||||
pmin = pmax - scope if pmax - scope > 0 else 1
|
pmin = pmax - scope if pmax - scope > 0 else 1
|
||||||
|
|
||||||
def get_url(page):
|
def get_url(page):
|
||||||
_url = "%spage/%s/" % (url, page)
|
_url = "%spage/%s/" % (url, page) if page > 1 else url
|
||||||
if url_args:
|
if url_args:
|
||||||
_url = "%s?%s" % (_url, werkzeug.url_encode(url_args))
|
_url = "%s?%s" % (_url, werkzeug.url_encode(url_args))
|
||||||
return _url
|
return _url
|
||||||
|
@ -351,6 +349,7 @@ class website(osv.osv):
|
||||||
router = request.httprequest.app.get_db_router(request.db)
|
router = request.httprequest.app.get_db_router(request.db)
|
||||||
# Force enumeration to be performed as public user
|
# Force enumeration to be performed as public user
|
||||||
uid = self.get_public_user(cr, uid, context=context)
|
uid = self.get_public_user(cr, uid, context=context)
|
||||||
|
url_list = []
|
||||||
for rule in router.iter_rules():
|
for rule in router.iter_rules():
|
||||||
if not self.rule_is_enumerable(rule):
|
if not self.rule_is_enumerable(rule):
|
||||||
continue
|
continue
|
||||||
|
@ -372,7 +371,9 @@ class website(osv.osv):
|
||||||
for values in generated:
|
for values in generated:
|
||||||
domain_part, url = rule.build(values, append_unknown=False)
|
domain_part, url = rule.build(values, append_unknown=False)
|
||||||
page = {'name': url, 'url': url}
|
page = {'name': url, 'url': url}
|
||||||
|
if url in url_list:
|
||||||
|
continue
|
||||||
|
url_list.append(url)
|
||||||
if not filtered and query_string and not self.page_matches(cr, uid, page, query_string, context=context):
|
if not filtered and query_string and not self.page_matches(cr, uid, page, query_string, context=context):
|
||||||
continue
|
continue
|
||||||
yield page
|
yield page
|
||||||
|
@ -497,9 +498,14 @@ class website_menu(osv.osv):
|
||||||
'parent_left': fields.integer('Parent Left', select=True),
|
'parent_left': fields.integer('Parent Left', select=True),
|
||||||
'parent_right': fields.integer('Parent Right', select=True),
|
'parent_right': fields.integer('Parent Right', select=True),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __defaults_sequence(self, cr, uid, context):
|
||||||
|
menu = self.search_read(cr, uid, [(1,"=",1)], ["sequence"], limit=1, order="sequence DESC", context=context)
|
||||||
|
return menu and menu[0]["sequence"] or 0
|
||||||
|
|
||||||
_defaults = {
|
_defaults = {
|
||||||
'url': '',
|
'url': '',
|
||||||
'sequence': 0,
|
'sequence': __defaults_sequence,
|
||||||
'new_window': False,
|
'new_window': False,
|
||||||
}
|
}
|
||||||
_parent_store = True
|
_parent_store = True
|
||||||
|
|
|
@ -270,6 +270,10 @@
|
||||||
-moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
|
-moz-box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
|
||||||
box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
|
box-shadow: 0 0 5px 3px rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
.oe_overlay .oe_handle.e:before, .oe_overlay .oe_handle.w:before {
|
||||||
|
content: "\f0d9-\f0da";
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
.oe_overlay .oe_handle.e {
|
.oe_overlay .oe_handle.e {
|
||||||
left: auto;
|
left: auto;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
@ -277,8 +281,17 @@
|
||||||
right: -6px;
|
right: -6px;
|
||||||
cursor: w-resize;
|
cursor: w-resize;
|
||||||
}
|
}
|
||||||
.oe_overlay .oe_handle.e:before {
|
.oe_overlay .oe_handle.w {
|
||||||
content: "\F061";
|
top: 2px;
|
||||||
|
height: 100%;
|
||||||
|
left: -6px;
|
||||||
|
cursor: e-resize;
|
||||||
|
}
|
||||||
|
.oe_overlay .oe_handle.s:before, .oe_overlay .oe_handle.n:before {
|
||||||
|
z-index: 0;
|
||||||
|
content: "\f07d";
|
||||||
|
text-align: center;
|
||||||
|
padding: 1px;
|
||||||
}
|
}
|
||||||
.oe_overlay .oe_handle.s {
|
.oe_overlay .oe_handle.s {
|
||||||
top: auto;
|
top: auto;
|
||||||
|
@ -287,9 +300,11 @@
|
||||||
bottom: -6px;
|
bottom: -6px;
|
||||||
cursor: n-resize;
|
cursor: n-resize;
|
||||||
}
|
}
|
||||||
.oe_overlay .oe_handle.s:before {
|
.oe_overlay .oe_handle.n {
|
||||||
z-index: 0;
|
left: 2px;
|
||||||
content: "\F063";
|
width: 100%;
|
||||||
|
top: -6px;
|
||||||
|
cursor: s-resize;
|
||||||
}
|
}
|
||||||
.oe_overlay .oe_handle.size {
|
.oe_overlay .oe_handle.size {
|
||||||
top: auto;
|
top: auto;
|
||||||
|
@ -320,24 +335,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
}
|
}
|
||||||
.oe_overlay .oe_handle.w {
|
|
||||||
top: 2px;
|
|
||||||
height: 100%;
|
|
||||||
left: -6px;
|
|
||||||
cursor: e-resize;
|
|
||||||
}
|
|
||||||
.oe_overlay .oe_handle.w:before {
|
|
||||||
content: "\F060";
|
|
||||||
}
|
|
||||||
.oe_overlay .oe_handle.n {
|
|
||||||
left: 2px;
|
|
||||||
width: 100%;
|
|
||||||
top: -6px;
|
|
||||||
cursor: s-resize;
|
|
||||||
}
|
|
||||||
.oe_overlay .oe_handle.n:before {
|
|
||||||
content: "\F062";
|
|
||||||
}
|
|
||||||
.oe_overlay .icon.btn {
|
.oe_overlay .icon.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
@ -362,6 +359,36 @@
|
||||||
.oe_overlay .oe_overlay_options .dropdown-menu select, .oe_overlay .oe_overlay_options .dropdown-menu input {
|
.oe_overlay .oe_overlay_options .dropdown-menu select, .oe_overlay .oe_overlay_options .dropdown-menu input {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
.oe_overlay.block-w-left .w:before {
|
||||||
|
content: "\F061" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-w-right .w:before {
|
||||||
|
content: "\F060" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-w-left.block-w-right .w {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-e-left .e:before {
|
||||||
|
content: "\F061" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-e-right .e:before {
|
||||||
|
content: "\F060" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-e-left.block-e-right .e {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-s-top .s:before {
|
||||||
|
content: "\F063" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-s-bottom .s:before {
|
||||||
|
content: "\f062" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-n-top .n:before {
|
||||||
|
content: "\F063" !important;
|
||||||
|
}
|
||||||
|
.oe_overlay.block-n-bottom .n:before {
|
||||||
|
content: "\f062" !important;
|
||||||
|
}
|
||||||
|
|
||||||
.s-resize-important, .s-resize-important * {
|
.s-resize-important, .s-resize-important * {
|
||||||
cursor: s-resize !important;
|
cursor: s-resize !important;
|
||||||
|
|
|
@ -147,6 +147,7 @@
|
||||||
|
|
||||||
.resize_editor_busy
|
.resize_editor_busy
|
||||||
background-color: rgba(0,0,0,0.3)
|
background-color: rgba(0,0,0,0.3)
|
||||||
|
|
||||||
.oe_overlay
|
.oe_overlay
|
||||||
display: none
|
display: none
|
||||||
position: absolute
|
position: absolute
|
||||||
|
@ -194,23 +195,38 @@
|
||||||
background: rgba(0, 0, 0, .5)
|
background: rgba(0, 0, 0, .5)
|
||||||
color: #fff
|
color: #fff
|
||||||
+box-shadow(0 0 5px 3px rgba(255,255,255,.7))
|
+box-shadow(0 0 5px 3px rgba(255,255,255,.7))
|
||||||
|
&.e, &.w
|
||||||
|
&:before
|
||||||
|
content: "\f0d9-\f0da"
|
||||||
|
line-height: 16px
|
||||||
&.e
|
&.e
|
||||||
left: auto
|
left: auto
|
||||||
top: 2px
|
top: 2px
|
||||||
height: 100%
|
height: 100%
|
||||||
right: -6px
|
right: -6px
|
||||||
cursor: w-resize
|
cursor: w-resize
|
||||||
|
&.w
|
||||||
|
top: 2px
|
||||||
|
height: 100%
|
||||||
|
left: -6px
|
||||||
|
cursor: e-resize
|
||||||
|
&.s, &.n
|
||||||
&:before
|
&:before
|
||||||
content: "\F061"
|
z-index: 0
|
||||||
|
content: "\f07d"
|
||||||
|
text-align: center
|
||||||
|
padding: 1px
|
||||||
&.s
|
&.s
|
||||||
top: auto
|
top: auto
|
||||||
left: 2px
|
left: 2px
|
||||||
width: 100%
|
width: 100%
|
||||||
bottom: -6px
|
bottom: -6px
|
||||||
cursor: n-resize
|
cursor: n-resize
|
||||||
&:before
|
&.n
|
||||||
z-index: 0
|
left: 2px
|
||||||
content: "\F063"
|
width: 100%
|
||||||
|
top: -6px
|
||||||
|
cursor: s-resize
|
||||||
&.size
|
&.size
|
||||||
top: auto
|
top: auto
|
||||||
left: 2px
|
left: 2px
|
||||||
|
@ -234,20 +250,6 @@
|
||||||
border-color: rgba(0, 0, 0, 0.5)
|
border-color: rgba(0, 0, 0, 0.5)
|
||||||
position: relative
|
position: relative
|
||||||
top: 8px
|
top: 8px
|
||||||
&.w
|
|
||||||
top: 2px
|
|
||||||
height: 100%
|
|
||||||
left: -6px
|
|
||||||
cursor: e-resize
|
|
||||||
&:before
|
|
||||||
content: "\F060"
|
|
||||||
&.n
|
|
||||||
left: 2px
|
|
||||||
width: 100%
|
|
||||||
top: -6px
|
|
||||||
cursor: s-resize
|
|
||||||
&:before
|
|
||||||
content: "\F062"
|
|
||||||
|
|
||||||
.icon.btn
|
.icon.btn
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
@ -269,6 +271,29 @@
|
||||||
.dropdown-menu select,.dropdown-menu input
|
.dropdown-menu select,.dropdown-menu input
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
|
&.block-w-left .w:before
|
||||||
|
content: "\F061" !important
|
||||||
|
&.block-w-right .w:before
|
||||||
|
content: "\F060" !important
|
||||||
|
&.block-w-left.block-w-right .w
|
||||||
|
display: none !important
|
||||||
|
&.block-e-left .e:before
|
||||||
|
content: "\F061" !important
|
||||||
|
&.block-e-right .e:before
|
||||||
|
content: "\F060" !important
|
||||||
|
&.block-e-left.block-e-right .e
|
||||||
|
display: none !important
|
||||||
|
|
||||||
|
&.block-s-top .s:before
|
||||||
|
content: "\F063" !important
|
||||||
|
&.block-s-bottom .s:before
|
||||||
|
content: "\f062" !important
|
||||||
|
&.block-n-top .n:before
|
||||||
|
content: "\F063" !important
|
||||||
|
&.block-n-bottom .n:before
|
||||||
|
content: "\f062" !important
|
||||||
|
|
||||||
|
|
||||||
.s-resize-important, .s-resize-important *
|
.s-resize-important, .s-resize-important *
|
||||||
cursor: s-resize !important
|
cursor: s-resize !important
|
||||||
.n-resize-important, .n-resize-important *
|
.n-resize-important, .n-resize-important *
|
||||||
|
|
|
@ -298,20 +298,23 @@ ul.nav-stacked > li > a {
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel .carousel-caption {
|
.carousel .carousel-caption {
|
||||||
left: auto;
|
position: absolute;
|
||||||
right: auto;
|
right: 50%;
|
||||||
bottom: 10%;
|
left: 50%;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
.carousel .carousel-caption > div {
|
||||||
|
position: absolute;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: rgba(0, 0, 0, 0.4);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
bottom: 20px;
|
||||||
}
|
}
|
||||||
.carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 {
|
.carousel .carousel-caption h1, .carousel .carousel-caption h2, .carousel .carousel-caption h3 {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
.carousel .carousel-image {
|
.carousel .carousel-image {
|
||||||
right: 50%;
|
|
||||||
left: 50%;
|
|
||||||
top: 5%;
|
top: 5%;
|
||||||
bottom: 5%;
|
bottom: 5%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -326,13 +329,35 @@ ul.nav-stacked > li > a {
|
||||||
right: 10%;
|
right: 10%;
|
||||||
top: 10%;
|
top: 10%;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
|
}
|
||||||
|
.carousel .item.text_only .carousel-caption > div {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
bottom: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.carousel .item.text_image .carousel-caption {
|
||||||
|
left: 10%;
|
||||||
|
}
|
||||||
|
.carousel .item.text_image .carousel-caption > div {
|
||||||
|
right: 50%;
|
||||||
|
margin-right: -20%;
|
||||||
|
max-width: 550px;
|
||||||
|
}
|
||||||
|
.carousel .item.text_image .carousel-image {
|
||||||
|
right: 10%;
|
||||||
|
left: 50%;
|
||||||
}
|
}
|
||||||
.carousel .item.image_text .carousel-caption {
|
.carousel .item.image_text .carousel-caption {
|
||||||
right: 10%;
|
right: 10%;
|
||||||
}
|
}
|
||||||
|
.carousel .item.image_text .carousel-caption > div {
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -20%;
|
||||||
|
max-width: 550px;
|
||||||
|
}
|
||||||
.carousel .item.image_text .carousel-image {
|
.carousel .item.image_text .carousel-image {
|
||||||
|
right: 50%;
|
||||||
left: 10%;
|
left: 10%;
|
||||||
}
|
}
|
||||||
.carousel .carousel-control {
|
.carousel .carousel-control {
|
||||||
|
|
|
@ -244,18 +244,20 @@ ul.nav-stacked > li > a
|
||||||
background-size: cover
|
background-size: cover
|
||||||
.carousel
|
.carousel
|
||||||
.carousel-caption
|
.carousel-caption
|
||||||
left: auto
|
position: absolute
|
||||||
right: auto
|
right: 50%
|
||||||
bottom: 10%
|
left: 50%
|
||||||
text-align: left
|
bottom: 20px
|
||||||
padding: 20px
|
> div
|
||||||
background: rgba(0, 0, 0, 0.4)
|
position: absolute
|
||||||
|
text-align: left
|
||||||
|
padding: 20px
|
||||||
|
background: rgba(0, 0, 0, 0.4)
|
||||||
|
bottom: 20px
|
||||||
h1, h2, h3
|
h1, h2, h3
|
||||||
margin-top: 10px
|
margin-top: 10px
|
||||||
margin-bottom: 10px
|
margin-bottom: 10px
|
||||||
.carousel-image
|
.carousel-image
|
||||||
right: 50%
|
|
||||||
left: 50%
|
|
||||||
top: 5%
|
top: 5%
|
||||||
bottom: 5%
|
bottom: 5%
|
||||||
position: absolute
|
position: absolute
|
||||||
|
@ -269,12 +271,30 @@ ul.nav-stacked > li > a
|
||||||
right: 10%
|
right: 10%
|
||||||
top: 10%
|
top: 10%
|
||||||
bottom: auto
|
bottom: auto
|
||||||
text-align: center
|
> div
|
||||||
background: transparent
|
text-align: center
|
||||||
|
background: transparent
|
||||||
|
bottom: auto
|
||||||
|
width: 100%
|
||||||
|
.item.text_image
|
||||||
|
.carousel-caption
|
||||||
|
left: 10%
|
||||||
|
> div
|
||||||
|
right: 50%
|
||||||
|
margin-right: -20%
|
||||||
|
max-width: 550px
|
||||||
|
.carousel-image
|
||||||
|
right: 10%
|
||||||
|
left: 50%
|
||||||
.item.image_text
|
.item.image_text
|
||||||
.carousel-caption
|
.carousel-caption
|
||||||
right: 10%
|
right: 10%
|
||||||
|
> div
|
||||||
|
left: 50%
|
||||||
|
margin-left: -20%
|
||||||
|
max-width: 550px
|
||||||
.carousel-image
|
.carousel-image
|
||||||
|
right: 50%
|
||||||
left: 10%
|
left: 10%
|
||||||
.carousel-control
|
.carousel-control
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
|
|
|
@ -41,12 +41,14 @@
|
||||||
isWellFormed: function () {
|
isWellFormed: function () {
|
||||||
if (document.implementation.createDocument) {
|
if (document.implementation.createDocument) {
|
||||||
var dom = new DOMParser().parseFromString(this.xml, "text/xml");
|
var dom = new DOMParser().parseFromString(this.xml, "text/xml");
|
||||||
return dom.getElementsByTagName("parsererror").length === 0;
|
var error = dom.getElementsByTagName("parsererror");
|
||||||
|
return error.length === 0 || error;
|
||||||
} else if (window.ActiveXObject) {
|
} else if (window.ActiveXObject) {
|
||||||
// TODO test in IE
|
// TODO test in IE
|
||||||
var msDom = new ActiveXObject("Microsoft.XMLDOM");
|
var msDom = new ActiveXObject("Microsoft.XMLDOM");
|
||||||
msDom.async = false;
|
msDom.async = false;
|
||||||
return !msDom.loadXML(this.xml);
|
msDom.loadXML(this.xml);
|
||||||
|
return !msDom.parseError.errorCode || msDom.parseError.reason + "\nline " + msDom.parseError.line;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -256,28 +258,35 @@
|
||||||
}), function (session) {
|
}), function (session) {
|
||||||
return session.isDirty;
|
return session.isDirty;
|
||||||
});
|
});
|
||||||
var requests = _.map(toSave, self.saveView);
|
this.clearError();
|
||||||
|
var requests = _.map(toSave, function (session) {
|
||||||
|
return self.saveView(session);
|
||||||
|
});
|
||||||
$.when.apply($, requests).then(function () {
|
$.when.apply($, requests).then(function () {
|
||||||
self.reloadPage.call(self);
|
self.reloadPage.call(self);
|
||||||
}).fail(function (source, error) {
|
}).fail(function (source, session, error) {
|
||||||
var message = _.isString(error) ? error
|
self.displayError.call(self, source, session, error);
|
||||||
: (error && error.data && error.data.arguments && error.data.arguments[0] === "Access Denied") ? "Access denied: please sign in"
|
|
||||||
: (error && error.message) ? error.message
|
|
||||||
: "Unexpected error";
|
|
||||||
self.displayError.call(self, message);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveView: function (session) {
|
saveView: function (session) {
|
||||||
|
var self = this;
|
||||||
var xml = new website.ace.XmlDocument(session.text);
|
var xml = new website.ace.XmlDocument(session.text);
|
||||||
if (xml.isWellFormed()) {
|
var isWellFormed = xml.isWellFormed();
|
||||||
return openerp.jsonRpc('/web/dataset/call', 'call', {
|
var def = $.Deferred();
|
||||||
|
if (isWellFormed === true) {
|
||||||
|
openerp.jsonRpc('/web/dataset/call', 'call', {
|
||||||
model: 'ir.ui.view',
|
model: 'ir.ui.view',
|
||||||
method: 'write',
|
method: 'write',
|
||||||
args: [[session.id], { 'arch': xml.xml }, website.get_context()],
|
args: [[session.id], { 'arch': xml.xml }, website.get_context()],
|
||||||
|
}).then(function () {
|
||||||
|
def.resolve();
|
||||||
|
}).fail(function (source, error) {
|
||||||
|
def.reject("server", session, error);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return $.Deferred().reject(null, "Malformed XML document");
|
def.reject(null, session, $(isWellFormed).text());
|
||||||
}
|
}
|
||||||
|
return def;
|
||||||
},
|
},
|
||||||
updateHash: function () {
|
updateHash: function () {
|
||||||
window.location.hash = hash + "?view=" + this.selectedViewId();
|
window.location.hash = hash + "?view=" + this.selectedViewId();
|
||||||
|
@ -286,9 +295,61 @@
|
||||||
this.updateHash();
|
this.updateHash();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
displayError: function (error) {
|
clearError: function () {
|
||||||
// TODO Improve feedback (e.g. update 'Save' button + tooltip)
|
this.$(".ace_layer.ace_text-layer .ace_line").css("background", "");
|
||||||
alert(error);
|
},
|
||||||
|
displayError: function (source, session, error) {
|
||||||
|
var self = this;
|
||||||
|
var line, test;
|
||||||
|
// format error message
|
||||||
|
var message = _.isString(error) ? error
|
||||||
|
: (error && error.data && error.data.arguments && error.data.arguments[0] === "Access Denied") ? "Access denied: please sign in"
|
||||||
|
: (error && error.data && error.data.message) ? error.data.message
|
||||||
|
: (error && error.message) ? error.message
|
||||||
|
: "Unexpected error";
|
||||||
|
if (source == "server") {
|
||||||
|
message = eval(message.replace(/^\(/g, '([')
|
||||||
|
.replace(/\)$/g, '])')
|
||||||
|
.replace(/u'/g, "'")
|
||||||
|
.replace(/<([^>]+)>/g, '<b style="color:#661100;"><\$1></b>'))[1];
|
||||||
|
line = -1;
|
||||||
|
} else {
|
||||||
|
line = message.match(/line ([0-9]+)/i);
|
||||||
|
line = line ? parseInt(line[1],10) : -1;
|
||||||
|
test = new RegExp("^\\s*"+line+"\\s*$");
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotoline() {
|
||||||
|
self.aceEditor.gotoLine(line);
|
||||||
|
setTimeout(function () {
|
||||||
|
var $lines = self.$(".ace_editor .ace_gutter .ace_gutter-cell");
|
||||||
|
var index = $lines.filter(function () {
|
||||||
|
return test.test($(this).text());
|
||||||
|
}).index();
|
||||||
|
if (index>0) {
|
||||||
|
self.$(".ace_layer.ace_text-layer .ace_line:eq(" + index + ")").css("background", "#661100");
|
||||||
|
}
|
||||||
|
},100);
|
||||||
|
}
|
||||||
|
function onchangeSession (e) {
|
||||||
|
self.aceEditor.off('changeSession', onchangeSession);
|
||||||
|
gotoline();
|
||||||
|
}
|
||||||
|
|
||||||
|
var $list = this.$("#ace-view-list");
|
||||||
|
if (+$list.val() == session.id) {
|
||||||
|
if (line>-1) gotoline();
|
||||||
|
} else {
|
||||||
|
if (line) self.aceEditor.on('changeSession', onchangeSession);
|
||||||
|
this.$("#ace-view-list").val(session.id).change();
|
||||||
|
}
|
||||||
|
|
||||||
|
var $dialog = $(openerp.qweb.render('website.error_dialog', {
|
||||||
|
title: session.text.match(/\s+name=['"]([^'"]+)['"]/i)[1],
|
||||||
|
message:"<b>Malformed XML document</b>:<br/>" + message
|
||||||
|
}));
|
||||||
|
$dialog.appendTo("body");
|
||||||
|
$dialog.modal('show');
|
||||||
},
|
},
|
||||||
open: function () {
|
open: function () {
|
||||||
this.$el.removeClass('oe_ace_closed').addClass('oe_ace_open');
|
this.$el.removeClass('oe_ace_closed').addClass('oe_ace_open');
|
||||||
|
|
|
@ -994,6 +994,7 @@
|
||||||
start: function () {
|
start: function () {
|
||||||
var sup = this._super();
|
var sup = this._super();
|
||||||
this.$el.modal({backdrop: 'static'});
|
this.$el.modal({backdrop: 'static'});
|
||||||
|
this.$('input:first').focus();
|
||||||
return sup;
|
return sup;
|
||||||
},
|
},
|
||||||
save: function () {
|
save: function () {
|
||||||
|
@ -1033,10 +1034,13 @@
|
||||||
},
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var last;
|
||||||
this.$('#link-page').select2({
|
this.$('#link-page').select2({
|
||||||
minimumInputLength: 1,
|
minimumInputLength: 1,
|
||||||
placeholder: _t("New or existing page"),
|
placeholder: _t("New or existing page"),
|
||||||
query: function (q) {
|
query: function (q) {
|
||||||
|
if (q.term == last) return;
|
||||||
|
last = q.term;
|
||||||
$.when(
|
$.when(
|
||||||
self.page_exists(q.term),
|
self.page_exists(q.term),
|
||||||
self.fetch_pages(q.term)
|
self.fetch_pages(q.term)
|
||||||
|
@ -1667,6 +1671,8 @@
|
||||||
}
|
}
|
||||||
switch(m.type) {
|
switch(m.type) {
|
||||||
case 'attributes': // ignore .cke_focus being added or removed
|
case 'attributes': // ignore .cke_focus being added or removed
|
||||||
|
// ignore id modification
|
||||||
|
if (m.attributeName === 'id') { return false; }
|
||||||
// if attribute is not a class, can't be .cke_focus change
|
// if attribute is not a class, can't be .cke_focus change
|
||||||
if (m.attributeName !== 'class') { return true; }
|
if (m.attributeName !== 'class') { return true; }
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue