[MERGE]: Merge with lp:openobject-addons

bzr revid: mma@tinyerp.com-20120912100259-k81vv67yg6ydqnwu
This commit is contained in:
Mayur Maheshwari (OpenERP) 2012-09-12 15:32:59 +05:30
commit 10fca1992b
632 changed files with 29485 additions and 10677 deletions

View File

@ -111,7 +111,7 @@ class account_payment_term_line(osv.osv):
'days': fields.integer('Number of Days', required=True, help="Number of days to add before computation of the day of month." \
"If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."),
'days2': fields.integer('Day of the Month', required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the beginning of the month)."),
'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True),
'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True, ondelete='cascade'),
}
_defaults = {
'value': 'balance',
@ -1375,7 +1375,7 @@ class account_move(osv.osv):
balance = 0.0
for line in line_ids:
if line[2]:
balance += (line[2]['debit'] or 0.00)- (line[2]['credit'] or 0.00)
balance += (line[2].get('debit',0.00)- (line[2].get('credit',0.00)))
return {'value': {'balance': balance}}
def write(self, cr, uid, ids, vals, context=None):
@ -1398,7 +1398,7 @@ class account_move(osv.osv):
if not l[0]:
l[2].update({
'reconcile_id':False,
'reconcil_partial_id':False,
'reconcile_partial_id':False,
'analytic_lines':False,
'invoice':False,
'ref':False,
@ -2516,22 +2516,25 @@ class account_account_template(osv.osv):
'nocreate': False,
}
def _check_type(self, cr, uid, ids, context=None):
if context is None:
context = {}
accounts = self.browse(cr, uid, ids, context=context)
for account in accounts:
if account.parent_id and account.parent_id.type != 'view':
return False
return True
_check_recursion = check_cycle
_constraints = [
(_check_recursion, 'Error!\nYou cannot create recursive account templates.', ['parent_id']),
(_check_type, 'Configuration Error!\nYou cannot define children to an account that has internal type other than "View".', ['type']),
]
def create(self, cr, uid, vals, context=None):
if 'parent_id' in vals:
parent = self.read(cr, uid, [vals['parent_id']], ['type'])
if parent and parent[0]['type'] != 'view':
raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'."))
return super(account_account_template, self).create(cr, uid, vals, context=context)
def write(self, cr, uid, ids, vals, context=None):
if 'parent_id' in vals:
parent = self.read(cr, uid, [vals['parent_id']], ['type'])
if parent and parent[0]['type'] != 'view':
raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'."))
return super(account_account_template, self).write(cr, uid, ids, vals, context=context)
def name_get(self, cr, uid, ids, context=None):
if not ids:
return []
@ -2999,6 +3002,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
_columns = {
'company_id':fields.many2one('res.company', 'Company', required=True),
'currency_id': fields.many2one('res.currency', 'Currency', help="Currency as per company's country."),
'only_one_chart_template': fields.boolean('Only One Chart Template Available'),
'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Cash and Banks', required=True),
@ -3009,6 +3013,13 @@ class wizard_multi_charts_accounts(osv.osv_memory):
'purchase_tax_rate': fields.float('Purchase Tax(%)'),
'complete_tax_set': fields.boolean('Complete Set of Taxes', help='This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete'),
}
def onchange_company_id(self, cr, uid, ids, company_id, context=None):
currency_id = False
if company_id:
currency_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.id
return {'value': {'currency_id': currency_id}}
def onchange_tax_rate(self, cr, uid, ids, rate=False, context=None):
return {'value': {'purchase_tax_rate': rate or False}}
@ -3039,6 +3050,13 @@ class wizard_multi_charts_accounts(osv.osv_memory):
res.update({'bank_accounts_id': [{'acc_name': _('Cash'), 'account_type': 'cash'},{'acc_name': _('Bank'), 'account_type': 'bank'}]})
if 'company_id' in fields:
res.update({'company_id': self.pool.get('res.users').browse(cr, uid, [uid], context=context)[0].company_id.id})
if 'currency_id' in fields:
company_id = res.get('company_id') or False
if company_id:
company_obj = self.pool.get('res.company')
country_id = company_obj.browse(cr, uid, company_id, context=context).country_id.id
currency_id = company_obj.on_change_country(cr, uid, company_id, country_id, context=context)['value']['currency_id']
res.update({'currency_id': currency_id})
ids = self.pool.get('account.chart.template').search(cr, uid, [('visible', '=', True)], context=context)
if ids:
@ -3343,6 +3361,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
ir_values_obj = self.pool.get('ir.values')
obj_wizard = self.browse(cr, uid, ids[0])
company_id = obj_wizard.company_id.id
self.pool.get('res.company').write(cr, uid, [company_id], {'currency_id': obj_wizard.currency_id.id})
# If the floats for sale/purchase rates have been filled, create templates from them
self._create_tax_templates_from_rates(cr, uid, obj_wizard, company_id, context=context)

View File

@ -430,7 +430,7 @@ class account_bank_statement(osv.osv):
'name': st_number,
'balance_end_real': st.balance_end
}, context=context)
self.message_append_note(cr, uid, [st.id], body=_('Statement %s is confirmed, journal items are created.') % (st_number,), context=context)
self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st_number,), context=context)
return self.write(cr, uid, ids, {'state':'confirm'}, context=context)
def button_cancel(self, cr, uid, ids, context=None):

View File

@ -15,7 +15,7 @@
<group name="accounting" col="2" colspan="2" attrs="{'invisible': [('company_id','=', False)]}">
<separator string="Accounting Information" colspan="2"/>
<field name="journal_id"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</group>
</group>
</field>
@ -28,7 +28,7 @@
<field name="inherit_id" ref="base.view_partner_bank_tree"/>
<field name="arch" type="xml">
<field name="acc_number" position="after">
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</field>
</field>
</record>

View File

@ -214,7 +214,7 @@ class account_invoice(osv.osv):
\n* The \'Cancelled\' state is used when user cancel invoice.'),
'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."),
'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"),
'date_due': fields.date('Due Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, select=True,
'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True,
help="If you use payment terms, the due date will be computed automatically at the generation "\
"of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."),
'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
@ -253,7 +253,7 @@ class account_invoice(osv.osv):
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',
store={
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
@ -767,17 +767,20 @@ class account_invoice(osv.osv):
if not key in tax_key:
raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.'))
def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines):
def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None):
if context is None:
context={}
total = 0
total_currency = 0
cur_obj = self.pool.get('res.currency')
for i in invoice_move_lines:
if inv.currency_id.id != company_currency:
context.update({'date': inv.date_invoice or time.strftime('%Y-%m-%d')})
i['currency_id'] = inv.currency_id.id
i['amount_currency'] = i['price']
i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
company_currency, i['price'],
context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')})
context=context)
else:
i['amount_currency'] = False
i['currency_id'] = False
@ -887,7 +890,7 @@ class account_invoice(osv.osv):
# create one move line for the total and possibly adjust the other lines amount
total = 0
total_currency = 0
total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml)
total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx)
acc_id = inv.account_id.id
name = inv['name'] or '/'
@ -1045,7 +1048,7 @@ class account_invoice(osv.osv):
if obj_inv.type in ('out_invoice', 'out_refund'):
ctx = self.get_log_context(cr, uid, context=ctx)
message = _("Invoice '%s' is validated.") % name
self.message_append_note(cr, uid, [inv_id], body=message, context=context)
self.message_post(cr, uid, [inv_id], body=message, context=context)
return True
def action_cancel(self, cr, uid, ids, *args):
@ -1275,7 +1278,7 @@ class account_invoice(osv.osv):
# TODO: use currency's formatting function
msg = _("Invoice '%s' is paid partially: %s%s of %s%s (%s%s remaining).") % \
(name, pay_amount, code, invoice.amount_total, code, total, code)
self.message_append_note(cr, uid, [inv_id], body=msg, context=context)
self.message_post(cr, uid, [inv_id], body=msg, context=context)
self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
# Update the stored value (fields.function), so we write to trigger recompute
@ -1288,24 +1291,25 @@ class account_invoice(osv.osv):
def _get_document_type(self, type):
type_dict = {
'out_invoice': 'Customer invoice',
'in_invoice': 'Supplier invoice',
'out_refund': 'Customer Refund',
'in_refund': 'Supplier Refund',
# Translation markers will have no effect at runtime, only used to properly flag export
'out_invoice': _('Customer invoice'),
'in_invoice': _('Supplier invoice'),
'out_refund': _('Customer Refund'),
'in_refund': _('Supplier Refund'),
}
return type_dict.get(type, 'Invoice')
def create_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_append_note(cr, uid, [obj.id],body=_("%s <b>created</b>.") % (self._get_document_type(obj.type)), context=context)
self.message_post(cr, uid, [obj.id], body=_("%s <b>created</b>.") % (_(self._get_document_type(obj.type))), context=context)
def confirm_paid_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_append_note(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (self._get_document_type(obj.type)), context=context)
for obj in self.browse(cr, uid, ids, context=context):
self.message_post(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (_(self._get_document_type(obj.type))), context=context)
def invoice_cancel_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_append_note(cr, uid, [obj.id], body=_("%s <b>cancelled</b>.") % (self._get_document_type(obj.type)), context=context)
self.message_post(cr, uid, [obj.id], body=_("%s <b>cancelled</b>.") % (_(self._get_document_type(obj.type))), context=context)
account_invoice()
@ -1361,10 +1365,16 @@ class account_invoice_line(osv.osv):
'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True)
}
def _default_account_id(self, cr, uid, ids, context=None):
prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context)
return prop and prop.id or False
_defaults = {
'quantity': 1,
'discount': 0.0,
'price_unit': _price_unit_default,
'account_id': _default_account_id,
}
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
@ -1473,10 +1483,11 @@ class account_invoice_line(osv.osv):
prod = self.pool.get('product.product').browse(cr, uid, product, context=context)
prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context)
if prod.uom_id.category_id.id != prod_uom.category_id.id:
warning = {
warning = {
'title': _('Warning!'),
'message': _('The selected unit of measure is not compatible with the unit of measure of the product.')
}
}
res['value'].update({'uos_id': prod.uom_id.id})
return {'value': res['value'], 'warning': warning}
return res

View File

@ -127,7 +127,7 @@
<field name="user_id"/>
<field name="date_due"/>
<field name="origin"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="residual" sum="Residual Amount"/>
<field name="amount_untaxed" sum="Untaxed Amount"/>
<field name="amount_total" sum="Total Amount"/>
@ -184,24 +184,30 @@
name="account_id" groups="account.group_account_user"/>
<field name="journal_id" groups="account.group_account_user"
on_change="onchange_journal_id(journal_id, context)" widget="selection"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</group>
</group>
<notebook>
<page string="Invoice">
<field context="{'partner_id': partner_id, 'price_type': 'price_type' in dir() and price_type or False, 'type': type}" name="invoice_line">
<tree string="Invoice lines" editable="bottom">
<field name="product_id" on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id" on_change="onchange_account_id(product_id,parent.partner_id,parent.type,parent.fiscal_position,account_id)" groups="base.group_account_user"/>
<field name="invoice_line_tax_id" view_mode="2" context="{'type':parent.type}" domain="[('parent_id','=',False)]"/>
<field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="product_id"
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
<field name="name"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
<field name="account_id" groups="account.group_account_user"
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '!=', 'view')]"
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]"/>
<field name="quantity"/>
<field name="uos_id" groups="product.group_uom"
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
<field name="price_unit"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}"
domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]"/>
<field name="price_subtotal"/>
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id" on_change="onchange_account_id(product_id,parent.partner_id,parent.type,parent.fiscal_position,account_id)" invisible="1"/>
<!-- Removed if subtotal is set -->
<field name="name" invisible="1"/>
<field name="uos_id" invisible="1"/>
</tree>
</field>
<group class="oe_subtotal_footer oe_right">
@ -265,7 +271,7 @@
<field name="debit"/>
<field name="credit"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</tree>
</field>
</page>
@ -326,8 +332,8 @@
<field domain="[('company_id', '=', company_id),('type','=', 'receivable')]"
name="account_id" groups="account.group_account_user"/>
<label for="currency_id"/>
<div>
<label for="currency_id" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="currency_id" class="oe_inline"/>
<!-- note fp: I don't think we need this feature ?
<button name="%(action_account_change_currency)d" type="action"
@ -342,17 +348,22 @@
<page string="Invoice Lines">
<field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type}">
<tree string="Invoice Lines" editable="bottom">
<field name="invoice_line_tax_id" invisible="1"/>
<field name="product_id" />
<field name="product_id"
on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
<field name="name"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
<field name="account_id" groups="account.group_account_user"
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]"
domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '!=', 'view')]"
on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/>
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"
domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]"/>
<field name="quantity"/>
<field name="uos_id" groups="product.group_uom"
on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/>
<field name="price_unit"/>
<field name="discount" groups="sale.group_discount_per_so_line"/>
<field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}"
domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]"/>
<field name="price_subtotal"/>
</tree>
</field>
@ -418,8 +429,8 @@
<field name="journal_id" groups="base.group_user"/>
<field name="debit"/>
<field name="credit"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</tree>
</field>
</page>

View File

@ -44,7 +44,7 @@
<menuitem id="menu_finance_periodical_processing_billing" name="Billing" parent="menu_finance_periodical_processing" sequence="35"/>
<menuitem id="next_id_22" name="Partners" parent="menu_finance_generic_reporting" sequence="1"/>
<menuitem id="menu_multi_currency" name="Multi-Currencies" parent="menu_finance_generic_reporting" sequence="10"/>
<menuitem id="menu_multi_currency" name="Multi-Currencies" parent="menu_finance_generic_reporting" sequence="10" groups="base.group_multi_currency"/>
<menuitem
parent="account.menu_finance_legal_statement"
id="final_accounting_reports"

View File

@ -204,7 +204,7 @@
attrs="{'invisible':[('type','!=','consolidation')]}"
widget="many2many_tags"/>
</group>
<group>
<group groups="base.group_multi_currency">
<field name="currency_id"/>
<field name="currency_mode" attrs="{'readonly': [('currency_id','=',False)]}"/>
</group>
@ -489,7 +489,7 @@
<group>
<field name="default_debit_account_id" attrs="{'required':[('type','in', ('cash', 'bank'))]}" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="default_credit_account_id" attrs="{'required':[('type','in',('cash', 'bank'))]}" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
@ -1068,7 +1068,7 @@
<field name="account_tax_id"/>
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('parent_id','!=',False)]"/>
<field name="amount_currency" attrs="{'readonly':[('state','=','valid')]}"/>
<field name="currency_id" attrs="{'readonly':[('state','=','valid')]}"/>
<field name="currency_id" attrs="{'readonly':[('state','=','valid')]}" groups="base.group_multi_currency"/>
<field name="reconcile_partial_id"/>
<field name="reconcile_id"/>
<field name="state"/>
@ -1119,7 +1119,7 @@
<field name="tax_amount"/>
<field name="account_tax_id" domain="[('parent_id','=',False)]"/>
</group>
<group attrs="{'readonly':[('state','=','valid')]}" string="Currency">
<group attrs="{'readonly':[('state','=','valid')]}" string="Currency" groups="base.group_multi_currency">
<field name="currency_id"/>
<field name="amount_currency"/>
</group>
@ -1167,8 +1167,8 @@
<field name="credit"/>
<separator colspan="4" string="Optional Information"/>
<field name="currency_id"/>
<field name="amount_currency"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="quantity"/>
<field name="move_id" required="False"/>
<newline/>
@ -1395,7 +1395,7 @@
<field name="account_tax_id" domain="[('parent_id','=',False)]"/>
</group>
<group string="Currency">
<group string="Currency" groups="base.group_multi_currency">
<field name="currency_id"/>
<field name="amount_currency"/>
</group>
@ -1433,7 +1433,7 @@
<field name="credit" sum="Total Credit"/>
<field name="analytic_account_id" domain="[('parent_id','!=',False)]" groups="analytic.group_analytic_accounting"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="tax_code_id"/>
<field name="tax_amount"/>
<field name="state"/>
@ -2076,7 +2076,7 @@
<field name="type"/>
<field name="user_type"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="reconcile"/>
<field name="chart_template_id"/>
</group>
@ -2369,20 +2369,15 @@
<group string="res_config_contents" position="replace">
<field name="only_one_chart_template" invisible="1"/>
<field name="complete_tax_set" invisible="1"/>
<div groups="base.group_multi_company">
<label for="company_id"/>
<field name="company_id" widget="selection"/> <!-- we assume that this wizard will be run only by administrators and as this field may cause problem if hidden (because of the default company of the user removed from the selection because already configured), we simply choosed to remove the group "multi company" of it -->
</div>
<group>
<div attrs="{'invisible': [('only_one_chart_template','=',True)]}">
<label for="chart_template_id"/>
<group col="1">
<group attrs="{'invisible': [('only_one_chart_template','=',True)]}">
<field name="chart_template_id" widget="selection" on_change="onchange_chart_template_id(chart_template_id)" domain="[('visible','=', True)]"/>
</div>
<newline/>
<group groups="account.group_account_user">
<field name="code_digits"/>
</group>
<group groups="base.group_multi_company">
<field name="company_id" widget="selection" on_change="onchange_company_id(company_id)"/> <!-- we assume that this wizard will be run only by administrators and as this field may cause problem if hidden (because of the default company of the user removed from the selection because already configured), we simply choosed to remove the group "multi company" of it -->
</group>
<group>
<field name="currency_id" class="oe_inline"/>
<field name="sale_tax" attrs="{'invisible': [('complete_tax_set', '!=', True)]}" domain="[('chart_template_id', '=', chart_template_id),('parent_id','=',False),('type_tax_use','in',('sale','all'))]"/>
<label for="sale_tax_rate" string="Sale Tax" attrs="{'invisible': [('complete_tax_set', '=', True)]}"/>
<div attrs="{'invisible': [('complete_tax_set', '=', True)]}">
@ -2394,17 +2389,10 @@
<field name="purchase_tax_rate" class="oe_inline"/> %%
</div>
</group>
<group groups="account.group_account_user">
<field name="code_digits"/>
</group>
</group>
<div groups="account.group_account_user">
<label for="bank_accounts_id" string="Bank Information"/>
<field name="bank_accounts_id">
<tree editable="bottom">
<field name="acc_name"/>
<field name="account_type"/>
<field name="currency_id" widget="selection"/>
</tree>
</field>
</div>
</group>
</field>
</record>
@ -2566,7 +2554,7 @@ action = pool.get('res.config').next(cr, uid, [], context)
<field name="date" attrs="{'readonly':[('state','!=','draft')]}" on_change="onchange_date(date, company_id)"/>
<field name="closing_date" readonly="1"/>
<field name="period_id" class="oe_inline"/>
<field name="currency" invisible="1"/>
<field name="currency" invisible="1" groups="base.group_multi_currency"/>
</group>
</group>
<notebook>

View File

@ -29,7 +29,6 @@
<form string="Account Board" version="7.0">
<board style="2-1">
<column>
<action name="%(account.action_invoice_tree1)d" creatable="true" string="Draft Customer Invoices" domain="[('state','in',('draft','proforma2')), ('type','=','out_invoice')]"/>
<action name="%(action_company_analysis_tree)d" string="Company Analysis"/>
</column>
<column>

View File

@ -45,7 +45,6 @@
<record id="conf_chart0" model="account.account.template">
<field name="code">0</field>
<field name="name">Configurable Account Chart</field>
<field eval="0" name="parent_id"/>
<field name="type">view</field>
<field name="user_type" ref="data_account_type_view"/>
</record>

View File

@ -19,7 +19,6 @@
<record id="chart0" model="account.account">
<field name="code">X0</field>
<field name="name">Chart For Automated Tests</field>
<field eval="0" name="parent_id"/>
<field name="type">view</field>
<field name="user_type" ref="data_account_type_view"/>
</record>
@ -383,7 +382,7 @@
<field name="type">cash</field>
<field name="profit_account_id" model="account.account" ref="rsa" />
<field name="loss_account_id" model="account.account" ref="rsa" />
<field name="internal_account_id" model="account.account" ref="chart0" />
<field name="internal_account_id" model="account.account" ref="rsa" />
<field name="with_last_closing_balance" eval="True" />
<field name="cash_control" eval="True" />
<field name="view_id" ref="account_journal_bank_view"/>

View File

@ -123,64 +123,6 @@
</div>
</div>
]]></field>
<field name="body_text"><![CDATA[
Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},
A new invoice is available for ${object.partner_id.name}:
| Invoice number: *${object.number}*
| Invoice total: *${object.amount_total} ${object.currency_id.name}*
| Invoice date: ${object.date_invoice}
% if object.origin:
| Order reference: ${object.origin}
% endif
| Your contact: ${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''}
You can view the invoice document, download it and pay online using the following link:
${ctx.get('edi_web_url_view') or 'n/a'}
% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'):
<%
comp_name = quote(object.company_id.name)
inv_number = quote(object.number)
paypal_account = quote(object.company_id.paypal_account)
inv_amount = quote(str(object.amount_total))
cur_name = quote(object.currency_id.name)
paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s"\
"&invoice=%s&amount=%s&currency_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s" % \
(paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name)
%>
It is also possible to directly pay with Paypal:
${paypal_url}
% endif
If you have any question, do not hesitate to contact us.
Thank you for choosing ${object.company_id.name}!
--
${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''}
${object.company_id.name}
% if object.company_id.street:
${object.company_id.street or ''}
% endif
% if object.company_id.street2:
${object.company_id.street2}
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip or ''} ${object.company_id.city or ''}
% endif
% if object.company_id.country_id:
${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
% endif
% if object.company_id.phone:
Phone: ${object.company_id.phone}
% endif
% if object.company_id.website:
${object.company_id.website or ''}
% endif
]]></field>
</record>
</data>
</openerp>

View File

@ -9538,7 +9538,7 @@ msgid "Refund"
msgstr ""
#. module: account
#: model:email.template,body_text:account.email_template_edi_invoice
#: model:email.template,body:account.email_template_edi_invoice
msgid "\n"
"Hello${object.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''},\n"
"\n"

View File

@ -56,7 +56,7 @@
<field name="credit"/>
<field name="balance"/>
<field name="state" invisible="1"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="date" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="partner_id" invisible="1"/>
@ -137,8 +137,8 @@
</group>
<group string="Amount">
<field name="amount"/>
<label for="amount_currency"/>
<div>
<label for="amount_currency" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount_currency" class="oe_inline"/>
<field name="currency_id" class="oe_inline"/>
</div>
@ -186,19 +186,23 @@
<search string="Search Analytic Lines">
<field name="name" string="Analytic Line"/>
<field name="date"/>
<filter name="sales" string="Sales" domain="[('journal_id.type','=','sale')]" icon="terp-camera_test" help="Analytic Journal Items related to a sale journal."/>
<filter name="purchases" string="Purchases" domain="[('journal_id.type','=','purchase')]" icon="terp-purchase" help="Analytic Journal Items related to a purchase journal."/>
<filter name="others" string="Others" domain="[('journal_id.type','in',('cash','general','situation'))]" icon="terp-folder-orange"/>
<filter name="sales" string="Sales" domain="[('journal_id.type','=','sale')]" help="Analytic Journal Items related to a sale journal."/>
<filter name="purchases" string="Purchases" domain="[('journal_id.type','=','purchase')]" help="Analytic Journal Items related to a purchase journal."/>
<filter name="others" string="Others" domain="[('journal_id.type','in',('cash','general','situation'))]"/>
<separator/>
<filter string="My Entries" domain="[('user_id','=',uid)]" icon="terp-personal"/>
<filter string="My Entries" domain="[('user_id','=',uid)]"/>
<field name="account_id"/>
<field name="user_id"/>
<group string="Group By..." expand="0">
<filter string="Account" context="{'group_by':'account_id'}" icon="terp-folder-green"/>
<filter string="Journal" context="{'group_by':'journal_id'}" icon="terp-folder-orange"/>
<filter string="User" context="{'group_by':'user_id'}" icon="terp-personal"/>
<filter string="Fin.Account" context="{'group_by':'general_account_id'}" icon="terp-folder-green"/>
<filter string="Product" context="{'group_by':'product_id'}" icon="terp-accessories-archiver"/>
<filter string="Analytic Account" context="{'group_by':'account_id'}"/>
<filter string="Fin. Account" context="{'group_by':'general_account_id'}"/>
<filter string="Journal" context="{'group_by':'journal_id'}" name="group_journal"/>
<separator/>
<filter string="Product" context="{'group_by':'product_id'}"/>
<filter string="User" context="{'group_by':'user_id'}"/>
<separator/>
<filter string="Date" context="{'group_by':'date'}" name="group_date"/>
</group>
</search>
</field>
@ -247,8 +251,8 @@
</group>
<group string="Amount">
<field name="amount"/>
<label for="amount_currency"/>
<div>
<label for="amount_currency" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount_currency" class="oe_inline"/>
<field name="currency_id" class="oe_inline"/>
</div>
@ -357,16 +361,6 @@
action="action_account_analytic_journal_tree"
id="account_analytic_journal_print" parent="account.next_id_40"/>
<act_window
context="{'search_default_account_id': [active_id], 'search_default_user_id': False, 'default_account_id': active_id}"
id="act_acc_analytic_acc_5_report_hr_timesheet_invoice_journal"
name="All Analytic Entries"
res_model="account.analytic.line"
src_model="account.analytic.account"
view_mode="tree,form"
view_type="form"/>
<record id="view_account_journal_1" model="ir.ui.view">
<field name="name">account.journal.form.1</field>
<field name="model">account.journal</field>

View File

@ -43,8 +43,8 @@ class account_config_settings(osv.osv_memory):
string='Default company currency', help="Main currency of the company."),
'paypal_account': fields.related('company_id', 'paypal_account', type='char', size=128,
string='Paypal account', help="Paypal account (email) for receiving online payments (credit card, etc.) If you set a paypal account, the customer will be able to pay your invoices or quotations with a button \"Pay with Paypal\" in automated emails or through the OpenERP portal."),
'company_footer': fields.related('company_id', 'rml_footer2', type='char', size=250, readonly=True,
string='Bank accounts on reports will display as followed', help="Bank accounts as printed in the footer of each customer document. This is for information purpose only, you should configure these bank accounts through the above button \"Configure Bank Accounts\"."),
'company_footer': fields.related('company_id', 'rml_footer', type='text', readonly=True,
string='Bank accounts footer preview', help="Bank accounts as printed in the footer of each printed document"),
'has_chart_of_accounts': fields.boolean('Company has a chart of accounts'),
'chart_template_id': fields.many2one('account.chart.template', 'Template', domain="[('visible','=', True)]"),
@ -114,6 +114,9 @@ class account_config_settings(osv.osv_memory):
help="This purchase tax will be assigned by default on new products."),
'decimal_precision': fields.integer('Decimal precision on journal entries',
help="""As an example, a decimal precision of 2 will allow journal entries like: 9.99 EUR, whereas a decimal precision of 4 will allow journal entries like: 0.0231 EUR."""),
'group_multi_currency': fields.boolean('allow multi currencies',
implied_group='base.group_multi_currency',
help="Allows you multi currency environment"),
}
def _default_company(self, cr, uid, context=None):
@ -145,42 +148,45 @@ class account_config_settings(osv.osv_memory):
def onchange_company_id(self, cr, uid, ids, company_id):
# update related fields
company = self.pool.get('res.company').browse(cr, uid, company_id)
has_chart_of_accounts = company_id not in self.pool.get('account.installer').get_unconfigured_cmp(cr, uid)
fiscalyear_count = self.pool.get('account.fiscalyear').search_count(cr, uid,
[('date_start', '<=', time.strftime('%Y-%m-%d')), ('date_stop', '>=', time.strftime('%Y-%m-%d')),
('company_id', '=', company_id)])
values = {
'expects_chart_of_accounts': company.expects_chart_of_accounts,
'currency_id': company.currency_id.id,
'paypal_account': company.paypal_account,
'company_footer': company.rml_footer2,
'has_chart_of_accounts': has_chart_of_accounts,
'has_fiscal_year': bool(fiscalyear_count),
'chart_template_id': False,
'tax_calculation_rounding_method': company.tax_calculation_rounding_method,
}
# update journals and sequences
for journal_type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
for suffix in ('_journal_id', '_sequence_prefix', '_sequence_next'):
values[journal_type + suffix] = False
journal_obj = self.pool.get('account.journal')
journal_ids = journal_obj.search(cr, uid, [('company_id', '=', company_id)])
for journal in journal_obj.browse(cr, uid, journal_ids):
if journal.type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
values.update({
journal.type + '_journal_id': journal.id,
journal.type + '_sequence_prefix': journal.sequence_id.prefix,
journal.type + '_sequence_next': journal.sequence_id.number_next,
})
# update taxes
ir_values = self.pool.get('ir.values')
taxes_id = ir_values.get_default(cr, uid, 'product.product', 'taxes_id', company_id=company_id)
supplier_taxes_id = ir_values.get_default(cr, uid, 'product.product', 'supplier_taxes_id', company_id=company_id)
values.update({
'default_sale_tax': isinstance(taxes_id, list) and taxes_id[0] or taxes_id,
'default_purchase_tax': isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id,
})
values = {}
values['currency_id'] = False
if company_id:
company = self.pool.get('res.company').browse(cr, uid, company_id)
has_chart_of_accounts = company_id not in self.pool.get('account.installer').get_unconfigured_cmp(cr, uid)
fiscalyear_count = self.pool.get('account.fiscalyear').search_count(cr, uid,
[('date_start', '<=', time.strftime('%Y-%m-%d')), ('date_stop', '>=', time.strftime('%Y-%m-%d')),
('company_id', '=', company_id)])
values = {
'expects_chart_of_accounts': company.expects_chart_of_accounts,
'currency_id': company.currency_id.id,
'paypal_account': company.paypal_account,
'company_footer': company.rml_footer,
'has_chart_of_accounts': has_chart_of_accounts,
'has_fiscal_year': bool(fiscalyear_count),
'chart_template_id': False,
'tax_calculation_rounding_method': company.tax_calculation_rounding_method,
}
# update journals and sequences
for journal_type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
for suffix in ('_journal_id', '_sequence_prefix', '_sequence_next'):
values[journal_type + suffix] = False
journal_obj = self.pool.get('account.journal')
journal_ids = journal_obj.search(cr, uid, [('company_id', '=', company_id)])
for journal in journal_obj.browse(cr, uid, journal_ids):
if journal.type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
values.update({
journal.type + '_journal_id': journal.id,
journal.type + '_sequence_prefix': journal.sequence_id.prefix,
journal.type + '_sequence_next': journal.sequence_id.number_next,
})
# update taxes
ir_values = self.pool.get('ir.values')
taxes_id = ir_values.get_default(cr, uid, 'product.product', 'taxes_id', company_id=company_id)
supplier_taxes_id = ir_values.get_default(cr, uid, 'product.product', 'supplier_taxes_id', company_id=company_id)
values.update({
'default_sale_tax': isinstance(taxes_id, list) and taxes_id[0] or taxes_id,
'default_purchase_tax': isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id,
})
return {'value': values}
def onchange_chart_template_id(self, cr, uid, ids, chart_template_id, context=None):

View File

@ -121,6 +121,10 @@
</div>
<label for="id" string="Features"/>
<div>
<div>
<field name="group_multi_currency" class="oe_inline"/>
<label for="group_multi_currency"/>
</div>
<div>
<field name="module_account_accountant" class="oe_inline"/>
<label for="module_account_accountant"/>
@ -219,12 +223,12 @@
<label for="id" string="Configuration"/>
<div>
<div>
<label for="company_footer"/>
<button name="%(action_bank_tree)d"
string="Configure your bank accounts"
icon="gtk-go-forward"
type="action"
class="oe_inline oe_link"/>
<label for="company_footer"/>
<field name="company_footer"/>
</div>
<div>

View File

@ -30,9 +30,11 @@ class account_common_report(osv.osv_memory):
_description = "Account Common Report"
def onchange_chart_id(self, cr, uid, ids, chart_account_id=False, context=None):
res = {}
if chart_account_id:
company_id = self.pool.get('account.account').browse(cr, uid, chart_account_id, context=context).company_id.id
return {'value': {'company_id': company_id}}
res['value'] = {'company_id': company_id}
return res
_columns = {
'chart_account_id': fields.many2one('account.account', 'Chart of Account', help='Select Charts of Accounts', required=True, domain = [('parent_id','=',False)]),

View File

@ -40,6 +40,10 @@ class CashBox(osv.osv_memory):
return {}
def _create_bank_statement_line(self, cr, uid, box, record, context=None):
values = self._compute_values_for_statement_line(cr, uid, box, record, context=context)
return self.pool.get('account.bank.statement.line').create(cr, uid, values, context=context)
class CashBoxIn(CashBox):
_name = 'cash.box.in'
@ -49,30 +53,24 @@ class CashBoxIn(CashBox):
'ref' : fields.char('Reference', size=32),
})
def _create_bank_statement_line(self, cr, uid, box, record, context=None):
absl_proxy = self.pool.get('account.bank.statement.line')
values = {
def _compute_values_for_statement_line(self, cr, uid, box, record, context=None):
return {
'statement_id' : record.id,
'journal_id' : record.journal_id.id,
'account_id' : record.journal_id.internal_account_id.id,
'amount' : box.amount or 0.0,
'ref' : "%s" % (box.ref or ''),
'ref' : '%s' % (box.ref or ''),
'name' : box.name,
}
return absl_proxy.create(cr, uid, values, context=context)
CashBoxIn()
class CashBoxOut(CashBox):
_name = 'cash.box.out'
def _create_bank_statement_line(self, cr, uid, box, record, context=None):
absl_proxy = self.pool.get('account.bank.statement.line')
def _compute_values_for_statement_line(self, cr, uid, box, record, context=None):
amount = box.amount or 0.0
values = {
return {
'statement_id' : record.id,
'journal_id' : record.journal_id.id,
'account_id' : record.journal_id.internal_account_id.id,
@ -80,6 +78,4 @@ class CashBoxOut(CashBox):
'name' : box.name,
}
return absl_proxy.create(cr, uid, values, context=context)
CashBoxOut()

View File

@ -21,13 +21,14 @@
</record>
<!-- Notify all employees of module installation -->
<function model="mail.group" name="message_append_note">
<!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' -->
<value eval="[ref('mail.group_all_employees')]"/>
<value>Module Accounting and Finance has been installed.</value>
<value>With OpenERP's accounting, you can get an instant access to all your financial data, setup your analytic accounting, forecast your taxes, control your budgets, easily create and send invoices, record bank statements, etc.
<record model="mail.message" id="module_install_notification">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
<field name="type">notification</field>
<field name="subject">Accounting and Finance application installed!</field>
<field name="body">With OpenERP's accounting, you get instant access to your financial data, and can setup analytic accounting, forecast taxes, control budgets, easily create and send invoices, record bank statements, etc.
The accounting features are fully integrated with others OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.</value>
</function>
The accounting features are fully integrated with other OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.</field>
</record>
</data>
</openerp>

View File

@ -6,12 +6,11 @@
Analytic Account form
-->
<act_window
id="action_sales_order"
name="Sales Orders"
res_model="sale.order"
src_model="account.analytic.account"
/>
<record model="ir.actions.act_window" id="action_sales_order">
<field name="name">Sales Orders</field>
<field name="res_model">sale.order</field>
<field name="src_model">account.analytic.account</field>
</record>
<record id="account_analytic_account_form_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.form.inherit</field>

View File

@ -100,7 +100,7 @@
</group>
<group>
<field name="purchase_date"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="company_id" widget="selection" groups="base.group_multi_company" on_change="onchange_company_id(company_id)"/>
</group>
</group>
@ -192,7 +192,7 @@
<field name="partner_id"/>
<field name="purchase_value"/>
<field name="value_residual"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="state"/>
</tree>
@ -211,7 +211,7 @@
<field name="purchase_date"/>
<field name="purchase_value"/>
<field name="value_residual"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="state"/>
</tree>

View File

@ -0,0 +1,385 @@
# Brazilian Portuguese translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-09-11 18:35+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Brazilian Portuguese <pt_BR@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: 2012-09-12 04:36+0000\n"
"X-Generator: Launchpad (build 15930)\n"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Search Bank Transactions"
msgstr "Procurar Transações Bancárias"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: selection:account.bank.statement.line,state:0
msgid "Confirmed"
msgstr "Confirmado"
#. module: account_bank_statement_extensions
#: view:account.bank.statement:0
#: view:account.bank.statement.line:0
msgid "Glob. Id"
msgstr "ID Global"
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "CODA"
msgstr "CODA"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,parent_id:0
msgid "Parent Code"
msgstr "Código da Conta-pai"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Debit"
msgstr "Débito"
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_cancel_statement_line
#: model:ir.model,name:account_bank_statement_extensions.model_cancel_statement_line
msgid "Cancel selected statement lines"
msgstr "Cancelar linhas de instrução selecionadas"
#. module: account_bank_statement_extensions
#: constraint:res.partner.bank:0
msgid "The RIB and/or IBAN is not valid"
msgstr "A RIB e/ ou IBAN não é válido."
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Group By..."
msgstr "Agrupar Por..."
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,state:0
msgid "State"
msgstr "Situação"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: selection:account.bank.statement.line,state:0
msgid "Draft"
msgstr "Provisório"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement"
msgstr "Demonstrativo"
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_confirm_statement_line
#: model:ir.model,name:account_bank_statement_extensions.model_confirm_statement_line
msgid "Confirm selected statement lines"
msgstr "Confirme as linhas do demonstrativo selecionadas"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
#: model:ir.actions.report.xml,name:account_bank_statement_extensions.bank_statement_balance_report
msgid "Bank Statement Balances Report"
msgstr "Relatório de Balanço Bancário"
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
msgid "Cancel Lines"
msgstr "Cancelar Linhas"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line_global
msgid "Batch Payment Info"
msgstr "Informações de Pagamento em Lote"
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Confirm Lines"
msgstr "Confirmar Linhas"
#. module: account_bank_statement_extensions
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130
#, python-format
msgid ""
"Delete operation not allowed ! Please go to the associated bank "
"statement in order to delete and/or modify this bank statement line"
msgstr ""
"Não é permitido excluir! Vá a linha do demonstrativo bancário associada para "
"excluir ou modificar esta linha do demonstrativo"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,type:0
msgid "Type"
msgstr "Tipo"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: field:account.bank.statement.line,journal_id:0
#: report:bank.statement.balance.report:0
msgid "Journal"
msgstr "Diário"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Confirmed Statement Lines."
msgstr "Linhas do Demonstrativo Confirmadas."
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Credit Transactions."
msgstr "Transações de Crédito"
#. module: account_bank_statement_extensions
#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_cancel_statement_line
msgid "cancel selected statement lines."
msgstr "cancelar linhas do demonstrativo selecionadas."
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_number:0
msgid "Counterparty Number"
msgstr "Número da Contrapartida"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
msgid "Transactions"
msgstr "Transações"
#. module: account_bank_statement_extensions
#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130
#, python-format
msgid "Warning"
msgstr "Aviso"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Closing Balance"
msgstr "Saldo final"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Date"
msgstr "Data"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
#: field:account.bank.statement.line,globalisation_amount:0
msgid "Glob. Amount"
msgstr "Valor Global"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Debit Transactions."
msgstr "Transações de Débito."
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Extended Filters..."
msgstr "Filtros Extendidos..."
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Confirmed lines cannot be changed anymore."
msgstr "Linhas confirmadas não podem ser alteradas."
#. module: account_bank_statement_extensions
#: constraint:res.partner.bank:0
msgid ""
"\n"
"Please define BIC/Swift code on bank for bank type IBAN Account to make "
"valid payments"
msgstr ""
"\n"
"Por favor defina o BIC/Swift code no Banco para o tipo de conta IBAN para "
"fazer pagamentos válidos"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,val_date:0
msgid "Valuta Date"
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_confirm_statement_line
msgid "Confirm selected statement lines."
msgstr "Confirmar as linhas do demonstrativo."
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
msgid "Are you sure you want to cancel the selected Bank Statement lines ?"
msgstr ""
"Você tem certeza de que deseja cancelar as Linhas de Demonstrativo Bancário "
"selecionadas?"
#. module: account_bank_statement_extensions
#: report:bank.statement.balance.report:0
msgid "Name"
msgstr "Nome"
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "ISO 20022"
msgstr "ISO 20022"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Notes"
msgstr "Notas"
#. module: account_bank_statement_extensions
#: selection:account.bank.statement.line.global,type:0
msgid "Manual"
msgstr "Manual"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Credit"
msgstr "Crédito"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,amount:0
msgid "Amount"
msgstr "Valor"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Fin.Account"
msgstr "Fin.Account"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_currency:0
msgid "Counterparty Currency"
msgstr "Moeda da Contrapartida"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_bic:0
msgid "Counterparty BIC"
msgstr "BIC da Contrapartida"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,child_ids:0
msgid "Child Codes"
msgstr "Códigos derivados (sub-contas)"
#. module: account_bank_statement_extensions
#: view:confirm.statement.line:0
msgid "Are you sure you want to confirm the selected Bank Statement lines ?"
msgstr ""
"Você deseja confirmar as Linhas do Demonstrativo Bancário selecionadas?"
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement.line:0
msgid ""
"The amount of the voucher must be the same amount as the one on the "
"statement line"
msgstr ""
"O valor do recibo deve ser o mesmo valor da linha equivalente no extrato"
#. module: account_bank_statement_extensions
#: help:account.bank.statement.line,globalisation_id:0
msgid ""
"Code to identify transactions belonging to the same globalisation level "
"within a batch payment"
msgstr ""
"Código para identificar transações que pertencem ao nível de globalização "
"dentro de um mesmo lote de pagamento"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Draft Statement Lines."
msgstr "Linhas de Demonstrativo Provisórias."
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Glob. Am."
msgstr ""
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Linha do Demonstrativo Bancário"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,code:0
msgid "Code"
msgstr "Código"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,counterparty_name:0
msgid "Counterparty Name"
msgstr "Nome da Contrapartida"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,name:0
msgid "Communication"
msgstr "Comunicação"
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank
msgid "Bank Accounts"
msgstr "Contas Bancárias"
#. module: account_bank_statement_extensions
#: constraint:account.bank.statement:0
msgid "The journal and period chosen have to belong to the same company."
msgstr "O diário e o período escolhido tem que pertencer à mesma empresa."
#. module: account_bank_statement_extensions
#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement
msgid "Bank Statement"
msgstr "Extrato Bancário"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Line"
msgstr "Linha do Demonstrativo"
#. module: account_bank_statement_extensions
#: sql_constraint:account.bank.statement.line.global:0
msgid "The code must be unique !"
msgstr "O código precisa ser único!"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line.global,bank_statement_line_ids:0
#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line
#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line
msgid "Bank Statement Lines"
msgstr "Linhas do Demonstrativo Bancário"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line.global:0
msgid "Child Batch Payments"
msgstr "Lote de Pagamentos Filho"
#. module: account_bank_statement_extensions
#: view:cancel.statement.line:0
#: view:confirm.statement.line:0
msgid "Cancel"
msgstr "Cancelar"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Statement Lines"
msgstr "Linhas do Demonstrativo"
#. module: account_bank_statement_extensions
#: view:account.bank.statement.line:0
msgid "Total Amount"
msgstr "Valor Total"
#. module: account_bank_statement_extensions
#: field:account.bank.statement.line,globalisation_id:0
msgid "Globalisation ID"
msgstr "ID Globalização"

View File

@ -0,0 +1,207 @@
# Spanish (Ecuador) translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:35+0000\n"
"PO-Revision-Date: 2012-09-12 01:39+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Spanish (Ecuador) <es_EC@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: 2012-09-12 04:36+0000\n"
"X-Generator: Launchpad (build 15930)\n"
#. module: account_check_writing
#: selection:res.company,check_layout:0
msgid "Check on Top"
msgstr "Cheque on Top"
#. module: account_check_writing
#: model:ir.actions.act_window,help:account_check_writing.action_write_check
msgid ""
"The check payment form allows you to track the payment you do to your "
"suppliers specially by check. When you select a supplier, the payment method "
"and an amount for the payment, OpenERP will propose to reconcile your "
"payment with the open supplier invoices or bills.You can print the check"
msgstr ""
"El pago de cheques permite rastrear el pago a sus proveedores. Cuando "
"selecciona un proveedor, el método de pago y monto, OpenERP propondrá "
"conciliarlo con tu factura, y podrá imprimir el cheque."
#. module: account_check_writing
#: view:account.voucher:0
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_bottom
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_middle
#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_top
msgid "Print Check"
msgstr "Imprimir Cheque"
#. module: account_check_writing
#: selection:res.company,check_layout:0
msgid "Check in middle"
msgstr "Cheque in middle"
#. module: account_check_writing
#: help:res.company,check_layout:0
msgid ""
"Check on top is compatible with Quicken, QuickBooks and Microsoft Money. "
"Check in middle is compatible with Peachtree, ACCPAC and DacEasy. Check on "
"bottom is compatible with Peachtree, ACCPAC and DacEasy only"
msgstr ""
#. module: account_check_writing
#: selection:res.company,check_layout:0
msgid "Check on bottom"
msgstr "Cheque on bottom"
#. module: account_check_writing
#: constraint:res.company:0
msgid "Error! You can not create recursive companies."
msgstr "Error! No puede crear compañías recursivas."
#. module: account_check_writing
#: help:account.journal,allow_check_writing:0
msgid "Check this if the journal is to be used for writing checks."
msgstr "Activar si este diario es usado para emitir cheques"
#. module: account_check_writing
#: field:account.journal,allow_check_writing:0
msgid "Allow Check writing"
msgstr "Permitir emisión de cheques"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Description"
msgstr "Descripción"
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_account_journal
msgid "Journal"
msgstr "Diario"
#. module: account_check_writing
#: model:ir.actions.act_window,name:account_check_writing.action_write_check
#: model:ir.ui.menu,name:account_check_writing.menu_action_write_check
msgid "Write Checks"
msgstr "Escribir Cheque"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Discount"
msgstr "Descuento"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Original Amount"
msgstr "Monto Inicial"
#. module: account_check_writing
#: view:res.company:0
msgid "Configuration"
msgstr "Configuración"
#. module: account_check_writing
#: field:account.voucher,allow_check:0
msgid "Allow Check Writing"
msgstr "Permitir Emisión de Cheques"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Payment"
msgstr "Pagos"
#. module: account_check_writing
#: field:account.journal,use_preprint_check:0
msgid "Use Preprinted Check"
msgstr "Usar cheque preimpreso"
#. module: account_check_writing
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr "¡El nombre de la compañía debe ser único!"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Due Date"
msgstr "Fecha de vencimiento"
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_res_company
msgid "Companies"
msgstr "Compañias"
#. module: account_check_writing
#: view:res.company:0
msgid "Default Check Layout"
msgstr ""
#. module: account_check_writing
#: constraint:account.journal:0
msgid ""
"Configuration error! The currency chosen should be shared by the default "
"accounts too."
msgstr ""
"Error de Configuración! La moneda seleccionada debe ser compartida por las "
"cuentas por defecto tambíen"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
msgid "Balance Due"
msgstr "Saldo Deudor"
#. module: account_check_writing
#: report:account.print.check.bottom:0
#: report:account.print.check.middle:0
#: report:account.print.check.top:0
msgid "Check Amount"
msgstr "Monto Cheque"
#. module: account_check_writing
#: model:ir.model,name:account_check_writing.model_account_voucher
msgid "Accounting Voucher"
msgstr "Comprobantes de Pago"
#. module: account_check_writing
#: sql_constraint:account.journal:0
msgid "The name of the journal must be unique per company !"
msgstr "El nombre del diaro debe ser único por compañía !"
#. module: account_check_writing
#: sql_constraint:account.journal:0
msgid "The code of the journal must be unique per company !"
msgstr "El código del diario debe ser único por compañía !"
#. module: account_check_writing
#: field:account.voucher,amount_in_word:0
msgid "Amount in Word"
msgstr "Monto en Letras"
#. module: account_check_writing
#: report:account.print.check.top:0
msgid "Open Balance"
msgstr "Saldo Inicial"
#. module: account_check_writing
#: field:res.company,check_layout:0
msgid "Choose Check layout"
msgstr "Elegir diseño de cheque"
#~ msgid "Default Check layout"
#~ msgstr "Diseño de cheque por defecto"

View File

@ -16,11 +16,11 @@
<filter string="Info" domain="[('state','=','info')]" icon="terp-folder-yellow"/>
<field name="bank_id"/>
<field name="journal"/>
<field name="currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="company_id" groups="base.group_multi_company"/>
<group expand="0" string="Group By...">
<filter string="Bank Account" icon="terp-folder-yellow" domain="[]" context="{'group_by':'bank_id'}"/>
<filter string="Currency" icon="terp-dolar" domain="[]" context="{'group_by':'currency'}"/>
<filter string="Currency" icon="terp-dolar" domain="[]" context="{'group_by':'currency'}" groups="base.group_multi_currency"/>
</group>
</search>
</field>
@ -32,7 +32,7 @@
<tree string="CODA Bank Account Configuration">
<field name="name"/>
<field name="bank_id"/>
<field name="currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="description1"/>
<field name="journal"/>
<field name="state"/>
@ -48,7 +48,7 @@
<field name="name" colspan="4"/>
<field name="bank_id" domain="[('partner_id.ref_companies', 'in', [company_id])]"/>
<field name="description1"/>
<field name="currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="description2"/>
<field name="coda_st_naming"/>
<field name="state" on_change="onchange_state(state)"/>
@ -305,7 +305,7 @@
<field name="name"/>
<field name="date"/>
<field name="coda_bank_account_id"/>
<field name="currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="period_id"/>
<field name="type"/>
<newline/>

View File

@ -213,8 +213,6 @@ class account_followup_print_all(osv.osv_memory):
mod_obj = self.pool.get('ir.model.data')
move_obj = self.pool.get('account.move.line')
user_obj = self.pool.get('res.users')
line_obj = self.pool.get('account_followup.stat')
mail_message = self.pool.get('mail.message')
if context is None:
context = {}
@ -235,13 +233,7 @@ class account_followup_print_all(osv.osv_memory):
total_amt += line.debit - line.credit
dest = False
if partner:
if partner.type=='contact':
if adr.email:
dest = [partner.email]
if (not dest) and partner.type=='default':
if partner.email:
dest = [partner.email]
src = tools.config.options['email_from']
dest = [partner.email]
if not data.partner_lang:
body = data.email_body
else:
@ -281,7 +273,12 @@ class account_followup_print_all(osv.osv_memory):
msg = ''
if dest:
try:
mail_message.schedule_with_attach(cr, uid, src, dest, sub, body, context=context)
vals = {'state': 'outgoing',
'subject': sub,
'body_html': '<pre>%s</pre>' % body,
'email_to': dest,
'email_from': data_user.email or tools.config.options['email_from']}
self.pool.get('mail.mail').create(cr, uid, vals, context=context)
msg_sent += partner.name + '\n'
except Exception, e:
raise osv.except_osv('Error !', e )

View File

@ -30,8 +30,8 @@
<field name="debit" sum="Total debit"/>
<field name="credit" sum="Total credit"/>
<field name="amount_to_pay"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="period_id" invisible="1"/>
</tree>
</field>
@ -131,8 +131,8 @@
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
<separator colspan="4" string="Transaction Information"/>
<field name="date"/>
<label for="amount_currency"/>
<div>
<label for="amount_currency" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/>
<field name="currency" nolabel="1" class="oe_inline"/>
</div>
@ -151,8 +151,8 @@
</page>
<page string="Information">
<group col="4" string="General Information">
<label for="amount"/>
<div>
<label for="amount" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount" class="oe_inline"/>
<field name="company_currency" class="oe_inline"/>
</div>
@ -171,8 +171,8 @@
<field name="bank_id" domain="[('partner_id', '=', partner_id)]"/>
<field name="ml_maturity_date"/>
<field name="date"/>
<field name="amount_currency" string="Amount"/>
<field name="currency"/>
<field name="amount_currency" string="Amount" groups="base.group_multi_currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="name"/>
<field name="amount" sum="Total in Company Currency" invisible="1"/>
</tree>
@ -257,8 +257,8 @@
<field name="move_line_id" on_change="onchange_move_line(move_line_id, False, currency, company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)]"/>
<separator colspan="4" string="Transaction Information"/>
<field name="date"/>
<label for="amount_currency"/>
<div>
<label for="amount_currency" groups="base.group_multi_currency"/>
<div groups="base.group_multi_currency">
<field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/>
<field name="currency" class="oe_inline"/>
</div>
@ -277,8 +277,11 @@
</page>
<page string="Information">
<group col="4" string="General Information">
<field name="amount"/>
<field name="company_currency" nolabel="1"/>
<label for="amoun"/>
<div>
<field name="amount" class="oe_inline"/>
<field name="company_currency" class="oe_inline"/>
</div>
</group>
<group col="4" string="Entry Information">
<field name="create_date"/>
@ -302,8 +305,8 @@
<field name="partner_id"/>
<field name="communication"/>
<field name="amount" sum="Amount Total"/>
<field name="amount_currency" sum="Currency Amount Total"/>
<field name="currency"/>
<field name="amount_currency" sum="Currency Amount Total" groups="base.group_multi_currency"/>
<field name="currency" groups="base.group_multi_currency"/>
<field name="bank_id" domain="[('partner_id', '=', partner_id)]"/>
<field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode)"/>
<field name="create_date"/>

View File

@ -1293,17 +1293,17 @@ class account_voucher(osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
message = "%s <b>created</b>." % self._document_type[obj.type or False]
self.message_append_note(cr, uid, [obj.id], body=message, context=context)
self.message_post(cr, uid, [obj.id], body=message, context=context)
def post_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
message = "%s '%s' is <b>posted</b>." % (self._document_type[obj.type or False], obj.move_id.name)
self.message_append_note(cr, uid, [obj.id], body=message, context=context)
self.message_post(cr, uid, [obj.id], body=message, context=context)
def reconcile_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
message = "%s <b>reconciled</b>." % self._document_type[obj.type or False]
self.message_append_note(cr, uid, [obj.id], body=message, context=context)
self.message_post(cr, uid, [obj.id], body=message, context=context)
account_voucher()

View File

@ -2,15 +2,16 @@
<openerp>
<data noupdate="1">
<!-- notify all employees of module installation -->
<function model="mail.group" name="message_append_note">
<!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' -->
<value eval="[ref('mail.group_all_employees')]"/>
<value>Module eInvoicing &amp; Payments has been installed.</value>
<value>OpenERP's electronic invoicing allows to ease and fasten the creation of invoices and collection of customer payments. Invoices are created in a few clicks and your customers receive them by email. They can pay online and/or import them in their own system.
<record model="mail.message" id="module_install_notification">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
<field name="type">notification</field>
<field name="subject">eInvoicing &amp; Payments application installed!</field>
<field name="body">OpenERP's electronic invoicing accelerates the creation of invoices and collection of customer payments. Invoices are created in a few clicks and your customers receive them by email. They can pay online and/or import them in their own system.
You can track customer payments easily and automate the reminders. You get an overview of the discussion with your customers on each invoice to ensure a full traceability.
You can track customer payments easily and automate follow-ups. You get an overview of the discussion with your customers on each invoice for easier traceability.
If you want to use advanced accounting features, you should install the "Accounting and Finance" module.</value>
</function>
For advanced accounting features, you should install the "Accounting and Finance" module.</field>
</record>
</data>
</openerp>

View File

@ -75,7 +75,7 @@
</group>
<group string="Other Information">
<field name="number"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</group>
<group col="4" attrs="{'invisible':[('type','in',['payment', 'receipt', False])]}">
<separator string="Total" colspan="4"/>

View File

@ -102,9 +102,9 @@
<field name="narration" nolabel="1" colspan="2"/>
</group>
<group string="Other Information" col="4">
<field name="currency_id" colspan="4"/>
<field name="currency_id" colspan="4" groups="base.group_multi_currency"/>
<field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/>
<field name="paid_amount_in_company_currency" colspan="4" invisible="1"/>
<field name="number" colspan="4"/>
</group>
@ -194,7 +194,7 @@
<separator string="Currency Options" colspan="4"/>
<field name="is_multi_currency" invisible="1"/>
<field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/>
<field name="paid_amount_in_company_currency" colspan="4" invisible="1"/>
</group>
<group col="2">
@ -231,8 +231,8 @@
<field name="credit"/>
<field name="state"/>
<field name="reconcile_id"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="amount_currency" groups="base.group_multi_currency"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</tree>
</field>
</page>
@ -366,7 +366,7 @@
<group col="4" attrs="{'invisible':[('currency_id','=',False),('is_multi_currency','=',False)]}">
<field name="is_multi_currency" invisible="1"/>
<field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/>
<field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/>
<field name="paid_amount_in_company_currency" colspan="4" invisible="1"/>
</group>
<group>
@ -401,7 +401,7 @@
<field name="state"/>
<field name="reconcile_id"/>
<field name="amount_currency"/>
<field name="currency_id"/>
<field name="currency_id" groups="base.group_multi_currency"/>
</tree>
</field>
</page>

View File

@ -297,7 +297,7 @@ class account_analytic_account(osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
for obj in self.browse(cr, uid, ids, context=context):
self.message_append_note(cr, uid, [obj.id], body=_("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context)
self.message_post(cr, uid, [obj.id], body=_("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context)
account_analytic_account()

View File

@ -8,12 +8,18 @@
<field name="arch" type="xml">
<form string="Analytic Account" version="7.0">
<sheet string="Analytic Account">
<div class="oe_right oe_button_box" name="buttons">
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" class="oe_inline"/>
</h1>
<div name="project"/>
<group>
<h1>
<field name="name" class="oe_inline"/>
</h1>
<div name="project"/>
</div>
<group name="main">
<group>
<field name="partner_id" on_change="on_change_partner_id(partner_id, name)" attrs="{'required':[('type','=','contract')]}"/>
<field name="manager_id"/>

View File

@ -4,7 +4,7 @@
<t t-extend="Login">
<t t-jquery=".oe_login .oe_login_logo" t-operation="after">
<ul class="openid_providers">
<ul class="openid_providers oe_semantic_html_override">
<li><a href="#login,password" title="Password" data-url="" id="btn_password">Password</a></li>
<li><a href="#google" title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li>
<li><a href="#googleapps" title="Google Apps" data-url="https://www.google.com/accounts/o8/site-xrds?hd={id}">Google</a></li>

View File

@ -9,16 +9,13 @@
<field name="email_from"><![CDATA[${object.company_id.name} <${object.company_id.email}>]]></field>
<field name="email_to" eval="False"><!--(set by reset_password module)--></field>
<field name="subject">Password reset</field>
<field name="body_text"><![CDATA[
A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}
<field name="body_html"><![CDATA[
<p>A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}</p>
You may change your password following this link:
<p>You may change your password following this <a href="${object._auth_reset_password_link()}">link</a>,
or by copy-pasting the following URL in your browser: ${object._auth_reset_password_link()}</p>
${object._auth_reset_password_link()}
If you don't have asked for password reset, you can safely ignore this email.
]]></field>
<p>Note: If you did not ask for a password reset, you can safely ignore this email.</p>]]></field>
</record>
<!-- TODO get own css -->

View File

@ -302,33 +302,27 @@ the rule to mark CC(mail to any other person defined in actions)."),
return self.format_body(body % data)
def email_send(self, cr, uid, obj, emails, body, emailfrom=None, context=None):
""" send email
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param email: pass the emails
@param emailfrom: Pass name the email From else False
@param context: A standard dictionary for contextual values """
if not emailfrom:
emailfrom = tools.config.get('email_from', False)
if context is None:
context = {}
mail_message = self.pool.get('mail.message')
emailfrom = tools.config.get('email_from')
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email:
emailfrom = obj.user_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
if not emailfrom and hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email:
emailfrom = obj.user_id.email
emailfrom = tools.ustr(emailfrom)
reply_to = emailfrom
if not emailfrom:
raise osv.except_osv(_('Error!'),
_("No email ID found for your company address."))
return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, res_id=obj.id)
_("Missing default email address or missing email on responsible user"))
return self.pool.get('mail.mail').create(cr, uid,
{ 'email_from': emailfrom,
'email_to': emails.join(','),
'reply_to': reply_to,
'state': 'outgoing',
'subject': '[%d] %s' % (obj.id, tools.ustr(obj.name)),
'body_html': '<pre>%s</pre>' % body,
'res_id': obj.id,
'model': obj._table_name,
'auto_delete': True
}, context=context)
def do_check(self, cr, uid, action, obj, context=None):
@ -438,11 +432,8 @@ the rule to mark CC(mail to any other person defined in actions)."),
if len(emails) and action.act_mail_body:
emails = list(set(emails))
email_from = safe_eval(action.act_email_from, {}, locals_for_emails)
def to_email(text):
return re.findall(r'([^ ,<@]+@[^> ,]+)', text or '')
emails = to_email(','.join(filter(None, emails)))
email_froms = to_email(email_from)
emails = tools.email_split(','.join(filter(None, emails)))
email_froms = tools.email_split(email_from)
if email_froms:
self.email_send(cr, uid, obj, emails, action.act_mail_body, emailfrom=email_froms[0])
return True

View File

@ -0,0 +1,536 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-03 16:43+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@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: 2012-09-04 04:52+0000\n"
"X-Generator: Launchpad (build 15890)\n"
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_user:0
msgid ""
"Check this if you want the rule to send an email to the responsible person."
msgstr ""
"Sjekk dette hvis du vil at regelen skal sende en e-post til ansvarlig person."
#. module: base_action_rule
#: field:base.action.rule,act_remind_partner:0
msgid "Remind Partner"
msgstr "Påminn partner"
#. module: base_action_rule
#: field:base.action.rule,trg_partner_categ_id:0
msgid "Partner Category"
msgstr "Partner Kategori"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_watchers:0
msgid "Mail to Watchers (CC)"
msgstr "Post til overvåkere (CC)"
#. module: base_action_rule
#: field:base.action.rule,trg_state_to:0
msgid "Button Pressed"
msgstr "Knapp trykket"
#. module: base_action_rule
#: field:base.action.rule,model_id:0
msgid "Object"
msgstr "Objekt"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_email:0
msgid "Mail to these Emails"
msgstr "Send mail til disse e-postene"
#. module: base_action_rule
#: field:base.action.rule,act_state:0
msgid "Set State to"
msgstr "Still stat til"
#. module: base_action_rule
#: field:base.action.rule,act_email_from:0
msgid "Email From"
msgstr "E-post fra"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Body"
msgstr "E-post kropp"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Days"
msgstr "Dager"
#. module: base_action_rule
#: field:base.action.rule,last_run:0
msgid "Last Run"
msgstr "Siste kjøring"
#. module: base_action_rule
#: code:addons/base_action_rule/base_action_rule.py:328
#, python-format
msgid "Error!"
msgstr "Feil!"
#. module: base_action_rule
#: field:base.action.rule,act_reply_to:0
msgid "Reply-To"
msgstr "Svar til"
#. module: base_action_rule
#: help:base.action.rule,act_email_cc:0
msgid ""
"These people will receive a copy of the future communication between partner "
"and users by email"
msgstr ""
"Disse menneskene vil motta en kopi av den fremtidige kommunikasjon mellom "
"partner og brukere av e-post"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Minutes"
msgstr "Minutter"
#. module: base_action_rule
#: field:base.action.rule,name:0
msgid "Rule Name"
msgstr "Regelnavn"
#. module: base_action_rule
#: help:base.action.rule,act_remind_partner:0
msgid ""
"Check this if you want the rule to send a reminder by email to the partner."
msgstr ""
"Sjekk dette hvis du vil at regelen skal sende en påminnelse via e-post til "
"partneren."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Model Partner"
msgstr "Forholdene på Modell Partner"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Deadline"
msgstr "Frist"
#. module: base_action_rule
#: field:base.action.rule,trg_partner_id:0
msgid "Partner"
msgstr "Partner"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_subject)s = Object subject"
msgstr "%(object_subject)s = Object subject"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Reminders"
msgstr "E-post påminnelser"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Special Keywords to be Used in the Body"
msgstr ""
#. module: base_action_rule
#: field:base.action.rule,trg_state_from:0
msgid "State"
msgstr "Stat"
#. module: base_action_rule
#: model:ir.actions.act_window,help:base_action_rule.base_action_rule_act
msgid ""
"Use automated actions to automatically trigger actions for various screens. "
"Example: a lead created by a specific user may be automatically set to a "
"specific sales team, or an opportunity which still has status pending after "
"14 days might trigger an automatic reminder email."
msgstr ""
"Bruke automatiserte tiltak for å automatisk utløse tiltak for ulike "
"skjermer. Eksempel: en leder er opprettet av en bestemt bruker kan bli satt "
"automatisk til en bestemt salgsteam, eller en mulighet som fortsatt har "
"status påvente etter 14 dager kan utløse en automatisk påminnelse e-post."
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_email:0
msgid "Email-id of the persons whom mail is to be sent"
msgstr "E-post ID av personer som post skal sendes"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Action Rule"
msgstr "Handling regel"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Fields to Change"
msgstr "Felter å endre"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Creation Date"
msgstr "Opprettelsesdato"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Last Action Date"
msgstr "Siste handlingsdato"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Hours"
msgstr "Timer"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_id)s = Object ID"
msgstr "%(object_ID)s = Object ID"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Delay After Trigger Date"
msgstr "Forsinkelse Etter utløser Dato"
#. module: base_action_rule
#: field:base.action.rule,act_remind_attach:0
msgid "Remind with Attachment"
msgstr "Minn med vedlegg"
#. module: base_action_rule
#: constraint:ir.cron:0
msgid "Invalid arguments"
msgstr "Ugyldige argumenter"
#. module: base_action_rule
#: field:base.action.rule,act_user_id:0
msgid "Set Responsible to"
msgstr "Satt Ansvarlig for å"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "None"
msgstr "Ingen"
#. module: base_action_rule
#: help:base.action.rule,act_email_to:0
msgid ""
"Use a python expression to specify the right field on which one than we will "
"use for the 'To' field of the header"
msgstr ""
"Bruk en python uttrykk for å angi høyre feltet på hvilken enn vi vil bruke "
"for Til-feltet på header."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user_phone)s = Responsible phone"
msgstr "% (object_bruker_telefonen) s = Ansvarlig telefon"
#. module: base_action_rule
#: view:base.action.rule:0
msgid ""
"The rule uses the AND operator. The model must match all non-empty fields so "
"that the rule executes the action described in the 'Actions' tab."
msgstr ""
"Regelen bruker AND-operatoren. Modellen må matche alle-ikke tomme felt, slik "
"at regelen utfører handlingen som er beskrevet i \"Handlinger\"-fanen."
#. module: base_action_rule
#: field:base.action.rule,trg_date_range_type:0
msgid "Delay type"
msgstr "forsinkelse typen"
#. module: base_action_rule
#: help:base.action.rule,regex_name:0
msgid ""
"Regular expression for matching name of the resource\n"
"e.g.: 'urgent.*' will search for records having name starting with the "
"string 'urgent'\n"
"Note: This is case sensitive search."
msgstr ""
"Regulært uttrykk for matchende navnet på ressursen\n"
"f.eks: \". haster * 'vil søke etter poster som har navn som starter med "
"strengen\" haster \"\n"
"Merk: Dette er små bokstaver søk."
#. module: base_action_rule
#: field:base.action.rule,act_method:0
msgid "Call Object Method"
msgstr "Kall objektmetode"
#. module: base_action_rule
#: field:base.action.rule,act_email_to:0
msgid "Email To"
msgstr "E-post til."
#. module: base_action_rule
#: help:base.action.rule,act_mail_to_watchers:0
msgid ""
"Check this if you want the rule to mark CC(mail to any other person defined "
"in actions)."
msgstr ""
"Sjekk dette hvis du vil at regelen skal merkes CC (mail til en annen person "
"som er definert i handlinger)."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(partner)s = Partner name"
msgstr "%(partner)s = Navn på partner"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Note"
msgstr "Notat"
#. module: base_action_rule
#: help:base.action.rule,act_email_from:0
msgid ""
"Use a python expression to specify the right field on which one than we will "
"use for the 'From' field of the header"
msgstr ""
"Bruk en python uttrykk for å angi høyre feltet på hvilken enn vi vil bruke "
"for Fra-feltet på overskriften"
#. module: base_action_rule
#: field:base.action.rule,trg_date_range:0
msgid "Delay after trigger date"
msgstr "Forsinkelse etter triggerdato"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions"
msgstr "Betingelser"
#. module: base_action_rule
#: help:base.action.rule,trg_date_range:0
msgid ""
"Delay After Trigger Date,specifies you can put a negative number. If you "
"need a delay before the trigger date, like sending a reminder 15 minutes "
"before a meeting."
msgstr ""
"Forsinkelse Etter utløser Dato, spesifiserer du kan sette et negativt tall. "
"Hvis du trenger en forsinkelse før avtrekkeren dato, som å sende en "
"påminnelse 15 minutter før et møte."
#. module: base_action_rule
#: field:base.action.rule,active:0
msgid "Active"
msgstr "Aktiv"
#. module: base_action_rule
#: code:addons/base_action_rule/base_action_rule.py:329
#, python-format
msgid "No Email ID Found for your Company address!"
msgstr ""
#. module: base_action_rule
#: field:base.action.rule,act_remind_user:0
msgid "Remind Responsible"
msgstr "Minn Ansvarlig"
#. module: base_action_rule
#: help:base.action.rule,sequence:0
msgid "Gives the sequence order when displaying a list of rules."
msgstr "Gir sekvens ordre når du viser en liste over regler."
#. module: base_action_rule
#: selection:base.action.rule,trg_date_range_type:0
msgid "Months"
msgstr "Måneder"
#. module: base_action_rule
#: field:base.action.rule,filter_id:0
msgid "Filter"
msgstr "Filtrer"
#. module: base_action_rule
#: selection:base.action.rule,trg_date_type:0
msgid "Date"
msgstr "Dato"
#. module: base_action_rule
#: help:base.action.rule,server_action_id:0
msgid ""
"Describes the action name.\n"
"eg:on which object which action to be taken on basis of which condition"
msgstr ""
"Beskriver handlingens navn.\n"
"f.eks: på hvilket objekt som tiltak som skal iverksettes på grunnlag av "
"hvilken tilstand"
#. module: base_action_rule
#: model:ir.model,name:base_action_rule.model_ir_cron
msgid "ir.cron"
msgstr "ir.actions.actions"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_description)s = Object description"
msgstr "% (object_beskrivelse) s = Object beskrivelse"
#. module: base_action_rule
#: constraint:base.action.rule:0
msgid "Error: The mail is not well formated"
msgstr "Feil: E-posten er ikke godt nok formatert"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Actions"
msgstr "E-post handlinger"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Email Information"
msgstr "E-post Informasjon"
#. module: base_action_rule
#: model:ir.model,name:base_action_rule.model_base_action_rule
msgid "Action Rules"
msgstr "Handlingsregler"
#. module: base_action_rule
#: help:base.action.rule,act_mail_body:0
msgid "Content of mail"
msgstr "Innholdet av post"
#. module: base_action_rule
#: field:base.action.rule,trg_user_id:0
msgid "Responsible"
msgstr "Ansvarlig"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(partner_email)s = Partner Email"
msgstr "% (partner_e-post) s = Partner E-post"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_date)s = Creation date"
msgstr "%(object_dato)s = opprettelsesdato"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user_email)s = Responsible Email"
msgstr "% (object_brukerens_e-post) s = Ansvarlig e-post"
#. module: base_action_rule
#: field:base.action.rule,act_mail_body:0
msgid "Mail body"
msgstr "Mail kropp"
#. module: base_action_rule
#: help:base.action.rule,act_remind_user:0
msgid ""
"Check this if you want the rule to send a reminder by email to the user."
msgstr ""
"Kryss av her hvis du vil at regelen skal sende en påminnelse til brukeren "
"via e-post."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Server Action to be Triggered"
msgstr "Server Tiltak som skal Utløses"
#. module: base_action_rule
#: field:base.action.rule,act_mail_to_user:0
msgid "Mail to Responsible"
msgstr "send mail til ansvarlig"
#. module: base_action_rule
#: field:base.action.rule,act_email_cc:0
msgid "Add Watchers (Cc)"
msgstr "Legg til overvåkere (Cc)"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Model Fields"
msgstr "Forholdene på Modell Felter"
#. module: base_action_rule
#: model:ir.actions.act_window,name:base_action_rule.base_action_rule_act
#: model:ir.ui.menu,name:base_action_rule.menu_base_action_rule_form
msgid "Automated Actions"
msgstr "Automatiserte handinger"
#. module: base_action_rule
#: field:base.action.rule,server_action_id:0
msgid "Server Action"
msgstr "Tjenerhandling"
#. module: base_action_rule
#: field:base.action.rule,regex_name:0
msgid "Regex on Resource Name"
msgstr "Regex på Ressursnavn"
#. module: base_action_rule
#: help:base.action.rule,act_remind_attach:0
msgid ""
"Check this if you want that all documents attached to the object be attached "
"to the reminder email sent."
msgstr ""
"Kryss av her om du vil at alle dokumenter knyttet til objektet festes til "
"påminnelse e-post sendt."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on Timing"
msgstr "Vilkår for timing"
#. module: base_action_rule
#: field:base.action.rule,sequence:0
msgid "Sequence"
msgstr "Sekvens"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Actions"
msgstr "Handlinger"
#. module: base_action_rule
#: help:base.action.rule,active:0
msgid ""
"If the active field is set to False, it will allow you to hide the rule "
"without removing it."
msgstr ""
"Hvis det aktive feltet er satt til False, vil det tillate deg å skjule "
"regelen uten å fjerne den."
#. module: base_action_rule
#: view:base.action.rule:0
msgid "%(object_user)s = Responsible name"
msgstr "% (object_bruker) s = Ansvarlig navn"
#. module: base_action_rule
#: field:base.action.rule,create_date:0
msgid "Create Date"
msgstr "Opprettet dato"
#. module: base_action_rule
#: view:base.action.rule:0
msgid "Conditions on States"
msgstr "Vilkår for tilstander"
#. module: base_action_rule
#: field:base.action.rule,trg_date_type:0
msgid "Trigger Date"
msgstr "Uttløser dato"
#~ msgid "Special Keywords to Be Used in The Body"
#~ msgstr "Spesielle nøkkelord for bruk i meldingsinnhold"
#, python-format
#~ msgid "No E-Mail ID Found for your Company address!"
#~ msgstr "Ingen E-post ID funnet for din firmaadresse!"

View File

@ -471,18 +471,10 @@ property or property parameter."),
def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context=None):
"""
Send mail for event invitation to event attendees.
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of attendees IDs.
@param email_from: Email address for user sending the mail
@param context: A standard dictionary for contextual values
@return: True
"""
if context is None:
context = {}
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name
mail_message = self.pool.get('mail.message')
for att in self.browse(cr, uid, ids, context=context):
sign = att.sent_by_uid and att.sent_by_uid.signature or ''
sign = '<br>'.join(sign and sign.split('\n') or [])
@ -508,17 +500,18 @@ property or property parameter."),
}
body = html_invitation % body_vals
if mail_to and email_from:
attach = self.get_ics_file(cr, uid, res_obj, context=context)
mail_message.schedule_with_attach(cr, uid,
email_from,
mail_to,
sub,
body,
attachments=attach and {'invitation.ics': attach} or None,
content_subtype='html',
reply_to=email_from,
context=context
)
ics_file = self.get_ics_file(cr, uid, res_obj, context=context)
vals = {'email_from': email_from,
'email_to': mail_to,
'state': 'outgoing',
'subject': sub,
'body_html': body,
'auto_delete': True}
if ics_file:
vals['attachment_ids'] = [(0,0,{'name': 'invitation.ics',
'datas_fname': 'invitation.ics',
'datas': str(ics_file).encode('base64')})]
self.pool.get('mail.mail').create(cr, uid, vals, context=context)
return True
def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
@ -812,7 +805,6 @@ class calendar_alarm(osv.osv):
"""
if context is None:
context = {}
mail_message = self.pool.get('mail.message')
current_datetime = datetime.now()
alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context)
@ -849,36 +841,10 @@ class calendar_alarm(osv.osv):
else:
re_dates = [alarm.trigger_date]
for r_date in re_dates:
ref = alarm.model_id.model + ',' + str(alarm.res_id)
# search for alreay sent requests
#if request_obj.search(cr, uid, [('trigger_date', '=', r_date), ('ref_doc1', '=', ref)], context=context):
#continue
# Deactivated because of the removing of res.request
# TODO: when cleaning calendar module, re-add this in a new mechanism
#if alarm.action == 'display':
#value = {
#'name': alarm.name,
#'act_from': alarm.user_id.id,
#'act_to': alarm.user_id.id,
#'body': alarm.description,
#'trigger_date': r_date,
#'ref_doc1': ref
#}
#request_id = request_obj.create(cr, uid, value)
#request_ids = [request_id]
#for attendee in res_obj.attendee_ids:
#if attendee.user_id:
#value['act_to'] = attendee.user_id.id
#request_id = request_obj.create(cr, uid, value)
#request_ids.append(request_id)
#request_obj.request_send(cr, uid, request_ids)
if re_dates:
if alarm.action == 'email':
sub = '[Openobject Reminder] %s' % (alarm.name)
body = """
sub = '[OpenERP Reminder] %s' % (alarm.name)
body = """<pre>
Event: %s
Event Date: %s
Description: %s
@ -888,20 +854,21 @@ From:
----
%s
</pre>
""" % (alarm.name, alarm.trigger_date, alarm.description, \
alarm.user_id.name, alarm.user_id.signature)
mail_to = [alarm.user_id.email]
for att in alarm.attendee_ids:
mail_to.append(att.user_id.email)
if mail_to:
mail_message.schedule_with_attach(cr, uid,
tools.config.get('email_from', False),
mail_to,
sub,
body,
context=context
)
vals = {
'state': 'outgoing',
'subject': sub,
'body_html': body,
'email_to': mail_to,
'email_from': tools.config.get('email_from', mail_to),
}
self.pool.get('mail.mail').create(cr, uid, vals, context=context)
if next_trigger_date:
update_vals.update({'trigger_date': next_trigger_date})
else:
@ -1616,36 +1583,6 @@ class calendar_todo(osv.osv):
calendar_todo()
class ir_attachment(osv.osv):
_name = 'ir.attachment'
_inherit = 'ir.attachment'
def search_count(self, cr, user, args, context=None):
new_args = []
for domain_item in args:
if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id':
new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2])))
else:
new_args.append(domain_item)
return super(ir_attachment, self).search_count(cr, user, new_args, context)
def create(self, cr, uid, vals, context=None):
if context:
id = context.get('default_res_id', False)
context.update({'default_res_id' : base_calendar_id2real_id(id)})
return super(ir_attachment, self).create(cr, uid, vals, context=context)
def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False):
new_args = []
for domain_item in args:
if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id':
new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2])))
else:
new_args.append(domain_item)
return super(ir_attachment, self).search(cr, uid, new_args, offset=offset,
limit=limit, order=order, context=context, count=False)
ir_attachment()
class ir_values(osv.osv):
_inherit = 'ir.values'

View File

@ -43,7 +43,7 @@ class crm_meeting(base_state, osv.Model):
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
_inherit = ["calendar.event", 'ir.needaction_mixin', "mail.thread"]
_inherit = ["calendar.event", "mail.thread", 'ir.needaction_mixin']
_columns = {
# base_state required fields
'create_date': fields.datetime('Creation Date', readonly=True),
@ -70,13 +70,17 @@ class crm_meeting(base_state, osv.Model):
# OpenChatter
# ----------------------------------------
# shows events of the day for this user
def needaction_domain_get(self, cr, uid, domain=[], context={}):
return [('date','<=',time.strftime('%Y-%M-%D 23:59:59')), ('date_deadline','>=', time.strftime('%Y-%M-%D 00:00:00')), ('user_id','=',uid)]
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Meeting'
def case_open_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>confirmed</b>."), context=context)
return self.message_post(cr, uid, ids, body=_("Meeting <b>confirmed</b>."), context=context)
def case_close_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>done</b>."), context=context)
return self.message_post(cr, uid, ids, body=_("Meeting <b>completed</b>."), context=context)

View File

@ -170,7 +170,8 @@
<page string="Invitation Detail">
<button string="Invite People"
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
icon="terp-partner" type="action"
type="action"
attrs="{'readonly': [('state', '=', 'done')]}"
context="{'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}" colspan="2"/>
<field name="attendee_ids" widget="one2many" mode="tree">
<tree string="Invitation details" editable="top">
@ -182,17 +183,16 @@
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
/>
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" icon="gtk-apply" />
type="object" />
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel" />
type="object" />
<button
name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d"
string="Delegate" type="action"
icon="gtk-sort-descending"
states="needs-action,tentative,declined,accepted"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
</tree>
@ -200,16 +200,16 @@
<header>
<button name="do_tentative" type="object"
states="needs-action,declined,accepted"
string="Uncertain" icon="terp-crm" />
string="Uncertain" />
<button name="do_accept" type="object"
states="needs-action,tentative,declined"
string="Accept" icon="gtk-apply" />
string="Accept" />
<button name="do_decline" type="object"
states="needs-action,tentative,accepted"
string="Decline" icon="gtk-cancel" />
string="Decline" />
<button name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d" type="action"
states="needs-action,tentative,declined,accepted"
string="Delegate" icon="gtk-sort-descending"
string="Delegate"
context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" />
<field name="state" widget="statusbar" statusbar_visible="draft,open,done"/>
</header>
@ -244,13 +244,13 @@
<field name="name">CRM - Meetings Tree</field>
<field name="model">crm.meeting</field>
<field name="arch" type="xml">
<tree string="Meetings" fonts="bold:needaction_pending==True">
<tree string="Meetings" fonts="bold:message_unread==True">
<field name="name" string="Subject" />
<field name="user_id"/>
<field name="date"/>
<field name="state" invisible="True"/>
<field name="duration" />
<field name="needaction_pending" invisible="1"/>
<field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
@ -287,9 +287,9 @@
<field name="arch" type="xml">
<search string="Search Meetings">
<field name="name" string="Meeting" filter_domain="[('name','ilike',self)]"/>
<filter string="Inbox" help="Unread messages" icon="terp-mail-message-new" name="needaction_pending" domain="[('needaction_pending','=',True)]"/>
<filter string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="My Meetings" help="My Meetings" icon="terp-personal" domain="[('user_id','=',uid)]"/>
<filter string="My Meetings" help="My Meetings" domain="[('user_id','=',uid)]"/>
<field name="user_id"/>
<field name="partner_ids"/>
</search>

View File

@ -0,0 +1,3 @@
import controllers
import models
import test_models

View File

@ -0,0 +1,39 @@
{
'name': 'Base import',
'description': """
New extensible file import for OpenERP
======================================
Re-implement openerp's file import system:
* Server side, the previous system forces most of the logic into the
client which duplicates the effort (between clients), makes the
import system much harder to use without a client (direct RPC or
other forms of automation) and makes knowledge about the
import/export system much harder to gather as it is spread over
3+ different projects.
* In a more extensible manner, so users and partners can build their
own front-end to import from other file formats (e.g. OpenDocument
files) which may be simpler to handle in their work flow or from
their data production sources.
* In a module, so that administrators and users of OpenERP who do not
need or want an online import can avoid it being available to users.
""",
'category': 'Uncategorized',
'website': 'http://www.openerp.com',
'author': 'OpenERP SA',
'depends': ['base'],
'installable': True,
'auto_install': False, # set to true and allow uninstall?
'css': [
'static/lib/select2/select2.css',
'static/src/css/import.css',
],
'js': [
'static/lib/select2/select2.js',
'static/src/js/import.js',
],
'qweb': ['static/src/xml/import.xml'],
}

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import simplejson
try:
import openerp.addons.web.common.http as openerpweb
except ImportError:
import web.common.http as openerpweb
class ImportController(openerpweb.Controller):
_cp_path = '/base_import'
@openerpweb.httprequest
def set_file(self, req, file, import_id, jsonp='callback'):
import_id = int(import_id)
written = req.session.model('base_import.import').write(import_id, {
'file': file.read(),
'file_name': file.filename,
'file_type': file.content_type,
}, req.session.eval_context(req.context))
return 'window.top.%s(%s)' % (
jsonp, simplejson.dumps({'result': written}))

View File

@ -0,0 +1,352 @@
import csv
import itertools
import logging
import operator
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
import psycopg2
from openerp.osv import orm, fields
from openerp.tools.translate import _
FIELDS_RECURSION_LIMIT = 2
ERROR_PREVIEW_BYTES = 200
_logger = logging.getLogger(__name__)
class ir_import(orm.TransientModel):
_name = 'base_import.import'
# allow imports to survive for 12h in case user is slow
_transient_max_hours = 12.0
_columns = {
'res_model': fields.char('Model', size=64),
'file': fields.binary(
'File', help="File to check and/or import, raw binary (not base64)"),
'file_name': fields.char('File Name', size=None),
'file_type': fields.char('File Type', size=None),
}
def get_fields(self, cr, uid, model, context=None,
depth=FIELDS_RECURSION_LIMIT):
""" Recursively get fields for the provided model (through
fields_get) and filter them according to importability
The output format is a list of ``Field``, with ``Field``
defined as:
.. class:: Field
.. attribute:: id (str)
A non-unique identifier for the field, used to compute
the span of the ``required`` attribute: if multiple
``required`` fields have the same id, only one of them
is necessary.
.. attribute:: name (str)
The field's logical (OpenERP) name within the scope of
its parent.
.. attribute:: string (str)
The field's human-readable name (``@string``)
.. attribute:: required (bool)
Whether the field is marked as required in the
model. Clients must provide non-empty import values
for all required fields or the import will error out.
.. attribute:: fields (list(Field))
The current field's subfields. The database and
external identifiers for m2o and m2m fields; a
filtered and transformed fields_get for o2m fields (to
a variable depth defined by ``depth``).
Fields with no sub-fields will have an empty list of
sub-fields.
:param str model: name of the model to get fields form
:param int landing: depth of recursion into o2m fields
"""
fields = [{
'id': 'id',
'name': 'id',
'string': _("External ID"),
'required': False,
'fields': [],
}]
fields_got = self.pool[model].fields_get(cr, uid, context=context)
for name, field in fields_got.iteritems():
if field.get('readonly'):
states = field.get('states')
if not states:
continue
# states = {state: [(attr, value), (attr2, value2)], state2:...}
if not any(attr == 'readonly' and value is False
for attr, value in itertools.chain.from_iterable(
states.itervalues())):
continue
f = {
'id': name,
'name': name,
'string': field['string'],
# Y U NO ALWAYS HAVE REQUIRED
'required': bool(field.get('required')),
'fields': [],
}
if field['type'] in ('many2many', 'many2one'):
f['fields'] = [
dict(f, name='id', string=_("External ID")),
dict(f, name='.id', string=_("Database ID")),
]
elif field['type'] == 'one2many' and depth:
f['fields'] = self.get_fields(
cr, uid, field['relation'], context=context, depth=depth-1)
fields.append(f)
# TODO: cache on model?
return fields
def _read_csv(self, record, options):
""" Returns a CSV-parsed iterator of all empty lines in the file
:throws csv.Error: if an error is detected during CSV parsing
:throws UnicodeDecodeError: if ``options.encoding`` is incorrect
"""
csv_iterator = csv.reader(
StringIO(record.file),
quotechar=options['quoting'],
delimiter=options['separator'])
csv_nonempty = itertools.ifilter(None, csv_iterator)
# TODO: guess encoding with chardet? Or https://github.com/aadsm/jschardet
encoding = options.get('encoding', 'utf-8')
return itertools.imap(
lambda row: [item.decode(encoding) for item in row],
csv_nonempty)
def _match_header(self, header, fields, options):
""" Attempts to match a given header to a field of the
imported model.
:param str header: header name from the CSV file
:param fields:
:param dict options:
:returns: an empty list if the header couldn't be matched, or
all the fields to traverse
:rtype: list(Field)
"""
for field in fields:
# FIXME: should match all translations & original
# TODO: use string distance (levenshtein? hamming?)
if header == field['name'] \
or header.lower() == field['string'].lower():
return [field]
if '/' not in header:
return []
# relational field path
traversal = []
subfields = fields
# Iteratively dive into fields tree
for section in header.split('/'):
# Strip section in case spaces are added around '/' for
# readability of paths
match = self._match_header(section.strip(), subfields, options)
# Any match failure, exit
if not match: return []
# prep subfields for next iteration within match[0]
field = match[0]
subfields = field['fields']
traversal.append(field)
return traversal
def _match_headers(self, rows, fields, options):
""" Attempts to match the imported model's fields to the
titles of the parsed CSV file, if the file is supposed to have
headers.
Will consume the first line of the ``rows`` iterator.
Returns a pair of (None, None) if headers were not requested
or the list of headers and a dict mapping cell indices
to key paths in the ``fields`` tree
:param Iterator rows:
:param dict fields:
:param dict options:
:rtype: (None, None) | (list(str), dict(int: list(str)))
"""
if not options.get('headers'):
return None, None
headers = next(rows)
return headers, dict(
(index, [field['name'] for field in self._match_header(header, fields, options)] or None)
for index, header in enumerate(headers)
)
def parse_preview(self, cr, uid, id, options, count=10, context=None):
""" Generates a preview of the uploaded files, and performs
fields-matching between the import's file data and the model's
columns.
If the headers are not requested (not options.headers),
``matches`` and ``headers`` are both ``False``.
:param id: identifier of the import
:param int count: number of preview lines to generate
:param options: format-specific options.
CSV: {encoding, quoting, separator, headers}
:type options: {str, str, str, bool}
:returns: {fields, matches, headers, preview} | {error, preview}
:rtype: {dict(str: dict(...)), dict(int, list(str)), list(str), list(list(str))} | {str, str}
"""
(record,) = self.browse(cr, uid, [id], context=context)
fields = self.get_fields(cr, uid, record.res_model, context=context)
try:
rows = self._read_csv(record, options)
headers, matches = self._match_headers(rows, fields, options)
# Match should have consumed the first row (iif headers), get
# the ``count`` next rows for preview
preview = itertools.islice(rows, count)
return {
'fields': fields,
'matches': matches or False,
'headers': headers or False,
'preview': list(preview),
}
except Exception, e:
# Due to lazy generators, UnicodeDecodeError (for
# instance) may only be raised when serializing the
# preview to a list in the return.
_logger.debug("Error during CSV parsing preview", exc_info=True)
return {
'error': str(e),
# iso-8859-1 ensures decoding will always succeed,
# even if it yields non-printable characters. This is
# in case of UnicodeDecodeError (or csv.Error
# compounded with UnicodeDecodeError)
'preview': record.file[:ERROR_PREVIEW_BYTES]
.decode( 'iso-8859-1'),
}
def _convert_import_data(self, record, fields, options, context=None):
""" Extracts the input browse_record and fields list (with
``False``-y placeholders for fields to *not* import) into a
format Model.import_data can use: a fields list without holes
and the precisely matching data matrix
:param browse_record record:
:param list(str|bool): fields
:returns: (data, fields)
:rtype: (list(list(str)), list(str))
:raises ValueError: in case the import data could not be converted
"""
# Get indices for non-empty fields
indices = [index for index, field in enumerate(fields) if field]
if not indices:
raise ValueError(_("You must configure at least one field to import"))
# If only one index, itemgetter will return an atom rather
# than a 1-tuple
if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
else: mapper = operator.itemgetter(*indices)
# Get only list of actually imported fields
import_fields = filter(None, fields)
rows_to_import = self._read_csv(record, options)
if options.get('headers'):
rows_to_import = itertools.islice(
rows_to_import, 1, None)
data = [
row for row in itertools.imap(mapper, rows_to_import)
# don't try inserting completely empty rows (e.g. from
# filtering out o2m fields)
if any(row)
]
return data, import_fields
def do(self, cr, uid, id, fields, options, dryrun=False, context=None):
""" Actual execution of the import
:param fields: import mapping: maps each column to a field,
``False`` for the columns to ignore
:type fields: list(str|bool)
:param dict options:
:param bool dryrun: performs all import operations (and
validations) but rollbacks writes, allows
getting as much errors as possible without
the risk of clobbering the database.
:returns: A list of errors. If the list is empty the import
executed fully and correctly. If the list is
non-empty it contains dicts with 3 keys ``type`` the
type of error (``error|warning``); ``message`` the
error message associated with the error (a string)
and ``record`` the data which failed to import (or
``false`` if that data isn't available or provided)
:rtype: list({type, message, record})
"""
cr.execute('SAVEPOINT import')
(record,) = self.browse(cr, uid, [id], context=context)
try:
data, import_fields = self._convert_import_data(
record, fields, options, context=context)
except ValueError, e:
return [{
'type': 'error',
'message': str(e),
'record': False,
}]
try:
_logger.info('importing %d rows...', len(data))
(code, record, message, _wat) = self.pool[record.res_model].import_data(
cr, uid, import_fields, data, context=context)
_logger.info('done')
except Exception, e:
_logger.exception("Import failed")
# TODO: remove when exceptions stop being an "expected"
# behavior of import_data on some (most) invalid
# input.
code, record, message = -1, None, str(e)
# If transaction aborted, RELEASE SAVEPOINT is going to raise
# an InternalError (ROLLBACK should work, maybe). Ignore that.
# TODO: to handle multiple errors, create savepoint around
# write and release it in case of write error (after
# adding error to errors array) => can keep on trying to
# import stuff, and rollback at the end if there is any
# error in the results.
try:
if dryrun:
cr.execute('ROLLBACK TO SAVEPOINT import')
else:
cr.execute('RELEASE SAVEPOINT import')
except psycopg2.InternalError:
pass
if code != -1:
return []
# TODO: add key for error location?
# TODO: error not within normal preview, how to display? Re-preview
# with higher ``count``?
return [{
'type': 'error',
'message': message,
'record': record or False
}]

View File

@ -0,0 +1,12 @@
Copyright 2012 Igor Vaynberg
Version: @@ver@@ Timestamp: @@timestamp@@
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

View File

@ -0,0 +1,68 @@
Select2
=================
Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. Look and feel of Select2 is based on the excellent [Chosen](http://harvesthq.github.com/chosen/) library.
To get started -- checkout http://ivaynberg.github.com/select2!
What Does Select2 Support That Chosen Does Not?
-------------------------------------------------
* Working with large datasets: Chosen requires the entire dataset to be loaded as `option` tags in the DOM, which limits
it to working with small-ish datasets. Select2 uses a function to find results on-the-fly, which allows it to partially
load results.
* Paging of results: Since Select2 works with large datasets and only loads a small amount of matching results at a time
it has to support paging. Select2 will call the search function when the user scrolls to the bottom of currently loaded
result set allowing for the 'infinite scrolling' of results.
* Custom markup for results: Chosen only supports rendering text results because that is the only markup supported by
`option` tags. Select2 provides an extension point which can be used to produce any kind of markup to represent results.
* Ability to add results on the fly: Select2 provides the ability to add results from the search term entered by the user, which allows it to be used for
tagging.
Browser Compatibility
--------------------
* IE 8+ (7 mostly works except for [issue with z-index](https://github.com/ivaynberg/select2/issues/37))
* Chrome 8+
* Firefox 3.5+
* Safari 3+
* Opera 10.6+
Integrations
------------
* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / Apache Wicket)
* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
* [Django](https://github.com/applegrew/django-select2)
Bug tracker
-----------
Have a bug? Please create an issue here on GitHub!
https://github.com/ivaynberg/select2/issues
Mailing list
------------
Have a question? Ask on our mailing list!
select2@googlegroups.com
https://groups.google.com/d/forum/select2
Copyright and License
---------------------
Copyright 2012 Igor Vaynberg
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

View File

@ -0,0 +1,77 @@
#!/bin/bash
set -e
echo -n "Enter the version for this release: "
read ver
if [ ! $ver ]; then
echo "Invalid version."
exit
fi
name="select2"
js="$name.js"
mini="$name.min.js"
css="$name.css"
release="$name-$ver"
releasedir="/tmp/$release"
tag="release-$ver"
branch="build-$ver"
curbranch=`git branch | grep "*" | sed "s/* //"`
timestamp=$(date)
tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g"
remote="github"
git branch "$branch"
git checkout "$branch"
echo "Tokenizing..."
find . -name "$js" | xargs sed -i -e "$tokens"
find . -name "$css" | xargs sed -i -e "$tokens"
git add "$js"
git add "$css"
echo "Minifying..."
echo "/*" > "$mini"
cat LICENSE | sed "$tokens" >> "$mini"
echo "*/" >> "$mini"
curl -s \
-d compilation_level=SIMPLE_OPTIMIZATIONS \
-d output_format=text \
-d output_info=compiled_code \
--data-urlencode "js_code@$js" \
http://closure-compiler.appspot.com/compile \
>> "$mini"
git add "$mini"
git commit -m "release $ver"
echo "Tagging..."
git tag -a "$tag" -m "tagged version $ver"
git push "$remote" --tags
echo "Archiving..."
rm -rf "$releasedir"
mkdir "$releasedir"
cp $name.* "$releasedir"
cp spinner.gif "$releasedir"
cp README.* "$releasedir"
zip -r "$releasedir.zip" "$releasedir"
rm -rf "$releasedir"
echo "Cleaning Up..."
git checkout "$curbranch"
git branch -D "$branch"
echo "Done. Release archive created: $releasedir.zip"

View File

@ -0,0 +1,524 @@
/*
Version: @@ver@@ Timestamp: @@timestamp@@
*/
.select2-container {
position: relative;
display: inline-block;
/* inline-block for ie7 */
zoom: 1;
*display: inline;
vertical-align: top;
}
.select2-container,
.select2-drop,
.select2-search,
.select2-search input{
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
box-sizing: border-box; /* css3 */
}
.select2-container .select2-choice {
background-color: #fff;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #aaa;
display: block;
overflow: hidden;
white-space: nowrap;
position: relative;
height: 26px;
line-height: 26px;
padding: 0 0 0 8px;
color: #444;
text-decoration: none;
}
.select2-container.select2-drop-above .select2-choice
{
border-bottom-color: #aaa;
-webkit-border-radius:0px 0px 4px 4px;
-moz-border-radius:0px 0px 4px 4px;
border-radius:0px 0px 4px 4px;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
}
.select2-container .select2-choice span {
margin-right: 26px;
display: block;
overflow: hidden;
white-space: nowrap;
-o-text-overflow: ellipsis;
-ms-text-overflow: ellipsis;
text-overflow: ellipsis;
}
.select2-container .select2-choice abbr {
display: block;
position: absolute;
right: 26px;
top: 8px;
width: 12px;
height: 12px;
font-size: 1px;
background: url('select2.png') right top no-repeat;
cursor: pointer;
text-decoration: none;
border:0;
outline: 0;
}
.select2-container .select2-choice abbr:hover {
background-position: right -11px;
cursor: pointer;
}
.select2-drop {
background: #fff;
color: #000;
border: 1px solid #aaa;
border-top: 0;
position: absolute;
top: 100%;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
z-index: 9999;
width:100%;
margin-top:-1px;
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
}
.select2-drop.select2-drop-above {
-webkit-border-radius: 4px 4px 0px 0px;
-moz-border-radius: 4px 4px 0px 0px;
border-radius: 4px 4px 0px 0px;
margin-top:1px;
border-top: 1px solid #aaa;
border-bottom: 0;
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
}
.select2-container .select2-choice div {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
background: #ccc;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
border-left: 1px solid #aaa;
position: absolute;
right: 0;
top: 0;
display: block;
height: 100%;
width: 18px;
}
.select2-container .select2-choice div b {
background: url('select2.png') no-repeat 0 1px;
display: block;
width: 100%;
height: 100%;
}
.select2-search {
display: inline-block;
white-space: nowrap;
z-index: 10000;
min-height: 26px;
width: 100%;
margin: 0;
padding-left: 4px;
padding-right: 4px;
}
.select2-search-hidden {
display: block;
position: absolute;
left: -10000px;
}
.select2-search input {
background: #fff url('select2.png') no-repeat 100% -22px;
background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
padding: 4px 20px 4px 5px;
outline: 0;
border: 1px solid #aaa;
font-family: sans-serif;
font-size: 1em;
width:100%;
margin:0;
height:auto !important;
min-height: 26px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
border-radius: 0;
-moz-border-radius: 0;
-webkit-border-radius: 0;
}
.select2-drop.select2-drop-above .select2-search input
{
margin-top:4px;
}
.select2-search input.select2-active {
background: #fff url('spinner.gif') no-repeat 100%;
background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
}
.select2-container-active .select2-choice,
.select2-container-active .select2-choices {
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
box-shadow : 0 0 5px rgba(0,0,0,.3);
border: 1px solid #5897fb;
outline: none;
}
.select2-dropdown-open .select2-choice {
border: 1px solid #aaa;
border-bottom-color: transparent;
-webkit-box-shadow: 0 1px 0 #fff inset;
-moz-box-shadow : 0 1px 0 #fff inset;
-o-box-shadow : 0 1px 0 #fff inset;
box-shadow : 0 1px 0 #fff inset;
background-color: #eee;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
-webkit-border-bottom-left-radius : 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomleft : 0;
-moz-border-radius-bottomright: 0;
border-bottom-left-radius : 0;
border-bottom-right-radius: 0;
}
.select2-dropdown-open .select2-choice div {
background: transparent;
border-left: none;
}
.select2-dropdown-open .select2-choice div b {
background-position: -18px 1px;
}
/* results */
.select2-results {
margin: 4px 4px 4px 0;
padding: 0 0 0 4px;
position: relative;
overflow-x: hidden;
overflow-y: auto;
max-height: 200px;
}
.select2-results ul.select2-result-sub {
margin: 0 0 0 0;
}
.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
.select2-results li {
list-style: none;
display: list-item;
}
.select2-results li.select2-result-with-children > .select2-result-label {
font-weight: bold;
}
.select2-results .select2-result-label {
padding: 3px 7px 4px;
margin: 0;
cursor: pointer;
}
.select2-results .select2-highlighted {
background: #3875d7;
color: #fff;
}
.select2-results li em {
background: #feffde;
font-style: normal;
}
.select2-results .select2-highlighted em {
background: transparent;
}
.select2-results .select2-no-results,
.select2-results .select2-searching,
.select2-results .select2-selection-limit {
background: #f4f4f4;
display: list-item;
}
/*
disabled look for already selected choices in the results dropdown
.select2-results .select2-disabled.select2-highlighted {
color: #666;
background: #f4f4f4;
display: list-item;
cursor: default;
}
.select2-results .select2-disabled {
background: #f4f4f4;
display: list-item;
cursor: default;
}
*/
.select2-results .select2-disabled {
display: none;
}
.select2-more-results.select2-active {
background: #f4f4f4 url('spinner.gif') no-repeat 100%;
}
.select2-more-results {
background: #f4f4f4;
display: list-item;
}
/* disabled styles */
.select2-container.select2-container-disabled .select2-choice {
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container.select2-container-disabled .select2-choice div {
background-color: #f4f4f4;
background-image: none;
border-left: 0;
}
/* multiselect */
.select2-container-multi .select2-choices {
background-color: #fff;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
border: 1px solid #aaa;
margin: 0;
padding: 0;
cursor: text;
overflow: hidden;
height: auto !important;
height: 1%;
position: relative;
}
.select2-container-multi .select2-choices {
min-height: 26px;
}
.select2-container-multi.select2-container-active .select2-choices {
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
box-shadow : 0 0 5px rgba(0,0,0,.3);
border: 1px solid #5897fb;
outline: none;
}
.select2-container-multi .select2-choices li {
float: left;
list-style: none;
}
.select2-container-multi .select2-choices .select2-search-field {
white-space: nowrap;
margin: 0;
padding: 0;
}
.select2-container-multi .select2-choices .select2-search-field input {
color: #666;
background: transparent !important;
font-family: sans-serif;
font-size: 100%;
height: 15px;
padding: 5px;
margin: 1px 0;
outline: 0;
border: 0;
-webkit-box-shadow: none;
-moz-box-shadow : none;
-o-box-shadow : none;
box-shadow : none;
}
.select2-container-multi .select2-choices .select2-search-field input.select2-active {
background: #fff url('spinner.gif') no-repeat 100% !important;
}
.select2-default {
color: #999 !important;
}
.select2-container-multi .select2-choices .select2-search-choice {
-webkit-border-radius: 3px;
-moz-border-radius : 3px;
border-radius : 3px;
-moz-background-clip : padding;
-webkit-background-clip: padding-box;
background-clip : padding-box;
background-color: #e4e4e4;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
color: #333;
border: 1px solid #aaaaaa;
line-height: 13px;
padding: 3px 5px 3px 18px;
margin: 3px 0 3px 5px;
position: relative;
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice span {
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice-focus {
background: #d4d4d4;
}
.select2-search-choice-close {
display: block;
position: absolute;
right: 3px;
top: 4px;
width: 12px;
height: 13px;
font-size: 1px;
background: url('select2.png') right top no-repeat;
outline: none;
}
.select2-container-multi .select2-search-choice-close {
left: 3px;
}
.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
background-position: right -11px;
}
.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
background-position: right -11px;
}
/* disabled styles */
.select2-container-multi.select2-container-disabled .select2-choices{
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
background-image: none;
background-color: #f4f4f4;
border: 1px solid #ddd;
padding: 3px 5px 3px 5px;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {
display: none;
}
/* end multiselect */
.select2-result-selectable .select2-match,
.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; }
.select2-result-unselectable .select2-match { text-decoration: none; }
.select2-offscreen { position: absolute; left: -10000px; }
/* Retina-ize icons */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
background-image: url(select2x2.png) !important;
background-repeat: no-repeat !important;
background-size: 60px 40px !important;
}
.select2-search input {
background-position: 100% -21px !important;
}
}

2407
addons/base_import/static/lib/select2/select2.js vendored Executable file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,57 @@
.openerp .oe_list_buttons .oe_alternative {
visibility: visible;
}
.openerp .oe_list_buttons.oe_editing .oe_list_button_import {
display: none;
}
.oe_import dd,
.oe_import .oe_import_toggled,
.oe_import .oe_import_grid,
.oe_import .oe_import_error_report,
.oe_import .oe_import_with_file,
.oe_import .oe_import_noheaders {
display: none;
}
.oe_import.oe_import_preview .oe_import_grid {
display: table;
}
.oe_import.oe_import_error .oe_import_error_report,
.oe_import.oe_import_with_file .oe_import_with_file,
.oe_import.oe_import_noheaders .oe_import_noheaders {
display: block;
}
.oe_import .oe_import_error_report ul .oe_import_report_error {
background-color: #FFD9DB;
}
.oe_import .oe_import_error_report ul .oe_import_report_warning {
background-color: #FEFFD9;
}
.oe_import .oe_import_noheaders {
color: #888;
}
.oe_import a.oe_import_toggle {
display: block;
}
.oe_import a.oe_import_toggle:before {
content: '> '
}
.oe_import .oe_import_options p {
margin: 0;
padding: 0;
}
.oe_import .oe_import_options label {
display: inline-block;
width: 10em;
text-align: right;
}
.oe_import_selector ul,
.oe_import_selector li {
margin: 0; padding: 0;
}

View File

@ -0,0 +1,291 @@
openerp.base_import = function (instance) {
var QWeb = instance.web.qweb;
var _t = instance.web._t;
var _lt = instance.web._lt;
/**
* Safari does not deal well at all with raw JSON data being
* returned. As a result, we're going to cheat by using a
* pseudo-jsonp: instead of getting JSON data in the iframe, we're
* getting a ``script`` tag which consists of a function call and
* the returned data (the json dump).
*
* The function is an auto-generated name bound to ``window``,
* which calls back into the callback provided here.
*
* @param {Object} form the form element (DOM or jQuery) to use in the call
* @param {Object} attributes jquery.form attributes object
* @param {Function} callback function to call with the returned data
*/
function jsonp(form, attributes, callback) {
attributes = attributes || {};
var options = {jsonp: _.uniqueId('import_callback_')};
window[options.jsonp] = function () {
delete window[options.jsonp];
callback.apply(null, arguments);
};
if ('data' in attributes) {
_.extend(attributes.data, options);
} else {
_.extend(attributes, {data: options});
}
_.extend(attributes, {
dataType: 'script',
});
$(form).ajaxSubmit(attributes);
}
// if true, the 'Import', 'Export', etc... buttons will be shown
instance.web.ListView.prototype.defaults.import_enabled = true;
instance.web.ListView.include({
on_loaded: function () {
var self = this;
var add_button = false;
if (!this.$buttons) {
add_button = true;
}
this._super.apply(this, arguments);
if(add_button) {
this.$buttons.on('click', '.oe_list_button_import', function() {
new instance.web.DataImport(self, self.dataset).open();
return false;
});
}
}
});
instance.web.DataImport = instance.web.Dialog.extend({
template: 'ImportView',
dialog_title: _lt("Import Data"),
opts: [
{name: 'encoding', label: _lt("Encoding:"), value: 'utf-8'},
{name: 'separator', label: _lt("Separator:"), value: ','},
{name: 'quoting', label: _lt("Quoting:"), value: '"'}
],
events: {
'change .oe_import_grid input': 'import_dryrun',
'change input.oe_import_file': 'file_update',
'change input.oe_import_has_header, .oe_import_options input': 'settings_updated',
'click a.oe_import_csv': function (e) {
e.preventDefault();
},
'click a.oe_import_export': function (e) {
e.preventDefault();
},
'click a.oe_import_toggle': function (e) {
e.preventDefault();
var $el = $(e.target);
($el.next().length
? $el.next()
: $el.parent().next())
.toggle();
}
},
init: function (parent, dataset) {
var self = this;
this._super(parent, {
buttons: [
{text: _t("Import File"), click: function () {
self.do_import();
}, 'class': 'oe_import_dialog_button'}
]
});
this.res_model = parent.model;
// import object id
this.id = null;
this.Import = new instance.web.Model('base_import.import');
},
start: function () {
var self = this;
return this.Import.call('create', [{
'res_model': this.res_model
}]).then(function (id) {
self.id = id;
self.$('input[name=import_id]').val(id);
});
},
import_options: function () {
var self = this;
var options = {
headers: this.$('input.oe_import_has_header').prop('checked')
};
_(this.opts).each(function (opt) {
options[opt.name] =
self.$('input.oe_import_' + opt.name).val();
});
return options;
},
//- File & settings change section
file_update: function (e) {
if (!this.$('input.oe_import_file').val()) { return; }
this.$el.removeClass('oe_import_preview oe_import_error');
jsonp(this.$el, {
url: '/base_import/set_file'
}, this.proxy('settings_updated'));
},
settings_updated: function () {
this.$el.addClass('oe_import_with_file');
// TODO: test that write // succeeded?
this.Import.call(
'parse_preview', [this.id, this.import_options()])
.then(this.proxy('preview'));
},
preview: function (result) {
this.$el.toggleClass(
'oe_import_noheaders',
!this.$('input.oe_import_has_header').prop('checked'));
if (result.error) {
this.$el.addClass('oe_import_error');
this.$('.oe_import_error_report').html(
QWeb.render('ImportView.preview.error', result));
return;
}
this.$el.addClass('oe_import_preview');
this.$('table').html(QWeb.render('ImportView.preview', result));
var $fields = this.$('.oe_import_fields input');
this.render_fields_matches(result, $fields);
var data = this.generate_fields_completion(result);
var item_finder = function (id, items) {
items = items || data;
for (var i=0; i < items.length; ++i) {
var item = items[i];
if (item.id === id) {
return item;
}
var val;
if (item.children && (val = item_finder(id, item.children))) {
return val;
}
}
return '';
};
$fields.select2({
allowClear: true,
minimumInputLength: 0,
data: data,
initSelection: function (element, callback) {
var default_value = element.val();
if (!default_value) {
callback('');
return;
}
callback(item_finder(default_value));
},
width: 'resolve',
dropdownCssClass: 'oe_import_selector'
});
this.import_dryrun();
},
generate_fields_completion: function (root) {
var basic = [];
var regulars = [];
var o2m = [];
function traverse(field, ancestors, collection) {
var subfields = field.fields;
var field_path = ancestors.concat(field);
var label = _(field_path).pluck('string').join(' / ');
var id = _(field_path).pluck('name').join('/');
// If non-relational, m2o or m2m, collection is regulars
if (!collection) {
if (field.name === 'id') {
collection = basic
} else if (_.isEmpty(subfields)
|| _.isEqual(_.pluck(subfields, 'name'), ['id', '.id'])) {
collection = regulars;
} else {
collection = o2m;
}
}
collection.push({
id: id,
text: label,
required: field.required
});
for(var i=0, end=subfields.length; i<end; ++i) {
traverse(subfields[i], field_path, collection);
}
}
_(root.fields).each(function (field) {
traverse(field, []);
});
var cmp = function (field1, field2) {
return field1.text.localeCompare(field2.text);
};
regulars.sort(cmp);
o2m.sort(cmp);
return basic.concat([
{ text: _t("Normal Fields"), children: regulars },
{ text: _t("Relation Fields"), children: o2m }
]);
},
render_fields_matches: function (result, $fields) {
if (_(result.matches).isEmpty()) { return; }
$fields.each(function (index, input) {
var match = result.matches[index];
if (!match) { return; }
var current_field = result;
input.value = _(match).chain()
.map(function (name) {
// WARNING: does both mapping and folding (over the
// ``field`` iterator variable)
return current_field = _(current_field.fields).find(function (subfield) {
return subfield.name === name;
});
})
.pluck('name')
.value()
.join('/');
});
},
//- import itself
call_import: function (options) {
var self = this;
var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) {
return $(el).select2('val') || false;
}).get();
return this.Import.call(
'do', [this.id, fields, this.import_options()], options);
},
import_dryrun: function () {
// this.call_import({ dryrun: true })
// .then(this.proxy('render_import_errors'));
},
do_import: function () {
var self = this;
this.call_import({ dryrun: false }).then(function (errors) {
if (_.isEmpty(errors)) {
if (self.getParent().reload_content) {
self.getParent().reload_content();
}
self.close();
return;
}
self.render_import_errors(errors);
});
},
render_import_errors: function (errors) {
if (_.isEmpty(errors)) {
this.$el.removeClass('oe_import_error');
return;
}
// import failed (or maybe just warnings, if we ever get
// warnings?)
this.$el.addClass('oe_import_error');
this.$('.oe_import_error_report').html(
QWeb.render('ImportView.error', {errors: errors}));
},
});
};

View File

@ -0,0 +1,103 @@
<templates>
<t t-name="ImportView">
<t t-set="_id" t-value="_.uniqueId('export')"/>
<form action="" method="post" enctype="multipart/form-data" class="oe_import">
<input type="hidden" name="session_id"
t-att-value="widget.session.session_id"/>
<input type="hidden" name="import_id"/>
<h2>Upload your file</h2>
<p>Select the <a href="#" class="oe_import_csv">.CSV</a>
file to import. If you need a sample importable file, you
can use <a href="#" class="oe_import_export">the export
tool</a> to generate one.</p>
<label t-attf-for="file_#{_id}" autofocus="autofocus">CSV File:</label>
<input type="file" id-attf-id="file_#{_id}"
name="file" class="oe_import_file"/>
<div class="oe_import_with_file">
<h2>Map your data to OpenERP</h2>
<input type="checkbox" class="oe_import_has_header"
id="oe_import_has_header" checked="checked"/>
<label for="oe_import_has_header">The first row of the
file contains the label of the column</label>
<p class="oe_import_noheaders">If the file contains
the column names, OpenERP can try auto-detecting the
field corresponding to the column. This makes imports
simpler especially when the file has many columns.</p>
<div class="oe_import_error_report"></div>
<table class="oe_import_grid" width="100%"/>
<a href="#" class="oe_import_toggle">
File Format Options…</a>
<div class="oe_import_toggled oe_import_options">
<p t-foreach="widget.opts" t-as="option">
<!-- no @name, avoid submission when file_update called -->
<label t-attf-for="#{option.name}_#{_id}">
<t t-esc="option.label"/></label>
<input t-attf-id="#{option.name}_#{_id}"
t-attf-class="oe_import_#{option.name}"
t-att-value="option.value"/>
</p>
</div>
<h2>Frequently Asked Questions</h2>
<dl>
<dt><a href="#" class="oe_import_toggle">
Need to import data from an other application?</a></dt>
<dd>
<p>In order to re-create relationships between
different records, you should use the unique
identifier from the original application and
map it to the <abbr title="External ID">ID</abbr>
column in OpenERP. When you
import an other record that links to the first
one, use <abbr title="XXX/External ID">XXX/ID</abbr>
to the original unique identifier.</p>
<p>The <abbr title="External ID">ID</abbr>
will also be used to update the original
import if you need to re-import modified data
later, it's thus good practice to specify it
whenever possible</p>
</dd>
</dl>
</div>
</form>
</t>
<t t-name="ImportView.preview">
<tr t-if="headers" class="oe_import_grid-header">
<td t-foreach="headers" t-as="header" class="oe_import_grid-cell"
><t t-esc="header"/></td>
</tr>
<tr class="oe_import_fields">
<!-- Iterate on first row to ensure we have all columns -->
<td t-foreach="preview[0]" t-as="column">
<input placeholder="Don't Import"
class="oe_import_match_field"/>
</td>
</tr>
<tr t-foreach="preview" t-as="row" class="oe_import_grid-row">
<td t-foreach="row" t-as="cell" class="oe_import_grid-cell"
><t t-esc="cell"/></td>
</tr>
</t>
<t t-name="ImportView.preview.error">
<p>Import preview failed due to: <t t-esc="error"/></p>
<p>Here is the start of the file we could not import:</p>
<pre><t t-esc="preview"/></pre>
</t>
<ul t-name="ImportView.error">
<li t-foreach="errors" t-as="error" t-attf-class="oe_import_report_#{error.type}">
<!-- can also have error.record, but may be *huge* if
e.g. has image fields -->
<t t-esc="error.message"/>
</li>
</ul>
<t t-extend="ListView.buttons">
<t t-jquery="span.oe_alternative">
this.attr('t-if', 'widget.options.import_enabled');
</t>
<t t-jquery="span.oe_alternative" t-operation="append">
<a href="#" class="oe_bold oe_list_button_import">Import</a>
</t>
</t>
</templates>

View File

@ -0,0 +1,101 @@
from openerp.osv import orm, fields
def name(n): return 'base_import.tests.models.%s' % n
class char(orm.Model):
_name = name('char')
_columns = {
'value': fields.char('unknown', size=None)
}
class char_required(orm.Model):
_name = name('char.required')
_columns = {
'value': fields.char('unknown', size=None, required=True)
}
class char_readonly(orm.Model):
_name = name('char.readonly')
_columns = {
'value': fields.char('unknown', size=None, readonly=True)
}
class char_states(orm.Model):
_name = name('char.states')
_columns = {
'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', False)]})
}
class char_noreadonly(orm.Model):
_name = name('char.noreadonly')
_columns = {
'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('invisible', True)]})
}
class char_stillreadonly(orm.Model):
_name = name('char.stillreadonly')
_columns = {
'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', True)]})
}
# TODO: complex field (m2m, o2m, m2o)
class m2o(orm.Model):
_name = name('m2o')
_columns = {
'value': fields.many2one(name('m2o.related'))
}
class m2o_related(orm.Model):
_name = name('m2o.related')
_columns = {
'value': fields.integer()
}
_defaults = {
'value': 42
}
class m2o_required(orm.Model):
_name = name('m2o.required')
_columns = {
'value': fields.many2one(name('m2o.required.related'), required=True)
}
class m2o_required_related(orm.Model):
_name = name('m2o.required.related')
_columns = {
'value': fields.integer()
}
_defaults = {
'value': 42
}
class o2m(orm.Model):
_name = name('o2m')
_columns = {
'value': fields.one2many(name('o2m.child'), 'parent_id')
}
class o2m_child(orm.Model):
_name = name('o2m.child')
_columns = {
'parent_id': fields.many2one(name('o2m')),
'value': fields.integer()
}
class preview_model(orm.Model):
_name = name('preview')
_columns = {
'name': fields.char('Name', size=None),
'somevalue': fields.integer('Some Value', required=True),
'othervalue': fields.integer('Other Variable'),
}

View File

@ -0,0 +1,3 @@
from . import test_cases
checks = [test_cases]

View File

@ -0,0 +1,342 @@
# -*- encoding: utf-8 -*-
import unittest2
from openerp.tests.common import TransactionCase
from .. import models
ID_FIELD = {'id': 'id', 'name': 'id', 'string': "External ID", 'required': False, 'fields': []}
def make_field(name='value', string='unknown', required=False, fields=[]):
return [
ID_FIELD,
{'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields},
]
class test_basic_fields(TransactionCase):
def get_fields(self, field):
return self.registry('base_import.import')\
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
def test_base(self):
""" A basic field is not required """
self.assertEqual(self.get_fields('char'), make_field())
def test_required(self):
""" Required fields should be flagged (so they can be fill-required) """
self.assertEqual(self.get_fields('char.required'), make_field(required=True))
def test_readonly(self):
""" Readonly fields should be filtered out"""
self.assertEqual(self.get_fields('char.readonly'), [ID_FIELD])
def test_readonly_states(self):
""" Readonly fields with states should not be filtered out"""
self.assertEqual(self.get_fields('char.states'), make_field())
def test_readonly_states_noreadonly(self):
""" Readonly fields with states having nothing to do with
readonly should still be filtered out"""
self.assertEqual(self.get_fields('char.noreadonly'), [ID_FIELD])
def test_readonly_states_stillreadonly(self):
""" Readonly fields with readonly states leaving them readonly
always... filtered out"""
self.assertEqual(self.get_fields('char.stillreadonly'), [ID_FIELD])
def test_m2o(self):
""" M2O fields should allow import of themselves (name_get),
their id and their xid"""
self.assertEqual(self.get_fields('m2o'), make_field(fields=[
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
]))
def test_m2o_required(self):
""" If an m2o field is required, its three sub-fields are
required as well (the client has to handle that: requiredness
is id-based)
"""
self.assertEqual(self.get_fields('m2o.required'), make_field(required=True, fields=[
{'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []},
{'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []},
]))
class test_o2m(TransactionCase):
def get_fields(self, field):
return self.registry('base_import.import')\
.get_fields(self.cr, self.uid, 'base_import.tests.models.' + field)
def test_shallow(self):
self.assertEqual(self.get_fields('o2m'), make_field(fields=[
{'id': 'id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
# FIXME: should reverse field be ignored?
{'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [
{'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []},
{'id': 'parent_id', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []},
]},
{'id': 'value', 'name': 'value', 'string': 'unknown', 'required': False, 'fields': []},
]))
class test_match_headers_single(TransactionCase):
def test_match_by_name(self):
match = self.registry('base_import.import')._match_header(
'f0', [{'name': 'f0'}], {})
self.assertEqual(match, [{'name': 'f0'}])
def test_match_by_string(self):
match = self.registry('base_import.import')._match_header(
'some field', [{'name': 'bob', 'string': "Some Field"}], {})
self.assertEqual(match, [{'name': 'bob', 'string': "Some Field"}])
def test_nomatch(self):
match = self.registry('base_import.import')._match_header(
'should not be', [{'name': 'bob', 'string': "wheee"}], {})
self.assertEqual(match, [])
def test_recursive_match(self):
f = {
'name': 'f0',
'string': "My Field",
'fields': [
{'name': 'f0', 'string': "Sub field 0", 'fields': []},
{'name': 'f1', 'string': "Sub field 2", 'fields': []},
]
}
match = self.registry('base_import.import')._match_header(
'f0/f1', [f], {})
self.assertEqual(match, [f, f['fields'][1]])
def test_recursive_nomatch(self):
""" Match first level, fail to match second level
"""
f = {
'name': 'f0',
'string': "My Field",
'fields': [
{'name': 'f0', 'string': "Sub field 0", 'fields': []},
{'name': 'f1', 'string': "Sub field 2", 'fields': []},
]
}
match = self.registry('base_import.import')._match_header(
'f0/f2', [f], {})
self.assertEqual(match, [])
class test_match_headers_multiple(TransactionCase):
def test_noheaders(self):
self.assertEqual(
self.registry('base_import.import')._match_headers(
[], [], {}),
(None, None)
)
def test_nomatch(self):
self.assertEqual(
self.registry('base_import.import')._match_headers(
iter([
['foo', 'bar', 'baz', 'qux'],
['v1', 'v2', 'v3', 'v4'],
]),
[],
{'headers': True}),
(
['foo', 'bar', 'baz', 'qux'],
dict.fromkeys(range(4))
)
)
def test_mixed(self):
self.assertEqual(
self.registry('base_import.import')._match_headers(
iter(['foo bar baz qux/corge'.split()]),
[
{'name': 'bar', 'string': 'Bar'},
{'name': 'bob', 'string': 'Baz'},
{'name': 'qux', 'string': 'Qux', 'fields': [
{'name': 'corge', 'fields': []},
]}
],
{'headers': True}),
(['foo', 'bar', 'baz', 'qux/corge'], {
0: None,
1: ['bar'],
2: ['bob'],
3: ['qux', 'corge'],
})
)
class test_preview(TransactionCase):
def make_import(self):
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'res.users',
'file': u"로그인,언어\nbob,1\n".encode('euc_kr'),
})
return Import, id
def test_encoding(self):
Import, id = self.make_import()
result = Import.parse_preview(self.cr, self.uid, id, {
'quoting': '"',
'separator': ',',
})
self.assertTrue('error' in result)
def test_csv_errors(self):
Import, id = self.make_import()
result = Import.parse_preview(self.cr, self.uid, id, {
'quoting': 'foo',
'separator': ',',
'encoding': 'euc_kr',
})
self.assertTrue('error' in result)
def test_csv_errors(self):
Import, id = self.make_import()
result = Import.parse_preview(self.cr, self.uid, id, {
'quoting': '"',
'separator': 'bob',
'encoding': 'euc_kr',
})
self.assertTrue('error' in result)
def test_success(self):
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
'bar,3,4\n'
'qux,5,6\n'
})
result = Import.parse_preview(self.cr, self.uid, id, {
'quoting': '"',
'separator': ',',
'headers': True,
})
self.assertEqual(result['matches'], {0: ['name'], 1: ['somevalue'], 2: None})
self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter'])
# Order depends on iteration order of fields_get
self.assertItemsEqual(result['fields'], [
{'id': 'id', 'name': 'id', 'string': 'External ID', 'required':False, 'fields': []},
{'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []},
{'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []},
{'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []},
])
self.assertEqual(result['preview'], [
['foo', '1', '2'],
['bar', '3', '4'],
['qux', '5', '6'],
])
# Ensure we only have the response fields we expect
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview'])
class test_convert_import_data(TransactionCase):
""" Tests conversion of base_import.import input into data which
can be fed to Model.import_data
"""
def test_all(self):
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
'bar,3,4\n'
'qux,5,6\n'
})
record = Import.browse(self.cr, self.uid, id)
data, fields = Import._convert_import_data(
record, ['name', 'somevalue', 'othervalue'],
{'quoting': '"', 'separator': ',', 'headers': True,})
self.assertItemsEqual(fields, ['name', 'somevalue', 'othervalue'])
self.assertItemsEqual(data, [
('foo', '1', '2'),
('bar', '3', '4'),
('qux', '5', '6'),
])
def test_filtered(self):
""" If ``False`` is provided as field mapping for a column,
that column should be removed from importable data
"""
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
'bar,3,4\n'
'qux,5,6\n'
})
record = Import.browse(self.cr, self.uid, id)
data, fields = Import._convert_import_data(
record, ['name', False, 'othervalue'],
{'quoting': '"', 'separator': ',', 'headers': True,})
self.assertItemsEqual(fields, ['name', 'othervalue'])
self.assertItemsEqual(data, [
('foo', '2'),
('bar', '4'),
('qux', '6'),
])
def test_norow(self):
""" If a row is composed only of empty values (due to having
filtered out non-empty values from it), it should be removed
"""
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
',3,\n'
',5,6\n'
})
record = Import.browse(self.cr, self.uid, id)
data, fields = Import._convert_import_data(
record, ['name', False, 'othervalue'],
{'quoting': '"', 'separator': ',', 'headers': True,})
self.assertItemsEqual(fields, ['name', 'othervalue'])
self.assertItemsEqual(data, [
('foo', '2'),
('', '6'),
])
def test_nofield(self):
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
})
record = Import.browse(self.cr, self.uid, id)
self.assertRaises(
ValueError,
Import._convert_import_data,
record, [],
{'quoting': '"', 'separator': ',', 'headers': True,})
def test_falsefields(self):
Import = self.registry('base_import.import')
id = Import.create(self.cr, self.uid, {
'res_model': 'base_import.tests.models.preview',
'file': 'name,Some Value,Counter\n'
'foo,1,2\n'
})
record = Import.browse(self.cr, self.uid, id)
self.assertRaises(
ValueError,
Import._convert_import_data,
record, [False, False, False],
{'quoting': '"', 'separator': ',', 'headers': True,})

View File

@ -0,0 +1,271 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-04 13:40+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@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: 2012-09-05 04:46+0000\n"
"X-Generator: Launchpad (build 15901)\n"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,category:0
msgid "Category"
msgstr "Kategori"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,save:0
msgid "Information"
msgstr "Informasjon"
#. module: base_module_record
#: model:ir.model,name:base_module_record.model_ir_module_record
msgid "ir.module.record"
msgstr "ir.modul.opptak"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_data,info,end:0
#: wizard_button:base_module_record.module_record_data,save_yaml,end:0
msgid "End"
msgstr "Slutt"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_data,init:0
#: wizard_view:base_module_record.module_record_objects,init:0
msgid "Choose objects to record"
msgstr "Velg objekter til opptak"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,author:0
msgid "Author"
msgstr "Forfatter"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,directory_name:0
msgid "Directory Name"
msgstr "Navn på katalog"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_data,init,filter_cond:0
#: wizard_field:base_module_record.module_record_objects,init,filter_cond:0
msgid "Records only"
msgstr "Bare opptak"
#. module: base_module_record
#: selection:base_module_record.module_record_objects,info,data_kind:0
msgid "Demo Data"
msgstr "Demo data"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,save,module_filename:0
msgid "Filename"
msgstr "Filnavn"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,version:0
msgid "Version"
msgstr "Versjon"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_data,info:0
#: wizard_view:base_module_record.module_record_data,init:0
#: wizard_view:base_module_record.module_record_data,save_yaml:0
#: wizard_view:base_module_record.module_record_objects,init:0
msgid "Objects Recording"
msgstr "objekter Innspilling"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,save:0
msgid ""
"If you think your module could interest other people, we'd like you to "
"publish it on http://www.openerp.com, in the 'Modules' section. You can do "
"it through the website or using features of the 'base_module_publish' module."
msgstr ""
"Hvis du tror din modul kan interessere andre mennesker, vil vi gjerne at du "
"publisere den på http://www.openerp.com, i 'Moduler-delen. Du kan gjøre det "
"gjennom nettstedet eller bruke funksjonene i «base_module_publish»-modulen."
#. module: base_module_record
#: wizard_field:base_module_record.module_record_data,init,check_date:0
#: wizard_field:base_module_record.module_record_objects,init,check_date:0
msgid "Record from Date"
msgstr "Dato fra opptak"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_data,end:0
#: wizard_view:base_module_record.module_record_objects,end:0
#: wizard_view:base_module_record.module_record_objects,info:0
#: wizard_view:base_module_record.module_record_objects,save:0
#: wizard_view:base_module_record.module_record_objects,save_yaml:0
msgid "Module Recording"
msgstr "Modul innspilling"
#. module: base_module_record
#: model:ir.actions.wizard,name:base_module_record.wizard_base_module_record_objects
#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_module_record_objects
msgid "Export Customizations As a Module"
msgstr "Eksporter Tilpasninger som en modul"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,save:0
msgid "Thanks in advance for your contribution."
msgstr "Takk på forhånd for ditt bidrag."
#. module: base_module_record
#: help:base_module_record.module_record_data,init,objects:0
#: help:base_module_record.module_record_objects,init,objects:0
msgid "List of objects to be recorded"
msgstr "Liste over objekter som skal spilles inn"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,description:0
msgid "Full Description"
msgstr "Full beskrivelse"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,name:0
msgid "Module Name"
msgstr "Modulnavn"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_data,init,objects:0
#: wizard_field:base_module_record.module_record_objects,init,objects:0
msgid "Objects"
msgstr "Objekter"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,save,module_file:0
#: wizard_field:base_module_record.module_record_objects,save_yaml,yaml_file:0
msgid "Module .zip File"
msgstr "Modul .zip fil"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,save:0
msgid "Module successfully created!"
msgstr ""
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,save_yaml:0
msgid "YAML file successfully created !"
msgstr "YAML fil opprettet!"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_data,info:0
#: wizard_view:base_module_record.module_record_data,save_yaml:0
msgid "Result, paste this to your module's xml"
msgstr "Resultatet, lim denne til modulen xml"
#. module: base_module_record
#: selection:base_module_record.module_record_data,init,filter_cond:0
#: selection:base_module_record.module_record_objects,init,filter_cond:0
msgid "Created"
msgstr "Opprettet"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_data,end:0
#: wizard_view:base_module_record.module_record_objects,end:0
msgid "Thanks For using Module Recorder"
msgstr "Takk for at du brukte Modul opptaker."
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,website:0
msgid "Documentation URL"
msgstr "Dokumentasjon URL"
#. module: base_module_record
#: selection:base_module_record.module_record_data,init,filter_cond:0
#: selection:base_module_record.module_record_objects,init,filter_cond:0
msgid "Modified"
msgstr "Modifisert"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_data,init,record:0
#: wizard_button:base_module_record.module_record_objects,init,record:0
msgid "Record"
msgstr "Opptak"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_objects,info,save:0
msgid "Continue"
msgstr "Fortsett"
#. module: base_module_record
#: model:ir.actions.wizard,name:base_module_record.wizard_base_module_record_data
#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_module_record_data
msgid "Export Customizations As Data File"
msgstr "Eksport Tilpasninger Som datafil"
#. module: base_module_record
#: code:addons/base_module_record/wizard/base_module_save.py:129
#, python-format
msgid "Error"
msgstr "Feil"
#. module: base_module_record
#: selection:base_module_record.module_record_objects,info,data_kind:0
msgid "Normal Data"
msgstr "Normal data"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_data,end,end:0
#: wizard_button:base_module_record.module_record_objects,end,end:0
msgid "OK"
msgstr "Ok"
#. module: base_module_record
#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_mod_rec
msgid "Module Creation"
msgstr "modul Skapelsen"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_objects,info,data_kind:0
msgid "Type of Data"
msgstr "Type data"
#. module: base_module_record
#: wizard_view:base_module_record.module_record_objects,info:0
msgid "Module Information"
msgstr "Modul informasjon"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_data,init,info_yaml:0
#: wizard_field:base_module_record.module_record_objects,init,info_yaml:0
msgid "YAML"
msgstr "YAML"
#. module: base_module_record
#: wizard_field:base_module_record.module_record_data,info,res_text:0
#: wizard_field:base_module_record.module_record_data,save_yaml,res_text:0
msgid "Result"
msgstr "Resultat"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_data,init,end:0
#: wizard_button:base_module_record.module_record_objects,info,end:0
#: wizard_button:base_module_record.module_record_objects,init,end:0
msgid "Cancel"
msgstr "Kanseller"
#. module: base_module_record
#: wizard_button:base_module_record.module_record_objects,save,end:0
#: wizard_button:base_module_record.module_record_objects,save_yaml,end:0
msgid "Close"
msgstr "Lukke"
#. module: base_module_record
#: selection:base_module_record.module_record_data,init,filter_cond:0
#: selection:base_module_record.module_record_objects,init,filter_cond:0
msgid "Created & Modified"
msgstr "Laget & Modifisert"
#~ msgid "Module successfully created !"
#~ msgstr "Modulen opprettet!"

View File

@ -0,0 +1,204 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-04 13:59+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@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: 2012-09-05 04:46+0000\n"
"X-Generator: Launchpad (build 15901)\n"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_sxw
msgid "base.report.sxw"
msgstr "basen.rapport.sxw"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "OpenERP Report Designer Configuration"
msgstr "OpenERP Rapport Designer Konfigurasjon"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid ""
"This plug-in allows you to create/modify OpenERP Reports into OpenOffice "
"Writer."
msgstr ""
"Denne plug-in tillater deg å lage / endre OpenERP rapporter i OpenOffice "
"Writer."
#. module: base_report_designer
#: view:base.report.file.sxw:0
msgid "Upload the modified report"
msgstr "Laste opp den endrede rapporten"
#. module: base_report_designer
#: view:base.report.file.sxw:0
msgid "The .SXW report"
msgstr ".SXW rapport"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_designer_installer
msgid "base_report_designer.installer"
msgstr "basen.rapport.designer.installatør"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "_Close"
msgstr "_Lukk"
#. module: base_report_designer
#: view:base.report.rml.save:0
msgid "The RML Report"
msgstr ""
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "Configure"
msgstr "Konfigurer"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "title"
msgstr "tittel"
#. module: base_report_designer
#: field:base.report.file.sxw,report_id:0
#: field:base.report.sxw,report_id:0
msgid "Report"
msgstr "Rapport"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_rml_save
msgid "base.report.rml.save"
msgstr "Basen.rapport.rml.lagre"
#. module: base_report_designer
#: model:ir.ui.menu,name:base_report_designer.menu_action_report_designer_wizard
msgid "Report Designer"
msgstr "Rapportdesigner"
#. module: base_report_designer
#: field:base_report_designer.installer,name:0
msgid "File name"
msgstr "Filnavn"
#. module: base_report_designer
#: view:base.report.file.sxw:0
#: view:base.report.sxw:0
msgid "Get a report"
msgstr "Få en rapport"
#. module: base_report_designer
#: view:base_report_designer.installer:0
#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_wizard
msgid "OpenERP Report Designer"
msgstr "OpenERP Rapport designer"
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Continue"
msgstr "Fortsett"
#. module: base_report_designer
#: field:base.report.rml.save,file_rml:0
msgid "Save As"
msgstr "Lagre som"
#. module: base_report_designer
#: help:base_report_designer.installer,plugin_file:0
msgid ""
"OpenObject Report Designer plug-in file. Save as this file and install this "
"plug-in in OpenOffice."
msgstr ""
"OpenObject Report Designer plug-in-filen. Lagre som denne filen og "
"installere denne plugin-modulen i OpenOffice."
#. module: base_report_designer
#: view:base.report.rml.save:0
msgid "Save RML FIle"
msgstr "Lagre RML fil"
#. module: base_report_designer
#: field:base.report.file.sxw,file_sxw:0
#: field:base.report.file.sxw,file_sxw_upload:0
msgid "Your .SXW file"
msgstr "Din .SXW fil"
#. module: base_report_designer
#: view:base_report_designer.installer:0
msgid "Installation and Configuration Steps"
msgstr "Installasjon og Konfigurasjon trinn"
#. module: base_report_designer
#: field:base_report_designer.installer,description:0
msgid "Description"
msgstr "Beskrivelse:"
#. module: base_report_designer
#: view:base.report.file.sxw:0
msgid ""
"This is the template of your requested report.\n"
"Save it as a .SXW file and open it with OpenOffice.\n"
"Don't forget to install the OpenERP SA OpenOffice package to modify it.\n"
"Once it is modified, re-upload it in OpenERP using this wizard."
msgstr ""
"Dette er malen for den forespurte rapporten.\n"
"Lagre det som en. Sxw fil og åpne den med OpenOffice.\n"
"Ikke glem å installere OpenERP SA OpenOffice-pakken til å endre det.\n"
"Når den er modifisert, laste opp det i OpenERP bruke denne veiviseren."
#. module: base_report_designer
#: field:base_report_designer.installer,config_logo:0
msgid "Image"
msgstr "Bilde"
#. module: base_report_designer
#: model:ir.actions.act_window,name:base_report_designer.action_view_base_report_sxw
msgid "Base Report sxw"
msgstr "Basen rapport sxw"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_base_report_file_sxw
msgid "base.report.file.sxw"
msgstr "basen.rapport.fil.sxw"
#. module: base_report_designer
#: field:base_report_designer.installer,plugin_file:0
msgid "OpenObject Report Designer Plug-in"
msgstr "OpenObject Rapport Designer Plug-in"
#. module: base_report_designer
#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_installer
msgid "OpenERP Report Designer Installation"
msgstr "OpenERP Rapport Designer Installasjon"
#. module: base_report_designer
#: view:base.report.file.sxw:0
#: view:base.report.rml.save:0
#: view:base.report.sxw:0
#: view:base_report_designer.installer:0
msgid "Cancel"
msgstr "Kanseller"
#. module: base_report_designer
#: model:ir.model,name:base_report_designer.model_ir_actions_report_xml
msgid "ir.actions.report.xml"
msgstr "ir.handlinger.rapport.xml"
#. module: base_report_designer
#: view:base.report.sxw:0
msgid "Select your report"
msgstr "Velg din rapport"
#~ msgid "The RML report"
#~ msgstr "RML rapport"

View File

@ -35,6 +35,7 @@ class base_config_settings(osv.osv_memory):
'module_auth_anonymous': fields.boolean('activate the public portal',
help="""Enable the public part of openerp, openerp becomes a public website."""),
'module_auth_oauth': fields.boolean('use external authentication providers, sign in with google, facebook, ...'),
'module_base_import': fields.boolean("Allow users to import data from CSV files"),
}
def open_company(self, cr, uid, ids, context=None):

View File

@ -14,9 +14,10 @@
</header>
<separator string="General Settings"/>
<div>
<label string="You will also find several configuration options on your company data:
address for the header and footer, overdue payments texts, etc."/>
<button type="object" name="open_company" string="Configure Your Company Data" icon="gtk-execute" class="oe_inline oe_link"/>
<p>
<label string="You will find more options in your company details: address for the header and footer, overdue payments texts, etc."/>
<button type="object" name="open_company" string="Configure Your Company Data" icon="gtk-execute" class="oe_inline oe_link"/>
</p>
</div>
<group>
<label for="id" string="Options"/>
@ -59,6 +60,15 @@
</div>
</div>
</group>
<group>
<label for="id" string="Import / Export"/>
<div>
<div>
<field name="module_base_import" class="oe_inline"/>
<label for="module_base_import"/>
</div>
</div>
</group>
</form>
</field>
</record>

View File

@ -297,55 +297,31 @@ class base_stage(object):
destination=False)
def remind_user(self, cr, uid, ids, context=None, attach=False, destination=True):
mail_message = self.pool.get('mail.message')
for case in self.browse(cr, uid, ids, context=context):
if not destination and not case.email_from:
return False
if not case.user_id.email:
return False
if destination and case.section_id.user_id:
case_email = case.section_id.user_id.email
else:
case_email = case.user_id.email
src = case_email
dest = case.user_id.email or ""
body = case.description or ""
for message in case.message_ids:
if message.email_from and message.body_text:
body = message.body_text
break
if not destination:
src, dest = dest, case.email_from
if body and case.user_id.signature:
if body:
body += '\n\n%s' % (case.user_id.signature)
else:
body = '\n\n%s' % (case.user_id.signature)
body = self.format_body(body)
attach_to_send = {}
if attach:
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas'])
attach_to_send = dict(map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send))
# Send an email
subject = "Reminder: [%s] %s" % (str(case.id), case.name, )
mail_message.schedule_with_attach(cr, uid,
src,
[dest],
subject,
body,
model=self._name,
reply_to=case.section_id.reply_to,
res_id=case.id,
attachments=attach_to_send,
context=context
)
if 'message_post' in self:
for case in self.browse(cr, uid, ids, context=context):
if destination:
recipient_id = case.user_id.partner_id.id
else:
if not case.email_from:
return False
recipient_id = self.pool.get('res.partner').find_or_create(cr, uid, case.email_from, context=context)
body = case.description or ""
for message in case.message_ids:
if message.type == 'email' and message.body:
body = message.body
break
body = self.format_body(body)
attach_to_send = {}
if attach:
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas'])
attach_to_send = dict(map(lambda x: (x['datas_fname'], x['datas'].decode('base64')), attach_to_send))
subject = "Reminder: [%s] %s" % (case.id, case.name)
self.message_post(cr, uid, case.id, body=body,
subject=subject, attachments=attach_to_send,
partner_ids=[recipient_id], context=context)
return True
def _check(self, cr, uid, ids=False, context=None):
@ -360,17 +336,6 @@ class base_stage(object):
def format_mail(self, obj, body):
return self.pool.get('base.action.rule').format_mail(obj, body)
def message_thread_followers(self, cr, uid, ids, context=None):
res = {}
for case in self.browse(cr, uid, ids, context=context):
l=[]
if case.email_cc:
l.append(case.email_cc)
if case.user_id and case.user_id.email:
l.append(case.user_id.email)
res[case.id] = l
return res
# ******************************
# Notifications
# ******************************
@ -395,31 +360,31 @@ class base_stage(object):
def case_open_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_close_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_cancel_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_pending_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_reset_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None):
@ -428,5 +393,5 @@ class base_stage(object):
msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name)
else:
msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True

View File

@ -179,13 +179,13 @@ class base_state(object):
# Notifications
# ******************************
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
def case_open_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None):
@ -194,29 +194,29 @@ class base_state(object):
msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name)
else:
msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_close_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_cancel_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_pending_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True
def case_reset_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=msg, context=context)
self.message_post(cr, uid, [id], body=msg, context=context)
return True

View File

@ -1,70 +1,54 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * base_vat
# Spanish (Mexico) translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
"PO-Revision-Date: 2010-12-25 18:58+0000\n"
"Last-Translator: Jordi Esteve (www.zikzakmedia.com) "
"<jesteve@zikzakmedia.com>\n"
"Language-Team: \n"
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-07 00:31+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Spanish (Mexico) <es_MX@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-09-05 05:10+0000\n"
"X-Generator: Launchpad (build 13830)\n"
"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n"
"X-Generator: Launchpad (build 15914)\n"
#. module: base_vat
#: code:addons/base_vat/base_vat.py:87
#: code:addons/base_vat/base_vat.py:141
#, python-format
msgid ""
"The Vat does not seems to be correct. You should have entered something like "
"this %s"
msgstr ""
"El CIF/NIF parece que no sea correcto. Debería haber introducido algo como "
"esto %s"
"This VAT number does not seem to be valid.\n"
"Note: the expected format is %s"
msgstr "El RFC no es válido. El formato esperado es %s"
#. module: base_vat
#: model:ir.module.module,description:base_vat.module_meta_information
msgid ""
"\n"
" Enable the VAT Number for the partner. Check the validity of that VAT "
"Number.\n"
"\n"
" This module follows the methods stated at http://sima-pc.com/nif.php "
"for\n"
" checking the validity of VAT Number assigned to partners in European "
"countries.\n"
" "
#: sql_constraint:res.company:0
msgid "The company name must be unique !"
msgstr ""
"\n"
" Permite la validación del CIF/NIF de las empresas. Comprueba si el "
"CIF/NIF es un número válido.\n"
"\n"
" Este módulo usa los métodos especificados en http://sima-pc.com/nif.php "
"para\n"
" la validación del CIF/NIF asignado a las empresas de los países "
"europeos.\n"
" "
#. module: base_vat
#: model:ir.module.module,shortdesc:base_vat.module_meta_information
msgid "Base VAT - To check VAT number validity"
msgstr "Base CIF/NIF - Para comprobar la validez de los CIF/NIF"
#. module: base_vat
#: constraint:res.partner:0
msgid "Error ! You can not create recursive associated members."
msgstr "¡Error! No puede crear miembros asociados recursivos."
msgid "Error ! You cannot create recursive associated members."
msgstr ""
#. module: base_vat
#: code:addons/base_vat/base_vat.py:88
#, python-format
msgid "The VAT is invalid, It should begin with the country code"
msgstr "El CIF/NIF no es válido, debería empezar con el código del país"
#: field:res.company,vat_check_vies:0
msgid "VIES VAT Check"
msgstr ""
#. module: base_vat
#: model:ir.model,name:base_vat.model_res_company
msgid "Companies"
msgstr ""
#. module: base_vat
#: constraint:res.company:0
msgid "Error! You can not create recursive companies."
msgstr ""
#. module: base_vat
#: help:res.partner,vat_subjected:0
@ -72,27 +56,20 @@ msgid ""
"Check this box if the partner is subjected to the VAT. It will be used for "
"the VAT legal statement."
msgstr ""
"Marque esta opción si la empresa está sujeta al IVA. Será utilizado para la "
"declaración legal del IVA."
#. module: base_vat
#: model:ir.model,name:base_vat.model_res_partner
msgid "Partner"
msgstr "Empresa"
msgstr ""
#. module: base_vat
#: help:res.company,vat_check_vies:0
msgid ""
"If checked, Partners VAT numbers will be fully validated against EU's VIES "
"service rather than via a simple format validation (checksum)."
msgstr ""
#. module: base_vat
#: field:res.partner,vat_subjected:0
msgid "VAT Legal Statement"
msgstr "Sujeto a IVA"
#~ msgid "Invalid XML for View Architecture!"
#~ msgstr "¡XML inválido para la definición de la vista!"
#~ msgid ""
#~ "Enable the VAT Number for the partner. Check the validity of that VAT Number."
#~ msgstr ""
#~ "Activa el IVA (Impuesto Valor Añadido) para la empresa. Comprueba la validez "
#~ "del CIF/NIF."
#~ msgid "VAT"
#~ msgstr "IVA"
msgstr ""

View File

@ -28,9 +28,7 @@
Lets the user create a custom dashboard.
========================================
This module also creates the Administration Dashboard.
The user can also publish notes.
Allows users to create custom dashboard.
""",
'author': 'OpenERP SA',
'depends': ['base'],

348
addons/board/i18n/nb.po Normal file
View File

@ -0,0 +1,348 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-06 14:01+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@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: 2012-09-07 04:58+0000\n"
"X-Generator: Launchpad (build 15914)\n"
#. module: board
#: view:res.log.report:0
msgid " Year "
msgstr " År "
#. module: board
#: model:ir.model,name:board.model_board_menu_create
msgid "Menu Create"
msgstr "Meny laget"
#. module: board
#: view:board.menu.create:0
msgid "Menu Information"
msgstr "Meny informasjon"
#. module: board
#: view:res.users:0
msgid "Latest Connections"
msgstr "Siste Tilkoblinger"
#. module: board
#: view:res.log.report:0
msgid "Log created in last month"
msgstr "Logg opprettet i forrige måned"
#. module: board
#: view:board.board:0
#: model:ir.actions.act_window,name:board.open_board_administration_form
msgid "Administration Dashboard"
msgstr "Administrasjon kontrollpanel"
#. module: board
#: view:res.log.report:0
msgid "Group By..."
msgstr "Grupper etter ..."
#. module: board
#: view:res.log.report:0
msgid "Log created in current year"
msgstr "Logg opprettet i gjeldende år."
#. module: board
#: model:ir.model,name:board.model_board_board
msgid "Board"
msgstr "Brett"
#. module: board
#: field:board.menu.create,menu_name:0
msgid "Menu Name"
msgstr "Menynavn"
#. module: board
#: model:ir.actions.act_window,name:board.board_weekly_res_log_report_action
#: view:res.log.report:0
msgid "Weekly Global Activity"
msgstr "Ukentlig global aktivitet"
#. module: board
#: field:board.board.line,name:0
msgid "Title"
msgstr "Tittel"
#. module: board
#: field:res.log.report,nbr:0
msgid "# of Entries"
msgstr "# av oppføringer"
#. module: board
#: view:res.log.report:0
#: field:res.log.report,month:0
msgid "Month"
msgstr "Måned"
#. module: board
#: view:res.log.report:0
msgid "Log created in current month"
msgstr "Logg opprettet i gjeldende måned."
#. module: board
#: model:ir.actions.act_window,name:board.board_monthly_res_log_report_action
#: view:res.log.report:0
msgid "Monthly Activity per Document"
msgstr "Månedlig aktivitet per dokument"
#. module: board
#: view:board.board:0
msgid "Configuration Overview"
msgstr "Konfigurasjonsoversikt"
#. module: board
#: model:ir.actions.act_window,name:board.action_view_board_list_form
#: model:ir.ui.menu,name:board.menu_view_board_form
msgid "Dashboard Definition"
msgstr "Kontrollpanel Definisjon"
#. module: board
#: selection:res.log.report,month:0
msgid "March"
msgstr "Mars"
#. module: board
#: selection:res.log.report,month:0
msgid "August"
msgstr "August"
#. module: board
#: model:ir.actions.act_window,name:board.action_user_connection_tree
msgid "User Connections"
msgstr "Brukertilkoblinger"
#. module: board
#: field:res.log.report,creation_date:0
msgid "Creation Date"
msgstr "Opprettelsesdato"
#. module: board
#: view:res.log.report:0
msgid "Log Analysis"
msgstr "Logg analyse"
#. module: board
#: field:res.log.report,res_model:0
msgid "Object"
msgstr "Objekt"
#. module: board
#: selection:res.log.report,month:0
msgid "June"
msgstr "Juni"
#. module: board
#: field:board.board,line_ids:0
msgid "Action Views"
msgstr "Handling Visninger"
#. module: board
#: model:ir.model,name:board.model_res_log_report
msgid "Log Report"
msgstr "Logg rapport"
#. module: board
#: code:addons/board/wizard/board_menu_create.py:46
#, python-format
msgid "Please Insert Dashboard View(s) !"
msgstr "Vennligst Sett Kontrollpanel Vinsning (er)!"
#. module: board
#: selection:res.log.report,month:0
msgid "July"
msgstr "juli"
#. module: board
#: view:res.log.report:0
#: field:res.log.report,day:0
msgid "Day"
msgstr "Dag"
#. module: board
#: view:board.menu.create:0
msgid "Create Menu For Dashboard"
msgstr "Opprett meny for Kontrollpanel."
#. module: board
#: selection:res.log.report,month:0
msgid "February"
msgstr "Februar"
#. module: board
#: selection:res.log.report,month:0
msgid "October"
msgstr "Oktober"
#. module: board
#: model:ir.model,name:board.model_board_board_line
msgid "Board Line"
msgstr "bord Linje"
#. module: board
#: field:board.menu.create,menu_parent_id:0
msgid "Parent Menu"
msgstr "Overordnet meny"
#. module: board
#: view:res.log.report:0
msgid " Month-1 "
msgstr " Måned-1 "
#. module: board
#: selection:res.log.report,month:0
msgid "January"
msgstr "Januar"
#. module: board
#: view:board.board:0
msgid "Users"
msgstr "Brukere"
#. module: board
#: selection:res.log.report,month:0
msgid "November"
msgstr "November"
#. module: board
#: help:board.board.line,sequence:0
msgid ""
"Gives the sequence order when displaying a list of "
"board lines."
msgstr "Gir rekkefølgen av når du viser en liste over bord linjer."
#. module: board
#: selection:res.log.report,month:0
msgid "April"
msgstr "April"
#. module: board
#: view:board.board:0
#: field:board.board,name:0
#: field:board.board.line,board_id:0
#: model:ir.ui.menu,name:board.menu_dasboard
msgid "Dashboard"
msgstr "Kontrollpanel"
#. module: board
#: code:addons/board/wizard/board_menu_create.py:45
#, python-format
msgid "User Error!"
msgstr "Bruker feil!"
#. module: board
#: field:board.board.line,action_id:0
msgid "Action"
msgstr "Handling"
#. module: board
#: field:board.board.line,position:0
msgid "Position"
msgstr "Posisjon"
#. module: board
#: view:res.log.report:0
msgid "Model"
msgstr "Modell"
#. module: board
#: model:ir.actions.act_window,name:board.board_homepage_action
msgid "Home Page"
msgstr "Hjemmeside"
#. module: board
#: model:ir.actions.act_window,name:board.action_latest_activities_tree
msgid "Latest Activities"
msgstr "Senest aktiveter"
#. module: board
#: selection:board.board.line,position:0
msgid "Left"
msgstr "Venstre"
#. module: board
#: field:board.board,view_id:0
msgid "Board View"
msgstr "Bord visning"
#. module: board
#: selection:board.board.line,position:0
msgid "Right"
msgstr "Høyre"
#. module: board
#: field:board.board.line,width:0
msgid "Width"
msgstr "Bredde"
#. module: board
#: view:res.log.report:0
msgid " Month "
msgstr " Måned "
#. module: board
#: field:board.board.line,sequence:0
msgid "Sequence"
msgstr "Sekvens"
#. module: board
#: selection:res.log.report,month:0
msgid "September"
msgstr "September"
#. module: board
#: selection:res.log.report,month:0
msgid "December"
msgstr "Desember"
#. module: board
#: view:board.board:0
#: view:board.menu.create:0
msgid "Create Menu"
msgstr "Opprett meny"
#. module: board
#: field:board.board.line,height:0
msgid "Height"
msgstr "Høyde"
#. module: board
#: model:ir.actions.act_window,name:board.action_board_menu_create
msgid "Create Board Menu"
msgstr "Opprett bord meny"
#. module: board
#: selection:res.log.report,month:0
msgid "May"
msgstr "Mai"
#. module: board
#: view:res.log.report:0
#: field:res.log.report,name:0
msgid "Year"
msgstr "År"
#. module: board
#: view:board.menu.create:0
msgid "Cancel"
msgstr "Kanseller"
#. module: board
#: view:board.board:0
msgid "Dashboard View"
msgstr "Kontrollpanel visning"

View File

@ -0,0 +1,3 @@
dashboard.css: dashboard.sass
sass --trace -t expanded dashboard.sass dashboard.css

View File

@ -1,3 +1,17 @@
.openerp .oe_dashboard_layout_selector ul {
white-space: nowrap;
}
.openerp .oe_dashboard_layout_selector li {
margin: 0;
padding: 0;
list-style-type: none;
float: left;
}
.openerp .oe_dashboard_layout_selector li .oe_dashboard_selected_layout {
margin-left: -30px;
vertical-align: bottom;
margin-bottom: 10px;
}
.openerp .oe_dashboard_links {
text-align: right;
margin: 0 4px 6px 0;

View File

@ -9,6 +9,18 @@
box-shadow: $bsval
.openerp
.oe_dashboard_layout_selector
ul
white-space: nowrap
li
margin: 0
padding: 0
list-style-type: none
float: left
.oe_dashboard_selected_layout
margin-left: -30px
vertical-align: bottom
margin-bottom: 10px
.oe_dashboard_links
text-align: right
margin: 0 4px 6px 0

View File

@ -220,36 +220,40 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
am.do_action = function (action) {
self.do_action(action);
};
if (action_attrs.creatable && action_attrs.creatable !== 'false') {
var action_id = parseInt(action_attrs.creatable, 10);
$action.parent().find('button.oe_dashboard_button_create').click(function() {
if (isNaN(action_id)) {
action_orig.flags.default_view = 'form';
self.do_action(action_orig);
} else {
self.rpc('/web/action/load', {
action_id: action_id
}, function(result) {
result.result.flags = result.result.flags || {};
result.result.flags.default_view = 'form';
self.do_action(result.result);
});
}
});
}
if (am.inner_widget) {
am.inner_widget.on_mode_switch.add(function(mode) {
var new_form_action = function(id, editable) {
var new_views = [];
_.each(action_orig.views, function(view) {
new_views[view[1] === mode ? 'unshift' : 'push'](view);
new_views[view[1] === 'form' ? 'unshift' : 'push'](view);
});
if (!new_views.length || new_views[0][1] !== mode) {
new_views.unshift([false, mode]);
if (!new_views.length || new_views[0][1] !== 'form') {
new_views.unshift([false, 'form']);
}
action_orig.views = new_views;
action_orig.res_id = am.inner_widget.dataset.ids[am.inner_widget.dataset.index];
action_orig.res_id = id;
action_orig.flags = {
form: {
"initial_mode": editable ? "edit" : "view",
}
};
self.do_action(action_orig);
});
};
var list = am.inner_widget.views.list;
if (list) {
list.deferred.then(function() {
$(list.controller.groups).off('row_link').on('row_link', function(e, id) {
new_form_action(id);
});
});
}
var kanban = am.inner_widget.views.kanban;
if (kanban) {
kanban.deferred.then(function() {
kanban.controller.open_record = function(id, editable) {
new_form_action(id, editable);
};
});
}
}
},
renderElement: function() {

View File

@ -26,7 +26,6 @@
<span class="oe_header_txt"> <t t-esc="action.attrs.string"/> </span>
<input class = "oe_header_text" type="text"/>
<t t-if="!action.attrs.string">&amp;nbsp;</t>
<button t-if="action.attrs.creatable and action.attrs.creatable !== 'false'" class="oe_button oe_button_create">Create</button>
<span class='oe_icon oe_close'></span>
<span class='oe_icon oe_minimize oe_fold' t-if="!action.attrs.fold"></span>
<span class='oe_icon oe_maximize oe_fold' t-if="action.attrs.fold"></span>

View File

@ -2,22 +2,6 @@
<openerp>
<data>
<!-- CRM dashboard -->
<record model="ir.actions.act_window" id="act_my_oppor">
<field name="name">My Opportunities</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">tree</field>
<field name="domain">[('user_id','=',uid),('type', '=', 'opportunity'),('state','not in',('cancel','done'))]</field>
<field name="view_id" ref="crm.crm_case_tree_view_oppor"/>
</record>
<record model="ir.actions.act_window" id="act_crm_tag_tree_view_leads_all">
<field name="name">New Leads</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">tree</field>
<field name="domain">[('user_id','=',uid),('state','=','draft'),('type','=','lead')]</field>
<field name="view_id" ref="crm.crm_case_tree_view_leads"/>
</record>
<record model="ir.ui.view" id="view_crm_opportunity_stage_graph">
<field name="name">Opportunities By Stage - Graph</field>
<field name="model">crm.lead.report</field>
@ -70,11 +54,9 @@
<form string="Statistics Dashboard" version="7.0">
<board style="1-1">
<column>
<action string="New Leads" name="%(act_crm_tag_tree_view_leads_all)d"/>
<action string="My Opportunities" name="%(act_my_oppor)d"/>
<action string="Planned Revenue by Stage and User" name="%(act_oppor_stage_user)d"/>
</column>
<column>
<action string="Planned Revenue by Stage and User" name="%(act_oppor_stage_user)d"/>
<action string="Opportunities by Stage" name="%(act_opportunity_stage)d"/>
</column>
</board>

View File

@ -46,21 +46,11 @@ class base_action_rule(osv.osv):
}
def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from', False), context=None):
mail_message = self.pool.get('mail.message')
body = self.format_mail(obj, body)
if not emailfrom:
if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email:
emailfrom = obj.user_id.email
name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
emailfrom = tools.ustr(emailfrom)
if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.alias_id:
mail_id = super(base_action_rule, self).email_send(cr, uid, obj, emails, body, emailfrom=emailfrom, context=context)
if mail_id and hasattr(obj, 'section_id') and obj.section_id and obj.section_id.alias_id:
reply_to = obj.section_id.alias_id.name_get()[0][1]
else:
reply_to = emailfrom
if not emailfrom:
raise osv.except_osv(_('Error!'), _("There is no email for your company address."))
return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model=obj._name, reply_to=reply_to, res_id=obj.id)
self.pool.get('mail.mail').write(cr, uid, [mail_id], {'reply_to': reply_to}, context=context)
return mail_id
def do_check(self, cr, uid, action, obj, context=None):
ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context)
@ -105,8 +95,8 @@ class base_action_rule(osv.osv):
write['email_cc'] = obj.act_email_cc
# Put state change by rule in communication history
if hasattr(obj, 'state') and hasattr(obj, 'message_append') and action.act_state:
model_obj.message_append(cr, uid, [obj], _(action.act_state))
if hasattr(obj, 'state') and hasattr(obj, 'message_post') and action.act_state:
model_obj.message_post(cr, uid, [obj], _(action.act_state), context=context)
model_obj.write(cr, uid, [obj.id], write, context)
super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)

View File

@ -55,21 +55,20 @@
</record>
<!-- notify all employees of module installation -->
<function model="mail.group" name="message_append_note">
<!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' -->
<value eval="[ref('mail.group_all_employees')]"/>
<value>Module CRM has been installed</value>
<value>From the top menu Sales, you can: trace leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects.
<record model="mail.message" id="module_install_notification">
<field name="model">mail.group</field>
<field name="res_id" ref="mail.group_all_employees"/>
<field name="type">notification</field>
<field name="subject">CRM application installed!</field>
<field name="body">From the top Sales menu you can track leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects.
To manage quotations and sale orders, install the "Sales Management" application.</field>
</record>
To manage quotations and sale orders, install the module "Sales Management".</value>
</function>
<record model="mail.alias" id="default_sales_alias">
<field name="alias_name">sales</field>
<field name="alias_model_id" ref="model_crm_lead"/>
<field name="alias_user_id" ref="base.user_root"/>
<field name="alias_defaults">{'type':'lead'}</field>
</record>
</data>
</openerp>

View File

@ -19,11 +19,9 @@
#
##############################################################################
import binascii
from base_status.base_stage import base_stage
import crm
from datetime import datetime
from mail.mail_message import to_email
from osv import fields, osv
import time
import tools
@ -40,8 +38,7 @@ class crm_lead(base_stage, osv.osv):
_name = "crm.lead"
_description = "Lead/Opportunity"
_order = "priority,date_action,id desc"
_inherit = ['ir.needaction_mixin', 'mail.thread']
_mail_compose_message = True
_inherit = ['mail.thread','ir.needaction_mixin']
def _get_default_section_id(self, cr, uid, context=None):
""" Gives default section by checking if present in the context """
@ -90,8 +87,8 @@ class crm_lead(base_stage, osv.osv):
search_domain = []
section_id = self._resolve_section_id_from_context(cr, uid, context=context)
if section_id:
search_domain += ['|', '&', ('section_ids', '=', section_id), ('fold', '=', False)]
search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
search_domain += ['|', ('section_ids', '=', section_id)]
search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)]
# retrieve type from the context (if set: choose 'type' or 'both')
type = self._resolve_type_from_context(cr, uid, context=context)
if type:
@ -101,7 +98,12 @@ class crm_lead(base_stage, osv.osv):
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
# restore order of the search
result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
return result
fold = {}
for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
fold[stage.id] = stage.fold or False
return result, fold
_group_by_full = {
'stage_id': _read_group_stage_ids
@ -175,16 +177,6 @@ class crm_lead(base_stage, osv.osv):
else:
return [('id', '=', '0')]
def _get_email_subject(self, cr, uid, ids, fields, args, context=None):
res = {}
for obj in self.browse(cr, uid, ids, context=context):
res[obj.id] = ''
for msg in obj.message_ids:
if msg.email_from:
res[obj.id] = msg.subject
break
return res
_columns = {
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null',
select=True, help="Optional linked partner, usually after conversion of the lead"),
@ -213,7 +205,7 @@ class crm_lead(base_stage, osv.osv):
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
'date_closed': fields.datetime('Closed', readonly=True),
'stage_id': fields.many2one('crm.case.stage', 'Stage',
domain="['&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"),
domain="['&', ('fold', '=', False), '&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"),
'user_id': fields.many2one('res.users', 'Salesperson'),
'referred': fields.char('Referred By', size=64),
'date_open': fields.datetime('Opened', readonly=True),
@ -228,7 +220,6 @@ class crm_lead(base_stage, osv.osv):
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', type='char', size=64),
# Only used for type opportunity
'probability': fields.float('Success Rate (%)',group_operator="avg"),
@ -449,7 +440,7 @@ class crm_lead(base_stage, osv.osv):
oldest_id = opportunity_ids[0]
return self.browse(cr, uid, oldest_id, context=context)
def _mail_body_text(self, cr, uid, lead, fields, title=False, context=None):
def _mail_body(self, cr, uid, lead, fields, title=False, context=None):
body = []
if title:
body.append("%s\n" % (title))
@ -486,11 +477,11 @@ class crm_lead(base_stage, osv.osv):
for opportunity in opportunities:
subject.append(opportunity.name)
title = "%s : %s" % (merge_message, opportunity.name)
details.append(self._mail_body_text(cr, uid, opportunity, fields, title=title, context=context))
details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context))
subject = subject[0] + ", ".join(subject[1:])
details = "\n\n".join(details)
return self.message_append_note(cr, uid, [opportunity_id], subject=subject, body=details)
return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context)
def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None):
message = self.pool.get('mail.message')
@ -546,7 +537,7 @@ class crm_lead(base_stage, osv.osv):
oldest = self._merge_find_oldest(cr, uid, ids, context=context)
if ctx_opportunities :
first_opportunity = ctx_opportunities[0]
tail_opportunities = opportunities_list
tail_opportunities = opportunities_list + ctx_opportunities[1:]
else:
first_opportunity = opportunities_list[0]
tail_opportunities = opportunities_list[1:]
@ -606,19 +597,13 @@ class crm_lead(base_stage, osv.osv):
for lead in self.browse(cr, uid, ids, context=context):
if lead.state in ('done', 'cancel'):
continue
if user_ids or section_id:
self.allocate_salesman(cr, uid, [lead.id], user_ids, section_id, context=context)
vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context)
self.write(cr, uid, [lead.id], vals, context=context)
self.convert_opportunity_send_note(cr, uid, lead, context=context)
#TOCHECK: why need to change partner details in all messages of lead ?
if lead.partner_id:
msg_ids = [ x.id for x in lead.message_ids]
mail_message.write(cr, uid, msg_ids, {
'partner_id': lead.partner_id.id
}, context=context)
if user_ids or section_id:
self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context)
return True
def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None):
@ -630,7 +615,7 @@ class crm_lead(base_stage, osv.osv):
'parent_id': parent_id,
'phone': lead.phone,
'mobile': lead.mobile,
'email': lead.email_from and to_email(lead.email_from)[0],
'email': lead.email_from and tools.email_split(lead.email_from)[0],
'fax': lead.fax,
'title': lead.title and lead.title.id or False,
'function': lead.function,
@ -650,7 +635,7 @@ class crm_lead(base_stage, osv.osv):
partner_id = False
if lead.partner_name and lead.contact_name:
partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context)
self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context)
partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context)
elif lead.partner_name and not lead.contact_name:
partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context)
elif not lead.partner_name and lead.contact_name:
@ -678,32 +663,16 @@ class crm_lead(base_stage, osv.osv):
if context is None:
context = {}
partner_ids = {}
force_partner_id = partner_id
for lead in self.browse(cr, uid, ids, context=context):
if action == 'create':
if not partner_id:
partner_id = self._create_lead_partner(cr, uid, lead, context)
partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context=context)
self._lead_set_partner(cr, uid, lead, partner_id, context=context)
partner_ids[lead.id] = partner_id
return partner_ids
def _send_mail_to_salesman(self, cr, uid, lead, context=None):
"""
Send mail to salesman with updated Lead details.
@ lead: browse record of 'crm.lead' object.
"""
#TOFIX: mail template should be used here instead of fix subject, body text.
message = self.pool.get('mail.message')
email_to = lead.user_id and lead.user_id.email
if not email_to:
return False
email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.email or email_to
partner = lead.partner_id and lead.partner_id.name or lead.partner_name
subject = "lead %s converted into opportunity" % lead.name
body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description)
return message.schedule_with_attach(cr, uid, email_from, [email_to], subject, body)
def allocate_salesman(self, cr, uid, ids, user_ids, team_id=False, context=None):
index = 0
for lead_id in ids:
@ -821,14 +790,13 @@ class crm_lead(base_stage, osv.osv):
if custom_values is None: custom_values = {}
custom_values.update({
'name': msg.get('subject') or _("No Subject"),
'description': msg.get('body_text'),
'description': msg.get('body'),
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
custom_values['priority'] = msg.get('priority')
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context))
return super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
@ -841,18 +809,18 @@ class crm_lead(base_stage, osv.osv):
if update_vals is None: update_vals = {}
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
vals['priority'] = msg.get('priority')
update_vals['priority'] = msg.get('priority')
maps = {
'cost':'planned_cost',
'revenue': 'planned_revenue',
'probability':'probability',
}
for line in msg.get('body_text', '').split('\n'):
for line in msg.get('body', '').split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
if res and maps.get(res.group(1).lower()):
key = maps.get(res.group(1).lower())
vals[key] = res.group(2).lower()
update_vals[key] = res.group(2).lower()
return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
@ -860,15 +828,10 @@ class crm_lead(base_stage, osv.osv):
# OpenChatter methods and notifications
# ----------------------------------------
def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
""" Add 'user_id' to the monitored fields """
res = super(crm_lead, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
return res + ['user_id']
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """
stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1]
return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
def case_get_note_msg_prefix(self, cr, uid, lead, context=None):
if isinstance(lead, (int, long)):
@ -878,33 +841,33 @@ class crm_lead(base_stage, osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
for id in ids:
message = _("%s has been <b>created</b>.")% (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], body=message, context=context)
self.message_post(cr, uid, [id], body=message, context=context)
return True
def case_mark_lost_send_note(self, cr, uid, ids, context=None):
message = _("Opportunity has been <b>lost</b>.")
return self.message_append_note(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, context=context)
def case_mark_won_send_note(self, cr, uid, ids, context=None):
message = _("Opportunity has been <b>won</b>.")
return self.message_append_note(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, context=context)
def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None):
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0]
if action == 'log': prefix = 'Logged'
else: prefix = 'Scheduled'
message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date)
return self.message_append_note(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, context=context)
def _lead_set_partner_send_note(self, cr, uid, ids, context=None):
for lead in self.browse(cr, uid, ids, context=context):
message = _("%s <b>partner</b> is now set to <em>%s</em>." % (self.case_get_note_msg_prefix(cr, uid, lead, context=context), lead.partner_id.name))
lead.message_append_note(body=message)
lead.message_post(body=message)
return True
def convert_opportunity_send_note(self, cr, uid, lead, context=None):
message = _("Lead has been <b>converted to an opportunity</b>.")
lead.message_append_note(body=message)
lead.message_post(body=message)
return True
crm_lead()

View File

@ -55,7 +55,7 @@
<record model="crm.case.stage" id="stage_lead7">
<field name="name">Dead</field>
<field eval="1" name="case_default"/>
<field eval="True" name="fold"/>
<field eval="False" name="fold"/>
<field name="state">cancel</field>
<field eval="'0'" name="probability"/>
<field eval="'16'" name="sequence"/>

View File

@ -23,7 +23,7 @@
<field name="channel_id" ref="crm_case_channel_email"/>
<field name="priority">1</field>
<field name="section_id" ref="crm_case_section_4"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="stage_lead1"/>
<field name="description">Hello,
I am Jason from Le Club SARL,
@ -47,7 +47,7 @@ Can you send details,</field>
<field name="channel_id" ref="crm_case_channel_website"/>
<field name="priority">4</field>
<field name="section_id" ref="crm_case_section_3"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="stage_lead1"/>
<field eval="1" name="active"/>
</record>
@ -108,7 +108,7 @@ Can you send details,</field>
<field name="channel_id" ref="crm_case_channel_website"/>
<field name="priority">3</field>
<field name="section_id" ref="section_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="stage_lead1"/>
<field name="description">Hi, Can you send a quotation for 20 Computers with speakers?
Regards,
@ -135,7 +135,7 @@ Contact: +1 813 494 5005</field>
<field name="channel_id" ref=""/>
<field name="priority">3</field>
<field name="section_id" ref="crm_case_section_4"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="stage_lead1"/>
<field eval="1" name="active"/>
</record>
@ -194,7 +194,7 @@ Contact: +1 813 494 5005</field>
<field name="channel_id" ref="crm_case_channel_phone"/>
<field name="priority">2</field>
<field name="section_id" ref="section_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="stage_lead1"/>
<field eval="1" name="active"/>
</record>
@ -297,7 +297,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-12')" name="date_action"/>
<field name="title_action">Meeting for pricing information.</field>
<field name="section_id" ref="crm_case_section_3"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="1" name="active"/>
</record>
@ -342,7 +342,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
<field name="title_action">Call to ask system requirement</field>
<field name="section_id" ref="crm_case_section_3"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="1" name="active"/>
</record>
@ -411,7 +411,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-4')" name="date_action"/>
<field name="title_action">Call to define real needs about training</field>
<field name="section_id" ref="crm_case_section_2"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="1" name="active"/>
</record>
@ -434,7 +434,7 @@ Andrew</field>
<field eval="time.strftime('%Y-%m-5')" name="date_action"/>
<field name="title_action">Ask for the good receprion of the proposition</field>
<field name="section_id" ref="crm_case_section_1"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="1" name="active"/>
</record>
@ -466,7 +466,7 @@ Andrew</field>
<field name="channel_id" ref="crm_case_channel_phone"/>
<field name="priority">3</field>
<field name="section_id" ref="crm_case_section_4"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead8"/>
<field eval="1" name="active"/>
</record>
@ -484,7 +484,7 @@ Andrew</field>
<field name="channel_id" ref="crm_case_channel_email"/>
<field name="priority">3</field>
<field name="section_id" ref="crm_case_section_4"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead8"/>
<field eval="1" name="active"/>
</record>
@ -519,7 +519,7 @@ Andrew</field>
<field name="priority">5</field>
<field eval="time.strftime('%Y-%m-6')" name="date_deadline"/>
<field name="section_id" ref="section_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead6"/>
<field eval="1" name="active"/>
</record>
@ -542,7 +542,7 @@ Andrew</field>
<field name="priority">2</field>
<field name="title_action">Conf call with technical service</field>
<field name="section_id" ref="crm_case_section_2"/>
<field name="user_id" ref="base.user_admin"/>
<field name="user_id" ref="base.user_root"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="1" name="active"/>
</record>
@ -552,86 +552,67 @@ Andrew</field>
<field name="subject">Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html">&lt;![CDATA[Email0 inquiry]]&gt;&lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;Can you please send me product catalogue?&lt;/font&gt;&lt;/div&gt;</field>
<field name="body">&lt;![CDATA[Email0 inquiry]]&gt;&lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;Can you please send me product catalogue?&lt;/font&gt;&lt;/div&gt;</field>
<field name="type">email</field>
<field name="state">received</field>
<field name="user_id" ref="base.user_demo"/>
</record>
<record id="message_note0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="parent_id" ref="message_email0"/>
<field name="content_subtype">plain</field>
<field name="body_text">Dear Customer,
<field name="type">comment</field>
<field name="body">Dear Customer,
Thanks for showing interest in our products.
We have attached the catalogue,
We would like to know your interests, Let us know if we can call you for more details.
Thanks</field>
<field name="type">email</field>
<field name="user_id" ref="base.user_root"/>
<field name="state">sent</field>
<field name="parent_id" ref="message_email0"/>
<field name="author_id" ref="base.partner_root"/>
</record>
<record id="message_note0_comment0" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html">&lt;div&gt;Thanks for the information,&lt;/div&gt;&lt;div&gt;I will visit the store soon.&lt;/div&gt;</field>
<field name="type">comment</field>
<field name="body">&lt;div&gt;Thanks for the information,&lt;/div&gt;&lt;div&gt;I will visit the store soon.&lt;/div&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="type">email</field>
<field name="user_id" ref="base.user_demo"/>
<field name="state">received</field>
<field name="author_id" ref="base.partner_demo"/>
</record>
<record id="message_note0_comment1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">html</field>
<field name="body_html">&lt;font color="#1f1f1f"&gt;Can you tell me if the store is open at 9:00 PM?&lt;/b&gt;&lt;/font&gt;</field>
<field name="type">comment</field>
<field name="body">&lt;font color="#1f1f1f"&gt;Can you tell me if the store is open at 9:00 PM?&lt;/b&gt;&lt;/font&gt;</field>
<field name="parent_id" ref="message_note0"/>
<field name="type">email</field>
<field name="state">received</field>
<field name="user_id" ref="base.user_demo"/>
<field name="author_id" ref="base.partner_demo"/>
</record>
<record id="message_email1" model="mail.message">
<field name="subject">Re: Plan to buy a Laptop</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_15"/>
<field name="content_subtype">plain</field>
<field name="body_text">Yes, its open till 10:00 PM, you are welcome!</field>
<field name="body">Yes, its open till 10:00 PM, you are welcome!</field>
<field name="type">email</field>
<field name="state">sent</field>
<field name="user_id" ref="base.user_root"/>
<field name="author_id" ref="base.partner_root"/>
</record>
<record id="message_email_12" model="mail.message">
<field name="subject">Inquiry</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_1"/>
<field name="content_subtype">plain</field>
<field name="body_text">Hello,
<field name="body">Hello,
I am Jason from Le Club SARL,
I am intertested to attend Training organized in your company,
Can you send details,</field>
<field name="type">email</field>
<field name="state">received</field>
<field name="user_id" ref="base.user_demo"/>
</record>
<record id="message_email_13" model="mail.message">
<field name="subject">Need Details</field>
<field name="model">crm.lead</field>
<field name="res_id" ref="crm_case_2"/>
<field name="content_subtype">plain</field>
<field name="body_text">Want to know features and benifits to use the new software.</field>
<field name="body">Want to know features and benifits to use the new software.</field>
<field name="type">comment</field>
<field name="user_id" ref="base.user_demo"/>
</record>
<!-- Call Function to set the opportunities as Unread -->
<function model="crm.lead" name="message_mark_as_unread"
eval="[ ref('crm_case_15'), ref('crm_case_16'),

View File

@ -246,7 +246,7 @@
<field name="name">Leads</field>
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<tree string="Leads" fonts="bold:needaction_pending==True" colors="grey:state in ('cancel', 'done')">
<tree string="Leads" fonts="bold:message_unread==True" colors="grey:state in ('cancel', 'done')">
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name"/>
@ -257,12 +257,11 @@
<field name="stage_id"/>
<field name="user_id" invisible="1"/>
<field name="section_id" invisible="context.get('invisible_section', True)" />
<field name="state" groups="base.group_no_one"/>
<field name="state" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/>
<field name="channel_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="needaction_pending" invisible="1"/>
<field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
@ -296,23 +295,16 @@
<field name="user_id"/>
<field name="partner_address_email"/>
<field name="message_summary"/>
<field name="needaction_pending"/>
<field name="message_unread"/>
<templates>
<t t-name="lead_details">
<ul class="oe_kanban_tooltip">
<li t-if="record.phone.raw_value"><b>Phone:</b> <field name="phone"/></li>
<li><b>Probability:</b> <field name="probability"/>%%</li>
<li><b>Creation date:</b> <field name="create_date"/></li>
<li t-if="record.date_deadline.raw_value"><b>Date Deadline:</b> <field name="date_deadline"/></li>
</ul>
</t>
<field name="date_deadline"/>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
<div class="oe_dropdown_toggle oe_dropdown_kanban">
<span class="oe_e">í</span>
<ul class="oe_dropdown_menu">
<li><a type="edit" >Edit...</a></li>
<li><a type="delete">Delete</a></li>
<t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t>
<t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t>
<li><a name="%(mail.action_email_compose_message_wizard)d" type="action">Send Email</a></li>
<li><a name="%(opportunity2phonecall_act)d" type="action">Log Call</a></li>
<li><a name="action_makeMeeting" type="object">Schedule Meeting</a></li>
@ -342,7 +334,6 @@
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
</div>
<div class="oe_kanban_footer_left">
<t t-if="record.needaction_pending.raw_value"><span class="oe_kanban_mail_new">New</span></t>
<t t-raw="record.message_summary.raw_value"/>
</div>
</div>
@ -362,10 +353,8 @@
<search string="Search Leads">
<field name="name" string="Lead / Customer" filter_domain="['|','|',('partner_name','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/>
<field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike',self)]" />
<!-- subjects is not set as store=True so, it is placed outside filter_domain-->
<field name="subjects"/>
<field name="create_date"/>
<filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/>
<filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter icon="terp-check" string="New" name="new" help="New Leads" domain="[('state','=','draft')]"/>
<filter icon="terp-camera_test" string="Open" name="open" domain="[('state','=','open')]"/>
@ -386,7 +375,6 @@
<filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" />
<separator orientation="vertical"/>
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Creation" help="Create date" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/>
</group>
<group string="Display">
@ -435,7 +423,7 @@
<label for="planned_revenue" class="oe_edit_only"/>
<h2>
<field name="planned_revenue" class="oe_inline"/>
<field name="company_currency" class="oe_inline"/> at
<field name="company_currency" class="oe_inline" groups="base.group_multi_currency"/> at
<field name="probability" class="oe_inline" widget="integer"/>%% success rate
</h2>
</div>
@ -515,7 +503,7 @@
<field name="day_open" groups="base.group_no_one"/>
<field name="day_close" groups="base.group_no_one"/>
<field name="referred"/>
<field name="state" groups="base.group_no_one"/>
<field name="state" invisible="1"/>
<field name="type" invisible="1"/>
</group>
<group string="References">
@ -545,7 +533,7 @@
<field name="name">Opportunities Tree</field>
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<tree string="Opportunities" fonts="bold:needaction_pending==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline &lt; current_date)">
<tree string="Opportunities" fonts="bold:message_unread==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline &lt; current_date)">
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name" string="Opportunity"/>
@ -555,7 +543,6 @@
<field name="title_action" />
<field name="channel_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="stage_id"/>
<field name="planned_revenue" sum="Expected Revenues"/>
<field name="probability" widget="progressbar" avg="Avg. of Probability"/>
@ -563,7 +550,7 @@
<field name="user_id"/>
<field name="priority" invisible="1"/>
<field name="state" groups="base.group_no_one"/>
<field name="needaction_pending" invisible="1"/>
<field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
@ -578,7 +565,7 @@
<field name="name" string="Opportunity / Customer"
filter_domain="['|','|','|',('partner_id','ilike',self),('partner_name','ilike',self),('email_from','ilike',self),('name', 'ilike', self)]"/>
<field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike', self)]" />
<filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/>
<filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter icon="terp-check" string="New" help="New Opportunities" name="new" domain="[('state','=','draft')]"/>
<filter icon="terp-camera_test" string="Open" help="Open Opportunities" name="open" domain="[('state','=','open')]"/>
@ -600,7 +587,6 @@
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
<filter string="Campaign" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" />
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Creation" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/>
<filter string="Exp.Closing" icon="terp-go-month" help="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}" />
</group>

View File

@ -44,7 +44,7 @@ class crm_meeting(osv.Model):
def create_send_note(self, cr, uid, ids, context=None):
if context is None:
context = {}
# update context: if come from phonecall, default state values can make the message_append_note crash
# update context: if come from phonecall, default state values can make the message_post crash
context.pop('default_state', False)
for meeting in self.browse(cr, uid, ids, context=context):
# in the message, transpose meeting.date to the timezone of the current user
@ -53,14 +53,14 @@ class crm_meeting(osv.Model):
if meeting.opportunity_id: # meeting can be create from phonecalls or opportunities, therefore checking for the parent
lead = meeting.opportunity_id
message = _("Meeting linked to the opportunity <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (lead.name, meeting_date_tz)
lead.message_append_note(_('System Notification'), message)
lead.message_post(body=message)
elif meeting.phonecall_id:
phonecall = meeting.phonecall_id
message = _("Meeting linked to the phonecall <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (phonecall.name, meeting_date_tz)
phonecall.message_append_note(body=message)
phonecall.message_post(body=message)
else:
message = _("A meeting has been <b>scheduled</b> on <em>%s</em>.") % (meeting_date_tz)
meeting.message_append_note(body=message)
meeting.message_post(body=message)
return True
class calendar_attendee(osv.osv):

View File

@ -32,7 +32,7 @@ class crm_phonecall(base_state, osv.osv):
_name = "crm.phonecall"
_description = "Phonecall"
_order = "id desc"
_inherit = ['ir.needaction_mixin', 'mail.thread']
_inherit = ['mail.thread']
_columns = {
# base_state required fields
'date_action_last': fields.datetime('Last Action', readonly=1),
@ -177,11 +177,11 @@ class crm_phonecall(base_state, osv.osv):
if context is None:
context = {}
partner_ids = {}
force_partner_id = partner_id
for call in self.browse(cr, uid, ids, context=context):
if action == 'create':
if not partner_id:
partner_id = self._call_create_partner(cr, uid, call, context=context)
self._call_create_partner_address(cr, uid, call, partner_id, context=context)
partner_id = force_partner_id or self._call_create_partner(cr, uid, call, context=context)
self._call_create_partner_address(cr, uid, call, partner_id, context=context)
self._call_set_partner(cr, uid, [call.id], partner_id, context=context)
partner_ids[call.id] = partner_id
return partner_ids
@ -266,7 +266,7 @@ class crm_phonecall(base_state, osv.osv):
def case_reset_send_note(self, cr, uid, ids, context=None):
message = _('Phonecall has been <b>reset and set as open</b>.')
return self.message_append_note(cr, uid, ids, body=message, context=context)
return self.message_post(cr, uid, ids, body=message, context=context)
def case_open_send_note(self, cr, uid, ids, context=None):
lead_obj = self.pool.get('crm.lead')
@ -280,11 +280,11 @@ class crm_phonecall(base_state, osv.osv):
message = _("Phonecall linked to the opportunity <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (lead.name, phonecall_date_str)
else:
message = _("Phonecall has been <b>created and opened</b>.")
phonecall.message_append_note(body=message)
phonecall.message_post(body=message)
return True
def _call_set_partner_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context)
return self.message_post(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -66,8 +66,7 @@
<field name="name">CRM - Phone Calls Tree</field>
<field name="model">crm.phonecall</field>
<field name="arch" type="xml">
<tree fonts="bold:needaction_pending==True" colors="gray:state in ('cancel','done');blue:state in ('pending',)" string="Phone Calls">
<field name="needaction_pending" invisible="1"/>
<tree colors="gray:state in ('cancel','done');blue:state in ('pending',)" string="Phone Calls">
<field name="date"/>
<field name="name"/>
<field name="partner_id"/>
@ -76,7 +75,6 @@
<field name="categ_id" invisible="1"/>
<field name="create_date" invisible="1"/>
<field name="opportunity_id" invisible="1"/>
<field name="needaction_pending" invisible="1"/>
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
states="open,pending"
@ -166,7 +164,7 @@
<field name="name">CRM - Logged Phone Calls Tree</field>
<field name="model">crm.phonecall</field>
<field name="arch" type="xml">
<tree string="Phone Calls" fonts="bold:needaction_pending==True" editable="top">
<tree string="Phone Calls" editable="top">
<field name="date"/>
<field name="name"/>
<field name="partner_id"
@ -180,7 +178,6 @@
<field name="state" invisible="1"/>
<field name="create_date" invisible="1"/>
<field name="opportunity_id" invisible="1"/>
<field name="needaction_pending" invisible="1"/>
<button string="Schedule Other Call"
icon="terp-call-start"
name="%(phonecall_to_phonecall_act)d"
@ -219,7 +216,6 @@
<search string="Search Phonecalls">
<field name="name" string="Phonecalls"/>
<field name="date"/>
<filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/>
<separator/>
<filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/>
<separator/>

View File

@ -105,21 +105,11 @@
<filter string="Company" icon="terp-go-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
<filter string="Stage" name="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
<filter string="Campaign" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'type_id'}" />
<filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" />
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}" />
<filter string="Year" icon="terp-go-year" domain="[]" context="{'group_by':'creation_year'}"/>
<filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'creation_month'}"/>
<filter string="Day" icon="terp-go-today" domain="[]" context="{'group_by':'creation_day'}"/>
<filter string="Exp. Closing" icon="terp-go-month" domain="[]" context="{'group_by':'deadline_month'}"/>
<filter string="Campaign" icon="terp-gtk-jump-to-rtl"
domain="[]" context="{'group_by':'type_id'}" />
<filter string="Channel" icon="terp-call-start"
domain="[]" context="{'group_by':'channel_id'}" />
<separator orientation="vertical" />
<filter string="Status" icon="terp-stock_effects-object-colorize"
domain="[]" context="{'group_by':'state'}" />
<separator orientation="vertical" />
<filter string="Year" icon="terp-go-year"
domain="[]" context="{'group_by':'creation_year'}"/>
<filter string="Month" icon="terp-go-month"

View File

@ -38,7 +38,7 @@ class crm_configuration(osv.osv_memory):
'module_import_google': fields.boolean("Google (Contacts and Calendar)",
help="""Import google contact in partner address and add google calendar events details in Meeting.
This installs the module import_google."""),
'module_google_map': fields.boolean("add google maps on customer",
'module_google_map': fields.boolean("add google maps on customers",
help="""Locate customers on Google Map.
This installs the module google_map."""),
'group_fund_raising': fields.boolean("Manage Fund Raising",

View File

@ -23,7 +23,7 @@
!python {model: mail.compose.message}: |
lead_ids = self.pool.get('crm.lead').search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')])
context.update({'active_model': 'crm.lead','active_id': lead_ids[0]})
id = self.create(cr, uid, {'body_text': "Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci", 'email_from': 'sales@mycompany.com'}, context=context)
id = self.create(cr, uid, {'body': "Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci", 'email_from': 'sales@mycompany.com'}, context=context)
try:
self.send_mail(cr, uid, [id], context=context)
except:
@ -34,12 +34,6 @@
!python {model: crm.lead}: |
lead_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')])
self.convert_partner(cr, uid, lead_ids, context=context)
-
Now, I search customer in regular customer list.
-
!python {model: crm.lead}: |
partner_ids = self.message_partner_by_email(cr, uid, 'Mr. John Right <info@customer.com>')
assert partner_ids.get('partner_id'), "Customer is not found in regular customer list."
-
I convert one phonecall request as a customer and put into regular customer list.
-

View File

@ -55,7 +55,7 @@
After communicated with customer, I put some notes with contract details.
-
!python {model: crm.lead}: |
self.message_append_note(cr, uid, [ref('crm_case_4')], subject='Test note', body='ces détails envoyés par le client sur le FAX pour la qualité')
self.message_post(cr, uid, [ref('crm_case_4')], subject='Test note', body='ces détails envoyés par le client sur le FAX pour la qualité')
-
I win this opportunity
-
@ -73,7 +73,7 @@
I convert mass lead into opportunity customer.
-
!python {model: crm.lead2opportunity.partner.mass}: |
context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_4")})
context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_11")})
id = self.create(cr, uid, {'user_ids': [ref('base.user_root')], 'section_id': ref('crm.section_sales_department')}, context=context)
self.mass_convert(cr, uid, [id], context=context)
-
@ -83,7 +83,8 @@
opp = self.browse(cr, uid, ref('crm_case_11'))
assert opp.name == "Need estimated cost for new project", "Opportunity name not correct"
assert opp.type == 'opportunity', 'Lead is not converted to opportunity!'
assert opp.partner_id.name == "Thomas Passot", 'Partner mismatch!'
expected_partner = "Thomas Passot"
assert opp.partner_id.name == expected_partner, 'Partner mismatch! %s vs %s' % (opp.partner_id.name, expected_partner)
assert opp.stage_id.id == ref("stage_lead1"), 'Stage of probability is incorrect!'
-
Then check for second lead converted on opportunity.

View File

@ -24,8 +24,6 @@ from tools.translate import _
import tools
import re
import time
class crm_lead2opportunity_partner(osv.osv_memory):
_name = 'crm.lead2opportunity.partner'
_description = 'Lead To Opportunity Partner'
@ -35,8 +33,8 @@ class crm_lead2opportunity_partner(osv.osv_memory):
'action': fields.selection([('exist', 'Link to an existing partner'), \
('create', 'Create a new partner'), \
('nothing', 'Do not link to a partner')], \
'Action', required=True),
'name': fields.selection([('convert', 'Convert to Opportunity'), ('merge', 'Merge with existing Opportunity')],'Select Action', required=True),
'Related Partner', required=True),
'name': fields.selection([('convert', 'Convert to Opportunities'), ('merge', 'Merge with existing Opportunities')], 'Conversion Action', required=True),
'opportunity_ids': fields.many2many('crm.lead', string='Opportunities', domain=[('type', '=', 'opportunity')]),
}
@ -68,8 +66,6 @@ class crm_lead2opportunity_partner(osv.osv_memory):
if ids:
opportunities.append(ids[0])
if not partner_id:
label = False
opp_ids = []
if email:
# Find email of existing opportunity matches the email_from of the lead
cr.execute("""select id from crm_lead where type='opportunity' and
@ -106,23 +102,36 @@ class crm_lead2opportunity_partner(osv.osv_memory):
if context is None:
context = {}
lead = self.pool.get('crm.lead')
partner_id = self._create_partner(cr, uid, ids, context=context)
res = False
partner_ids_map = self._create_partner(cr, uid, ids, context=context)
lead_ids = vals.get('lead_ids', [])
user_ids = vals.get('user_ids', False)
team_id = vals.get('section_id', False)
return lead.convert_opportunity(cr, uid, lead_ids, partner_id, user_ids, team_id, context=context)
for lead_id in lead_ids:
partner_id = partner_ids_map.get(lead_id, False)
# FIXME: cannot pass user_ids as the salesman allocation only works in batch
res = lead.convert_opportunity(cr, uid, [lead_id], partner_id, [], team_id, context=context)
# FIXME: must perform salesman allocation in batch separately here
user_ids = vals.get('user_ids', False)
if user_ids:
lead.allocate_salesman(cr, uid, lead_ids, user_ids, team_id=team_id, context=context)
return res
def _merge_opportunity(self, cr, uid, ids, opportunity_ids, action='merge', context=None):
#TOFIX: is it usefully ?
if context is None:
context = {}
merge_opportunity = self.pool.get('crm.merge.opportunity')
res = False
#If we convert in mass, don't merge if there is no other opportunity but no warning
if action == 'merge' and (len(opportunity_ids) > 1 or not context.get('mass_convert') ):
self.write(cr, uid, ids, {'opportunity_ids' : [(6,0, [opportunity_ids[0].id])]}, context=context)
context.update({'lead_ids' : record_id, "convert" : True})
res = merge_opportunity.merge(cr, uid, data.opportunity_ids, context=context)
# Expected: all newly-converted leads (active_ids) will be merged with the opportunity(ies)
# that have been selected in the 'opportunity_ids' m2m, with all these records
# merged into the first opportunity (and the rest deleted)
opportunity_ids = [o.id for o in opportunity_ids]
lead_ids = context.get('active_ids', [])
if action == 'merge' and lead_ids and opportunity_ids:
# Add the leads in the to-merge list, next to other opps
# (the fact that they're passed in context['lead_ids'] means that
# they cannot be selected to contain the result of the merge.
opportunity_ids.extend(lead_ids)
context.update({'lead_ids': lead_ids, "convert" : True})
res = self.pool.get('crm.lead').merge_opportunity(cr, uid, opportunity_ids, context=context)
return res
def action_apply(self, cr, uid, ids, context=None):
@ -131,27 +140,37 @@ class crm_lead2opportunity_partner(osv.osv_memory):
"""
if not context:
context = {}
lead = self.pool.get('crm.lead')
lead_ids = context.get('active_ids', [])
data = self.browse(cr, uid, ids, context=context)[0]
self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids}, context=context)
self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.action, context=context)
self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.name, context=context)
return lead.redirect_opportunity_view(cr, uid, lead_ids[0], context=context)
crm_lead2opportunity_partner()
class crm_lead2opportunity_mass_convert(osv.osv_memory):
_name = 'crm.lead2opportunity.partner.mass'
_description = 'Mass Lead To Opportunity Partner'
_inherit = 'crm.lead2opportunity.partner'
_columns = {
'user_ids': fields.many2many('res.users', string='Salesmans'),
'user_ids': fields.many2many('res.users', string='Salesmen'),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
}
def default_get(self, cr, uid, fields, context=None):
res = super(crm_lead2opportunity_mass_convert, self).default_get(cr, uid, fields, context)
if 'partner_id' in fields:
# avoid forcing the partner of the first lead as default
res['partner_id'] = False
if 'action' in fields:
res['action'] = 'create'
if 'name' in fields:
res['name'] = 'convert'
if 'opportunity_ids' in fields:
res['opportunity_ids'] = False
return res
def _convert_opportunity(self, cr, uid, ids, vals, context=None):
data = self.browse(cr, uid, ids, context=context)[0]
salesteam_id = data.section_id and data.section_id.id or False
@ -162,9 +181,6 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory):
return super(crm_lead2opportunity_mass_convert, self)._convert_opportunity(cr, uid, ids, vals, context=context)
def mass_convert(self, cr, uid, ids, context=None):
value = self.default_get(cr, uid, ['partner_id', 'opportunity_ids'], context=context)
value['opportunity_ids'] = [(6, 0, value['opportunity_ids'])]
self.write(cr, uid, ids, value, context=context)
return self.action_apply(cr, uid, ids, context=context)
crm_lead2opportunity_mass_convert()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -37,24 +37,38 @@
<field name="model">crm.lead2opportunity.partner.mass</field>
<field name="arch" type="xml">
<form string="Convert to Opportunity" version="7.0">
<field name="action"/>
<field name="name" colspan="4"/>
<group string="Assigned Opportunities to">
<field name="section_id"/>
<group string="Conversion Options">
<field name="action"/>
<field name="partner_id" attrs="{'invisible':[('action','!=','exist')],'required': [('action', '=', 'exist')]}"/>
<field name="name"/>
</group>
<separator string="Select Salesman"/>
<field name="user_ids">
<group string="Select Opportunities" attrs="{'invisible': [('name', '=', 'convert')]}">
<field name="opportunity_ids" colspan="4" nolabel="1" attrs="{'invisible': [('name', '=', 'convert')]}">
<tree>
<field name="name"/>
<field name="name" />
<field name="partner_id" />
<field name="user_id" />
<field name="section_id" />
</tree>
</field>
</group>
<group string="Assign opportunities to">
<field name="section_id" />
<field name="user_ids" colspan="4">
<tree>
<field name="name" />
</tree>
</field>
</group>
<footer>
<button name="mass_convert" string="Convert into Opportunities" type="object" class="oe_highlight"/>
<button name="mass_convert" string="Convert to Opportunities" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</footer>
</form>
</field>
</field>
</record>
<record id="action_crm_lead2opportunity_partner" model="ir.actions.act_window">

View File

@ -50,20 +50,20 @@ class crm_lead2partner(osv.osv_memory):
def _select_partner(self, cr, uid, context=None):
if context is None:
context = {}
lead = self.pool.get('crm.lead')
partner = self.pool.get('res.partner')
lead_ids = list(context and context.get('active_ids', []) or [])
if not len(lead_ids):
if not context.get('active_model') == 'crm.lead' or not context.get('active_id'):
return False
this = lead.browse(cr, uid, lead_ids[0], context=context)
# Find partner address matches the email_from of the lead
res = lead.message_partner_by_email(cr, uid, this.email_from, context=context)
partner_id = res.get('partner_id', False)
# Find partner name that matches the name of the lead
if not partner_id and this.partner_name:
partner = self.pool.get('res.partner')
lead = self.pool.get('crm.lead')
this = lead.browse(cr, uid, context.get('active_id'), context=context)
partner_id = False
if this.email_from:
partner_ids = partner.search(cr, uid, [('email', '=', this.email_from)], context=context)
if partner_ids:
partner_id = partner_ids[0]
if not this.partner_id and this.partner_name:
partner_ids = partner.search(cr, uid, [('name', '=', this.partner_name)], context=context)
if partner_ids and len(partner_ids):
partner_id = partner_ids[0]
if partner_ids:
partner_id = partner_ids[0]
return partner_id
def default_get(self, cr, uid, fields, context=None):
@ -107,15 +107,16 @@ class crm_lead2partner(osv.osv_memory):
lead_ids = context and context.get('active_ids') or []
data = self.browse(cr, uid, ids, context=context)[0]
partner_id = data.partner_id and data.partner_id.id or False
partner_ids = lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context)
return partner_ids[lead_ids[0]]
return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context)
def make_partner(self, cr, uid, ids, context=None):
"""
This function Makes partner based on action.
"""
partner_id = self._create_partner(cr, uid, ids, context=context)
return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_id, context=context)
# Only called from Form view, so only meant to convert one Lead.
lead_id = context and context.get('active_id') or False
partner_ids_map = self._create_partner(cr, uid, ids, context=context)
return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context)
crm_lead2partner()

View File

@ -23,23 +23,5 @@
</field>
</record>
<!-- partner To Opportunity Action -->
<record model="ir.actions.act_window" id="action_view_crm_partner2opportunity">
<field name="name">Create Opportunity</field>
<field name="res_model">crm.partner2opportunity</field>
<field name="view_type">form</field>
<field name="view_mode">form,tree,kanban,calendar</field>
<field name="target">new</field>
</record>
<!-- partner To Opportunity wizard -->
<act_window id="crm_partner2opportunity"
key2="client_action_multi" name="Create Opportunity"
res_model="crm.partner2opportunity" src_model="res.partner"
view_id="view_crm_partner2opportunity"
view_mode="form" target="new" view_type="form"
groups="base.group_sale_salesman"/>
</data>
</openerp>

View File

@ -57,12 +57,10 @@ class crm_phonecall2partner(osv.osv_memory):
if context is None:
context = {}
phonecall = self.pool.get('crm.phonecall')
data = self.browse(cr, uid, ids, context=context)[0]
call_ids = context and context.get('active_ids') or []
partner_id = data.partner_id and data.partner_id.id or False
partner_ids = phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context)
return partner_ids[call_ids[0]]
return phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context)
crm_phonecall2partner()

View File

@ -0,0 +1,33 @@
# Norwegian Bokmal translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-08 00:36+0000\n"
"PO-Revision-Date: 2012-09-04 14:04+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@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: 2012-09-05 04:46+0000\n"
"X-Generator: Launchpad (build 15901)\n"
#. module: crm_caldav
#: model:ir.actions.act_window,name:crm_caldav.action_caldav_browse
msgid "Caldav Browse"
msgstr "CalDAV Bla"
#. module: crm_caldav
#: model:ir.ui.menu,name:crm_caldav.menu_caldav_browse
msgid "Synchronize This Calendar"
msgstr "Synkroniser denne kalenderen"
#. module: crm_caldav
#: model:ir.model,name:crm_caldav.model_crm_meeting
msgid "Meeting"
msgstr "Møte"

View File

@ -72,7 +72,7 @@ class crm_claim(base_stage, osv.osv):
_description = "Claim"
_order = "priority,date desc"
_inherit = ['mail.thread']
_mail_compose_message = True
_columns = {
'id': fields.integer('ID', readonly=True),
'name': fields.char('Claim Subject', size=128, required=True),
@ -194,13 +194,12 @@ class crm_claim(base_stage, osv.osv):
if custom_values is None: custom_values = {}
custom_values.update({
'name': msg.get('subject') or _("No Subject"),
'description': msg.get('body_text'),
'description': msg.get('body'),
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
})
if msg.get('priority'):
custom_values['priority'] = msg.get('priority')
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
return super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
@ -220,7 +219,7 @@ class crm_claim(base_stage, osv.osv):
'revenue': 'planned_revenue',
'probability':'probability'
}
for line in msg['body_text'].split('\n'):
for line in msg['body'].split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
if res and maps.get(res.group(1).lower()):
@ -239,16 +238,16 @@ class crm_claim(base_stage, osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
msg = _('Claim has been <b>created</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
return self.message_post(cr, uid, ids, body=msg, context=context)
def case_refuse_send_note(self, cr, uid, ids, context=None):
msg = _('Claim has been <b>refused</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
return self.message_post(cr, uid, ids, body=msg, context=context)
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """
stage_name = self.pool.get('crm.claim.stage').name_get(cr, uid, [stage_id], context=context)[0][1]
return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
class res_partner(osv.osv):

View File

@ -9,7 +9,6 @@
-
!python {model: crm.claim}: |
try:
self.message_update(cr, uid,[ref('crm_claim_4')], {'subject': 'Claim Update record','body_text': 'first training session completed',})
self.message_update(cr, uid,[ref('crm_claim_4')], {'subject': 'Claim Update record','body': 'first training session completed',})
except:
pass

View File

@ -38,7 +38,7 @@ class crm_helpdesk(base_state, osv.osv):
_description = "Helpdesk"
_order = "id desc"
_inherit = ['mail.thread']
_mail_compose_message = True
_columns = {
'id': fields.integer('ID', readonly=True),
'name': fields.char('Name', size=128, required=True),
@ -105,12 +105,11 @@ class crm_helpdesk(base_state, osv.osv):
if custom_values is None: custom_values = {}
custom_values.update({
'name': msg.get('subject') or _("No Subject"),
'description': msg.get('body_text'),
'description': msg.get('body'),
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'user_id': False,
})
custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context))
return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=custom_values, context=context)
def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
@ -130,7 +129,7 @@ class crm_helpdesk(base_state, osv.osv):
'revenue': 'planned_revenue',
'probability':'probability'
}
for line in msg['body_text'].split('\n'):
for line in msg['body'].split('\n'):
line = line.strip()
res = tools.misc.command_re.match(line)
if res and maps.get(res.group(1).lower()):
@ -149,7 +148,7 @@ class crm_helpdesk(base_state, osv.osv):
def create_send_note(self, cr, uid, ids, context=None):
msg = _('Case has been <b>created</b>.')
self.message_append_note(cr, uid, ids, body=msg, context=context)
self.message_post(cr, uid, ids, body=msg, context=context)
return True

View File

@ -97,7 +97,7 @@ class crm_helpdesk_report(osv.osv):
c.planned_cost,
count(*) as nbr,
extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close,
(SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND email_from IS NOT NULL) AS email,
(SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND type = 'email') AS email,
abs(avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24)) as delay_expected
from
crm_helpdesk c

View File

@ -23,7 +23,7 @@
!python {model: crm.helpdesk}: |
question_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')])
try:
self.message_update(cr, uid, question_ids, {'subject': 'Link of product', 'body_text': 'www.openerp.com'})
self.message_update(cr, uid, question_ids, {'subject': 'Link of product', 'body': 'www.openerp.com'})
except:
pass

View File

@ -19,8 +19,7 @@
attrs="{'invisible':[('partner_assigned_id','=',False)]}"
name="%(crm_lead_forward_to_partner_act)d"
icon="terp-mail-forward" type="action"
context="{'default_name': 'partner', 'default_partner_id': partner_assigned_id}"
/>
context="{'default_composition_mode': 'forward', 'default_partner_ids': [partner_assigned_id]}"/>
</div>
</group>
<group string="Geo Assignation">
@ -37,8 +36,6 @@
</field>
</record>
<record id="view_crm_opportunity_geo_assign_tree" model="ir.ui.view">
<field name="name">crm.lead.geo_assign.tree.inherit</field>
<field name="model">crm.lead</field>
@ -49,6 +46,7 @@
</field>
</field>
</record>
<record model="ir.ui.view" id="crm_opportunity_partner_filter">
<field name="name">crm.opportunity.partner.filter.assigned</field>
<field name="model">crm.lead</field>
@ -63,5 +61,6 @@
</field>
</field>
</record>
</data>
</openerp>

View File

@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-28 06:39+0000\n"
"X-Generator: Launchpad (build 15864)\n"
"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n"
"X-Generator: Launchpad (build 15914)\n"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,send_to:0
@ -74,7 +74,7 @@ msgid "Geo Localize"
msgstr "التمركز الجغرافي"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body_text:0
#: help:crm.lead.forward.to.partner,body:0
msgid "Plain-text version of the message"
msgstr ""
@ -134,7 +134,7 @@ msgid "Highest"
msgstr "أعلى"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body_text:0
#: field:crm.lead.forward.to.partner,body:0
msgid "Text contents"
msgstr ""

View File

@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-28 06:39+0000\n"
"X-Generator: Launchpad (build 15864)\n"
"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n"
"X-Generator: Launchpad (build 15914)\n"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,send_to:0
@ -74,7 +74,7 @@ msgid "Geo Localize"
msgstr ""
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body_text:0
#: help:crm.lead.forward.to.partner,body:0
msgid "Plain-text version of the message"
msgstr ""
@ -133,7 +133,7 @@ msgid "Highest"
msgstr "Най-висок"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body_text:0
#: field:crm.lead.forward.to.partner,body:0
msgid "Text contents"
msgstr ""

View File

@ -14,8 +14,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n"
"X-Generator: Launchpad (build 15864)\n"
"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n"
"X-Generator: Launchpad (build 15914)\n"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,send_to:0
@ -74,7 +74,7 @@ msgid "Geo Localize"
msgstr "Geo localitzar"
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body_text:0
#: help:crm.lead.forward.to.partner,body:0
msgid "Plain-text version of the message"
msgstr ""
@ -135,7 +135,7 @@ msgid "Highest"
msgstr "El més alt"
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body_text:0
#: field:crm.lead.forward.to.partner,body:0
msgid "Text contents"
msgstr ""

View File

@ -72,7 +72,7 @@ msgid "Geo Localize"
msgstr ""
#. module: crm_partner_assign
#: help:crm.lead.forward.to.partner,body_text:0
#: help:crm.lead.forward.to.partner,body:0
msgid "Plain-text version of the message"
msgstr ""
@ -129,7 +129,7 @@ msgid "Highest"
msgstr ""
#. module: crm_partner_assign
#: field:crm.lead.forward.to.partner,body_text:0
#: field:crm.lead.forward.to.partner,body:0
msgid "Text contents"
msgstr ""

Some files were not shown because too many files have changed in this diff Show More