diff --git a/addons/account/account.py b/addons/account/account.py index 1a44e480e03..3959799f1bb 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -132,7 +132,6 @@ class account_payment_term_line(osv.osv): (_check_percent, 'Percentages for Payment Term Line must be between 0 and 1, Example: 0.02 for 2%.', ['value_amount']), ] -account_payment_term_line() class account_account_type(osv.osv): _name = "account.account.type" @@ -198,7 +197,6 @@ class account_account_type(osv.osv): } _order = "code" -account_account_type() def _code_get(self, cr, uid, context=None): acc_type_obj = self.pool.get('account.account.type') @@ -212,7 +210,6 @@ def _code_get(self, cr, uid, context=None): class account_tax(osv.osv): _name = 'account.tax' -account_tax() class account_account(osv.osv): _order = "parent_left" @@ -697,7 +694,6 @@ class account_account(osv.osv): self._check_moves(cr, uid, ids, "unlink", context=context) return super(account_account, self).unlink(cr, uid, ids, context=context) -account_account() class account_journal(osv.osv): _name = "account.journal" @@ -849,7 +845,6 @@ class account_journal(osv.osv): return self.name_get(cr, user, ids, context=context) -account_journal() class account_fiscalyear(osv.osv): _name = "account.fiscalyear" @@ -945,7 +940,6 @@ class account_fiscalyear(osv.osv): ids = self.search(cr, user, [('name', operator, name)]+ args, limit=limit) return self.name_get(cr, user, ids, context=context) -account_fiscalyear() class account_period(osv.osv): _name = "account.period" @@ -1007,8 +1001,7 @@ class account_period(osv.osv): def find(self, cr, uid, dt=None, context=None): if context is None: context = {} if not dt: - dt = fields.date.context_today(self,cr,uid,context=context) -#CHECKME: shouldn't we check the state of the period? + dt = fields.date.context_today(self, cr, uid, context=context) args = [('date_start', '<=' ,dt), ('date_stop', '>=', dt)] if context.get('company_id', False): args.append(('company_id', '=', context['company_id'])) @@ -1016,7 +1009,7 @@ class account_period(osv.osv): company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id args.append(('company_id', '=', company_id)) result = [] - if context.get('account_period_prefer_normal'): + if context.get('account_period_prefer_normal', True): # look for non-special periods first, and fallback to all if no result is found result = self.search(cr, uid, args + [('special', '=', False)], context=context) if not result: @@ -1071,7 +1064,6 @@ class account_period(osv.osv): return self.search(cr, uid, [('date_start', '>=', period_date_start), ('date_stop', '<=', period_date_stop), ('company_id', '=', company1_id)]) return self.search(cr, uid, [('date_start', '>=', period_date_start), ('date_stop', '<=', period_date_stop), ('company_id', '=', company1_id), ('special', '=', False)]) -account_period() class account_journal_period(osv.osv): _name = "account.journal.period" @@ -1128,7 +1120,6 @@ class account_journal_period(osv.osv): } _order = "period_id" -account_journal_period() class account_fiscalyear(osv.osv): _inherit = "account.fiscalyear" @@ -1145,7 +1136,6 @@ class account_fiscalyear(osv.osv): }) return super(account_fiscalyear, self).copy(cr, uid, id, default=default, context=context) -account_fiscalyear() #---------------------------------------------------------- # Entries #---------------------------------------------------------- @@ -1223,7 +1213,7 @@ class account_move(osv.osv): return res def _get_period(self, cr, uid, context=None): - ctx = dict(context or {}, account_period_prefer_normal=True) + ctx = dict(context or {}) period_ids = self.pool.get('account.period').find(cr, uid, context=ctx) return period_ids[0] @@ -1384,6 +1374,7 @@ class account_move(osv.osv): 'ref':False, 'balance':False, 'account_tax_id':False, + 'statement_id': False, }) if 'journal_id' in vals and vals.get('journal_id', False): @@ -1420,6 +1411,7 @@ class account_move(osv.osv): context = {} if context is None else context.copy() default.update({ 'state':'draft', + 'ref': False, 'name':'/', }) context.update({ @@ -1641,7 +1633,6 @@ class account_move(osv.osv): valid_moves = [move.id for move in valid_moves] return len(valid_moves) > 0 and valid_moves or False -account_move() class account_move_reconcile(osv.osv): _name = "account.move.reconcile" @@ -1715,7 +1706,6 @@ class account_move_reconcile(osv.osv): result.append((r.id,r.name)) return result -account_move_reconcile() #---------------------------------------------------------- # Tax @@ -1795,7 +1785,7 @@ class account_tax_code(osv.osv): if context.get('period_id', False): period_id = context['period_id'] else: - period_id = self.pool.get('account.period').find(cr, uid) + period_id = self.pool.get('account.period').find(cr, uid, context=context) if not period_id: return dict.fromkeys(ids, 0.0) period_id = period_id[0] @@ -1863,7 +1853,6 @@ class account_tax_code(osv.osv): ] _order = 'code' -account_tax_code() class account_tax(osv.osv): """ @@ -2271,7 +2260,6 @@ class account_tax(osv.osv): total += r['amount'] return res -account_tax() # --------------------------------------------------------- # Account Entries Models @@ -2383,7 +2371,6 @@ class account_model(osv.osv): return {'value': {'company_id': company_id}} -account_model() class account_model_line(osv.osv): _name = "account.model.line" @@ -2407,7 +2394,6 @@ class account_model_line(osv.osv): ('credit_debit1', 'CHECK (credit*debit=0)', 'Wrong credit or debit value in model, they must be positive!'), ('credit_debit2', 'CHECK (credit+debit>=0)', 'Wrong credit or debit value in model, they must be positive!'), ] -account_model_line() # --------------------------------------------------------- # Account Subscription @@ -2481,7 +2467,6 @@ class account_subscription(osv.osv): self.write(cr, uid, ids, {'state':'running'}) return True -account_subscription() class account_subscription_line(osv.osv): _name = "account.subscription.line" @@ -2510,7 +2495,6 @@ class account_subscription_line(osv.osv): _rec_name = 'date' -account_subscription_line() # --------------------------------------------------------------- # Account Templates: Account, Tax, Tax Code and chart. + Wizard @@ -2518,7 +2502,6 @@ account_subscription_line() class account_tax_template(osv.osv): _name = 'account.tax.template' -account_tax_template() class account_account_template(osv.osv): _order = "code" @@ -2646,7 +2629,6 @@ class account_account_template(osv.osv): obj_acc._parent_store_compute(cr) return acc_template_ref -account_account_template() class account_add_tmpl_wizard(osv.osv_memory): """Add one more account from the template. @@ -2700,7 +2682,6 @@ class account_add_tmpl_wizard(osv.osv_memory): def action_cancel(self, cr, uid, ids, context=None): return { 'type': 'state', 'state': 'end' } -account_add_tmpl_wizard() class account_tax_code_template(osv.osv): @@ -2772,7 +2753,6 @@ class account_tax_code_template(osv.osv): (_check_recursion, 'Error!\nYou cannot create recursive Tax Codes.', ['parent_id']) ] _order = 'code,name' -account_tax_code_template() class account_chart_template(osv.osv): @@ -2805,7 +2785,6 @@ class account_chart_template(osv.osv): 'complete_tax_set': True, } -account_chart_template() class account_tax_template(osv.osv): @@ -2935,7 +2914,6 @@ class account_tax_template(osv.osv): res.update({'tax_template_to_tax': tax_template_to_tax, 'account_dict': todo_dict}) return res -account_tax_template() # Fiscal Position Templates @@ -2983,7 +2961,6 @@ class account_fiscal_position_template(osv.osv): }) return True -account_fiscal_position_template() class account_fiscal_position_tax_template(osv.osv): _name = 'account.fiscal.position.tax.template' @@ -2996,7 +2973,6 @@ class account_fiscal_position_tax_template(osv.osv): 'tax_dest_id': fields.many2one('account.tax.template', 'Replacement Tax') } -account_fiscal_position_tax_template() class account_fiscal_position_account_template(osv.osv): _name = 'account.fiscal.position.account.template' @@ -3008,7 +2984,6 @@ class account_fiscal_position_account_template(osv.osv): 'account_dest_id': fields.many2one('account.account.template', 'Account Destination', domain=[('type','<>','view')], required=True) } -account_fiscal_position_account_template() # --------------------------------------------------------- # Account generation from template wizards @@ -3401,7 +3376,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): try: tmp2 = obj_data.get_object_reference(cr, uid, *ref) if tmp2: - self.pool.get(tmp2[0]).write(cr, uid, tmp2[1], { + self.pool[tmp2[0]].write(cr, uid, tmp2[1], { 'currency_id': obj_wizard.currency_id.id }) except ValueError, e: @@ -3548,7 +3523,6 @@ class wizard_multi_charts_accounts(osv.osv_memory): current_num += 1 return True -wizard_multi_charts_accounts() class account_bank_accounts_wizard(osv.osv_memory): _name='account.bank.accounts.wizard' @@ -3560,6 +3534,5 @@ class account_bank_accounts_wizard(osv.osv_memory): 'account_type': fields.selection([('cash','Cash'), ('check','Check'), ('bank','Bank')], 'Account Type', size=32), } -account_bank_accounts_wizard() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index f3617106f28..e141f33b9d1 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -143,7 +143,6 @@ class account_analytic_line(osv.osv): return res return False -account_analytic_line() class res_partner(osv.osv): """ Inherits partner and adds contract information in the partner form """ @@ -154,6 +153,5 @@ class res_partner(osv.osv): 'partner_id', 'Contracts', readonly=True), } -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 8e1a5dd26c6..023765d73f0 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -61,7 +61,7 @@ class account_bank_statement(osv.osv): return res def _get_period(self, cr, uid, context=None): - periods = self.pool.get('account.period').find(cr, uid,context=context) + periods = self.pool.get('account.period').find(cr, uid, context=context) if periods: return periods[0] return False @@ -500,7 +500,6 @@ class account_bank_statement(osv.osv): 'context':ctx, } -account_bank_statement() class account_bank_statement_line(osv.osv): @@ -576,6 +575,5 @@ class account_bank_statement_line(osv.osv): 'type': 'general', } -account_bank_statement_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py index 86d3b0d24de..40f0ca80738 100644 --- a/addons/account/account_cash_statement.py +++ b/addons/account/account_cash_statement.py @@ -66,7 +66,6 @@ class account_cashbox_line(osv.osv): 'bank_statement_id' : fields.many2one('account.bank.statement', ondelete='cascade'), } -account_cashbox_line() class account_cash_statement(osv.osv): @@ -316,7 +315,6 @@ class account_cash_statement(osv.osv): return self.write(cr, uid, ids, {'closing_date': time.strftime("%Y-%m-%d %H:%M:%S")}, context=context) -account_cash_statement() class account_journal(osv.osv): _inherit = 'account.journal' @@ -336,7 +334,6 @@ class account_journal(osv.osv): 'cashbox_line_ids' : _default_cashbox_line_ids, } -account_journal() class account_journal_cashbox_line(osv.osv): _name = 'account.journal.cashbox.line' @@ -348,6 +345,5 @@ class account_journal_cashbox_line(osv.osv): _order = 'pieces asc' -account_journal_cashbox_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_financial_report.py b/addons/account/account_financial_report.py index e8b58f61882..0c2e5845b89 100644 --- a/addons/account/account_financial_report.py +++ b/addons/account/account_financial_report.py @@ -138,6 +138,5 @@ class account_financial_report(osv.osv): 'style_overwrite': 0, } -account_financial_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 222a0bebbb4..c47cf70b673 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -286,7 +286,10 @@ class account_invoice(osv.osv): 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments'), 'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}), 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, track_visibility='onchange', states={'draft':[('readonly',False)]}), - 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}) + 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}), + 'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one', + relation='res.partner', store=True, readonly=True, + help="The commercial entity that will be used on Journal Entries for this invoice") } _defaults = { 'type': _get_type, @@ -313,7 +316,7 @@ class account_invoice(osv.osv): context = {} if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']: - partner = self.pool.get(context['active_model']).read(cr, uid, context['active_ids'], ['supplier','customer'])[0] + partner = self.pool[context['active_model']].read(cr, uid, context['active_ids'], ['supplier','customer'])[0] if not view_type: view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')]) view_type = 'tree' @@ -367,18 +370,6 @@ class account_invoice(osv.osv): context['view_id'] = view_id return context - def create(self, cr, uid, vals, context=None): - if context is None: - context = {} - try: - return super(account_invoice, self).create(cr, uid, vals, context) - except Exception, e: - if '"journal_id" viol' in e.args[0]: - raise orm.except_orm(_('Configuration Error!'), - _('There is no Sale/Purchase Journal(s) defined.')) - else: - raise orm.except_orm(_('Unknown Error!'), str(e)) - def invoice_print(self, cr, uid, ids, context=None): ''' This function prints the invoice and mark it as sent, so that we can see more easily the next step of the workflow @@ -997,8 +988,7 @@ class account_invoice(osv.osv): 'narration':inv.comment } period_id = inv.period_id and inv.period_id.id or False - ctx.update(company_id=inv.company_id.id, - account_period_prefer_normal=True) + ctx.update(company_id=inv.company_id.id) if not period_id: period_ids = period_obj.find(cr, uid, inv.date_invoice, context=ctx) period_id = period_ids and period_ids[0] or False @@ -1274,9 +1264,7 @@ class account_invoice(osv.osv): ref = invoice.reference else: ref = self._convert_ref(cr, uid, invoice.number) - partner = invoice.partner_id - if partner.parent_id and not partner.is_company: - partner = partner.parent_id + partner = self.pool['res.partner']._find_accounting_partner(invoice.partner_id) # Pay attention to the sign for both debit/credit AND amount_currency l1 = { 'debit': direction * pay_amount>0 and direction * pay_amount, @@ -1596,7 +1584,6 @@ class account_invoice_line(osv.osv): unique_tax_ids = product_change_result['value']['invoice_line_tax_id'] return {'value':{'invoice_line_tax_id': unique_tax_ids}} -account_invoice_line() class account_invoice_tax(osv.osv): _name = "account.invoice.tax" @@ -1747,15 +1734,11 @@ class res_partner(osv.osv): 'invoice_ids': fields.one2many('account.invoice.line', 'partner_id', 'Invoices', readonly=True), } - def _find_accounting_partner(self, part): + def _find_accounting_partner(self, partner): ''' Find the partner for which the accounting entries will be created ''' - #if the chosen partner is not a company and has a parent company, use the parent for the journal entries - #because you want to invoice 'Agrolait, accounting department' but the journal items are for 'Agrolait' - if part.parent_id and not part.is_company: - part = part.parent_id - return part + return partner.commercial_partner_id def copy(self, cr, uid, id, default=None, context=None): default = default or {} diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index c2f21c0d8bc..7469c38decb 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -117,6 +117,7 @@ + @@ -320,7 +321,8 @@ + options='{"always_reload": True}' + domain="[('customer', '=', True)]"/> @@ -447,19 +449,20 @@ account.invoice - + - + - + + @@ -622,8 +625,6 @@ - - - + @@ -1194,7 +1194,12 @@ sequence="1" groups="group_account_user" /> - + + Journal Items + account.move.line + {'search_default_partner_id': [active_id], 'default_partner_id': active_id} + + account.move.line @@ -1288,7 +1293,7 @@ - + @@ -1352,7 +1357,7 @@ - + @@ -1771,23 +1776,6 @@ - - - - - , 2012. +# Els Van Vossel , 2012, 2013. msgid "" msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2012-12-19 18:03+0000\n" +"PO-Revision-Date: 2013-04-15 23:02+0000\n" "Last-Translator: Els Van Vossel (Agaplan) \n" "Language-Team: Els Van Vossel\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:20+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-17 05:15+0000\n" +"X-Generator: Launchpad (build 16567)\n" "Language: nl\n" #. module: account @@ -258,7 +258,7 @@ msgstr "Belgische rapporten" #. module: account #: model:mail.message.subtype,name:account.mt_invoice_validated msgid "Validated" -msgstr "" +msgstr "Goedgekeurd" #. module: account #: model:account.account.type,name:account.account_type_income_view1 @@ -473,14 +473,14 @@ msgstr "" #. module: account #: help:account.bank.statement.line,name:0 msgid "Originator to Beneficiary Information" -msgstr "" +msgstr "Informatie Afzender naar Begunstigde" #. module: account #. openerp-web #: code:addons/account/static/src/xml/account_move_line_quickadd.xml:8 #, python-format msgid "Period :" -msgstr "" +msgstr "Periode:" #. module: account #: field:account.account.template,chart_template_id:0 @@ -494,6 +494,7 @@ msgstr "Boekhoudplansjabloon" #: selection:account.invoice.refund,filter_refund:0 msgid "Modify: create refund, reconcile and create a new draft invoice" msgstr "" +"Wijzigen: factuur crediteren, afpunten en een nieuwe conceptfactuur maken" #. module: account #: help:account.config.settings,tax_calculation_rounding_method:0 @@ -803,7 +804,7 @@ msgstr "Stel de bankrekeningen van uw bedrijf in" #. module: account #: view:account.invoice.refund:0 msgid "Create Refund" -msgstr "" +msgstr "Creditnota maken" #. module: account #: constraint:account.move.line:0 @@ -833,7 +834,7 @@ msgstr "Bent u zeker dat u de boeking wilt uitvoeren?" #: code:addons/account/account_invoice.py:1330 #, python-format msgid "Invoice partially paid: %s%s of %s%s (%s%s remaining)." -msgstr "" +msgstr "Factuur is gedeeltelijk betaald: %s%s of %s%s (%s%s blijft open)" #. module: account #: view:account.invoice:0 @@ -1010,6 +1011,8 @@ msgid "" " opening/closing fiscal " "year process." msgstr "" +"U kunt geen afpunting ongedaan maken als deze afpunting voortkomt uit een " +"heropening." #. module: account #: model:ir.actions.act_window,name:account.action_subscription_form_new @@ -1052,7 +1055,7 @@ msgstr "Aankoopjournaal" #. module: account #: model:mail.message.subtype,description:account.mt_invoice_paid msgid "Invoice paid" -msgstr "" +msgstr "Factuur betaald" #. module: account #: view:validate.account.move:0 @@ -1375,6 +1378,8 @@ msgid "" "The amount expressed in the secondary currency must be positif when journal " "item are debit and negatif when journal item are credit." msgstr "" +"Het bedrag in secundaire munt moet positief zijn als de boekingslijn debet " +"is en negatief bij een creditbedrag." #. module: account #: view:account.invoice.cancel:0 @@ -1940,7 +1945,7 @@ msgstr "Verkopen per rekeningtype" #: model:account.payment.term,name:account.account_payment_term_15days #: model:account.payment.term,note:account.account_payment_term_15days msgid "15 Days" -msgstr "" +msgstr "15 dagen" #. module: account #: model:ir.ui.menu,name:account.periodical_processing_invoicing @@ -2084,7 +2089,7 @@ msgstr "Voorlopig rekeninguittreksel" #. module: account #: model:mail.message.subtype,description:account.mt_invoice_validated msgid "Invoice validated" -msgstr "" +msgstr "Factuur goedgekeurd" #. module: account #: field:account.config.settings,module_account_check_writing:0 @@ -2342,6 +2347,7 @@ msgid "" "You cannot change the type of account to '%s' type as it contains journal " "items!" msgstr "" +"U kunt het rekeningtype niet wijzigen in '%s' omdat er al boekingen zijn." #. module: account #: model:ir.model,name:account.model_account_aged_trial_balance @@ -2358,7 +2364,7 @@ msgstr "Boekjaar afsluiten" #: code:addons/account/static/src/xml/account_move_line_quickadd.xml:14 #, python-format msgid "Journal :" -msgstr "" +msgstr "Journaal:" #. module: account #: sql_constraint:account.fiscal.position.tax:0 @@ -2718,6 +2724,8 @@ msgid "" "You cannot change the type of account from 'Closed' to any other type as it " "contains journal items!" msgstr "" +"U kunt het rekeningtype niet wijzigen van 'Afgesloten' in een ander type als " +"er boekingen zijn." #. module: account #: field:account.invoice.report,account_line_id:0 @@ -2811,7 +2819,7 @@ msgstr "Rekeningeigenschappen" #. module: account #: selection:account.invoice.refund,filter_refund:0 msgid "Create a draft refund" -msgstr "" +msgstr "Maak een voorlopige creditnota" #. module: account #: view:account.partner.reconcile.process:0 @@ -3360,7 +3368,7 @@ msgstr "" #: view:account.unreconcile:0 #: view:account.unreconcile.reconcile:0 msgid "Unreconcile Transactions" -msgstr "" +msgstr "Afpuntingen ongedaan maken" #. module: account #: field:wizard.multi.charts.accounts,only_one_chart_template:0 @@ -3558,7 +3566,7 @@ msgstr "Aantal cijfers voor de rekeningcode" #. module: account #: field:res.partner,property_supplier_payment_term:0 msgid "Supplier Payment Term" -msgstr "" +msgstr "Betaaltermijn leverancier" #. module: account #: view:account.fiscalyear:0 @@ -3633,7 +3641,7 @@ msgstr "Elektronisch bestand" #. module: account #: field:account.move.line,reconcile:0 msgid "Reconcile Ref" -msgstr "" +msgstr "Afpuntingsreferentie" #. module: account #: field:account.config.settings,has_chart_of_accounts:0 @@ -3734,6 +3742,88 @@ msgid "" "\n" " " msgstr "" +"\n" +"
\n" +"\n" +"

Hallo ${object.partner_id.name},

\n" +"\n" +"

Er is een nieuwe factuur voor u:

\n" +" \n" +"

\n" +"   REFERENTIE
\n" +"   Factuurnummer: ${object.number}
\n" +"   Totaal: ${object.amount_total} " +"${object.currency_id.name}
\n" +"   Datum: ${object.date_invoice}
\n" +" % if object.origin:\n" +"   Referentie: ${object.origin}
\n" +" % endif\n" +" % if object.user_id:\n" +"   Uw contactpersoon: ${object.user_id.name}\n" +" % endif\n" +"

\n" +" \n" +" % if object.paypal_url:\n" +"
\n" +"

U kunt ook onmiddellijk betalen via Paypal:

\n" +" \n" +" \n" +" \n" +" % endif\n" +" \n" +"
\n" +"

Neem gerust contact met ons op als u vragen heeft.

\n" +"

Bedankt dat u hebt gekozen voor ${object.company_id.name or " +"'ons'}!

\n" +"
\n" +"
\n" +"
\n" +"

\n" +" ${object.company_id.name}

\n" +"
\n" +"
\n" +" \n" +" % if object.company_id.street:\n" +" ${object.company_id.street}
\n" +" % endif\n" +" % if object.company_id.street2:\n" +" ${object.company_id.street2}
\n" +" % endif\n" +" % if object.company_id.city or object.company_id.zip:\n" +" ${object.company_id.zip} ${object.company_id.city}
\n" +" % endif\n" +" % if object.company_id.country_id:\n" +" ${object.company_id.state_id and ('%s, ' % " +"object.company_id.state_id.name) or ''} ${object.company_id.country_id.name " +"or ''}
\n" +" % endif\n" +"
\n" +" % if object.company_id.phone:\n" +"
\n" +" Tel.:  ${object.company_id.phone}\n" +"
\n" +" % endif\n" +" % if object.company_id.website:\n" +"
\n" +" Web: ${object.company_id.website}\n" +"
\n" +" %endif\n" +"

\n" +"
\n" +"
\n" +" " #. module: account #: view:account.period:0 @@ -3922,6 +4012,8 @@ msgid "" "You cannot create journal items with a secondary currency without recording " "both 'currency' and 'amount currency' field." msgstr "" +"U kunt geen boekingslijnen in een secundaire munt maken zonder beide velden " +"'valuta' en 'bedrag valuta' in te vullen." #. module: account #: field:account.financial.report,display_detail:0 @@ -4224,7 +4316,7 @@ msgstr "" #. module: account #: model:ir.model,name:account.model_account_journal_cashbox_line msgid "account.journal.cashbox.line" -msgstr "" +msgstr "account.journal.cashbox.line" #. module: account #: model:ir.model,name:account.model_account_partner_reconcile_process @@ -4498,7 +4590,7 @@ msgstr "Uw bankrekeningen instellen" #. module: account #: xsl:account.transfer:0 msgid "Partner ID" -msgstr "" +msgstr "Relatie-ID" #. module: account #: help:account.bank.statement,message_ids:0 @@ -4704,6 +4796,8 @@ msgid "" "This payment term will be used instead of the default one for sale orders " "and customer invoices" msgstr "" +"Deze betalingsvoorwaarde vervangt de standaardvoorwaarde van de huidige " +"relatie." #. module: account #: view:account.config.settings:0 @@ -4731,7 +4825,7 @@ msgstr "Geboekte lijnen" #. module: account #: field:account.move.line,blocked:0 msgid "No Follow-up" -msgstr "" +msgstr "Geen aanmaning" #. module: account #: view:account.tax.template:0 @@ -4858,6 +4952,7 @@ msgstr "Maand" #, python-format msgid "You cannot change the code of account which contains journal items!" msgstr "" +"U kunt de code van een rekening niet wijzigen als er al boekingen zijn." #. module: account #: field:account.config.settings,purchase_sequence_prefix:0 @@ -4895,7 +4990,7 @@ msgstr "Rek.type" #. module: account #: selection:account.journal,type:0 msgid "Bank and Checks" -msgstr "" +msgstr "Bank en cheques" #. module: account #: field:account.account.template,note:0 @@ -4977,7 +5072,7 @@ msgstr "Schakel dit in als u ook rekeningen met een nulsaldo wilt weergeven." #. module: account #: field:account.move.reconcile,opening_reconciliation:0 msgid "Opening Entries Reconciliation" -msgstr "" +msgstr "Afpunting openingsboekingen" #. module: account #. openerp-web @@ -5018,7 +5113,7 @@ msgstr "Boekhoudplan" #. module: account #: field:account.invoice,reference_type:0 msgid "Payment Reference" -msgstr "" +msgstr "Betaalreferentie" #. module: account #: selection:account.financial.report,style_overwrite:0 @@ -5092,7 +5187,7 @@ msgstr "Af te punten boekingen" #. module: account #: model:ir.model,name:account.model_account_tax_template msgid "Templates for Taxes" -msgstr "" +msgstr "Btw-sjablonen" #. module: account #: sql_constraint:account.period:0 @@ -5667,6 +5762,8 @@ msgstr "Doelbewegingen" msgid "" "Move cannot be deleted if linked to an invoice. (Invoice: %s - Move ID:%s)" msgstr "" +"Boeking kan niet worden verwijderd als deze is gekoppeld aan een factuur " +"(Factuur: %s - boeking: %s)" #. module: account #: view:account.bank.statement:0 @@ -6225,6 +6322,8 @@ msgid "" "This payment term will be used instead of the default one for purchase " "orders and supplier invoices" msgstr "" +"Deze betalingsvoorwaarde vervangt de standaardvoorwaarde van de huidige " +"relatie voor aankooporders en aankoopfacturen." #. module: account #: help:account.automatic.reconcile,power:0 @@ -6734,7 +6833,7 @@ msgstr "Analytische lijn" #. module: account #: model:ir.ui.menu,name:account.menu_action_model_form msgid "Models" -msgstr "" +msgstr "Modellen" #. module: account #: code:addons/account/account_invoice.py:1091 @@ -7055,6 +7154,12 @@ msgid "" "due date, make sure that the payment term is not set on the invoice. If you " "keep the payment term and the due date empty, it means direct payment." msgstr "" +"Als u betalingstermijnen gebruikt, wordt de vervaldatum automatisch berekend " +"bij het maken van de boekingen. De betalingsvoorwaarde kan verschillende " +"vervaldatums berekenen, vb. 50% nu en 50% binnen een maand. Als u een " +"specifieke vervaldatum wilt instellen, gebruikt u beter geen " +"betalingstermijn. Als u zowel betalingstermijn als vervaldatum leeglaat, " +"gaat het om een contante betaling." #. module: account #: code:addons/account/account.py:414 @@ -7296,6 +7401,8 @@ msgid "" "If you unreconcile transactions, you must also verify all the actions that " "are linked to those transactions because they will not be disabled" msgstr "" +"Als u afgepunte transacties ongedaan maakt, moet u alle gekoppelde acties " +"nakijken, want deze worden niet ongedaan gemaakt." #. module: account #: view:account.account.template:0 @@ -7330,6 +7437,7 @@ msgid "" "You cannot provide a secondary currency if it is the same than the company " "one." msgstr "" +"U kunt geen secundaire munt ingeven die identiek is aan de firmamunt." #. module: account #: selection:account.tax.template,applicable_type:0 @@ -7467,7 +7575,7 @@ msgstr "Manueel" #. module: account #: selection:account.invoice.refund,filter_refund:0 msgid "Cancel: create refund and reconcile" -msgstr "" +msgstr "Annuleren: maak een creditnota en punt af" #. module: account #: code:addons/account/wizard/account_report_aged_partner_balance.py:58 @@ -7564,7 +7672,7 @@ msgstr "Alle boekingen" #. module: account #: constraint:account.move.reconcile:0 msgid "You can only reconcile journal items with the same partner." -msgstr "" +msgstr "U kunt enkel boekingen met dezelfde relatie afpunten." #. module: account #: view:account.journal.select:0 @@ -7682,7 +7790,7 @@ msgstr "" #. module: account #: field:account.invoice,paypal_url:0 msgid "Paypal Url" -msgstr "" +msgstr "Paypal-url" #. module: account #: field:account.config.settings,module_account_voucher:0 @@ -8390,7 +8498,7 @@ msgstr "" #. module: account #: field:account.move.line,amount_residual_currency:0 msgid "Residual Amount in Currency" -msgstr "" +msgstr "Restbedrag in valuta" #. module: account #: field:account.config.settings,sale_refund_sequence_prefix:0 @@ -8444,6 +8552,8 @@ msgid "" "Refund base on this type. You can not Modify and Cancel if the invoice is " "already reconciled" msgstr "" +"Creditnota voor dit type. U kunt niet wijzigen of annuleren als de factuur " +"al is afgepunt." #. module: account #: field:account.bank.statement.line,sequence:0 @@ -8461,7 +8571,7 @@ msgstr "Volgorde" #. module: account #: field:account.config.settings,paypal_account:0 msgid "Paypal account" -msgstr "" +msgstr "Paypal-rekening" #. module: account #: selection:account.print.journal,sort_selection:0 @@ -8730,7 +8840,7 @@ msgstr "Omgekeerde analytische balans -" #: help:account.move.reconcile,opening_reconciliation:0 msgid "" "Is this reconciliation produced by the opening of a new fiscal year ?." -msgstr "" +msgstr "Komt deze afpunting van een openingsboeking?" #. module: account #: view:account.analytic.line:0 @@ -9011,7 +9121,7 @@ msgstr "Eindbalans" #. module: account #: field:account.journal,centralisation:0 msgid "Centralized Counterpart" -msgstr "" +msgstr "Gecentraliseerde tegenboeking" #. module: account #: help:account.move.line,blocked:0 @@ -9047,6 +9157,12 @@ msgid "" "invoice will be created \n" " so that you can edit it." msgstr "" +"Gebruik deze optie als u een factuur wilt annuleren en een nieuwe maken.\n" +" De creditnota wordt gemaakt, goedgekeurd " +"en afgepunt\n" +" met de huidige factuur. Een nieuwe, " +"voorlopige factuur wordt gemaakt\n" +" die u kunt bewerken." #. module: account #: model:process.transition,name:account.process_transition_filestatement0 @@ -9079,7 +9195,7 @@ msgstr "Rekeningtypen" #. module: account #: model:email.template,subject:account.email_template_edi_invoice msgid "${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})" -msgstr "" +msgstr "${object.company_id.name} Factuur (Ref. ${object.number or 'nvt' })" #. module: account #: code:addons/account/account_move_line.py:1213 @@ -9149,6 +9265,19 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klik om een journaal toe te voegen.\n" +"

\n" +" Een journaal groepeert boekingen in functie\n" +" van de dagelijkse bezigheden.\n" +"

\n" +" Een firma geruikt doorgaans een journaal per betaalmethode " +"(kas,\n" +" bankrekeningen, cheques), een aankoopdagboek, een " +"verkoopdagboek\n" +" en een diversendagboek.\n" +"

\n" +" " #. module: account #: model:ir.model,name:account.model_account_fiscalyear_close_state @@ -9276,6 +9405,9 @@ msgid "" "computed. Because it is space consuming, we do not allow to use it while " "doing a comparison." msgstr "" +"Met deze optie krijgt u meer details over de manier waarop de saldi worden " +"berekend. Omdat dit ruimte inneemt, is deze optie niet mogelijk bij " +"vergelijkingen." #. module: account #: model:ir.model,name:account.model_account_fiscalyear_close @@ -9292,6 +9424,8 @@ msgstr "De code van de rekening moet uniek zijn per firma." #: help:product.template,property_account_expense:0 msgid "This account will be used to value outgoing stock using cost price." msgstr "" +"Deze rekening dient voor de voorraadwaardering van de uitgaande voorraad op " +"basis van de kostprijs." #. module: account #: view:account.invoice:0 @@ -9354,6 +9488,17 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klik als u een nieuwe recurrente boeking wilt maken.\n" +"

\n" +" Een terugkerende boeking wordt regelmatig op een bepaald " +"tijdstip herhaald,\n" +" vb. bij vervallen van een contract of overeenkomst met een\n" +" klant of een leverancier. U kunt dergelijke boekingen " +"voorbereiden\n" +" zodat deze automatisch worden geboekt.\n" +"

\n" +" " #. module: account #: view:account.journal:0 @@ -9396,6 +9541,8 @@ msgid "" "This allows you to check writing and printing.\n" " This installs the module account_check_writing." msgstr "" +"Hiermee kunt u cheques schrijven en afdrukken.\n" +" Hiermee wordt de module account_check_writing geïnstalleerd." #. module: account #: model:res.groups,name:account.group_account_invoice @@ -9681,6 +9828,9 @@ msgid "" "chart\n" " of accounts." msgstr "" +"Bevestigde facturen kunnen niet meer\n" +" worden gewijzigd. Facturen krijgen een uniek nummer\n" +" en de boekingen worden gemaakt." #. module: account #: model:process.node,note:account.process_node_bankstatement0 @@ -9906,11 +10056,15 @@ msgid "" "payments.\n" " This installs the module account_payment." msgstr "" +"Hiermee kunt u betaalopdrachten maken\n" +" * die als basis dienen voor verdere automatisering,\n" +" * om efficiënter betalingen te kunnen uitvoeren.\n" +" Hiermee wordt de module account_payment geïnstalleerd." #. module: account #: xsl:account.transfer:0 msgid "Document" -msgstr "" +msgstr "Document" #. module: account #: view:account.chart.template:0 @@ -10132,7 +10286,7 @@ msgstr "Kan geen boekingen maken tussen verschillende firma's." #. module: account #: model:ir.ui.menu,name:account.menu_finance_periodical_processing msgid "Periodic Processing" -msgstr "" +msgstr "Periodieke verwerking" #. module: account #: view:account.invoice.report:0 @@ -10212,7 +10366,7 @@ msgstr "Vervaldatum" #: model:account.payment.term,name:account.account_payment_term_immediate #: model:account.payment.term,note:account.account_payment_term_immediate msgid "Immediate Payment" -msgstr "" +msgstr "Contante betaling" #. module: account #: code:addons/account/account.py:1464 @@ -10424,11 +10578,16 @@ msgid "" "analytic account.\n" " This installs the module account_budget." msgstr "" +"Hiermee kunnen accountants budgetten beheren.\n" +" Als de hoofdbudgetten zijn ingesteld, kunnen de " +"projectleiders\n" +" het geplande bedrag instellen per analytische rekening.\n" +" Hiermee wordt de module account_budget geïnstalleerd." #. module: account #: field:account.bank.statement.line,name:0 msgid "OBI" -msgstr "" +msgstr "Omschrijving" #. module: account #: help:res.partner,property_account_payable:0 @@ -10915,6 +11074,8 @@ msgid "" "If you unreconcile transactions, you must also verify all the actions that " "are linked to those transactions because they will not be disable" msgstr "" +"Als u afgepunte transacties ongedaan maakt, moet u alle gekoppelde acties " +"nakijken, want deze worden niet ongedaan gemaakt." #. module: account #: code:addons/account/account_move_line.py:1059 @@ -10949,6 +11110,9 @@ msgid "" "customer. The tool search can also be used to personalise your Invoices " "reports and so, match this analysis to your needs." msgstr "" +"Dit rapport biedt een overzicht van het bedrag gefactureerd aan uw klant. De " +"zoekfunctie kan worden aangepast om het overzicht van uw facturen te " +"personaliseren, zodat u de gewenste analyse krijgt." #. module: account #: view:account.partner.reconcile.process:0 @@ -11207,6 +11371,16 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Klik als u een nieuw btw-vak wilt toevoegen.\n" +"

\n" +" Afhankelijk van uw land, dient een btw-vak om uw btw-" +"aangifte in te vullen.\n" +" In OpenERP kunt u een btw-structuur instellen en elke btw-" +"berekening\n" +" kan in een of meer btw-vakken worden opgenomen.\n" +"

\n" +" " #. module: account #: selection:account.entries.report,month:0 @@ -11233,6 +11407,18 @@ msgid "" "

\n" " " msgstr "" +"

\n" +" Selecteer de periode en het journaal.\n" +"

\n" +" Hiermee kan de boekhouder in een sneltempo boekingen " +"invoeren in\n" +" OpenERP. Als u een aankoopfactuur wilt inboeken,\n" +" begint u met de kostenrekening. OpenERP stelt automatisch\n" +" de betrokken btw voor die is gekoppeld aan deze rekening, " +"net\n" +" als de centralisatierekening.\n" +"

\n" +" " #. module: account #: help:account.invoice.line,account_id:0 @@ -11403,7 +11589,7 @@ msgstr "Rekeningmodel" #: code:addons/account/account_cash_statement.py:292 #, python-format msgid "Loss" -msgstr "" +msgstr "Verlies" #. module: account #: selection:account.entries.report,month:0 @@ -11475,7 +11661,7 @@ msgstr "Kostenrekening van productsjabloon" #. module: account #: field:res.partner,property_payment_term:0 msgid "Customer Payment Term" -msgstr "" +msgstr "Betaaltermijn klant" #. module: account #: help:accounting.report,label_filter:0 diff --git a/addons/account/i18n/pt_BR.po b/addons/account/i18n/pt_BR.po index ba985c5e0a8..d1816589139 100644 --- a/addons/account/i18n/pt_BR.po +++ b/addons/account/i18n/pt_BR.po @@ -7,15 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2012-12-22 23:17+0000\n" -"Last-Translator: Fábio Martinelli - http://zupy.com.br " -"\n" +"PO-Revision-Date: 2013-04-18 17:44+0000\n" +"Last-Translator: Thiago Tognoli \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:19+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-19 05:24+0000\n" +"X-Generator: Launchpad (build 16567)\n" #. module: account #: model:process.transition,name:account.process_transition_supplierreconcilepaid0 @@ -431,7 +430,7 @@ msgstr "Data de criação" #. module: account #: selection:account.journal,type:0 msgid "Purchase Refund" -msgstr "Devolução da Venda" +msgstr "Devolução de Compra" #. module: account #: selection:account.journal,type:0 @@ -12678,13 +12677,6 @@ msgstr "" #~ msgid "This period is already closed !" #~ msgstr "Este período já está fechado" -#, python-format -#~ msgid "" -#~ "Selected Move lines does not have any account move enties in draft state" -#~ msgstr "" -#~ "As linhas do movimento selecionado nao tem nenhuma conta a ser movida para o " -#~ "estado de esboço" - #~ msgid "Unpaid Customer Refunds" #~ msgstr "Reembolsos a clientes não pagos" @@ -13232,6 +13224,13 @@ msgstr "" #~ msgid "Can not %s draft/proforma/cancel invoice." #~ msgstr "Não pode %s provisório/proforma/cancelar fatura." +#, python-format +#~ msgid "" +#~ "Selected Move lines does not have any account move enties in draft state" +#~ msgstr "" +#~ "As linhas de movimento selecionadas não tem nenhum movimento nesta conta no " +#~ "modo provisório" + #, python-format #~ msgid "Can not pay draft/proforma/cancel invoice." #~ msgstr "Não se pode pagar uma fatura provisória/proforma/cancelada" diff --git a/addons/account/i18n/zh_CN.po b/addons/account/i18n/zh_CN.po index ff84dd866e8..0e216ddb68f 100644 --- a/addons/account/i18n/zh_CN.po +++ b/addons/account/i18n/zh_CN.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2013-01-30 03:58+0000\n" -"Last-Translator: Wei \"oldrev\" Li \n" +"PO-Revision-Date: 2013-04-25 09:04+0000\n" +"Last-Translator: Oliver Yuan \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:20+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-26 05:34+0000\n" +"X-Generator: Launchpad (build 16580)\n" #. module: account #: model:process.transition,name:account.process_transition_supplierreconcilepaid0 @@ -10748,7 +10748,7 @@ msgstr "手动的发票税(非主营业务纳税)" #: code:addons/account/account_invoice.py:550 #, python-format msgid "The payment term of supplier does not have a payment term line." -msgstr "" +msgstr "供应商付款条件没有包含付款条件行" #. module: account #: field:account.account,parent_right:0 diff --git a/addons/account/installer.py b/addons/account/installer.py index c00b80071b8..537371d0f64 100644 --- a/addons/account/installer.py +++ b/addons/account/installer.py @@ -23,10 +23,16 @@ import datetime from dateutil.relativedelta import relativedelta import logging from operator import itemgetter -from os.path import join as opj import time +import urllib2 +import urlparse -from openerp import tools +try: + import simplejson as json +except ImportError: + import json # noqa + +from openerp.release import serie from openerp.tools.translate import _ from openerp.osv import fields, osv @@ -38,13 +44,28 @@ class account_installer(osv.osv_memory): def _get_charts(self, cr, uid, context=None): modules = self.pool.get('ir.module.module') + + # try get the list on apps server + try: + apps_server = self.pool.get('ir.config_parameter').get_param(cr, uid, 'apps.server', 'https://apps.openerp.com') + + up = urlparse.urlparse(apps_server) + url = '{0.scheme}://{0.netloc}/apps/charts?serie={1}'.format(up, serie) + + j = urllib2.urlopen(url, timeout=3).read() + apps_charts = json.loads(j) + + charts = dict(apps_charts) + except Exception: + charts = dict() + # Looking for the module with the 'Account Charts' category category_name, category_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'module_category_localization_account_charts') ids = modules.search(cr, uid, [('category_id', '=', category_id)], context=context) - charts = list( - sorted(((m.name, m.shortdesc) - for m in modules.browse(cr, uid, ids, context=context)), - key=itemgetter(1))) + if ids: + charts.update((m.name, m.shortdesc) for m in modules.browse(cr, uid, ids, context=context)) + + charts = sorted(charts.items(), key=itemgetter(1)) charts.insert(0, ('configurable', _('Custom'))) return charts @@ -57,9 +78,9 @@ class account_installer(osv.osv_memory): "country."), 'date_start': fields.date('Start Date', required=True), 'date_stop': fields.date('End Date', required=True), - 'period': fields.selection([('month', 'Monthly'), ('3months','3 Monthly')], 'Periods', required=True), + 'period': fields.selection([('month', 'Monthly'), ('3months', '3 Monthly')], 'Periods', required=True), 'company_id': fields.many2one('res.company', 'Company', required=True), - 'has_default_company' : fields.boolean('Has Default Company', readonly=True), + 'has_default_company': fields.boolean('Has Default Company', readonly=True), } def _default_company(self, cr, uid, context=None): @@ -78,30 +99,29 @@ class account_installer(osv.osv_memory): 'has_default_company': _default_has_default_company, 'charts': 'configurable' } - + def get_unconfigured_cmp(self, cr, uid, context=None): """ get the list of companies that have not been configured yet but don't care about the demo chart of accounts """ - cmp_select = [] company_ids = self.pool.get('res.company').search(cr, uid, [], context=context) cr.execute("SELECT company_id FROM account_account WHERE active = 't' AND account_account.parent_id IS NULL AND name != %s", ("Chart For Automated Tests",)) configured_cmp = [r[0] for r in cr.fetchall()] return list(set(company_ids)-set(configured_cmp)) - + def check_unconfigured_cmp(self, cr, uid, context=None): """ check if there are still unconfigured companies """ if not self.get_unconfigured_cmp(cr, uid, context=context): raise osv.except_osv(_('No unconfigured company !'), _("There is currently no company without chart of account. The wizard will therefore not be executed.")) - + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - if context is None:context = {} - res = super(account_installer, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False) + if context is None: context = {} + res = super(account_installer, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False) cmp_select = [] # display in the widget selection only the companies that haven't been configured yet unconfigured_cmp = self.get_unconfigured_cmp(cr, uid, context=context) for field in res['fields']: if field == 'company_id': - res['fields'][field]['domain'] = [('id','in',unconfigured_cmp)] + res['fields'][field]['domain'] = [('id', 'in', unconfigured_cmp)] res['fields'][field]['selection'] = [('', '')] if unconfigured_cmp: cmp_select = [(line.id, line.name) for line in self.pool.get('res.company').browse(cr, uid, unconfigured_cmp)] @@ -117,7 +137,7 @@ class account_installer(osv.osv_memory): def execute(self, cr, uid, ids, context=None): self.execute_simple(cr, uid, ids, context) - super(account_installer, self).execute(cr, uid, ids, context=context) + return super(account_installer, self).execute(cr, uid, ids, context=context) def execute_simple(self, cr, uid, ids, context=None): if context is None: @@ -129,8 +149,8 @@ class account_installer(osv.osv_memory): if not f_ids: name = code = res['date_start'][:4] if int(name) != int(res['date_stop'][:4]): - name = res['date_start'][:4] +'-'+ res['date_stop'][:4] - code = res['date_start'][2:4] +'-'+ res['date_stop'][2:4] + name = res['date_start'][:4] + '-' + res['date_stop'][:4] + code = res['date_start'][2:4] + '-' + res['date_stop'][2:4] vals = { 'name': name, 'code': code, @@ -150,8 +170,7 @@ class account_installer(osv.osv_memory): chart = self.read(cr, uid, ids, ['charts'], context=context)[0]['charts'] _logger.debug('Installing chart of accounts %s', chart) - return modules | set([chart]) + return (modules | set([chart])) - set(['has_default_company', 'configurable']) -account_installer() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/ir_sequence.py b/addons/account/ir_sequence.py index 56e1a4d0367..d3615a847b7 100644 --- a/addons/account/ir_sequence.py +++ b/addons/account/ir_sequence.py @@ -38,7 +38,6 @@ class ir_sequence_fiscalyear(osv.osv): 'Main Sequence must be different from current !'), ] -ir_sequence_fiscalyear() class ir_sequence(osv.osv): _inherit = 'ir.sequence' @@ -56,6 +55,5 @@ class ir_sequence(osv.osv): return super(ir_sequence, self)._next(cr, uid, [line.sequence_id.id], context) return super(ir_sequence, self)._next(cr, uid, seq_ids, context) -ir_sequence() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/partner.py b/addons/account/partner.py index ae8d2fc1871..f582da6b765 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -66,7 +66,6 @@ class account_fiscal_position(osv.osv): break return account_id -account_fiscal_position() class account_fiscal_position_tax(osv.osv): _name = 'account.fiscal.position.tax' @@ -84,7 +83,6 @@ class account_fiscal_position_tax(osv.osv): 'A tax fiscal position could be defined only once time on same taxes.') ] -account_fiscal_position_tax() class account_fiscal_position_account(osv.osv): _name = 'account.fiscal.position.account' @@ -102,7 +100,6 @@ class account_fiscal_position_account(osv.osv): 'An account fiscal position could be defined only once time on same accounts.') ] -account_fiscal_position_account() class res_partner(osv.osv): _name = 'res.partner' @@ -236,6 +233,10 @@ class res_partner(osv.osv): 'last_reconciliation_date': fields.datetime('Latest Full Reconciliation Date', help='Date on which the partner accounting entries were fully reconciled last time. It differs from the last date where a reconciliation has been made for this partner, as here we depict the fact that nothing more was to be reconciled at this date. This can be achieved in 2 different ways: either the last unreconciled debit/credit entry of this partner was reconciled, either the user pressed the button "Nothing more to reconcile" during the manual reconciliation process.') } -res_partner() + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + \ + ['debit_limit', 'property_account_payable', 'property_account_receivable', 'property_account_position', + 'property_payment_term', 'property_supplier_payment_term', 'last_reconciliation_date'] + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml index 1267e20165b..b6fae3140b5 100644 --- a/addons/account/partner_view.xml +++ b/addons/account/partner_view.xml @@ -50,6 +50,29 @@
+ + {'search_default_partner_id': [active_id], 'default_partner_id': active_id} + Contracts/Analytic Accounts + account.analytic.account + tree,form + + + partner.view.buttons + res.partner + + + + + + +
+ +
+
+
+
+ + + + +
+
+ +
+
+
+ +
+
+
+
+
+
+ \ No newline at end of file diff --git a/addons/im_livechat/__init__.py b/addons/im_livechat/__init__.py new file mode 100644 index 00000000000..2825a75179c --- /dev/null +++ b/addons/im_livechat/__init__.py @@ -0,0 +1,2 @@ + +import im_livechat diff --git a/addons/im_livechat/__openerp__.py b/addons/im_livechat/__openerp__.py new file mode 100644 index 00000000000..8b9ea3924d5 --- /dev/null +++ b/addons/im_livechat/__openerp__.py @@ -0,0 +1,29 @@ +{ + 'name' : 'Live Support', + 'version': '1.0', + 'summary': 'Live Chat with Visitors/Customers', + 'category': 'Tools', + 'complexity': 'easy', + 'description': + """ +Live Chat Support +================= + +Allow to drop instant messaging widgets on any web page that will communicate +with the current server and dispatch visitors request amongst several live +chat operators. + + """, + 'data': [ + "security/im_livechat_security.xml", + "security/ir.model.access.csv", + "im_livechat_view.xml", + ], + 'demo': [ + "im_livechat_demo.xml", + ], + 'depends' : ["im", "mail", "portal_anonymous"], + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/addons/im_livechat/im_livechat.py b/addons/im_livechat/im_livechat.py new file mode 100644 index 00000000000..1ffb422fe6a --- /dev/null +++ b/addons/im_livechat/im_livechat.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import openerp +import openerp.addons.im.im as im +import json +import random +import jinja2 +from openerp.osv import osv, fields +from openerp import tools + +env = jinja2.Environment( + loader=jinja2.PackageLoader('openerp.addons.im_livechat', "."), + autoescape=False +) +env.filters["json"] = json.dumps + +class LiveChatController(openerp.addons.web.http.Controller): + _cp_path = '/im_livechat' + + @openerp.addons.web.http.httprequest + def loader(self, req, **kwargs): + p = json.loads(kwargs["p"]) + db = p["db"] + channel = p["channel"] + user_name = p.get("user_name", None) + req.session._db = db + req.session._uid = None + req.session._login = "anonymous" + req.session._password = "anonymous" + info = req.session.model('im_livechat.channel').get_info_for_chat_src(channel) + info["db"] = db + info["channel"] = channel + info["userName"] = user_name + return req.make_response(env.get_template("loader.js").render(info), + headers=[('Content-Type', "text/javascript")]) + + @openerp.addons.web.http.httprequest + def web_page(self, req, **kwargs): + p = json.loads(kwargs["p"]) + db = p["db"] + channel = p["channel"] + req.session._db = db + req.session._uid = None + req.session._login = "anonymous" + req.session._password = "anonymous" + script = req.session.model('im_livechat.channel').read(channel, ["script"])["script"] + info = req.session.model('im_livechat.channel').get_info_for_chat_src(channel) + info["script"] = script + return req.make_response(env.get_template("web_page.html").render(info), + headers=[('Content-Type', "text/html")]) + + @openerp.addons.web.http.jsonrequest + def available(self, req, db, channel): + req.session._db = db + req.session._uid = None + req.session._login = "anonymous" + req.session._password = "anonymous" + return req.session.model('im_livechat.channel').get_available_user(channel) > 0 + +class im_livechat_channel(osv.osv): + _name = 'im_livechat.channel' + + def _get_default_image(self, cr, uid, context=None): + image_path = openerp.modules.get_module_resource('im_livechat', 'static/src/img', 'default.png') + return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) + def _get_image(self, cr, uid, ids, name, args, context=None): + result = dict.fromkeys(ids, False) + for obj in self.browse(cr, uid, ids, context=context): + result[obj.id] = tools.image_get_resized_images(obj.image) + return result + def _set_image(self, cr, uid, id, name, value, args, context=None): + return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) + + + def _are_you_inside(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = False + for user in record.user_ids: + if user.id == uid: + res[record.id] = True + break + return res + + def _script(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = env.get_template("include.html").render({ + "url": self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url'), + "parameters": {"db":cr.dbname, "channel":record.id}, + }) + return res + + def _web_page(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + \ + "/im_livechat/web_page?p=" + json.dumps({"db":cr.dbname, "channel":record.id}) + return res + + _columns = { + 'name': fields.char(string="Channel Name", size=200, required=True), + 'user_ids': fields.many2many('res.users', 'im_livechat_channel_im_user', 'channel_id', 'user_id', string="Users"), + 'are_you_inside': fields.function(_are_you_inside, type='boolean', string='Are you inside the matrix?', store=False), + 'script': fields.function(_script, type='text', string='Script', store=False), + 'web_page': fields.function(_web_page, type='url', string='Web Page', store=False, size="200"), + 'button_text': fields.char(string="Text of the Button", size=200), + 'input_placeholder': fields.char(string="Chat Input Placeholder", size=200), + 'default_message': fields.char(string="Welcome Message", size=200, help="This is an automated 'welcome' message that your visitor will see when they initiate a new chat session."), + # image: all image fields are base64 encoded and PIL-supported + 'image': fields.binary("Photo", + help="This field holds the image used as photo for the group, limited to 1024x1024px."), + 'image_medium': fields.function(_get_image, fnct_inv=_set_image, + string="Medium-sized photo", type="binary", multi="_get_image", + store={ + 'im_livechat.channel': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), + }, + help="Medium-sized photo of the group. It is automatically "\ + "resized as a 128x128px image, with aspect ratio preserved. "\ + "Use this field in form views or some kanban views."), + 'image_small': fields.function(_get_image, fnct_inv=_set_image, + string="Small-sized photo", type="binary", multi="_get_image", + store={ + 'im_livechat.channel': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), + }, + help="Small-sized photo of the group. It is automatically "\ + "resized as a 64x64px image, with aspect ratio preserved. "\ + "Use this field anywhere a small image is required."), + } + + def _default_user_ids(self, cr, uid, context=None): + return [(6, 0, [uid])] + + _defaults = { + 'button_text': "Have a Question? Chat with us.", + 'input_placeholder': "How may I help you?", + 'default_message': '', + 'user_ids': _default_user_ids, + 'image': _get_default_image, + } + + def get_available_user(self, cr, uid, channel_id, context=None): + channel = self.browse(cr, openerp.SUPERUSER_ID, channel_id, context=context) + users = [] + for user in channel.user_ids: + iuid = self.pool.get("im.user").get_by_user_id(cr, uid, user.id, context=context)["id"] + imuser = self.pool.get("im.user").browse(cr, uid, iuid, context=context) + if imuser.im_status: + users.append(imuser) + if len(users) == 0: + return False + return random.choice(users).id + + def test_channel(self, cr, uid, channel, context=None): + if not channel: + return {} + return { + 'url': self.browse(cr, uid, channel[0], context=context or {}).web_page, + 'type': 'ir.actions.act_url' + } + + def get_info_for_chat_src(self, cr, uid, channel, context=None): + url = self.pool.get('ir.config_parameter').get_param(cr, openerp.SUPERUSER_ID, 'web.base.url') + chan = self.browse(cr, uid, channel, context=context) + return { + "url": url, + 'buttonText': chan.button_text, + 'inputPlaceholder': chan.input_placeholder, + 'defaultMessage': chan.default_message, + "channelName": chan.name, + } + + def join(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'user_ids': [(4, uid)]}) + return True + + def quit(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'user_ids': [(3, uid)]}) + return True + + +class im_message(osv.osv): + _inherit = 'im.message' + + def _support_member(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = False + if record.to_id.user and record.from_id.user: + continue + elif record.to_id.user: + res[record.id] = record.to_id.user.id + elif record.from_id.user: + res[record.id] = record.from_id.user.id + return res + + def _customer(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = False + if record.to_id.uuid and record.from_id.uuid: + continue + elif record.to_id.uuid: + res[record.id] = record.to_id.id + elif record.from_id.uuid: + res[record.id] = record.from_id.id + return res + + def _direction(self, cr, uid, ids, name, arg, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = False + if not not record.to_id.user and not not record.from_id.user: + continue + elif not not record.to_id.user: + res[record.id] = "c2s" + elif not not record.from_id.user: + res[record.id] = "s2c" + return res + + _columns = { + 'support_member_id': fields.function(_support_member, type='many2one', relation='res.users', string='Support Member', store=True, select=True), + 'customer_id': fields.function(_customer, type='many2one', relation='im.user', string='Customer', store=True, select=True), + 'direction': fields.function(_direction, type="selection", selection=[("s2c", "Support Member to Customer"), ("c2s", "Customer to Support Member")], + string='Direction', store=False), + } diff --git a/addons/im_livechat/im_livechat_demo.xml b/addons/im_livechat/im_livechat_demo.xml new file mode 100644 index 00000000000..f199179fb50 --- /dev/null +++ b/addons/im_livechat/im_livechat_demo.xml @@ -0,0 +1,15 @@ + + + + + + YourWebsite.com + Hello, how may I help you? + + + + + + + + diff --git a/addons/im_livechat/im_livechat_view.xml b/addons/im_livechat/im_livechat_view.xml new file mode 100644 index 00000000000..f684fd561fc --- /dev/null +++ b/addons/im_livechat/im_livechat_view.xml @@ -0,0 +1,146 @@ + + + + + + + Live Chat Channels + im_livechat.channel + kanban,form + +

+ Click to define a new live chat channel. +

+ You can create channels for each website on which you want + to integrate the live chat widget, allowing you website + visitors to talk in real time with your operators. +

+ Each channel has it's own URL that you can send by email to + your customers in order to start chatting with you. +

+
+
+ + + + + support_channel.kanban + im_livechat.channel + + + + + + + + +
+ +
+
+

+ +
+ + +
+
+
+
+
+
+
+ + + + support_channel.form + im_livechat.channel + +
+ + +
+
+ + + + + + + + +
+ X +
+ +

+
+
+
+
+
+
+
+ + + + + +
+ +
+ +

+ Copy and paste this code into your website, within the &lt;head&gt; tag: +

+ +

+ or copy this url and send it by email to your customers or suppliers: +

+ +
+ +
+
+
+
+ + + History + im.message + list + ["|", ('to_id.user', '=', None), ('from_id.user', '=', None)] + + + + + im.message.tree + im.message + + + + + + + + + + + +
+
diff --git a/addons/im_livechat/include.html b/addons/im_livechat/include.html new file mode 100644 index 00000000000..55de6212361 --- /dev/null +++ b/addons/im_livechat/include.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/addons/im_livechat/loader.js b/addons/im_livechat/loader.js new file mode 100644 index 00000000000..669591b410f --- /dev/null +++ b/addons/im_livechat/loader.js @@ -0,0 +1,24 @@ + +require.config({ + context: "oelivesupport", + baseUrl: {{url | json}} + "/im_livechat/static/ext/static/js", + shim: { + underscore: { + init: function() { + return _.noConflict(); + }, + }, + "jquery.achtung": { + deps: ['jquery'], + }, + }, +})(["livesupport", "jquery"], function(livesupport, jQuery) { + jQuery.noConflict(); + livesupport.main({{url | json}}, {{db | json}}, "anonymous", "anonymous", {{channel | json}}, { + buttonText: {{buttonText | json}}, + inputPlaceholder: {{inputPlaceholder | json}}, + defaultMessage: {{(defaultMessage or None) | json}}, + auto: window.oe_im_livechat_auto || false, + userName: {{userName | json}} || undefined, + }); +}); diff --git a/addons/im_livechat/security/im_livechat_security.xml b/addons/im_livechat/security/im_livechat_security.xml new file mode 100644 index 00000000000..5ad020a8a78 --- /dev/null +++ b/addons/im_livechat/security/im_livechat_security.xml @@ -0,0 +1,36 @@ + + + + + + Live Support + + + + + User + + The user will be able to join support channels. + + + + Manager + The user will be able to delete support channels. + + + + + + + Live Support Managers can read messages from live support + + + ["|", ('to_id.user', '=', None), ('from_id.user', '=', None)] + + + + + + + + diff --git a/addons/im_livechat/security/ir.model.access.csv b/addons/im_livechat/security/ir.model.access.csv new file mode 100644 index 00000000000..6e17c1a127f --- /dev/null +++ b/addons/im_livechat/security/ir.model.access.csv @@ -0,0 +1,6 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ls_chann1,im_livechat.channel,model_im_livechat_channel,,1,0,0,0 +access_ls_chann2,im_livechat.channel,model_im_livechat_channel,group_im_livechat,1,1,1,0 +access_ls_chann3,im_livechat.channel,model_im_livechat_channel,group_im_livechat_manager,1,1,1,1 +access_ls_message,im_livechat.im.message,im.model_im_message,portal.group_anonymous,0,0,0,0 +access_im_user,im_livechat.im.user,im.model_im_user,portal.group_anonymous,1,0,0,0 \ No newline at end of file diff --git a/addons/im_livechat/static/ext/Makefile b/addons/im_livechat/static/ext/Makefile new file mode 100644 index 00000000000..b73ca4f2b89 --- /dev/null +++ b/addons/im_livechat/static/ext/Makefile @@ -0,0 +1,3 @@ + +static/js/livesupport_templates.js: static/js/livesupport_templates.html + python static/js/to_jsonp.py static/js/livesupport_templates.html oe_livesupport_templates_callback > static/js/livesupport_templates.js \ No newline at end of file diff --git a/addons/im_livechat/static/ext/static/audio/Ting.mp3 b/addons/im_livechat/static/ext/static/audio/Ting.mp3 new file mode 100644 index 00000000000..6fd090a89ce Binary files /dev/null and b/addons/im_livechat/static/ext/static/audio/Ting.mp3 differ diff --git a/addons/im_livechat/static/ext/static/audio/Ting.ogg b/addons/im_livechat/static/ext/static/audio/Ting.ogg new file mode 100644 index 00000000000..8d17ea85bd3 Binary files /dev/null and b/addons/im_livechat/static/ext/static/audio/Ting.ogg differ diff --git a/addons/im_livechat/static/ext/static/css/livesupport.css b/addons/im_livechat/static/ext/static/css/livesupport.css new file mode 100644 index 00000000000..bfe26b5e838 --- /dev/null +++ b/addons/im_livechat/static/ext/static/css/livesupport.css @@ -0,0 +1,190 @@ + + + +.openerp_style { /* base style of openerp */ + font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif; + color: #4c4c4c; + font-size: 13px; + background: white; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); +} + +/* button */ + +.oe_chat_button { + position: fixed; + bottom: 0px; + right: 6px; + display: inline-block; + min-width: 100px; + background-color: rgba(60, 60, 60, 0.6); + font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, Verdana, sans-serif; + font-size: 14px; + font-weight: bold; + padding: 10px; + color: white; + text-shadow: rgb(59, 76, 88) 1px 1px 0px; + border: 1px solid rgb(80, 80, 80); + border-bottom: 0px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + cursor: pointer; +} + +/* conversations */ + +.oe_im_chatview { + position: fixed; + overflow: hidden; + bottom: 42px; + margin-right: 6px; + background: rgba(60, 60, 60, 0.8); + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: 0 0 3px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.3); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3); + width: 240px; +} +.oe_im_chatview .oe_im_chatview_disconnected { + display:none; + z-index: 100; + width: 100%; + background: #E8EBEF; + padding: 5px; + font-size: 11px; + color: #999; + line-height: 14px; + height: 28px; + overflow: hidden; +} +.oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_disconnected { + display: block; +} +.oe_im_chatview .oe_im_chatview_header { + padding: 3px 6px 2px; + background: #DEDEDE; + background: -moz-linear-gradient(#FCFCFC, #DEDEDE); + background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE)); + -moz-border-radius: 3px 3px 0 0; + -webkit-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; + border-bottom: 1px solid #AEB9BD; + cursor: pointer; +} +.oe_im_chatview .oe_im_chatview_close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + font-size: 18px; + line-height: 16px; + float: right; + font-weight: bold; + color: black; + text-shadow: 0 1px 0 white; + opacity: 0.2; +} +.oe_im_chatview .oe_im_chatview_content { + overflow: auto; + height: 287px; + width: 240px; +} +.oe_im_chatview.oe_im_chatview_disconnected_status .oe_im_chatview_content { + height: 249px; +} +.oe_im_chatview .oe_im_chatview_footer { + position: relative; + padding: 3px; + border-top: 1px solid #AEB9BD; + background: #DEDEDE; + background: -moz-linear-gradient(#FCFCFC, #DEDEDE); + background: -webkit-gradient(linear, left top, left bottom, from(#FCFCFC), to(#DEDEDE)); + -moz-border-radius: 0 0 3px 3px; + -webkit-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.oe_im_chatview .oe_im_chatview_input { + width: 222px; + font-family: Lato, Helvetica, sans-serif; + font-size: 13px; + color: #333; + padding: 1px 5px; + border: 1px solid #AEB9BD; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + -moz-box-shadow: inset 0 1px 4px rgba(0,0,0,0.2); + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.2); +} +.oe_im_chatview .oe_im_chatview_bubble { + background: white; + position: relative; + padding: 3px; + margin: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} +.oe_im_chatview .oe_im_chatview_clip { + position: relative; + float: left; + width: 26px; + height: 26px; + margin-right: 4px; + -moz-box-shadow: 0 0 2px 1px rgba(0,0,0,0.25); + -webkit-box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); + box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.25); +} +.oe_im_chatview .oe_im_chatview_avatar { + float: left; + width: 26px; + height: auto; + clip: rect(0, 26px, 26px, 0); + max-width: 100%; + width: auto 9; + height: auto; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} +.oe_im_chatview .oe_im_chatview_time { + position: absolute; + right: 0px; + top: 0px; + margin: 3px; + text-align: right; + line-height: 13px; + font-size: 11px; + color: #999; + width: 60px; + overflow: hidden; +} +.oe_im_chatview .oe_im_chatview_from { + margin: 0 0 2px 0; + line-height: 14px; + font-weight: bold; + font-size: 12px; + width: 140px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + color: #3A87AD; +} +.oe_im_chatview .oe_im_chatview_bubble_list { +} +.oe_im_chatview .oe_im_chatview_bubble_item { + margin: 0 0 2px 30px; + line-height: 14px; + word-wrap: break-word; +} + +.oe_im_chatview_online { + display: none; + margin-top: -4px; + width: 11px; + height: 11px; +} diff --git a/addons/im_livechat/static/ext/static/img/avatar/avatar.jpeg b/addons/im_livechat/static/ext/static/img/avatar/avatar.jpeg new file mode 100644 index 00000000000..7168794022e Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/avatar/avatar.jpeg differ diff --git a/addons/im_livechat/static/ext/static/img/button-gloss.png b/addons/im_livechat/static/ext/static/img/button-gloss.png new file mode 100755 index 00000000000..6f3957702fe Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/button-gloss.png differ diff --git a/addons/im_livechat/static/ext/static/img/glyphicons-halflings-white.png b/addons/im_livechat/static/ext/static/img/glyphicons-halflings-white.png new file mode 100755 index 00000000000..3bf6484a29d Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/glyphicons-halflings-white.png differ diff --git a/addons/im_livechat/static/ext/static/img/glyphicons-halflings.png b/addons/im_livechat/static/ext/static/img/glyphicons-halflings.png new file mode 100755 index 00000000000..a9969993201 Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/glyphicons-halflings.png differ diff --git a/addons/im_livechat/static/ext/static/img/green.png b/addons/im_livechat/static/ext/static/img/green.png new file mode 100644 index 00000000000..01fb373c251 Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/green.png differ diff --git a/addons/im_livechat/static/ext/static/img/logo.png b/addons/im_livechat/static/ext/static/img/logo.png new file mode 100644 index 00000000000..aca5f4c60d8 Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/logo.png differ diff --git a/addons/im_livechat/static/ext/static/img/wood.png b/addons/im_livechat/static/ext/static/img/wood.png new file mode 100644 index 00000000000..22f2450d3ad Binary files /dev/null and b/addons/im_livechat/static/ext/static/img/wood.png differ diff --git a/addons/im_livechat/static/ext/static/js/jquery.achtung.css b/addons/im_livechat/static/ext/static/js/jquery.achtung.css new file mode 100644 index 00000000000..820ae3e9194 --- /dev/null +++ b/addons/im_livechat/static/ext/static/js/jquery.achtung.css @@ -0,0 +1,306 @@ +/** + * achtung 0.3.0 + * + * Growl-like notifications for jQuery + * + * Copyright (c) 2009 Josh Varner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Portions of this file are from the jQuery UI CSS framework. + * + * @license http://www.opensource.org/licenses/mit-license.php + * @author Josh Varner + */ + +/* IE 6 doesn't support position: fixed */ +* html #achtung-overlay { + position:absolute; +} + +/* IE6 includes padding in width */ +* html .achtung { + width: 280px; +} + +#achtung-overlay { + overflow: hidden; + position: fixed; + top: 15px; + right: 15px; + width: 280px; + z-index:50; +} + +.achtung { + display:none; + margin-bottom: 8px; + padding: 15px 15px; + background-color: #000; + color: white; + width: 250px; + font-weight: bold; + position:relative; + overflow: hidden; + -moz-box-shadow: #aaa 1px 1px 2px; + -webkit-box-shadow: #aaa 1px 1px 2px; + box-shadow: #aaa 1px 1px 2px; + -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; + /* Note that if using show/hide animations, IE will lose + this setting */ + opacity: .85; + filter:Alpha(Opacity=85); +} + +/** + * This section from jQuery UI CSS framework + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) + * Can (and should) be removed if you are already loading the jQuery UI CSS + * to reduce payload size. + */ +.ui-icon { display: block; overflow: hidden; background-repeat: no-repeat; } +.ui-icon { width: 16px; height: 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + +.achtung .achtung-message-icon { + margin-top: 0px; + margin-left: -.5em; + margin-right: .5em; + float: left; + zoom: 1; +} + +.achtung .ui-icon.achtung-close-button { + float: right; + margin-right: -8px; + margin-top: -12px; + cursor: pointer; + color: white; + text-align: right; +} + +.achtung .ui-icon.achtung-close-button:after { + content: "x" +} + +/* Slightly darker for these colors (readability) */ +.achtungSuccess, .achtungFail, .achtungWait { + /* Note that if using show/hide animations, IE will lose + this setting */ + opacity: .93; filter:Alpha(Opacity=93); +} + +.achtungSuccess { + background-color: #4DB559; +} + +.achtungFail { + background-color: #D64450; +} + +.achtungWait { + background-color: #658093; +} + +.achtungSuccess .ui-icon.achtung-close-button, +.achtungFail .ui-icon.achtung-close-button { +} + +.achtungSuccess .ui-icon.achtung-close-button-hover, +.achtungFail .ui-icon.achtung-close-button-hover { +} + +.achtung .wait-icon { +} + +.achtung .achtung-message { + display: inline; +} diff --git a/addons/im_livechat/static/ext/static/js/jquery.achtung.js b/addons/im_livechat/static/ext/static/js/jquery.achtung.js new file mode 100644 index 00000000000..1aa69469c1d --- /dev/null +++ b/addons/im_livechat/static/ext/static/js/jquery.achtung.js @@ -0,0 +1,273 @@ +/** + * achtung 0.3.0 + * + * Growl-like notifications for jQuery + * + * Copyright (c) 2009 Josh Varner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @license http://www.opensource.org/licenses/mit-license.php + * @author Josh Varner + */ + +/*globals jQuery,clearTimeout,document,navigator,setTimeout +*/ +(function($) { + +/** + * This is based on the jQuery UI $.widget code. I would have just made this + * a $.widget but I didn't want the jQuery UI dependency. + */ +$.fn.achtung = function(options) +{ + var isMethodCall = (typeof options === 'string'), + args = Array.prototype.slice.call(arguments, 0), + name = 'achtung'; + + // handle initialization and non-getter methods + return this.each(function() { + var instance = $.data(this, name); + + // prevent calls to internal methods + if (isMethodCall && options.substring(0, 1) === '_') { + return this; + } + + // constructor + (!instance && !isMethodCall && + $.data(this, name, new $.achtung(this))._init(args)); + + // method call + (instance && isMethodCall && $.isFunction(instance[options]) && + instance[options].apply(instance, args.slice(1))); + }); +}; + +$.achtung = function(element) +{ + var args = Array.prototype.slice.call(arguments, 0), $el; + + if (!element || !element.nodeType) { + $el = $('
'); + return $el.achtung.apply($el, args); + } + + this.$container = $(element); +}; + + +/** + * Static members + **/ +$.extend($.achtung, { + version: '0.3.0', + $overlay: false, + defaults: { + timeout: 10, + disableClose: false, + icon: false, + className: '', + animateClassSwitch: false, + showEffects: {'opacity':'toggle','height':'toggle'}, + hideEffects: {'opacity':'toggle','height':'toggle'}, + showEffectDuration: 500, + hideEffectDuration: 700 + } +}); + +/** + * Non-static members + **/ +$.extend($.achtung.prototype, { + $container: false, + closeTimer: false, + options: {}, + + _init: function(args) + { + var o, self = this; + + args = $.isArray(args) ? args : []; + + + args.unshift($.achtung.defaults); + args.unshift({}); + + o = this.options = $.extend.apply($, args); + + if (!$.achtung.$overlay) { + $.achtung.$overlay = $('
').appendTo(document.body); + } + + if (!o.disableClose) { + $('') + .click(function () { self.close(); }) + .hover(function () { $(this).addClass('achtung-close-button-hover'); }, + function () { $(this).removeClass('achtung-close-button-hover'); }) + .prependTo(this.$container); + } + + this.changeIcon(o.icon, true); + + if (o.message) { + this.$container.append($('' + o.message + '')); + } + + (o.className && this.$container.addClass(o.className)); + (o.css && this.$container.css(o.css)); + + this.$container + .addClass('achtung') + .appendTo($.achtung.$overlay); + + if (o.showEffects) { + this.$container.toggle(); + } else { + this.$container.show(); + } + + if (o.timeout > 0) { + this.timeout(o.timeout); + } + }, + + timeout: function(timeout) + { + var self = this; + + if (this.closeTimer) { + clearTimeout(this.closeTimer); + } + + this.closeTimer = setTimeout(function() { self.close(); }, timeout * 1000); + this.options.timeout = timeout; + }, + + /** + * Change the CSS class associated with this message, using + * a transition if available (not availble in Safari/Webkit). + * If no transition is available, the switch is immediate. + * + * #LATER Check if this has been corrected in Webkit or jQuery UI + * #TODO Make transition time configurable + * @param newClass string Name of new class to associate + */ + changeClass: function(newClass) + { + var self = this; + + if (this.options.className === newClass) { + return; + } + + this.$container.queue(function() { + if (!self.options.animateClassSwitch || + /webkit/.test(navigator.userAgent.toLowerCase()) || + !$.isFunction($.fn.switchClass)) { + self.$container.removeClass(self.options.className); + self.$container.addClass(newClass); + } else { + self.$container.switchClass(self.options.className, newClass, 500); + } + + self.options.className = newClass; + self.$container.dequeue(); + }); + }, + + changeIcon: function(newIcon, force) + { + var self = this; + + if ((force !== true || newIcon === false) && this.options.icon === newIcon) { + return; + } + + if (force || this.options.icon === false) { + this.$container.prepend($('')); + this.options.icon = newIcon; + return; + } else if (newIcon === false) { + this.$container.find('.achtung-message-icon').remove(); + this.options.icon = false; + return; + } + + this.$container.queue(function() { + var $span = $('.achtung-message-icon', self.$container); + + if (!self.options.animateClassSwitch || + /webkit/.test(navigator.userAgent.toLowerCase()) || + !$.isFunction($.fn.switchClass)) { + $span.removeClass(self.options.icon); + $span.addClass(newIcon); + } else { + $span.switchClass(self.options.icon, newIcon, 500); + } + + self.options.icon = newIcon; + self.$container.dequeue(); + }); + }, + + + changeMessage: function(newMessage) + { + this.$container.queue(function() { + $('.achtung-message', $(this)).html(newMessage); + $(this).dequeue(); + }); + }, + + + update: function(options) + { + (options.className && this.changeClass(options.className)); + (options.css && this.$container.css(options.css)); + (typeof(options.icon) !== 'undefined' && this.changeIcon(options.icon)); + (options.message && this.changeMessage(options.message)); + (options.timeout && this.timeout(options.timeout)); + }, + + close: function() + { + var o = this.options, $container = this.$container; + + if (o.hideEffects) { + this.$container.animate(o.hideEffects, o.hideEffectDuration); + } else { + this.$container.hide(); + } + + $container.queue(function() { + $container.removeData('achtung'); + $container.remove(); + + if ($.achtung.$overlay && $.achtung.$overlay.is(':empty')) { + $.achtung.$overlay.remove(); + $.achtung.$overlay = false; + } + + $container.dequeue(); + }); + } +}); + +})(jQuery); \ No newline at end of file diff --git a/addons/im_livechat/static/ext/static/js/jquery.js b/addons/im_livechat/static/ext/static/js/jquery.js new file mode 100644 index 00000000000..ded03845983 --- /dev/null +++ b/addons/im_livechat/static/ext/static/js/jquery.js @@ -0,0 +1,9555 @@ +/*! + * jQuery JavaScript Library v1.9.0 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-1-14 + */ +(function( window, undefined ) { +"use strict"; +var + // A central reference to the root jQuery(document) + rootjQuery, + + // The deferred used on DOM ready + readyList, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.9.0", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler and self cleanup method + DOMContentLoaded = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + } else if ( document.readyState === "complete" ) { + // we're here because readyState === "complete" in oldIE + // which is good enough for us to call the dom ready! + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + return jQuery.inArray( fn, list ) > -1; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, all, a, select, opt, input, fragment, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + checkOn: !!input.value, + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: document.compatMode === "CSS1Compat", + + // Will be defined later + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + body.style.zoom = 1; + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})(); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt /* For internal use only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data, false ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name, false ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + // Try to fetch any internally stored data first + return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + } + + this.each(function() { + jQuery.data( this, key, value ); + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + hooks.cur = fn; + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + // Toggle whole class name + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + // In IE9+, Flash objects don't have .getAttribute (#12945) + // Support: IE9+ + if ( typeof elem.getAttribute !== "undefined" ) { + ret = elem.getAttribute( name ); + } + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( rboolean.test( name ) ) { + // Set corresponding property to false for boolean attributes + // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8 + if ( !getSetAttribute && ruseDefault.test( name ) ) { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } else { + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + var + // Use .prop to determine if this attribute is understood as boolean + prop = jQuery.prop( elem, name ), + + // Fetch it accordingly + attr = typeof prop === "boolean" && elem.getAttribute( name ), + detail = typeof prop === "boolean" ? + + getSetInput && getSetAttribute ? + attr != null : + // oldIE fabricates an empty string for missing boolean attributes + // and conflates checked/selected into attroperties + ruseDefault.test( name ) ? + elem[ jQuery.camelCase( "default-" + name ) ] : + !!attr : + + // fetch an attribute node for properties not recognized as boolean + elem.getAttributeNode( name ); + + return detail && detail.value !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; + +// fix oldIE value attroperty +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return jQuery.nodeName( elem, "input" ) ? + + // Ignore the value *property* by using defaultValue + elem.defaultValue : + + ret && ret.specified ? ret.value : undefined; + }, + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret == null ? undefined : ret; + } + }); + }); + + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + // Don't attach events to noData or text/comment nodes (but allow plain objects) + elemData = elem.nodeType !== 3 && elem.nodeType !== 8 && jQuery._data( elem ); + + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = event.type || event, + namespaces = event.namespace ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + event.isTrigger = true; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur != this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + } + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== document.activeElement && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === document.activeElement && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var i, + cachedruns, + Expr, + getText, + isXML, + compile, + hasDuplicate, + outermostContext, + + // Local document vars + setDocument, + document, + docElem, + documentIsXML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + sortOrder, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + support = {}, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Array methods + arr = [], + pop = arr.pop, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rsibling = /[\x20\t\r\n\f]*[+~]/, + + rnative = /\{\s*\[native code\]\s*\}/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g, + funescape = function( _, escaped ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + return high !== high ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Use a stripped-down slice if we can't use a native one +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +/** + * For feature detection + * @param {Function} fn The function to test for native support + */ +function isNative( fn ) { + return rnative.test( fn + "" ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var cache, + keys = []; + + return (cache = function( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + }); +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( !documentIsXML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + + // QSA path + if ( support.qsa && !rbuggyQSA.test(selector) ) { + old = true; + nid = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsXML = isXML( doc ); + + // Check if getElementsByTagName("*") returns only elements + support.tagNameNoComments = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if attributes should be retrieved by attribute nodes + support.attributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }); + + // Check if getElementsByClassName can be trusted + support.getByClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }); + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + support.getByName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = doc.getElementsByName && + // buggy browsers will return fewer than the correct 2 + doc.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + doc.getElementsByName( expando + 0 ).length; + support.getIdNotName = !doc.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + + // IE6/7 return modified attributes + Expr.attrHandle = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }) ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }; + + // ID find and filter + if ( support.getIdNotName ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.tagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Name + Expr.find["NAME"] = support.getByName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }; + + // Class + Expr.find["CLASS"] = support.getByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) { + return context.getElementsByClassName( className ); + } + }; + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21), + // no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ]; + + if ( (support.qsa = isNative(doc.querySelectorAll)) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE8 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = ""; + if ( div.querySelectorAll("[i^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = new RegExp( rbuggyMatches.join("|") ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + var compare; + + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) { + if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) { + if ( a === doc || contains( preferredDoc, a ) ) { + return -1; + } + if ( b === doc || contains( preferredDoc, b ) ) { + return 1; + } + return 0; + } + return compare & 4 ? -1 : 1; + } + + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return ( ~b.sourceIndex || MAX_NEGATIVE ) - ( contains( preferredDoc, a ) && ~a.sourceIndex || MAX_NEGATIVE ); + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + // Always assume the presence of duplicates if sort doesn't + // pass them to our comparison function (as in Google Chrome). + hasDuplicate = false; + [0, 0].sort( sortOrder ); + support.detectDuplicates = hasDuplicate; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyQSA always contains :focus, so no need for an existence check + if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + var val; + + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( !documentIsXML ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( documentIsXML || support.attributes ) { + return elem.getAttribute( name ); + } + return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ? + name : + val && val.specified ? val.value : null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +function siblingCheck( a, b ) { + var cur = a && b && a.nextSibling; + + for ( ; cur; cur = cur.nextSibling ) { + if ( cur === b ) { + return -1; + } + } + + return a ? 1 : -1; +} + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[4] ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + + nodeName = nodeName.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifider + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsXML ? + elem.getAttribute("xml:lang") || elem.getAttribute("lang") : + elem.lang) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push( { + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + // `i` starts as a string, so matchedCount would equal "00" if there are no elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !documentIsXML && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["needsContext"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + documentIsXML, + results, + rsibling.test( selector ) + ); + return results; +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Easy API for creating new setFilters +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Initialize with the default document +setDocument(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, ret, self; + + if ( typeof selector !== "string" ) { + self = this; + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < self.length; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + ret = []; + for ( i = 0; i < this.length; i++ ) { + jQuery.find( selector, this[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( jQuery.unique( ret ) ); + ret.selector = ( this.selector ? this.selector + " " : "" ) + selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true) ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + var isFunc = jQuery.isFunction( value ); + + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( !isFunc && typeof value !== "string" ) { + value = jQuery( value ).not( this ).detach(); + } + + return this.domManip( [ value ], true, function( elem ) { + var next = this.nextSibling, + parent = this.parentNode; + + if ( parent && this.nodeType === 1 || this.nodeType === 11 ) { + + jQuery( this ).remove(); + + if ( next ) { + next.parentNode.insertBefore( elem, next ); + } else { + parent.appendChild( elem ); + } + } + }); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, table ? self.html() : undefined ); + } + self.domManip( args, table, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + node, + i + ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery.ajax({ + url: node.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + var attr = elem.getAttributeNode("type"); + elem.type = ( attr && attr.specified ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, data, e; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== "undefined" ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, srcElements, node, i, clone, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var contains, elem, tag, tmp, wrap, tbody, j, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var data, id, elem, type, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== "undefined" ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + } +}); +var curCSS, getStyles, iframe, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var elem, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + values[ index ] = jQuery._data( elem, "olddisplay" ); + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && elem.style.display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else if ( !values[ index ] && !isHidden( elem ) ) { + jQuery._data( elem, "olddisplay", jQuery.css( elem, "display" ) ); + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + var bool = typeof state === "boolean"; + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery(" + + \ No newline at end of file diff --git a/addons/im_livechat/static/src/img/default.png b/addons/im_livechat/static/src/img/default.png new file mode 100644 index 00000000000..07d2503ce4e Binary files /dev/null and b/addons/im_livechat/static/src/img/default.png differ diff --git a/addons/im_livechat/static/src/img/icon.png b/addons/im_livechat/static/src/img/icon.png new file mode 100644 index 00000000000..07d2503ce4e Binary files /dev/null and b/addons/im_livechat/static/src/img/icon.png differ diff --git a/addons/im_livechat/web_page.html b/addons/im_livechat/web_page.html new file mode 100644 index 00000000000..2e1ea609126 --- /dev/null +++ b/addons/im_livechat/web_page.html @@ -0,0 +1,61 @@ + + + + + {{script}} + + + +
+

{{channelName | escape}}

+
Live Chat Powered by OpenERP.
+
+ + diff --git a/addons/l10n_at/account_wizard.py b/addons/l10n_at/account_wizard.py index 278837322ac..0aec5392a8a 100644 --- a/addons/l10n_at/account_wizard.py +++ b/addons/l10n_at/account_wizard.py @@ -30,7 +30,6 @@ class AccountWizard_cd(osv.osv_memory): 'code_digits' : 0, } -AccountWizard_cd() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_be/fiscal_templates.xml b/addons/l10n_be/fiscal_templates.xml index 5e26a7e67c1..5cf5a21381b 100644 --- a/addons/l10n_be/fiscal_templates.xml +++ b/addons/l10n_be/fiscal_templates.xml @@ -22,12 +22,6 @@ - - - - - - diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py index d89d427ef6b..b74cfa21b8a 100644 --- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py +++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py @@ -209,6 +209,5 @@ class l10n_be_vat_declaration(osv.osv_memory): 'target': 'new', } -l10n_be_vat_declaration() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py index 7aa612d95ff..29a1a1508a4 100644 --- a/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py +++ b/addons/l10n_be/wizard/l10n_be_partner_vat_listing.py @@ -39,7 +39,6 @@ class vat_listing_clients(osv.osv_memory): 'vat_amount': fields.float('VAT Amount'), } -vat_listing_clients() class partner_vat(osv.osv_memory): """ Vat Listing """ @@ -116,7 +115,6 @@ class partner_vat(osv.osv_memory): 'limit_amount': 250, } -partner_vat() class partner_vat_list(osv.osv_memory): """ Partner Vat Listing """ @@ -321,7 +319,6 @@ class partner_vat_list(osv.osv_memory): 'datas': datas, } -partner_vat_list() class partner_vat_listing_print(report_sxw.rml_parse): diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra.py b/addons/l10n_be/wizard/l10n_be_vat_intra.py index ffd42290ed0..703ab9db59b 100644 --- a/addons/l10n_be/wizard/l10n_be_vat_intra.py +++ b/addons/l10n_be/wizard/l10n_be_vat_intra.py @@ -282,7 +282,6 @@ class partner_vat_intra(osv.osv_memory): 'datas': datas, } -partner_vat_intra() class vat_intra_print(report_sxw.rml_parse): def __init__(self, cr, uid, name, context): diff --git a/addons/l10n_be_coda/wizard/account_coda_import.py b/addons/l10n_be_coda/wizard/account_coda_import.py index 213040d6204..8aabe601bc8 100644 --- a/addons/l10n_be_coda/wizard/account_coda_import.py +++ b/addons/l10n_be_coda/wizard/account_coda_import.py @@ -128,6 +128,8 @@ class account_coda_import(osv.osv_memory): raise osv.except_osv(_('Error') + ' R1004', _("No matching Bank Account (with Account Journal) found.\n\nPlease set-up a Bank Account with as Account Number '%s' and as Currency '%s' and an Account Journal.") % (statement['acc_number'], statement['currency'])) statement['description'] = rmspaces(line[90:125]) statement['balance_start'] = float(rmspaces(line[43:58])) / 1000 + if line[42] == '1': #1 = Debit, the starting balance is negative + statement['balance_start'] = - statement['balance_start'] statement['balance_start_date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[58:64]), '%d%m%y')) statement['accountHolder'] = rmspaces(line[64:90]) statement['paperSeqNumber'] = rmspaces(line[2:5]) @@ -389,7 +391,7 @@ class account_coda_import(osv.osv_memory): if statement['coda_note'] != '': self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context) model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree') - action = self.pool.get(model).browse(cr, uid, action_id, context=context) + action = self.pool[model].browse(cr, uid, action_id, context=context) return { 'name': action.name, 'view_type': action.view_type, diff --git a/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.py b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.py index 79909cf79af..e62ea076079 100644 --- a/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.py +++ b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll.py @@ -39,7 +39,6 @@ class hr_contract_be(osv.osv): 'retained_net_amount': fields.float('Net retained ', digits_compute=dp.get_precision('Payroll')), } -hr_contract_be() class hr_employee_be(osv.osv): _inherit = 'hr.employee' @@ -52,6 +51,5 @@ class hr_employee_be(osv.osv): 'disabled_children_number': fields.integer('Number of disabled children'), } -hr_employee_be() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_be_invoice_bba/partner.py b/addons/l10n_be_invoice_bba/partner.py index 9e0eec10713..bf8d5d7a0c3 100644 --- a/addons/l10n_be_invoice_bba/partner.py +++ b/addons/l10n_be_invoice_bba/partner.py @@ -44,9 +44,13 @@ class res_partner(osv.osv): help='Select Algorithm to generate the Structured Communication on Outgoing Invoices.' ), } + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + \ + ['out_inv_comm_type', 'out_inv_comm_algorithm'] + + _default = { 'out_inv_comm_type': 'none', } -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_fr/l10n_fr.py b/addons/l10n_fr/l10n_fr.py index c995b44a614..676121b4292 100644 --- a/addons/l10n_fr/l10n_fr.py +++ b/addons/l10n_fr/l10n_fr.py @@ -33,7 +33,6 @@ class l10n_fr_report(osv.osv): ('code_uniq', 'unique (code)','The code report must be unique !') ] -l10n_fr_report() class l10n_fr_line(osv.osv): _name = 'l10n.fr.line' @@ -48,7 +47,6 @@ class l10n_fr_line(osv.osv): ('code_uniq', 'unique (code)', 'The variable name must be unique !') ] -l10n_fr_line() class res_company(osv.osv): _inherit = 'res.company' @@ -58,6 +56,5 @@ class res_company(osv.osv): 'ape': fields.char('APE', size=64), } -res_company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_fr/wizard/fr_report_bilan.py b/addons/l10n_fr/wizard/fr_report_bilan.py index 90e97389eed..291557f8fba 100644 --- a/addons/l10n_fr/wizard/fr_report_bilan.py +++ b/addons/l10n_fr/wizard/fr_report_bilan.py @@ -52,6 +52,5 @@ class account_bilan_report(osv.osv_memory): data['form']['fiscalyear_id'] = self.browse(cr, uid, ids)[0].fiscalyear_id.id return {'type': 'ir.actions.report.xml', 'report_name': 'l10n.fr.bilan', 'datas': data} -account_bilan_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_fr/wizard/fr_report_compute_resultant.py b/addons/l10n_fr/wizard/fr_report_compute_resultant.py index eff52189bc9..8d8aed8baff 100644 --- a/addons/l10n_fr/wizard/fr_report_compute_resultant.py +++ b/addons/l10n_fr/wizard/fr_report_compute_resultant.py @@ -52,6 +52,5 @@ class account_cdr_report(osv.osv_memory): data['form']['fiscalyear_id'] = self.browse(cr, uid, ids)[0].fiscalyear_id.id return { 'type': 'ir.actions.report.xml', 'report_name': 'l10n.fr.compute_resultant', 'datas': data} -account_cdr_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_fr_hr_payroll/l10n_fr_hr_payroll.py b/addons/l10n_fr_hr_payroll/l10n_fr_hr_payroll.py index 0519fd86a47..ec17792dacd 100644 --- a/addons/l10n_fr_hr_payroll/l10n_fr_hr_payroll.py +++ b/addons/l10n_fr_hr_payroll/l10n_fr_hr_payroll.py @@ -34,7 +34,6 @@ class res_company(osv.osv): 'conv_coll': fields.char('Convention collective', size=64), } -res_company() class hr_contract(osv.osv): _inherit = 'hr.contract' @@ -44,7 +43,6 @@ class hr_contract(osv.osv): 'niveau': fields.char('Niveau', size=64), 'coef': fields.char('Coefficient', size=64), } -hr_contract() class hr_payslip(osv.osv): _inherit = 'hr.payslip' @@ -52,6 +50,5 @@ class hr_payslip(osv.osv): _columns = { 'payment_mode': fields.char('Mode de paiement', size=64), } -hr_payslip() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_fr_rib/bank.py b/addons/l10n_fr_rib/bank.py index 26e10053141..1f5264ef5ac 100644 --- a/addons/l10n_fr_rib/bank.py +++ b/addons/l10n_fr_rib/bank.py @@ -80,7 +80,6 @@ class res_partner_bank(osv.osv): _constraints = [(_check_key, 'The RIB and/or IBAN is not valid', ['rib_acc_number', 'bank_code', 'office', 'key'])] -res_partner_bank() class res_bank(osv.osv): """Add the bank code to make it easier to enter RIB data""" @@ -102,6 +101,5 @@ class res_bank(osv.osv): _columns = { 'rib_code': fields.char('RIB Bank Code', size=64), } -res_bank() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py index 6a7638d919a..348bb71c95e 100644 --- a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py @@ -48,7 +48,6 @@ class hr_contract(osv.osv): 'supplementary_allowance': fields.float('Supplementary Allowance', digits_compute=dp.get_precision('Payroll')), } -hr_contract() class payroll_advice(osv.osv): ''' @@ -157,7 +156,6 @@ class payroll_advice(osv.osv): return { 'value':res } -payroll_advice() class hr_payslip_run(osv.osv): @@ -216,7 +214,6 @@ class hr_payslip_run(osv.osv): advice_line_pool.create(cr, uid, advice_line, context=context) return self.write(cr, uid, ids, {'available_advice' : True}) -hr_payslip_run() class payroll_advice_line(osv.osv): ''' @@ -247,7 +244,6 @@ class payroll_advice_line(osv.osv): 'debit_credit': 'C', } -payroll_advice_line() class hr_payslip(osv.osv): ''' @@ -265,7 +261,6 @@ class hr_payslip(osv.osv): default.update({'advice_id' : False}) return super(hr_payslip, self).copy(cr, uid, id, default, context=context) -hr_payslip() class res_company(osv.osv): @@ -277,6 +272,5 @@ class res_company(osv.osv): 'dearness_allowance': True, } -res_company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/report/payment_advice_report.py b/addons/l10n_in_hr_payroll/report/payment_advice_report.py index 59456b13b55..32c589d2a90 100644 --- a/addons/l10n_in_hr_payroll/report/payment_advice_report.py +++ b/addons/l10n_in_hr_payroll/report/payment_advice_report.py @@ -82,6 +82,5 @@ class payment_advice_report(osv.osv): l.employee_id,l.advice_id,l.bysal,l.ifsc_code, l.name ) """) -payment_advice_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/report/payslip_report.py b/addons/l10n_in_hr_payroll/report/payslip_report.py index b44f123547b..2e1a543a80a 100644 --- a/addons/l10n_in_hr_payroll/report/payslip_report.py +++ b/addons/l10n_in_hr_payroll/report/payslip_report.py @@ -83,6 +83,5 @@ class payslip_report(osv.osv): l.employee_id,p.struct_id,l.category_id ) """) -payslip_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py index 62d332ea5b8..93594e1dff8 100644 --- a/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py +++ b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py @@ -66,6 +66,5 @@ class hr_salary_employee_bymonth(osv.osv_memory): 'datas': datas, } -hr_salary_employee_bymonth() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py index ed2a02e8d42..de3680f2dd3 100644 --- a/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py +++ b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py @@ -61,6 +61,5 @@ class yearly_salary_detail(osv.osv_memory): 'datas': datas, } -yearly_salary_detail() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_lu/wizard/print_vat.py b/addons/l10n_lu/wizard/print_vat.py index abd5c7ce298..bfa058720d3 100644 --- a/addons/l10n_lu/wizard/print_vat.py +++ b/addons/l10n_lu/wizard/print_vat.py @@ -9,10 +9,10 @@ from __future__ import with_statement import openerp from openerp.osv import fields, osv from openerp import tools +from openerp.modules.module import get_module_resource from openerp.tools.translate import _ from openerp.report.render import render from openerp.report.interface import report_int -from openerp import addons import tempfile import os @@ -53,7 +53,7 @@ class report_custom(report_int): try: tmp_file = tempfile.mkstemp(".pdf")[1] try: - tools.pdf_utils.fill_pdf(addons.get_module_resource('l10n_lu','wizard', '2008_DECL_F_M10.pdf'), tmp_file, result) + tools.pdf_utils.fill_pdf(get_module_resource('l10n_lu','wizard', '2008_DECL_F_M10.pdf'), tmp_file, result) with open(tmp_file, "r") as ofile: self.obj = external_pdf(ofile.read()) finally: @@ -92,6 +92,5 @@ class vat_declaration_report(osv.osv_memory): data['form']['period_id'] = self.browse(cr, uid, ids)[0].period_id.id return { 'type': 'ir.actions.report.xml', 'report_name': 'l10n_lu.tax.report.print', 'datas': data} -vat_declaration_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_ma/l10n_ma.py b/addons/l10n_ma/l10n_ma.py index 6321516cf0f..a2a28a21f82 100644 --- a/addons/l10n_ma/l10n_ma.py +++ b/addons/l10n_ma/l10n_ma.py @@ -33,7 +33,6 @@ class l10n_ma_report(osv.osv): _sql_constraints = [ ('code_uniq', 'unique (code)','The code report must be unique !') ] -l10n_ma_report() class l10n_ma_line(osv.osv): _name = 'l10n.ma.line' @@ -47,6 +46,5 @@ class l10n_ma_line(osv.osv): _sql_constraints = [ ('code_uniq', 'unique (code)', 'The variable name must be unique !') ] -l10n_ma_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_multilang/account.py b/addons/l10n_multilang/account.py index 4cb0e28b672..55770c5ec14 100644 --- a/addons/l10n_multilang/account.py +++ b/addons/l10n_multilang/account.py @@ -31,21 +31,18 @@ class account_account_template(osv.osv): _columns = { 'name': fields.char('Name', size=128, required=True, select=True, translate=True), } -account_account_template() class account_account(osv.osv): _inherit = 'account.account' _columns = { 'name': fields.char('Name', size=128, required=True, select=True, translate=True), } -account_account() class account_tax(osv.osv): _inherit = 'account.tax' _columns = { 'name': fields.char('Tax Name', size=128, required=True, select=True, translate=True), } -account_tax() class account_tax_template(osv.osv): @@ -53,7 +50,6 @@ class account_tax_template(osv.osv): _columns = { 'name': fields.char('Tax Name', size=128, required=True, select=True, translate=True), } -account_tax_template() class account_tax_code_template(osv.osv): @@ -61,7 +57,6 @@ class account_tax_code_template(osv.osv): _columns = { 'name': fields.char('Tax Case Name', size=64, required=True, translate=True), } -account_tax_code_template() class account_chart_template(osv.osv): @@ -71,7 +66,6 @@ class account_chart_template(osv.osv): 'spoken_languages': fields.char('Spoken Languages', size=64, help="State here the languages for which the translations of templates could be loaded at the time of installation of this localization module and copied in the final object when generating them from templates. You must provide the language codes separated by ';'"), } _order = 'name' -account_chart_template() class account_fiscal_position(osv.osv): @@ -79,7 +73,6 @@ class account_fiscal_position(osv.osv): _columns = { 'name': fields.char('Fiscal Position', size=64, required=True, translate=True), } -account_fiscal_position() class account_fiscal_position_template(osv.osv): @@ -87,7 +80,6 @@ class account_fiscal_position_template(osv.osv): _columns = { 'name': fields.char('Fiscal Position Template', size=64, required=True, translate=True), } -account_fiscal_position_template() class account_journal(osv.osv): @@ -95,7 +87,6 @@ class account_journal(osv.osv): _columns = { 'name': fields.char('Journal Name', size=64, required=True, translate=True), } -account_journal() class account_analytic_account(osv.osv): @@ -103,7 +94,6 @@ class account_analytic_account(osv.osv): _columns = { 'name': fields.char('Account Name', size=128, required=True, translate=True), } -account_analytic_account() class account_analytic_journal(osv.osv): @@ -111,4 +101,3 @@ class account_analytic_journal(osv.osv): _columns = { 'name': fields.char('Journal Name', size=64, required=True, translate=True), } -account_analytic_journal() diff --git a/addons/l10n_multilang/l10n_multilang.py b/addons/l10n_multilang/l10n_multilang.py index 9596b292ff3..b7564a9960a 100644 --- a/addons/l10n_multilang/l10n_multilang.py +++ b/addons/l10n_multilang/l10n_multilang.py @@ -78,7 +78,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): else: #replace the value in the destination object only if it's the user lang if context.get('lang') == lang: - self.pool.get(out_obj._name).write(cr, uid, out_ids[j], {in_field: value[in_id]}) + self.pool[out_obj._name].write(cr, uid, out_ids[j], {in_field: value[in_id]}) else: _logger.info('Language: %s. Translation from template: there is no translation available for %s!' %(lang, src[in_id]))#out_obj._name)) return True @@ -150,6 +150,5 @@ class wizard_multi_charts_accounts(osv.osv_memory): out_ids = obj_fiscal_position.search(cr, uid, [('company_id', '=', company_id)], order='id') return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, force_write=False, context=context) -wizard_multi_charts_accounts() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_ro/res_partner.py b/addons/l10n_ro/res_partner.py index 1cfe7390a77..96de4b4e86a 100755 --- a/addons/l10n_ro/res_partner.py +++ b/addons/l10n_ro/res_partner.py @@ -28,10 +28,32 @@ class res_partner(osv.osv): _columns = { 'nrc' : fields.char('NRC', size=16, help='Registration number at the Registry of Commerce'), } + + # The SQL constraints are no-ops but present only to display the right error message to the + # user when the partial unique indexes defined below raise errors/ + # The real constraints need to be implemented with PARTIAL UNIQUE INDEXES (see auto_init), + # due to the way accounting data is delegated by contacts to their companies in OpenERP 7.0. _sql_constraints = [ - ('vat_uniq', 'unique (vat)', 'The vat of the partner must be unique !'), - ('nrc_uniq', 'unique (nrc)', 'The code of the partner must be unique !') + ('vat_uniq', 'unique (id)', 'The vat of the partner must be unique !'), + ('nrc_uniq', 'unique (id)', 'The code of the partner must be unique !') ] -res_partner() + + def _auto_init(self, cr, context=None): + result = super(res_partner, self)._auto_init(cr, context=context) + # Real implementation of the vat/nrc constraints: only "commercial entities" need to have + # unique numbers, and the condition for being a commercial entity is "is_company or parent_id IS NULL". + # Contacts inside a company automatically have a copy of the company's commercial fields + # (see _commercial_fields()), so they are automatically consistent. + cr.execute(""" + DROP INDEX IF EXISTS res_partner_vat_uniq_for_companies; + DROP INDEX IF EXISTS res_partner_nrc_uniq_for_companies; + CREATE UNIQUE INDEX res_partner_vat_uniq_for_companies ON res_partner (vat) WHERE is_company OR parent_id IS NULL; + CREATE UNIQUE INDEX res_partner_nrc_uniq_for_companies ON res_partner (nrc) WHERE is_company OR parent_id IS NULL; + """) + return result + + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['nrc'] + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/lunch/report/report_lunch_order.py b/addons/lunch/report/report_lunch_order.py index 3a439a4a4dd..13c885e7896 100644 --- a/addons/lunch/report/report_lunch_order.py +++ b/addons/lunch/report/report_lunch_order.py @@ -60,6 +60,5 @@ class report_lunch_order(osv.osv): lo.date,lo.user_id,lo.note ) """) -report_lunch_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 3f517ee7858..02cfd626768 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -27,6 +27,19 @@ 1000 + + Garbage Collect Mail Attachments + + + 1 + weeks + -1 + + mail.thread + _garbage_collect_attachments + () + + Discussions diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index 55746b11678..8730599ad72 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -160,11 +160,11 @@ class mail_alias(osv.Model): alias_id_column.required = False # call _auto_init - child_model_auto_init_fct(cr, context=context) + res = child_model_auto_init_fct(cr, context=context) registry = RegistryManager.get(cr.dbname) mail_alias = registry.get('mail.alias') - child_class_model = registry.get(child_model_name) + child_class_model = registry[child_model_name] no_alias_ids = child_class_model.search(cr, SUPERUSER_ID, [('alias_id', '=', False)], context={'active_test':False}) # Use read() not browse(), to avoid prefetching uninitialized inherited fields for obj_data in child_class_model.read(cr, SUPERUSER_ID, no_alias_ids, [alias_key]): @@ -188,6 +188,8 @@ class mail_alias(osv.Model): # set back the unique alias_id constraint alias_id_column.required = True + return res + def create_unique_alias(self, cr, uid, vals, model_name=None, context=None): """Creates an email.alias record according to the values provided in ``vals``, with 2 alterations: the ``alias_name`` value may be suffixed in order to diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 450b81dabbf..152b9704fd7 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -118,8 +118,8 @@ class mail_notification(osv.Model): Administrator

- Send by Your Company using OpenERP. OR - Send by Administrator using OpenERP. + Sent by Your Company using OpenERP. OR + Sent by Administrator using OpenERP.
""" footer = "" @@ -139,7 +139,7 @@ class mail_notification(osv.Model): company = user.company_id.website and "%s" % (user.company_id.website, user.company_id.name) or user.company_id.name else: company = user.name - signature_company = _('Send by %(company)s using %(openerp)s.') % { + signature_company = _('Sent by %(company)s using %(openerp)s.') % { 'company': company, 'openerp': "OpenERP" } diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index ab2b6f3e922..3c30a950230 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -108,8 +108,8 @@ class mail_mail(osv.Model): res_id = message.res_id # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias - if model and res_id and hasattr(self.pool.get(model), 'message_get_reply_to'): - email_reply_to = self.pool.get(model).message_get_reply_to(cr, uid, [res_id], context=context)[0] + if model and res_id and hasattr(self.pool[model], 'message_get_reply_to'): + email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part if not email_reply_to and values.get('email_from'): emails = tools.email_split(values.get('email_from')) @@ -118,7 +118,7 @@ class mail_mail(osv.Model): # format 'Document name ' if email_reply_to and model and res_id: - document_name = self.pool.get(model).name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] + document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name sanitized_doc_name = re.sub(r'[^\w+.]+', '-', document_name[1]) @@ -217,7 +217,7 @@ class mail_mail(osv.Model): and self.check_access_rights(cr, partner.user_ids[0].id, 'read', raise_exception=False): related_user = partner.user_ids[0] try: - self.pool.get(mail.model).check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) + self.pool[mail.model].check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') # the parameters to encode for the query and fragment part of url query = {'db': cr.dbname} diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4f830b21e50..defc12a2f73 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -76,9 +76,9 @@ class mail_message(osv.Model): # TDE note: regroup by model/ids, to have less queries to perform result = dict.fromkeys(ids, False) for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context): - if not message.get('model') or not message.get('res_id') or not self.pool.get(message['model']): + if not message.get('model') or not message.get('res_id') or message['model'] not in self.pool: continue - result[message['id']] = self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1] + result[message['id']] = self.pool[message['model']].name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1] return result def _get_to_read(self, cr, uid, ids, name, arg, context=None): @@ -369,6 +369,7 @@ class mail_message(osv.Model): return {'id': message.id, 'type': message.type, + 'subtype': message.subtype_id.name if message.subtype_id else False, 'body': body_html, 'model': message.model, 'res_id': message.res_id, @@ -569,7 +570,7 @@ class mail_message(osv.Model): def _find_allowed_model_wise(self, cr, uid, doc_model, doc_dict, context=None): doc_ids = doc_dict.keys() - allowed_doc_ids = self.pool.get(doc_model).search(cr, uid, [('id', 'in', doc_ids)], context=context) + allowed_doc_ids = self.pool[doc_model].search(cr, uid, [('id', 'in', doc_ids)], context=context) return set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]]) def _find_allowed_doc_ids(self, cr, uid, model_ids, context=None): @@ -717,7 +718,7 @@ class mail_message(osv.Model): model_record_ids = _generate_model_record_ids(message_values, other_ids) document_related_ids = [] for model, doc_dict in model_record_ids.items(): - model_obj = self.pool.get(model) + model_obj = self.pool[model] mids = model_obj.exists(cr, uid, doc_dict.keys()) if operation in ['create', 'write', 'unlink']: model_obj.check_access_rights(cr, uid, 'write') @@ -773,7 +774,7 @@ class mail_message(osv.Model): attachments_to_delete = [] for message in self.browse(cr, uid, ids, context=context): for attach in message.attachment_ids: - if attach.res_model == self._name and attach.res_id == message.id: + if attach.res_model == self._name and (attach.res_id == message.id or attach.res_id == 0): attachments_to_delete.append(attach.id) if attachments_to_delete: self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 7ca9fcb7b53..5ddf412e66b 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -408,7 +408,7 @@ class mail_thread(osv.AbstractModel): posted = False for subtype in subtypes: try: - subtype_rec = self.pool.get('ir.model.data').get_object(cr, uid, subtype.split('.')[0], subtype.split('.')[1]) + subtype_rec = self.pool.get('ir.model.data').get_object(cr, uid, subtype.split('.')[0], subtype.split('.')[1], context=context) except ValueError, e: _logger.debug('subtype %s not found, giving error "%s"' % (subtype, e)) continue @@ -429,6 +429,26 @@ class mail_thread(osv.AbstractModel): return [('message_unread', '=', True)] return [] + def _garbage_collect_attachments(self, cr, uid, context=None): + """ Garbage collect lost mail attachments. Those are attachments + - linked to res_model 'mail.compose.message', the composer wizard + - with res_id 0, because they were created outside of an existing + wizard (typically user input through Chatter or reports + created on-the-fly by the templates) + - unused since at least one day (create_date and write_date) + """ + limit_date = datetime.datetime.utcnow() - datetime.timedelta(days=1) + limit_date_str = datetime.datetime.strftime(limit_date, tools.DEFAULT_SERVER_DATETIME_FORMAT) + ir_attachment_obj = self.pool.get('ir.attachment') + attach_ids = ir_attachment_obj.search(cr, uid, [ + ('res_model', '=', 'mail.compose.message'), + ('res_id', '=', 0), + ('create_date', '<', limit_date_str), + ('write_date', '<', limit_date_str), + ], context=context) + ir_attachment_obj.unlink(cr, uid, attach_ids, context=context) + return True + #------------------------------------------------------ # Email specific #------------------------------------------------------ @@ -449,13 +469,15 @@ class mail_thread(osv.AbstractModel): """ Used by the plugin addon, based for plugin_outlook and others. """ ret_dict = {} for model_name in self.pool.obj_list(): - model = self.pool.get(model_name) + model = self.pool[model_name] if 'mail.thread' in getattr(model, '_inherit', []): ret_dict[model_name] = model._description return ret_dict def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None): - """ Find partners related to some header fields of the message. """ + """ Find partners related to some header fields of the message. + + TDE TODO: merge me with other partner finding methods in 8.0 """ partner_obj = self.pool.get('res.partner') partner_ids = [] s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)]) @@ -467,6 +489,7 @@ class mail_thread(osv.AbstractModel): return partner_ids def _message_find_user_id(self, cr, uid, message, context=None): + """ TDE TODO: check and maybe merge me with other user finding methods in 8.0 """ from_local_part = tools.email_split(decode(message.get('From')))[0] # FP Note: canonification required, the minimu: .lower() user_ids = self.pool.get('res.users').search(cr, uid, ['|', @@ -520,12 +543,12 @@ class mail_thread(osv.AbstractModel): if ref_match: thread_id = int(ref_match.group(1)) model = ref_match.group(2) or model - model_pool = self.pool.get(model) - if thread_id and model and model_pool and model_pool.exists(cr, uid, thread_id) \ - and hasattr(model_pool, 'message_update'): - _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s', - email_from, email_to, message_id, model, thread_id, custom_values, uid) - return [(model, thread_id, custom_values, uid)] + if thread_id and model in self.pool: + model_obj = self.pool[model] + if model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'): + _logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s', + email_from, email_to, message_id, model, thread_id, custom_values, uid) + return [(model, thread_id, custom_values, uid)] # Verify whether this is a reply to a private message if in_reply_to: @@ -666,7 +689,7 @@ class mail_thread(osv.AbstractModel): if self._name == 'mail.thread': context.update({'thread_model': model}) if model: - model_pool = self.pool.get(model) + model_pool = self.pool[model] assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \ "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \ (msg['message_id'], model) @@ -720,7 +743,7 @@ class mail_thread(osv.AbstractModel): if isinstance(custom_values, dict): data = custom_values.copy() model = context.get('thread_model') or self._name - model_pool = self.pool.get(model) + model_pool = self.pool[model] fields = model_pool.fields_get(cr, uid, context=context) if 'name' in fields and not data.get('name'): data['name'] = msg_dict.get('subject', '') @@ -892,7 +915,8 @@ class mail_thread(osv.AbstractModel): recipient in the result dictionary. The form is : partner_id, partner_name or partner_name, reason """ if email and not partner: - partner_info = self.message_get_partner_info_from_emails(cr, uid, [email], context=context)[0] + # get partner info from email + partner_info = self.message_get_partner_info_from_emails(cr, uid, [email], context=context, res_id=obj.id)[0] if partner_info.get('partner_id'): partner = self.pool.get('res.partner').browse(cr, SUPERUSER_ID, [partner_info.get('partner_id')], context=context)[0] if email and email in [val[1] for val in result[obj.id]]: # already existing email -> skip @@ -920,29 +944,49 @@ class mail_thread(osv.AbstractModel): self._message_add_suggested_recipient(cr, uid, result, obj, partner=obj.user_id.partner_id, reason=self._all_columns['user_id'].column.string, context=context) return result - def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None): + def message_get_partner_info_from_emails(self, cr, uid, emails, link_mail=False, context=None, res_id=None): + """ Wrapper with weird order parameter because of 7.0 fix. + + TDE TODO: remove me in 8.0 """ + return self.message_find_partner_from_emails(cr, uid, res_id, emails, link_mail=link_mail, context=context) + + def message_find_partner_from_emails(self, cr, uid, id, emails, link_mail=False, context=None): """ Convert a list of emails into a list partner_ids and a list new_partner_ids. The return value is non conventional because it is meant to be used by the mail widget. :return dict: partner_ids and new_partner_ids - """ + + TDE TODO: merge me with other partner finding methods in 8.0 """ mail_message_obj = self.pool.get('mail.message') partner_obj = self.pool.get('res.partner') result = list() + if id and self._name != 'mail.thread': + obj = self.browse(cr, SUPERUSER_ID, id, context=context) + else: + obj = None for email in emails: partner_info = {'full_name': email, 'partner_id': False} m = re.search(r"((.+?)\s*<)?([^<>]+@[^<>]+)>?", email, re.IGNORECASE | re.DOTALL) if not m: continue email_address = m.group(3) - ids = partner_obj.search(cr, SUPERUSER_ID, [('email', '=', email_address)], context=context) - if ids: - partner_info['partner_id'] = ids[0] + # first try: check in document's followers + if obj: + for follower in obj.message_follower_ids: + if follower.email == email_address: + partner_info['partner_id'] = follower.id + # second try: check in partners + if not partner_info.get('partner_id'): + ids = partner_obj.search(cr, SUPERUSER_ID, [('email', 'ilike', email_address), ('user_ids', '!=', False)], limit=1, context=context) + if not ids: + ids = partner_obj.search(cr, SUPERUSER_ID, [('email', 'ilike', email_address)], limit=1, context=context) + if ids: + partner_info['partner_id'] = ids[0] result.append(partner_info) # link mail with this from mail to the new partner id - if link_mail and ids: + if link_mail and partner_info['partner_id']: message_ids = mail_message_obj.search(cr, SUPERUSER_ID, [ '|', ('email_from', '=', email), @@ -950,7 +994,7 @@ class mail_thread(osv.AbstractModel): ('author_id', '=', False) ], context=context) if message_ids: - mail_message_obj.write(cr, SUPERUSER_ID, message_ids, {'author_id': ids[0]}, context=context) + mail_message_obj.write(cr, SUPERUSER_ID, message_ids, {'author_id': partner_info['partner_id']}, context=context) return result def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', @@ -997,7 +1041,7 @@ class mail_thread(osv.AbstractModel): model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name if model != self._name: del context['thread_model'] - return self.pool.get(model).message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs) + return self.pool[model].message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs) # 0: Parse email-from, try to find a better author_id based on document's followers for incoming emails email_from = kwargs.get('email_from') @@ -1046,7 +1090,6 @@ class mail_thread(osv.AbstractModel): if attachment_ids: filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [ ('res_model', '=', 'mail.compose.message'), - ('res_id', '=', 0), ('create_uid', '=', uid), ('id', 'in', attachment_ids)], context=context) if filtered_attachment_ids: diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index f7c91939b98..5545745b235 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -60,8 +60,9 @@ class res_users(osv.Model): def _auto_init(self, cr, context=None): """ Installation hook: aliases, partner following themselves """ # create aliases for all users and avoid constraint errors - self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(res_users, self)._auto_init, + res = self.pool.get('mail.alias').migrate_to_alias(cr, self._name, self._table, super(res_users, self)._auto_init, self._columns['alias_id'], 'login', alias_force_key='id', context=context) + return res def create(self, cr, uid, data, context=None): # create default alias same as the login diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 934fb6b5487..6bc086a5b58 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -57,6 +57,9 @@ .openerp .oe_mail .oe_msg.oe_msg_nobody{ background: #F8F8F8; } +.openerp .oe_mail .oe_msg.oe_msg_notification{ + background: #F8F8F8; +} .openerp .oe_mail .oe_msg .oe_msg_left{ position: absolute; left:0; top: 0; bottom: 0; width: 40px; diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index ec4feed82e2..b1a579ba057 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -217,6 +217,7 @@ openerp.mail = function (session) { this.res_id = datasets.res_id || this.context.default_res_id || false, this.parent_id = datasets.parent_id || false, this.type = datasets.type || false, + this.subtype = datasets.subtype || false, this.is_author = datasets.is_author || false, this.is_private = datasets.is_private || false, this.subject = datasets.subject || false, @@ -619,7 +620,10 @@ openerp.mail = function (session) { // have unknown names -> call message_get_partner_info_from_emails to try to find partner_id var find_done = $.Deferred(); if (names_to_find.length > 0) { - find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [names_to_find]); + var values = { + 'res_id': this.context.default_res_id, + } + find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [names_to_find], values); } else { find_done.resolve([]); @@ -665,7 +669,11 @@ openerp.mail = function (session) { var new_names_to_find = _.difference(names_to_find, names_to_remove); find_done = $.Deferred(); if (new_names_to_find.length > 0) { - find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [new_names_to_find, true]); + var values = { + 'link_mail': true, + 'res_id': self.context.default_res_id, + } + find_done = self.parent_thread.ds_thread._model.call('message_get_partner_info_from_emails', [new_names_to_find], values); } else { find_done.resolve([]); diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index 6fc2f0fdfcd..f812844194b 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -148,8 +148,6 @@ openerp_mail_followers = function(session, mail) { }).then(self.proxy('display_generic')); }, _format_followers: function(count){ - // TDE note: why redefining _t ? - function _t(str) { return str; } var str = ''; if(count <= 0){ str = _t('No followers'); diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index db53a60cdbc..7e7d772397a 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -226,7 +226,7 @@ -
+
@@ -263,13 +263,23 @@ - + + updated document + + + + + logged a note - + to + + nobody + - + @@ -279,6 +289,9 @@ and more + + notified + diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py index a68011298c7..e4269d59d8b 100644 --- a/addons/mail/tests/test_mail_gateway.py +++ b/addons/mail/tests/test_mail_gateway.py @@ -83,7 +83,47 @@ Sylvie class TestMailgateway(TestMailBase): - def test_00_message_process(self): + def test_00_partner_find_from_email(self): + """ Tests designed for partner fetch based on emails. """ + cr, uid, user_raoul, group_pigs = self.cr, self.uid, self.user_raoul, self.group_pigs + + # -------------------------------------------------- + # Data creation + # -------------------------------------------------- + # 1 - Partner ARaoul + p_a_id = self.res_partner.create(cr, uid, {'name': 'ARaoul', 'email': 'test@test.fr'}) + + # -------------------------------------------------- + # CASE1: without object + # -------------------------------------------------- + + # Do: find partner with email -> first partner should be found + partner_info = self.mail_thread.message_find_partner_from_emails(cr, uid, None, ['Maybe Raoul '], link_mail=False)[0] + self.assertEqual(partner_info['full_name'], 'Maybe Raoul ', + 'mail_thread: message_find_partner_from_emails did not handle email') + self.assertEqual(partner_info['partner_id'], p_a_id, + 'mail_thread: message_find_partner_from_emails wrong partner found') + + # Data: add some data about partners + # 2 - User BRaoul + p_b_id = self.res_partner.create(cr, uid, {'name': 'BRaoul', 'email': 'test@test.fr', 'user_ids': [(4, user_raoul.id)]}) + + # Do: find partner with email -> first user should be found + partner_info = self.mail_thread.message_find_partner_from_emails(cr, uid, None, ['Maybe Raoul '], link_mail=False)[0] + self.assertEqual(partner_info['partner_id'], p_b_id, + 'mail_thread: message_find_partner_from_emails wrong partner found') + + # -------------------------------------------------- + # CASE1: with object + # -------------------------------------------------- + + # Do: find partner in group where there is a follower with the email -> should be taken + self.mail_group.message_subscribe(cr, uid, [group_pigs.id], [p_b_id]) + partner_info = self.mail_group.message_find_partner_from_emails(cr, uid, group_pigs.id, ['Maybe Raoul '], link_mail=False)[0] + self.assertEqual(partner_info['partner_id'], p_b_id, + 'mail_thread: message_find_partner_from_emails wrong partner found') + + def test_10_message_process(self): """ Testing incoming emails processing. """ cr, uid, user_raoul = self.cr, self.uid, self.user_raoul @@ -325,7 +365,7 @@ class TestMailgateway(TestMailBase): self.assertEqual(msg.body, '
\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n
', 'message_process: plaintext incoming email incorrectly parsed') - def test_10_thread_parent_resolution(self): + def test_20_thread_parent_resolution(self): """ Testing parent/child relationships are correctly established when processing incoming mails """ cr, uid = self.cr, self.uid @@ -370,7 +410,7 @@ class TestMailgateway(TestMailBase): self.assertEqual(6, len(group_pigs.message_ids), 'message_process: group should contain 6 messages') self.assertEqual(3, len(msg1.child_ids), 'message_process: msg1 should have 3 children now') - def test_20_private_discussion(self): + def test_30_private_discussion(self): """ Testing private discussion between partners. """ cr, uid = self.cr, self.uid diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 2eb48f3d998..4ff6584d789 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -37,10 +37,10 @@ class invite_wizard(osv.osv_memory): res_id = result.get('res_id') if 'message' in fields and model and res_id: ir_model = self.pool.get('ir.model') - model_ids = ir_model.search(cr, uid, [('model', '=', self.pool.get(model)._name)], context=context) + model_ids = ir_model.search(cr, uid, [('model', '=', self.pool[model]._name)], context=context) model_name = ir_model.name_get(cr, uid, model_ids, context=context)[0][1] - document_name = self.pool.get(model).name_get(cr, uid, [res_id], context=context)[0][1] + document_name = self.pool[model].name_get(cr, uid, [res_id], context=context)[0][1] message = _('

Hello,

%s invited you to follow %s document: %s.

') % (user_name, model_name, document_name) result['message'] = message elif 'message' in fields: @@ -63,7 +63,7 @@ class invite_wizard(osv.osv_memory): def add_followers(self, cr, uid, ids, context=None): for wizard in self.browse(cr, uid, ids, context=context): - model_obj = self.pool.get(wizard.res_model) + model_obj = self.pool[wizard.res_model] document = model_obj.browse(cr, uid, wizard.res_id, context=context) # filter partner_ids to get the new followers, to avoid sending email to already following partners diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 629dd6a88f0..dfda3038de8 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -19,10 +19,8 @@ # ############################################################################## -import base64 import re from openerp import tools - from openerp import SUPERUSER_ID from openerp.osv import osv from openerp.osv import fields @@ -174,7 +172,7 @@ class mail_compose_message(osv.TransientModel): related to. :param int res_id: id of the document record this mail is related to """ - doc_name_get = self.pool.get(model).name_get(cr, uid, [res_id], context=context) + doc_name_get = self.pool[model].name_get(cr, uid, [res_id], context=context) record_name = False if doc_name_get: record_name = doc_name_get[0][1] @@ -230,33 +228,35 @@ class mail_compose_message(osv.TransientModel): email(s), rendering any template patterns on the fly if needed. """ if context is None: context = {} + ir_attachment_obj = self.pool.get('ir.attachment') active_ids = context.get('active_ids') is_log = context.get('mail_compose_log', False) for wizard in self.browse(cr, uid, ids, context=context): mass_mail_mode = wizard.composition_mode == 'mass_mail' - if mass_mail_mode: # mass mail: avoid any auto subscription because this could lead to people being follower of plenty of documents - context['mail_create_nosubscribe'] = True - active_model_pool = self.pool.get(wizard.model if wizard.model else 'mail.thread') + active_model_pool = self.pool[wizard.model if wizard.model else 'mail.thread'] # wizard works in batch mode: [res_id] or active_ids res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id] for res_id in res_ids: - # default values, according to the wizard options + # mail.message values, according to the wizard options post_values = { 'subject': wizard.subject, 'body': wizard.body, 'parent_id': wizard.parent_id and wizard.parent_id.id, 'partner_ids': [partner.id for partner in wizard.partner_ids], - 'attachments': [(attach.datas_fname or attach.name, base64.b64decode(attach.datas)) for attach in wizard.attachment_ids], + 'attachment_ids': [attach.id for attach in wizard.attachment_ids], } # mass mailing: render and override default values if mass_mail_mode and wizard.model: email_dict = self.render_message(cr, uid, wizard, res_id, context=context) - new_partner_ids = email_dict.pop('partner_ids', []) - post_values['partner_ids'] += new_partner_ids - new_attachments = email_dict.pop('attachments', []) - post_values['attachments'] += new_attachments + post_values['partner_ids'] += email_dict.pop('partner_ids', []) + post_values['attachments'] = email_dict.pop('attachments', []) + attachment_ids = [] + for attach_id in post_values.pop('attachment_ids'): + new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context) + attachment_ids.append(new_attach_id) + post_values['attachment_ids'] = attachment_ids post_values.update(email_dict) # email_from: mass mailing only can specify another email_from if email_dict.get('email_from'): @@ -319,7 +319,7 @@ class mail_compose_message(osv.TransientModel): exp = str(match.group()[2:-1]).strip() result = eval(exp, { 'user': self.pool.get('res.users').browse(cr, uid, uid, context=context), - 'object': self.pool.get(model).browse(cr, uid, res_id, context=context), + 'object': self.pool[model].browse(cr, uid, res_id, context=context), 'context': dict(context), # copy context to prevent side-effects of eval }) return result and tools.ustr(result) or '' diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py index dfa13259383..fdec2fb39e0 100644 --- a/addons/marketing_campaign/marketing_campaign.py +++ b/addons/marketing_campaign/marketing_campaign.py @@ -165,7 +165,7 @@ Normal - the campaign runs normally and automatically sends all emails and repor # dead code def signal(self, cr, uid, model, res_id, signal, run_existing=True, context=None): - record = self.pool.get(model).browse(cr, uid, res_id, context) + record = self.pool[model].browse(cr, uid, res_id, context) return self._signal(cr, uid, record, signal, run_existing, context) #dead code @@ -228,7 +228,7 @@ Normal - the campaign runs normally and automatically sends all emails and repor if unique_value: if unique_field.ttype == 'many2one': unique_value = unique_value.id - similar_res_ids = self.pool.get(campaign_rec.object_id.model).search(cr, uid, + similar_res_ids = self.pool[campaign_rec.object_id.model].search(cr, uid, [(unique_field.name, '=', unique_value)], context=context) if similar_res_ids: duplicate_workitem_domain = [('res_id','in', similar_res_ids), @@ -236,7 +236,6 @@ Normal - the campaign runs normally and automatically sends all emails and repor return Workitems.search(cr, uid, duplicate_workitem_domain, context=context) -marketing_campaign() class marketing_campaign_segment(osv.osv): _name = "marketing.campaign.segment" @@ -349,7 +348,7 @@ class marketing_campaign_segment(osv.osv): act_ids = self.pool.get('marketing.campaign.activity').search(cr, uid, [('start', '=', True), ('campaign_id', '=', segment.campaign_id.id)], context=context) - model_obj = self.pool.get(segment.object_id.model) + model_obj = self.pool[segment.object_id.model] criteria = [] if segment.sync_last_date and segment.sync_mode != 'all': criteria += [(segment.sync_mode, '>', segment.sync_last_date)] @@ -383,7 +382,6 @@ class marketing_campaign_segment(osv.osv): Workitems.process_all(cr, uid, list(campaigns), context=context) return True -marketing_campaign_segment() class marketing_campaign_activity(osv.osv): _name = "marketing.campaign.activity" @@ -510,7 +508,6 @@ class marketing_campaign_activity(osv.osv): workitem = workitem_obj.browse(cr, uid, wi_id, context=context) return action(cr, uid, activity, workitem, context=context) -marketing_campaign_activity() class marketing_campaign_transition(osv.osv): _name = "marketing.campaign.transition" @@ -582,7 +579,6 @@ class marketing_campaign_transition(osv.osv): ('interval_positive', 'CHECK(interval_nbr >= 0)', 'The interval must be positive or zero') ] -marketing_campaign_transition() class marketing_campaign_workitem(osv.osv): _name = "marketing.campaign.workitem" @@ -594,7 +590,7 @@ class marketing_campaign_workitem(osv.osv): if not wi.res_id: continue - proxy = self.pool.get(wi.object_id.model) + proxy = self.pool[wi.object_id.model] if not proxy.exists(cr, uid, [wi.res_id]): continue ng = proxy.name_get(cr, uid, [wi.res_id], context=context) @@ -628,7 +624,7 @@ class marketing_campaign_workitem(osv.osv): for id, res_id, model in res: workitem_map.setdefault(model,{}).setdefault(res_id,set()).add(id) for model, id_map in workitem_map.iteritems(): - model_pool = self.pool.get(model) + model_pool = self.pool[model] condition_name[0] = model_pool._rec_name condition = [('id', 'in', id_map.keys()), condition_name] for res_id in model_pool.search(cr, uid, condition, context=context): @@ -676,7 +672,7 @@ class marketing_campaign_workitem(osv.osv): return False activity = workitem.activity_id - proxy = self.pool.get(workitem.object_id.model) + proxy = self.pool[workitem.object_id.model] object_id = proxy.browse(cr, uid, workitem.res_id, context=context) eval_context = { @@ -818,7 +814,6 @@ class marketing_campaign_workitem(osv.osv): raise osv.except_osv(_('No preview'),_('The current step for this item has no email or report to preview.')) return res -marketing_campaign_workitem() class email_template(osv.osv): _inherit = "email.template" @@ -828,7 +823,6 @@ class email_template(osv.osv): # TODO: add constraint to prevent disabling / disapproving an email account used in a running campaign -email_template() class report_xml(osv.osv): _inherit = 'ir.actions.report.xml' @@ -841,7 +835,6 @@ class report_xml(osv.osv): args.append(('model', '=', model)) return super(report_xml, self).search(cr, uid, args, offset, limit, order, context, count) -report_xml() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/marketing_campaign/report/campaign_analysis.py b/addons/marketing_campaign/report/campaign_analysis.py index 1fc8a5f1dc7..f09ed966857 100644 --- a/addons/marketing_campaign/report/campaign_analysis.py +++ b/addons/marketing_campaign/report/campaign_analysis.py @@ -98,6 +98,5 @@ class campaign_analysis(osv.osv): wi.date::date ) """) -campaign_analysis() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/marketing_campaign/res_partner.py b/addons/marketing_campaign/res_partner.py index c151c2f4050..b012cbb7181 100644 --- a/addons/marketing_campaign/res_partner.py +++ b/addons/marketing_campaign/res_partner.py @@ -36,7 +36,6 @@ class res_partner(osv.osv): }) return super(res_partner, self).copy(cr, uid, id, default=default, context=context) -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/membership/membership.py b/addons/membership/membership.py index ae7f4948d72..b75284637c9 100644 --- a/addons/membership/membership.py +++ b/addons/membership/membership.py @@ -169,7 +169,6 @@ class membership_line(osv.osv): (_check_membership_date, 'Error, this membership product is out of date', []) ] -membership_line() class Partner(osv.osv): @@ -442,7 +441,6 @@ class Partner(osv.osv): self.pool.get('res.partner').write(cr, uid, ids, {}) return invoice_list -Partner() class Product(osv.osv): @@ -476,7 +474,6 @@ class Product(osv.osv): 'membership': False, } -Product() class Invoice(osv.osv): @@ -494,7 +491,6 @@ class Invoice(osv.osv): member_line_obj.write(cr, uid, mlines, {'date_cancel': today}) return super(Invoice, self).action_cancel(cr, uid, ids, context=context) -Invoice() class account_invoice_line(osv.osv): _inherit='account.invoice.line' @@ -561,6 +557,5 @@ class account_invoice_line(osv.osv): }, context=context) return result -account_invoice_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/membership/report/report_membership.py b/addons/membership/report/report_membership.py index f68e52992af..081a2f0d8a5 100644 --- a/addons/membership/report/report_membership.py +++ b/addons/membership/report/report_membership.py @@ -139,6 +139,5 @@ class report_membership(osv.osv): membership_amount )""") -report_membership() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/membership/wizard/membership_invoice.py b/addons/membership/wizard/membership_invoice.py index 2425cd648ea..a1e07146da0 100644 --- a/addons/membership/wizard/membership_invoice.py +++ b/addons/membership/wizard/membership_invoice.py @@ -73,6 +73,5 @@ class membership_invoice(osv.osv_memory): 'search_view_id': search_view_id, } -membership_invoice() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/company.py b/addons/mrp/company.py index af8a21f4934..375a318c9d0 100644 --- a/addons/mrp/company.py +++ b/addons/mrp/company.py @@ -30,7 +30,6 @@ class company(osv.osv): _defaults = { 'manufacturing_lead': lambda *a: 1.0, } -company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 58fcacaa1f5..7fb40ce12af 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -70,7 +70,6 @@ class mrp_workcenter(osv.osv): value = {'costs_hour': cost.standard_price} return {'value': value} -mrp_workcenter() class mrp_routing(osv.osv): @@ -98,7 +97,6 @@ class mrp_routing(osv.osv): 'active': lambda *a: 1, 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.routing', context=context) } -mrp_routing() class mrp_routing_workcenter(osv.osv): """ @@ -124,7 +122,6 @@ class mrp_routing_workcenter(osv.osv): 'cycle_nbr': lambda *a: 1.0, 'hour_nbr': lambda *a: 0.0, } -mrp_routing_workcenter() class mrp_bom(osv.osv): """ diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index 1f94f0034ad..3ad7155dad6 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -122,6 +122,5 @@ class procurement_order(osv.osv): body = _("Manufacturing Order %s created.") % ( procurement.production_id.name,) self.message_post(cr, uid, [procurement.id], body=body, context=context) -procurement_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/product.py b/addons/mrp/product.py index f5e29a1d103..d7a19c718fa 100644 --- a/addons/mrp/product.py +++ b/addons/mrp/product.py @@ -37,6 +37,5 @@ class product_product(osv.osv): return super(product_product, self).copy(cr, uid, id, default, context=context) -product_product() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/report/mrp_report.py b/addons/mrp/report/mrp_report.py index 7dff886a4ba..76474ccb6ab 100644 --- a/addons/mrp/report/mrp_report.py +++ b/addons/mrp/report/mrp_report.py @@ -52,7 +52,6 @@ class report_workcenter_load(osv.osv): to_char(p.date_planned,'YYYY:mm:dd') )""") -report_workcenter_load() class report_mrp_inout(osv.osv): @@ -97,7 +96,6 @@ class report_mrp_inout(osv.osv): to_char(sm.date,'YYYY:IW') )""") -report_mrp_inout() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index 2feffee94a1..0ebc7bd199d 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -145,7 +145,6 @@ class StockMove(osv.osv): res.append(new_move) return res -StockMove() class StockPicking(osv.osv): @@ -162,7 +161,6 @@ class StockPicking(osv.osv): todo.extend(move_obj._action_explode(cr, uid, move)) return list(set(todo)) -StockPicking() class split_in_production_lot(osv.osv_memory): @@ -179,6 +177,5 @@ class split_in_production_lot(osv.osv_memory): production_obj.write(cr, uid, production_ids, {'move_lines': [(4, m) for m in new_moves]}) return new_moves -split_in_production_lot() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index 74309b50825..41b6022a178 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -101,6 +101,5 @@ class change_production_qty(osv.osv_memory): self._update_product_to_produce(cr, uid, prod, wiz_qty.product_qty, context=context) return {} -change_production_qty() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/wizard/mrp_price.py b/addons/mrp/wizard/mrp_price.py index 08506ccf0e2..70b4482b019 100644 --- a/addons/mrp/wizard/mrp_price.py +++ b/addons/mrp/wizard/mrp_price.py @@ -52,6 +52,5 @@ class mrp_price(osv.osv_memory): 'datas' : datas, } -mrp_price() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py index 3a3c5961d1e..0851657587c 100644 --- a/addons/mrp/wizard/mrp_product_produce.py +++ b/addons/mrp/wizard/mrp_product_produce.py @@ -67,6 +67,5 @@ class mrp_product_produce(osv.osv_memory): data.product_qty, data.mode, context=context) return {} -mrp_product_produce() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp/wizard/mrp_workcenter_load.py b/addons/mrp/wizard/mrp_workcenter_load.py index 265c6ddcea0..79905673a6b 100644 --- a/addons/mrp/wizard/mrp_workcenter_load.py +++ b/addons/mrp/wizard/mrp_workcenter_load.py @@ -51,6 +51,5 @@ class mrp_workcenter_load(osv.osv_memory): 'datas' : datas, } -mrp_workcenter_load() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp_byproduct/mrp_byproduct.py b/addons/mrp_byproduct/mrp_byproduct.py index 22bbface59d..2425c2d8fea 100644 --- a/addons/mrp_byproduct/mrp_byproduct.py +++ b/addons/mrp_byproduct/mrp_byproduct.py @@ -64,7 +64,6 @@ class mrp_subproduct(osv.osv): res['value'].update({'product_uom': product.uom_id.id}) return res -mrp_subproduct() class mrp_bom(osv.osv): _name = 'mrp.bom' @@ -75,7 +74,6 @@ class mrp_bom(osv.osv): 'sub_products':fields.one2many('mrp.subproduct', 'bom_id', 'Byproducts'), } -mrp_bom() class mrp_production(osv.osv): _description = 'Production' @@ -143,7 +141,6 @@ class mrp_production(osv.osv): return subproduct_factor return super(mrp_production, self)._get_subproduct_factor(cr, uid, production_id, move_id, context=context) -mrp_production() class change_production_qty(osv.osv_memory): _inherit = 'change.production.qty' diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index c069def7554..d6f026530dc 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -46,7 +46,6 @@ class stock_move(osv.osv): }) return super(stock_move, self).copy(cr, uid, id, default, context) -stock_move() class mrp_production_workcenter_line(osv.osv): @@ -371,7 +370,6 @@ class mrp_production(osv.osv): self._compute_planned_workcenter(cr, uid, ids, context=context) return result -mrp_production() class mrp_operations_operation_code(osv.osv): _name="mrp_operations.operation.code" @@ -380,7 +378,6 @@ class mrp_operations_operation_code(osv.osv): 'code': fields.char('Code', size=16, required=True), 'start_stop': fields.selection([('start','Start'),('pause','Pause'),('resume','Resume'),('cancel','Cancelled'),('done','Done')], 'Status', required=True), } -mrp_operations_operation_code() class mrp_operations_operation(osv.osv): _name="mrp_operations.operation" @@ -560,6 +557,5 @@ class mrp_operations_operation(osv.osv): 'date_start': lambda *a:datetime.now().strftime('%Y-%m-%d %H:%M:%S') } -mrp_operations_operation() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp_operations/report/mrp_workorder_analysis.py b/addons/mrp_operations/report/mrp_workorder_analysis.py index 73982b09f8b..f91961ad74c 100644 --- a/addons/mrp_operations/report/mrp_workorder_analysis.py +++ b/addons/mrp_operations/report/mrp_workorder_analysis.py @@ -71,6 +71,5 @@ class mrp_workorder(osv.osv): w.costs_hour, mp.product_id, mp.name, wl.state, wl.date_planned, wl.production_id, wl.workcenter_id )""") -mrp_workorder() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index 6c3f0ce21e1..43ee34b0c05 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -699,7 +699,6 @@ class mrp_repair_line(osv.osv, ProductChangeMixin): 'location_dest_id': scrap_location_ids and scrap_location_ids[0] or False, }} -mrp_repair_line() class mrp_repair_fee(osv.osv, ProductChangeMixin): _name = 'mrp.repair.fee' @@ -741,5 +740,4 @@ class mrp_repair_fee(osv.osv, ProductChangeMixin): 'to_invoice': lambda *a: True, } -mrp_repair_fee() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp_repair/mrp_repair_view.xml b/addons/mrp_repair/mrp_repair_view.xml index d35a9ebcf1d..a4526b5b581 100644 --- a/addons/mrp_repair/mrp_repair_view.xml +++ b/addons/mrp_repair/mrp_repair_view.xml @@ -210,7 +210,7 @@ - + diff --git a/addons/mrp_repair/wizard/cancel_repair.py b/addons/mrp_repair/wizard/cancel_repair.py index 12b85efd202..4c60f6c3251 100644 --- a/addons/mrp_repair/wizard/cancel_repair.py +++ b/addons/mrp_repair/wizard/cancel_repair.py @@ -81,7 +81,6 @@ class repair_cancel(osv.osv_memory): """ return res -repair_cancel() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mrp_repair/wizard/make_invoice.py b/addons/mrp_repair/wizard/make_invoice.py index 9442746d68e..f9c84cb7bdb 100644 --- a/addons/mrp_repair/wizard/make_invoice.py +++ b/addons/mrp_repair/wizard/make_invoice.py @@ -68,7 +68,6 @@ class make_invoice(osv.osv_memory): 'type': 'ir.actions.act_window' } -make_invoice() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/note/i18n/lt.po b/addons/note/i18n/lt.po new file mode 100644 index 00000000000..3e030054520 --- /dev/null +++ b/addons/note/i18n/lt.po @@ -0,0 +1,284 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:04+0000\n" +"PO-Revision-Date: 2013-04-29 15:23+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: note +#: field:note.note,memo:0 +msgid "Note Content" +msgstr "" + +#. module: note +#: view:note.stage:0 +msgid "Stages of Notes" +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_04 +#: model:note.stage,name:note.note_stage_02 +msgid "This Week" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_base_config_settings +msgid "base.config.settings" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_note_tag +msgid "Note Tag" +msgstr "" + +#. module: note +#: model:res.groups,name:note.group_note_fancy +msgid "Notes / Fancy mode" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_note_note +#: view:note.note:0 +msgid "Note" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "Group By..." +msgstr "" + +#. module: note +#: field:note.note,message_follower_ids:0 +msgid "Followers" +msgstr "" + +#. module: note +#: model:ir.actions.act_window,help:note.action_note_note +msgid "" +"

\n" +" Click to add a personal note.\n" +"

\n" +" Use notes to organize personal tasks or notes. All\n" +" notes are private; no one else will be able to see them. " +"However\n" +" you can share some notes with other people by inviting " +"followers\n" +" on the note. (Useful for meeting minutes, especially if\n" +" you activate the pad feature for collaborative writings).\n" +"

\n" +" You can customize how you process your notes/tasks by adding,\n" +" removing or modifying columns.\n" +"

\n" +" " +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_01 +#: model:note.stage,name:note.note_stage_01 +msgid "Today" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_res_users +msgid "Users" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "í" +msgstr "" + +#. module: note +#: view:note.stage:0 +msgid "Stage of Notes" +msgstr "" + +#. module: note +#: field:note.note,message_unread:0 +msgid "Unread Messages" +msgstr "" + +#. module: note +#: field:note.note,current_partner_id:0 +msgid "unknown" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "By sticky note Category" +msgstr "" + +#. module: note +#: help:note.note,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "" + +#. module: note +#: field:note.stage,name:0 +msgid "Stage Name" +msgstr "" + +#. module: note +#: field:note.note,message_is_follower:0 +msgid "Is a Follower" +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_02 +msgid "Tomorrow" +msgstr "" + +#. module: note +#: view:note.note:0 +#: field:note.note,open:0 +msgid "Active" +msgstr "" + +#. module: note +#: help:note.stage,user_id:0 +msgid "Owner of the note stage." +msgstr "" + +#. module: note +#: model:ir.ui.menu,name:note.menu_notes_stage +msgid "Categories" +msgstr "" + +#. module: note +#: view:note.note:0 +#: field:note.note,stage_id:0 +msgid "Stage" +msgstr "" + +#. module: note +#: field:note.tag,name:0 +msgid "Tag Name" +msgstr "" + +#. module: note +#: field:note.note,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: note +#: view:base.config.settings:0 +#: model:ir.actions.act_window,name:note.action_note_note +#: model:ir.ui.menu,name:note.menu_note_notes +#: view:note.note:0 +#: model:note.stage,name:note.note_stage_04 +msgid "Notes" +msgstr "" + +#. module: note +#: model:note.stage,name:note.demo_note_stage_03 +#: model:note.stage,name:note.note_stage_03 +msgid "Later" +msgstr "" + +#. module: note +#: model:ir.model,name:note.model_note_stage +msgid "Note Stage" +msgstr "" + +#. module: note +#: field:note.note,message_summary:0 +msgid "Summary" +msgstr "" + +#. module: note +#: field:note.note,stage_ids:0 +msgid "Stages of Users" +msgstr "" + +#. module: note +#: field:note.note,name:0 +msgid "Note Summary" +msgstr "" + +#. module: note +#: model:ir.actions.act_window,name:note.action_note_stage +#: view:note.note:0 +msgid "Stages" +msgstr "" + +#. module: note +#: help:note.note,message_ids:0 +msgid "Messages and communication history" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "Delete" +msgstr "" + +#. module: note +#: field:note.note,color:0 +msgid "Color Index" +msgstr "" + +#. module: note +#: field:note.note,sequence:0 +#: field:note.stage,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: note +#: view:note.note:0 +#: field:note.note,tag_ids:0 +msgid "Tags" +msgstr "" + +#. module: note +#: view:note.note:0 +msgid "Archive" +msgstr "" + +#. module: note +#: field:base.config.settings,module_note_pad:0 +msgid "Use collaborative pads (etherpad)" +msgstr "" + +#. module: note +#: help:note.note,message_summary:0 +msgid "" +"Holds the Chatter summary (number of messages, ...). This summary is " +"directly in html format in order to be inserted in kanban views." +msgstr "" + +#. module: note +#: field:base.config.settings,group_note_fancy:0 +msgid "Use fancy layouts for notes" +msgstr "" + +#. module: note +#: field:note.note,current_partner_id:0 +#: field:note.stage,user_id:0 +msgid "Owner" +msgstr "" + +#. module: note +#: help:note.stage,sequence:0 +msgid "Used to order the note stages" +msgstr "" + +#. module: note +#: field:note.note,date_done:0 +msgid "Date done" +msgstr "" + +#. module: note +#: field:note.stage,fold:0 +msgid "Folded by Default" +msgstr "" diff --git a/addons/note_pad/i18n/lt.po b/addons/note_pad/i18n/lt.po new file mode 100644 index 00000000000..3c814e3f7eb --- /dev/null +++ b/addons/note_pad/i18n/lt.po @@ -0,0 +1,28 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-24 18:32+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: note_pad +#: model:ir.model,name:note_pad.model_note_note +msgid "Note" +msgstr "Užrašinė" + +#. module: note_pad +#: field:note.note,note_pad_url:0 +msgid "Pad Url" +msgstr "Nuorodą į užrašinę" diff --git a/addons/pad/pad.py b/addons/pad/pad.py index 90f5354054c..4cd9e91b76e 100644 --- a/addons/pad/pad.py +++ b/addons/pad/pad.py @@ -42,7 +42,7 @@ class pad_common(osv.osv_memory): myPad.createPad(path) #get attr on the field model - model = self.pool.get(context["model"]) + model = self.pool[context["model"]] field = model._all_columns[context['field_name']] real_field = field.column.pad_content_field diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js index b96047340ad..1a2de41745b 100644 --- a/addons/pad/static/src/js/pad.js +++ b/addons/pad/static/src/js/pad.js @@ -1,67 +1,63 @@ openerp.pad = function(instance) { - instance.web.form.FieldPad = instance.web.form.AbstractField.extend({ + instance.web.form.FieldPad = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeWidgetMixin, { template: 'FieldPad', - configured: false, content: "", - start: function() { - this._super(); - var self = this; - this.on('change:effective_readonly',this,function(){ - self.renderElement(); + init: function() { + this._super.apply(this, arguments); + this.set("configured", true); + this.on("change:configured", this, this.switch_configured); + }, + initialize_content: function() { + this.switch_configured(); + this.$('.oe_pad_switch').click(function() { + self.$el.toggleClass('oe_pad_fullscreen'); }); + this.render_value(); + }, + switch_configured: function() { + this.$(".oe_unconfigured").toggle(! this.get("configured")); + this.$(".oe_configured").toggle(this.get("configured")); }, render_value: function() { - var self = this; - var _super = _.bind(this._super, this); - if (this.get("value") === false || this.get("value") === "") { - self.view.dataset.call('pad_generate_url',{context:{ + var self = this; + if (this.get("configured") && ! this.get("value")) { + self.view.dataset.call('pad_generate_url', { + context: { model: self.view.model, field_name: self.name, object_id: self.view.datarecord.id - }}).done(function(data) { - if(data&&data.url){ - self.set({value: data.url}); - _super(data.url); - self.renderElement(); + }, + }).done(function(data) { + if (! data.url) { + self.set("configured", false); + } else { + self.set("value", data.url); } }); - } else { - self.renderElement(); } - this._dirty_flag = true; - }, - renderElement: function(){ - var self = this; + this.$('.oe_pad_content').html(""); var value = this.get('value'); if (this.pad_loading_request) { this.pad_loading_request.abort(); } - if(!_.str.startsWith(value,'http')){ - this.configured = false; - this.content = ""; - }else{ - this.configured = true; - if(!this.get('effective_readonly')){ - this.content = ''; - }else{ + if (_.str.startsWith(value, 'http')) { + if (! this.get('effective_readonly')) { + var content = ''; + this.$('.oe_pad_content').html(content); + this._dirty_flag = true; + } else { this.content = '
... Loading pad ...
'; - this.pad_loading_request = $.get(value+'/export/html') - .done(function(data){ + this.pad_loading_request = $.get(value + '/export/html').done(function(data) { groups = /\<\s*body\s*\>(.*?)\<\s*\/body\s*\>/.exec(data); data = (groups || []).length >= 2 ? groups[1] : ''; self.$('.oe_pad_content').html('
'); self.$('.oe_pad_readonly').html(data); - }).error(function(){ + }).fail(function() { self.$('.oe_pad_content').text('Unable to load pad'); }); } } - this._super(); - this.$('.oe_pad_content').html(this.content); - this.$('.oe_pad_switch').click(function(){ - self.$el.toggleClass('oe_pad_fullscreen'); - }); }, }); diff --git a/addons/pad/static/src/xml/pad.xml b/addons/pad/static/src/xml/pad.xml index bd3e296f9b3..f915e867b35 100644 --- a/addons/pad/static/src/xml/pad.xml +++ b/addons/pad/static/src/xml/pad.xml @@ -5,32 +5,25 @@ - -
-

- You must configure the etherpad through the menu Settings > Companies > Companies, in the configuration tab of your company. -

-
-
- - +
+

+ You must configure the etherpad through the menu Settings > Companies > Companies, in the configuration tab of your company. +

-
-
-
+
+ +
+ &Ntilde; +
+
+
+
+
+ + - -
-
- &Ntilde; -
-
-
-
-
-
diff --git a/addons/pad_project/i18n/lt.po b/addons/pad_project/i18n/lt.po new file mode 100644 index 00000000000..2f7340a36e2 --- /dev/null +++ b/addons/pad_project/i18n/lt.po @@ -0,0 +1,39 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-24 18:35+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! Task end-date must be greater then task start-date" +msgstr "" +"Klaida! Užduotės pabaigos data negali būti ankstesnė nei pradžios data" + +#. module: pad_project +#: field:project.task,description_pad:0 +msgid "Description PAD" +msgstr "" + +#. module: pad_project +#: model:ir.model,name:pad_project.model_project_task +msgid "Task" +msgstr "Užduotis" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! You cannot create recursive tasks." +msgstr "Klaida! Negalima sukurti rekursinių užduočių." diff --git a/addons/plugin/i18n/lt.po b/addons/plugin/i18n/lt.po new file mode 100644 index 00000000000..dc120cadcf2 --- /dev/null +++ b/addons/plugin/i18n/lt.po @@ -0,0 +1,23 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-24 18:14+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: plugin +#: model:ir.model,name:plugin.model_plugin_handler +msgid "plugin.handler" +msgstr "plugin.handler" diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index f13fc255a6d..c278db2f19a 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -58,7 +58,7 @@ class plugin_handler(osv.osv_memory): res_id = msg.res_id model = msg.model url = self._make_url(cr, uid, res_id, model) - name = self.pool.get(model).name_get(cr, uid, [res_id])[0][1] + name = self.pool[model].name_get(cr, uid, [res_id])[0][1] return (model, res_id, url, name) def document_type(self, cr, uid, context=None): @@ -82,7 +82,7 @@ class plugin_handler(osv.osv_memory): @return : the result of name_search a list of tuple [(id, 'name')] """ - return self.pool.get(model).name_search(cr, uid, name) + return self.pool[model].name_search(cr, uid, name) def push_message(self, cr, uid, model, email, res_id=0): """ @@ -92,7 +92,7 @@ class plugin_handler(osv.osv_memory): @return Dictionary which contain model , url and resource id. """ mail_message = self.pool.get('mail.message') - model_obj = self.pool.get(model) + model_obj = self.pool[model] msg = self.pool.get('mail.thread').message_parse(cr, uid, email) message_id = msg.get('message-id') mail_ids = mail_message.search(cr, uid, [('message_id', '=', message_id), ('res_id', '=', res_id), ('model', '=', model)]) diff --git a/addons/point_of_sale/account_bank_statement.py b/addons/point_of_sale/account_bank_statement.py index 31415bb847c..9275b975d46 100644 --- a/addons/point_of_sale/account_bank_statement.py +++ b/addons/point_of_sale/account_bank_statement.py @@ -34,7 +34,6 @@ class account_journal(osv.osv): 'self_checkout_payment_method' : False, } -account_journal() class account_cash_statement(osv.osv): _inherit = 'account.bank.statement' @@ -42,6 +41,5 @@ class account_cash_statement(osv.osv): 'pos_session_id' : fields.many2one('pos.session'), } -account_cash_statement() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/i18n/pt_BR.po b/addons/point_of_sale/i18n/pt_BR.po index eac24526afc..4d4efabdec3 100644 --- a/addons/point_of_sale/i18n/pt_BR.po +++ b/addons/point_of_sale/i18n/pt_BR.po @@ -7,19 +7,19 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2012-10-17 00:01+0000\n" -"Last-Translator: Syllas F. de O. Neto \n" +"PO-Revision-Date: 2013-04-18 18:07+0000\n" +"Last-Translator: Thiago Tognoli \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:07+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-19 05:24+0000\n" +"X-Generator: Launchpad (build 16567)\n" #. module: point_of_sale #: field:report.transaction.pos,product_nb:0 msgid "Product Nb." -msgstr "Produto No." +msgstr "Núm. Produto" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_trans_pos_tree_today @@ -286,7 +286,7 @@ msgstr "Desconto (%)" #: report:pos.details:0 #: report:pos.details_summary:0 msgid "Total discount" -msgstr "Total de Desconto" +msgstr "Desconto total" #. module: point_of_sale #. openerp-web @@ -354,6 +354,9 @@ msgid "" "Check this if this point of sale should open by default in a self checkout " "mode. If unchecked, OpenERP uses the normal cashier mode by default." msgstr "" +"Marque esta opção se este ponto de venda deve ser aberto por padrão em modo " +"de auto atendimento. Se não marcada, o OpenERP irá usar o modo normal por " +"padrão." #. module: point_of_sale #: model:ir.actions.report.xml,name:point_of_sale.pos_sales_user @@ -444,7 +447,7 @@ msgstr "Você precisa associar um Ponto de Vendas a sua sessão." #. module: point_of_sale #: view:pos.order.line:0 msgid "Total qty" -msgstr "Total de Qtd" +msgstr "Qtd Total" #. module: point_of_sale #: model:product.template,name:point_of_sale.fanta_orange_33cl_product_template @@ -460,6 +463,10 @@ msgid "" "close this session, you can update the 'Closing Cash Control' to avoid any " "difference." msgstr "" +"Por favor, defina suas contas de lucros e perdas no seu método de pagamento " +"'%s'. Isso permitirá o OpenERP colocar a diferença de %.2f em seu saldo " +"final. Para fechar a sessão, você pode atualizar o 'Fechando Controle de " +"Caixa'para evitar qualquer diferença." #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:315 @@ -777,7 +784,7 @@ msgstr "Imprimir Relatório" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_bolognese_product_template msgid "Dr. Oetker Ristorante Bolognese" -msgstr "" +msgstr "Dr. Oetker Ristorante Bolognese" #. module: point_of_sale #: model:pos.category,name:point_of_sale.pizza @@ -787,7 +794,7 @@ msgstr "Pizza" #. module: point_of_sale #: view:pos.session:0 msgid "= Theoretical Balance" -msgstr "" +msgstr "= Balanço Teórico" #. module: point_of_sale #: code:addons/point_of_sale/wizard/pos_return.py:85 @@ -974,7 +981,7 @@ msgstr "Lista de Caixas Registradoras" #. module: point_of_sale #: model:product.template,name:point_of_sale.maes_50cl_product_template msgid "Maes 50cl" -msgstr "" +msgstr "Maes 50cl" #. module: point_of_sale #: view:report.pos.order:0 @@ -1030,7 +1037,7 @@ msgstr "Spa Reine 33cl" #. module: point_of_sale #: model:ir.model,name:point_of_sale.model_pos_discount msgid "Add a Global Discount" -msgstr "Adicionar Desconto Global" +msgstr "Adicionar um Desconto Global" #. module: point_of_sale #: view:pos.config:0 @@ -1040,7 +1047,7 @@ msgstr "Diários" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_prosciutto_product_template msgid "Dr. Oetker Ristorante Prosciutto" -msgstr "" +msgstr "Dr. Oetker Ristorante Prosciutto" #. module: point_of_sale #: model:product.template,name:point_of_sale.lays_light_paprika_170g_product_template @@ -1126,7 +1133,7 @@ msgstr "Re-impressão" #. module: point_of_sale #: model:product.template,name:point_of_sale.chimay_bleu_75cl_product_template msgid "Chimay Bleu 75cl" -msgstr "" +msgstr "Chimay Bleu 75cl" #. module: point_of_sale #: report:pos.payment.report.user:0 @@ -1156,7 +1163,7 @@ msgstr "No. de Linhas" #: code:addons/point_of_sale/static/src/xml/pos.xml:89 #, python-format msgid "Disc" -msgstr "Disco" +msgstr "Desc." #. module: point_of_sale #: view:pos.order:0 @@ -1166,7 +1173,7 @@ msgstr "(atualizar)" #. module: point_of_sale #: model:product.template,name:point_of_sale.ijsboerke_vanille_2,5l_product_template msgid "IJsboerke Vanilla 2.5L" -msgstr "" +msgstr "IJsboerke Vanilla 2.5L" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_report_pos_details @@ -1177,7 +1184,7 @@ msgstr "Detalhes da Venda" #. module: point_of_sale #: model:product.template,name:point_of_sale.evian_2l_product_template msgid "2L Evian" -msgstr "" +msgstr "2L Evian" #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:373 @@ -1185,7 +1192,7 @@ msgstr "" #: code:addons/point_of_sale/wizard/pos_session_opening.py:34 #, python-format msgid "Start Point Of Sale" -msgstr "" +msgstr "Iniciar Ponto de Venda" #. module: point_of_sale #: model:pos.category,name:point_of_sale.pils @@ -1195,7 +1202,7 @@ msgstr "" #. module: point_of_sale #: help:pos.session,cash_register_balance_end_real:0 msgid "Computed using the cash control lines" -msgstr "" +msgstr "Calculado usando as linhas de controle de caixa" #. module: point_of_sale #: report:all.closed.cashbox.of.the.day:0 @@ -1217,7 +1224,7 @@ msgstr "ABC" #. module: point_of_sale #: model:product.template,name:point_of_sale.ijsboerke_dame_blanche_2,5l_product_template msgid "IJsboerke 2.5L White Lady" -msgstr "" +msgstr "IJsboerke 2.5L White Lady" #. module: point_of_sale #: field:pos.order,lines:0 @@ -1239,7 +1246,7 @@ msgstr "" #: code:addons/point_of_sale/static/src/xml/pos.xml:485 #, python-format msgid "Read Weighting Scale" -msgstr "" +msgstr "Lendo a Balança" #. module: point_of_sale #. openerp-web @@ -1302,7 +1309,7 @@ msgstr "Marcar como Obsoleto" #. module: point_of_sale #: model:product.template,name:point_of_sale.limon_product_template msgid "Stringers" -msgstr "" +msgstr "Limões" #. module: point_of_sale #: field:pos.order,pricelist_id:0 @@ -1369,19 +1376,19 @@ msgstr "Stella Artois 33cl" #. module: point_of_sale #: model:product.template,name:point_of_sale.lays_naturel_300g_product_template msgid "Lays Natural XXL 300g" -msgstr "" +msgstr "Lays Natural XXL 300g" #. module: point_of_sale #. openerp-web #: code:addons/point_of_sale/static/src/xml/pos.xml:479 #, python-format msgid "Scan Item Unrecognized" -msgstr "" +msgstr "item Escaneado não Reconhecido" #. module: point_of_sale #: report:all.closed.cashbox.of.the.day:0 msgid "Today's Closed Cashbox" -msgstr "Fechamento de Caixa do dia" +msgstr "Fechamento de Caixa do Dia" #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:897 @@ -1397,7 +1404,7 @@ msgstr "Fatura Provisória" #. module: point_of_sale #: model:product.template,name:point_of_sale.lays_paprika_oven_150g_product_template msgid "Oven Baked Lays Paprika 150g" -msgstr "" +msgstr "Oven Baked Lays Paprika 150g" #. module: point_of_sale #: report:pos.invoice:0 @@ -1449,7 +1456,7 @@ msgstr "Coca-Cola Light 2L" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_funghi_product_template msgid "Dr. Oetker Ristorante Funghi" -msgstr "" +msgstr "Dr. Oetker Ristorante Funghi" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.pos_category_action @@ -1477,7 +1484,7 @@ msgstr "Ean Inválido" #. module: point_of_sale #: model:product.template,name:point_of_sale.lindemans_kriek_37,5cl_product_template msgid "Lindemans Kriek 37.5cl" -msgstr "" +msgstr "Lindemans Kriek 37.5cl" #. module: point_of_sale #: view:pos.config:0 @@ -1494,7 +1501,7 @@ msgstr "Coca-Cola Zero 33cl" #: code:addons/point_of_sale/static/src/xml/pos.xml:405 #, python-format msgid "ä" -msgstr "" +msgstr "ä" #. module: point_of_sale #: report:pos.invoice:0 @@ -1523,7 +1530,7 @@ msgstr "Desconhecido" #. module: point_of_sale #: field:product.product,income_pdt:0 msgid "Point of Sale Cash In" -msgstr "" +msgstr "Colocar dinheiro no Ponto de Venda" #. module: point_of_sale #. openerp-web @@ -1608,7 +1615,7 @@ msgstr "" #. module: point_of_sale #: model:product.template,name:point_of_sale.boon_framboise_37,5cl_product_template msgid "Boon Framboise 37.5cl" -msgstr "" +msgstr "Boon Framboise 37.5cl" #. module: point_of_sale #: model:ir.model,name:point_of_sale.model_pos_config @@ -1627,7 +1634,7 @@ msgstr "" #. module: point_of_sale #: field:product.product,expense_pdt:0 msgid "Point of Sale Cash Out" -msgstr "" +msgstr "Sacar do Ponto de Venda" #. module: point_of_sale #: selection:report.pos.order,month:0 @@ -1639,7 +1646,7 @@ msgstr "Novembro" #: code:addons/point_of_sale/static/src/xml/pos.xml:267 #, python-format msgid "Please scan an item or your member card" -msgstr "" +msgstr "Por favor escaneie um item ou seu cartão de associado" #. module: point_of_sale #: model:product.template,name:point_of_sale.poivron_verts_product_template @@ -1649,7 +1656,7 @@ msgstr "Pimentões Verdes" #. module: point_of_sale #: model:product.template,name:point_of_sale.timmermans_faro_37,5cl_product_template msgid "Timmermans Faro 37.5cl" -msgstr "" +msgstr "Timmermans Faro 37.5cl" #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:410 @@ -1662,7 +1669,7 @@ msgstr "" #. module: point_of_sale #: view:pos.session:0 msgid "Validate Closing & Post Entries" -msgstr "" +msgstr "Validar Encerramento e Lançar Entradas" #. module: point_of_sale #: field:report.transaction.pos,no_trans:0 @@ -1705,7 +1712,7 @@ msgstr "Cancelar" #: code:addons/point_of_sale/static/src/xml/pos.xml:296 #, python-format msgid "Please put your product on the scale" -msgstr "" +msgstr "Por favor, coloque seu produto na balança" #. module: point_of_sale #: model:ir.actions.report.xml,name:point_of_sale.pos_details_summary @@ -1715,12 +1722,12 @@ msgstr "Vendas(Resumo)" #. module: point_of_sale #: model:product.template,name:point_of_sale.nectarine_product_template msgid "Peach" -msgstr "" +msgstr "Pêssego" #. module: point_of_sale #: model:product.template,name:point_of_sale.timmermans_kriek_37,5cl_product_template msgid "Timmermans Kriek 37.5cl" -msgstr "" +msgstr "Timmermans Kriek 37.5cl" #. module: point_of_sale #: field:pos.config,sequence_id:0 @@ -1753,7 +1760,7 @@ msgstr "fechar" #. module: point_of_sale #: model:ir.actions.report.xml,name:point_of_sale.report_user_label msgid "User Labels" -msgstr "" +msgstr "Etiquetas do Usuário" #. module: point_of_sale #: model:ir.model,name:point_of_sale.model_pos_order_line @@ -1897,6 +1904,9 @@ msgid "" "complete\n" " your purchase" msgstr "" +"Por favor, insira seu cartão no leitor e aguarde as instruções para " +"completar\n" +" sua compra" #. module: point_of_sale #: help:product.product,income_pdt:0 @@ -1935,6 +1945,8 @@ msgid "" "Unable to open the session. You have to assign a sale journal to your point " "of sale." msgstr "" +"Não é possível abrir a sessão. Você tem que atribuir um diário de venda para " +"o seu ponto de venda." #. module: point_of_sale #: view:report.pos.order:0 @@ -1988,7 +2000,7 @@ msgstr "Estabelecimento:" #. module: point_of_sale #: field:account.journal,self_checkout_payment_method:0 msgid "Self Checkout Payment Method" -msgstr "" +msgstr "Forma de Pagamento do Auto Atendimento" #. module: point_of_sale #: view:pos.order:0 @@ -2124,7 +2136,7 @@ msgstr "Produtos" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_4formaggi_product_template msgid "Dr. Oetker Ristorante Quattro Formaggi" -msgstr "" +msgstr "Dr. Oetker Ristorante Quattro Formaggi" #. module: point_of_sale #: model:product.template,name:point_of_sale.croky_naturel_45g_product_template @@ -2654,7 +2666,7 @@ msgstr "Fatura" #: code:addons/point_of_sale/static/src/xml/pos.xml:701 #, python-format msgid "shift" -msgstr "" +msgstr "shift" #. module: point_of_sale #: model:product.template,name:point_of_sale.rochefort_8_33cl_product_template @@ -2786,7 +2798,7 @@ msgstr "Produto" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_pollo_product_template msgid "Dr. Oetker Ristorante Pollo" -msgstr "" +msgstr "Dr. Oetker Ristorante Pollo" #. module: point_of_sale #: constraint:pos.session:0 @@ -2836,7 +2848,7 @@ msgstr "" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_margherita_product_template msgid "Dr. Oetker La Margherita" -msgstr "" +msgstr "Dr. Oetker La Margherita" #. module: point_of_sale #: view:pos.order:0 @@ -2936,7 +2948,7 @@ msgstr "Gerente" #. module: point_of_sale #: model:product.template,name:point_of_sale.lays_poivre_sel_oven_150g_product_template msgid "Lays Salt and Pepper Oven Baked 150g" -msgstr "" +msgstr "Lays Salt and Pepper Oven Baked 150g" #. module: point_of_sale #: field:pos.details,date_start:0 @@ -2993,7 +3005,7 @@ msgstr "Procurar Pedido de Venda" #. module: point_of_sale #: field:pos.config,iface_self_checkout:0 msgid "Self Checkout Mode" -msgstr "" +msgstr "Modo de Auto Atendimento" #. module: point_of_sale #: field:account.journal,journal_user:0 @@ -3027,7 +3039,7 @@ msgstr "Relatório de Pagamento" #. module: point_of_sale #: model:product.template,name:point_of_sale.fanta_orange_25cl_product_template msgid "Fanta Orange 25cl" -msgstr "" +msgstr "Fanta Laranja 25cl" #. module: point_of_sale #: view:pos.confirm:0 @@ -3063,7 +3075,7 @@ msgstr "Motivo" #. module: point_of_sale #: model:product.template,name:point_of_sale.orangina_33cl_product_template msgid "Orangina 33cl" -msgstr "" +msgstr "Orangina 33cl" #. module: point_of_sale #: view:pos.config:0 @@ -3073,7 +3085,7 @@ msgstr "Definir como Inativo" #. module: point_of_sale #: model:product.template,name:point_of_sale.chimay_bleu_33cl_product_template msgid "Chimay Bleu 33cl" -msgstr "" +msgstr "Chimay Bleu 33cl" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_account_journal_form @@ -3089,7 +3101,7 @@ msgstr "Fritas" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_spinaci_product_template msgid "Dr. Oetker Ristorante Spinaci" -msgstr "" +msgstr "Dr. Oetker Ristorante Spinaci" #. module: point_of_sale #. openerp-web @@ -3121,7 +3133,7 @@ msgstr "Colocar dinheiro em" #. module: point_of_sale #: model:product.template,name:point_of_sale.fanta_zero_orange_1,5l_product_template msgid "Fanta Orange Zero 1.5L" -msgstr "" +msgstr "Fanta Laranja Zero 1.5L" #. module: point_of_sale #: model:product.template,name:point_of_sale.boni_orange_product_template @@ -3234,7 +3246,7 @@ msgstr "Relatório do PDV" #. module: point_of_sale #: model:product.template,name:point_of_sale.chaudfontaine_1,5l_product_template msgid "Chaudfontaine 1.5l" -msgstr "" +msgstr "Chaudfontaine 1.5l" #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:1069 @@ -3304,7 +3316,7 @@ msgstr "Coca-Cola Zero 50cl" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_tonno_product_template msgid "Dr. Oetker Ristorante Tonno" -msgstr "" +msgstr "Dr. Oetker Ristorante Tonno" #. module: point_of_sale #: report:pos.invoice:0 @@ -3346,7 +3358,7 @@ msgstr "cash.box.out" #. module: point_of_sale #: model:product.template,name:point_of_sale.fanta_zero_orange_33cl_product_template msgid "Fanta Zero Orange 33cl" -msgstr "" +msgstr "Fanta Laranja Zero 33cl" #. module: point_of_sale #: help:product.product,available_in_pos:0 @@ -3361,7 +3373,7 @@ msgstr "Dia da data do pedido" #. module: point_of_sale #: model:product.template,name:point_of_sale.maes_33cl_product_template msgid "Maes 33cl" -msgstr "" +msgstr "Maes 33cl" #. module: point_of_sale #. openerp-web @@ -3378,7 +3390,7 @@ msgstr "Configuração" #. module: point_of_sale #: model:product.template,name:point_of_sale.orval_33cl_product_template msgid "Orval 33cl" -msgstr "" +msgstr "Orval 33cl" #. module: point_of_sale #. openerp-web @@ -3411,7 +3423,7 @@ msgstr "Frutas Frescas" #. module: point_of_sale #: model:product.template,name:point_of_sale.lindemans_pecheresse_37,,5cl_product_template msgid "Lindemans sinful 37.5cl" -msgstr "" +msgstr "Lindemans sinful 37.5cl" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_pos_invoice @@ -3431,7 +3443,7 @@ msgstr "Imagem" #. module: point_of_sale #: model:product.template,name:point_of_sale.spa_gazeuse_1,5l_product_template msgid "Spa Barisart 1.5l" -msgstr "" +msgstr "Spa Barisart 1.5l" #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:789 @@ -3443,7 +3455,7 @@ msgstr "Retornar Produtos" #. module: point_of_sale #: model:product.template,name:point_of_sale.jupiler_33cl_product_template msgid "Jupiler 33cl" -msgstr "" +msgstr "Jupiler 33cl" #. module: point_of_sale #. openerp-web @@ -3551,7 +3563,7 @@ msgstr "Balança Eletrônica" #. module: point_of_sale #: model:product.template,name:point_of_sale.spa_gazeuse_50cl_product_template msgid "Spa Barisart 50cl" -msgstr "" +msgstr "Spa Barisart 50cl" #. module: point_of_sale #: field:res.users,pos_config:0 @@ -3595,7 +3607,7 @@ msgstr "Pago" #: code:addons/point_of_sale/static/src/xml/pos.xml:464 #, python-format msgid "3.141Kg Oranges" -msgstr "" +msgstr "3.141Kg de Laranjas" #. module: point_of_sale #: report:pos.invoice:0 @@ -3611,7 +3623,7 @@ msgstr "Por favor informe um parceiro para a venda" #. module: point_of_sale #: model:pos.category,name:point_of_sale.fruity_beers msgid "Fruity Beers" -msgstr "" +msgstr "Cerveja de Frutas" #. module: point_of_sale #: view:pos.session:0 @@ -3623,7 +3635,7 @@ msgstr "" #: code:addons/point_of_sale/static/src/xml/pos.xml:463 #, python-format msgid "Soda 33cl" -msgstr "" +msgstr "Soda 33cl" #. module: point_of_sale #: help:pos.session,cash_register_balance_start:0 @@ -3644,7 +3656,7 @@ msgstr "Vendas do Usuário" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_hawaii_product_template msgid "Dr. Oetker Ristorante Hawaii" -msgstr "" +msgstr "Dr. Oetker Ristorante Hawaii" #. module: point_of_sale #. openerp-web @@ -3678,12 +3690,12 @@ msgstr "" #. module: point_of_sale #: model:product.template,name:point_of_sale.chaudfontaine_petillante_33cl_product_template msgid "Chaudfontaine Petillante 33cl" -msgstr "" +msgstr "Chaudfontaine Petillante 33cl" #. module: point_of_sale #: model:product.template,name:point_of_sale.pepsi_max_2l_product_template msgid "Pepsi Max 2L" -msgstr "" +msgstr "Pepsi Max 2L" #. module: point_of_sale #: field:pos.session,details_ids:0 @@ -3693,17 +3705,17 @@ msgstr "Controle de Caixa" #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_vegetale_product_template msgid "Dr. Oetker Ristorante Vegetable" -msgstr "" +msgstr "Dr. Oetker Ristorante Vegetable" #. module: point_of_sale #: model:pos.category,name:point_of_sale.soda msgid "Soda" -msgstr "" +msgstr "Soda" #. module: point_of_sale #: model:product.template,name:point_of_sale.lays_paprika_300g_product_template msgid "Lays Paprika XXL 300g" -msgstr "" +msgstr "Lays Paprika XXL 300g" #. module: point_of_sale #: report:pos.invoice:0 @@ -3715,7 +3727,7 @@ msgstr "Reembolso" #: code:addons/point_of_sale/static/src/xml/pos.xml:417 #, python-format msgid "Your shopping cart is empty" -msgstr "" +msgstr "Seu Carrinho de Compras está vazio" #. module: point_of_sale #: code:addons/point_of_sale/report/pos_invoice.py:46 @@ -3726,7 +3738,7 @@ msgstr "Por favor crie uma fatura para esta venda." #. module: point_of_sale #: model:product.template,name:point_of_sale.oetker_mozzarella_product_template msgid "Dr. Oetker Ristorante Mozzarella" -msgstr "" +msgstr "Dr. Oetker Ristorante Mozzarella" #. module: point_of_sale #: report:pos.details:0 @@ -3737,7 +3749,7 @@ msgstr "Desc(%)" #. module: point_of_sale #: view:pos.session.opening:0 msgid "The session" -msgstr "" +msgstr "A sessão" #. module: point_of_sale #: view:pos.order:0 @@ -3762,7 +3774,7 @@ msgstr "Ordem de Linhas" #. module: point_of_sale #: view:pos.session.opening:0 msgid "PoS Session Opening" -msgstr "" +msgstr "Abrindo Sessão do PDV" #. module: point_of_sale #: field:pos.order.line,price_subtotal:0 @@ -3780,7 +3792,7 @@ msgstr "excluir" #. module: point_of_sale #: model:product.template,name:point_of_sale.spa_50cl_product_template msgid "Spa Reine 50cl" -msgstr "" +msgstr "Spa Reine 50cl" #. module: point_of_sale #: model:product.template,name:point_of_sale.chicon_flandria_extra_product_template @@ -3791,22 +3803,22 @@ msgstr "" #: model:product.template,name:point_of_sale.lays_light_naturel_170g_product_template #: model:product.template,name:point_of_sale.lays_naturel_170g_product_template msgid "Lays Natural Light 170g" -msgstr "" +msgstr "Lays Natural Light 170g" #. module: point_of_sale #: model:product.template,name:point_of_sale.leffe_blonde_33cl_product_template msgid "Leffe Blonde 33cl" -msgstr "" +msgstr "Leffe Blonde 33cl" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_report_pos_payment msgid "Pyament Report" -msgstr "" +msgstr "Relatório de Pagamento" #. module: point_of_sale #: field:report.transaction.pos,jl_id:0 msgid "Cash Journals" -msgstr "" +msgstr "Diários de Dinheiro" #. module: point_of_sale #: view:pos.session:0 @@ -3816,12 +3828,12 @@ msgstr "Sessão:" #. module: point_of_sale #: field:pos.config,iface_print_via_proxy:0 msgid "Print via Proxy" -msgstr "" +msgstr "Imprimir via Proxy" #. module: point_of_sale #: report:pos.sales.user.today:0 msgid "Today's Sales By User" -msgstr "" +msgstr "Vendas de hoje por Usuário" #. module: point_of_sale #: report:pos.invoice:0 @@ -3858,7 +3870,7 @@ msgstr "Anotações Internas" #. module: point_of_sale #: model:product.template,name:point_of_sale.ijsboerke_chocolat_2,5l_product_template msgid "IJsboerke Chocolat 2.5L" -msgstr "" +msgstr "IJsboerke Chocolat 2.5L" #. module: point_of_sale #: help:pos.category,image_small:0 @@ -3871,7 +3883,7 @@ msgstr "" #. module: point_of_sale #: field:pos.session.opening,pos_state:0 msgid "Session Status" -msgstr "" +msgstr "Status da Sessão" #. module: point_of_sale #: field:pos.order,picking_id:0 @@ -3915,7 +3927,7 @@ msgstr "Editar" #. module: point_of_sale #: view:pos.session.opening:0 msgid "Click to continue the session." -msgstr "" +msgstr "Clique para continuar a sessão." #. module: point_of_sale #: view:pos.config:0 @@ -3930,7 +3942,7 @@ msgstr "" #. module: point_of_sale #: view:pos.session.opening:0 msgid "Start Selling" -msgstr "" +msgstr "Começar a Vender" #. module: point_of_sale #: selection:report.pos.order,month:0 @@ -3963,7 +3975,7 @@ msgstr "" #. module: point_of_sale #: model:product.template,name:point_of_sale.leffe_9_33cl_product_template msgid "Leffe Brune \"9\" 33cl" -msgstr "" +msgstr "Leffe Brune \"9\" 33cl" #. module: point_of_sale #: help:account.journal,journal_user:0 @@ -4011,7 +4023,7 @@ msgstr "" #: code:addons/point_of_sale/static/src/xml/pos.xml:351 #, python-format msgid "Sorry, we could not create a session for this user." -msgstr "" +msgstr "Desculpe, não podemos criar uma sessão para esse usuário." #. module: point_of_sale #: code:addons/point_of_sale/point_of_sale.py:98 @@ -4019,7 +4031,7 @@ msgstr "" #: selection:pos.session.opening,pos_state:0 #, python-format msgid "Opening Control" -msgstr "" +msgstr "Abrindo Controle" #. module: point_of_sale #: view:report.pos.order:0 diff --git a/addons/point_of_sale/i18n/zh_CN.po b/addons/point_of_sale/i18n/zh_CN.po index e4d914ae56e..23dc97f2376 100644 --- a/addons/point_of_sale/i18n/zh_CN.po +++ b/addons/point_of_sale/i18n/zh_CN.po @@ -7,14 +7,14 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-12-21 17:04+0000\n" -"PO-Revision-Date: 2012-05-10 18:26+0000\n" -"Last-Translator: 开阖软件 Jeff Wang \n" +"PO-Revision-Date: 2013-04-24 06:41+0000\n" +"Last-Translator: viney \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:07+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" #. module: point_of_sale #: field:report.transaction.pos,product_nb:0 @@ -67,7 +67,7 @@ msgstr "" #. module: point_of_sale #: model:pos.category,name:point_of_sale.plain_water msgid "Plain Water" -msgstr "白开水" +msgstr "纯净水" #. module: point_of_sale #: model:product.template,name:point_of_sale.poire_conference_product_template @@ -2269,7 +2269,7 @@ msgstr "7月" #: model:ir.ui.menu,name:point_of_sale.menu_pos_config_pos #: view:pos.session:0 msgid "Point of Sales" -msgstr "" +msgstr "零售" #. module: point_of_sale #: report:pos.details:0 @@ -2303,7 +2303,7 @@ msgstr "打印数" #. module: point_of_sale #: model:ir.model,name:point_of_sale.model_pos_make_payment msgid "Point of Sale Payment" -msgstr "POS付款" +msgstr "付款" #. module: point_of_sale #: model:product.template,name:point_of_sale.coca_light_50cl_product_template @@ -2407,7 +2407,7 @@ msgstr "" #. module: point_of_sale #: model:ir.model,name:point_of_sale.model_pos_receipt msgid "Point of sale receipt" -msgstr "POS收银条" +msgstr "收银条" #. module: point_of_sale #: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_margin_pos_today diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 7e8487b2b2b..349ad8b4f81 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -50,7 +50,7 @@ class pos_config(osv.osv): required=True, help="An internal identification of the point of sale"), 'journal_ids' : fields.many2many('account.journal', 'pos_config_journal_rel', 'pos_config_id', 'journal_id', 'Available Payment Methods', - domain="[('journal_user', '=', True )]",), + domain="[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]",), 'shop_id' : fields.many2one('sale.shop', 'Shop', required=True), 'journal_id' : fields.many2one('account.journal', 'Sale Journal', @@ -408,6 +408,9 @@ class pos_session(osv.osv): if not self.pool.get('ir.model.access').check_groups(cr, uid, "point_of_sale.group_pos_manager"): raise osv.except_osv( _('Error!'), _("Your ending balance is too different from the theorical cash closing (%.2f), the maximum allowed is: %.2f. You can contact your manager to force it.") % (st.difference, st.journal_id.amount_authorized_diff)) + if (st.journal_id.type not in ['bank', 'cash']): + raise osv.except_osv(_('Error!'), + _("The type of the journal for your payment method should be bank or cash ")) if st.difference and st.journal_id.cash_control == True: if st.difference > 0.0: name= _('Point of Sale Profit') @@ -428,7 +431,6 @@ class pos_session(osv.osv): if st.journal_id.type == 'bank': st.write({'balance_end_real' : st.balance_end}) - getattr(st, 'button_confirm_%s' % st.journal_id.type)(context=context) self._confirm_orders(cr, uid, ids, context=context) self.write(cr, uid, ids, {'state' : 'closed'}, context=context) @@ -522,6 +524,24 @@ class pos_order(osv.osv): self.signal_paid(cr, uid, [order_id]) return order_ids + def write(self, cr, uid, ids, vals, context=None): + res = super(pos_order, self).write(cr, uid, ids, vals, context=context) + #If you change the partner of the PoS order, change also the partner of the associated bank statement lines + partner_obj = self.pool.get('res.partner') + bsl_obj = self.pool.get("account.bank.statement.line") + if 'partner_id' in vals: + for posorder in self.browse(cr, uid, ids, context=context): + if posorder.invoice_id: + raise osv.except_osv( _('Error!'), _("You cannot change the partner of a POS order for which an invoice has already been issued.")) + if vals['partner_id']: + p_id = partner_obj.browse(cr, uid, vals['partner_id'], context=context) + part_id = partner_obj._find_accounting_partner(p_id).id + else: + part_id = False + bsl_ids = [x.id for x in posorder.statement_ids] + bsl_obj.write(cr, uid, bsl_ids, {'partner_id': part_id}, context=context) + return res + def unlink(self, cr, uid, ids, context=None): for rec in self.browse(cr, uid, ids, context=context): if rec.state not in ('draft','cancel'): @@ -886,6 +906,7 @@ class pos_order(osv.osv): account_tax_obj = self.pool.get('account.tax') user_proxy = self.pool.get('res.users') property_obj = self.pool.get('ir.property') + cur_obj = self.pool.get('res.currency') period = account_period_obj.find(cr, uid, context=context)[0] @@ -948,9 +969,9 @@ class pos_order(osv.osv): }) if data_type == 'product': - key = ('product', values['product_id'],) + key = ('product', values['partner_id'], values['product_id']) elif data_type == 'tax': - key = ('tax', values['tax_code_id'],) + key = ('tax', values['partner_id'], values['tax_code_id'],) elif data_type == 'counter_part': key = ('counter_part', values['partner_id'], values['account_id']) else: @@ -981,17 +1002,19 @@ class pos_order(osv.osv): #TOFIX: a deep refactoring of this method (and class!) is needed in order to get rid of this stupid hack assert order.lines, _('The POS order must have lines when calling this method') # Create an move for each order line + + cur = order.pricelist_id.currency_id for line in order.lines: tax_amount = 0 taxes = [t for t in line.product_id.taxes_id] computed_taxes = account_tax_obj.compute_all(cr, uid, taxes, line.price_unit * (100.0-line.discount) / 100.0, line.qty)['taxes'] for tax in computed_taxes: - tax_amount += round(tax['amount'], 2) + tax_amount += cur_obj.round(cr, uid, cur, tax['amount']) group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id'], tax['id']) group_tax.setdefault(group_key, 0) - group_tax[group_key] += round(tax['amount'], 2) + group_tax[group_key] += cur_obj.round(cr, uid, cur, tax['amount']) amount = line.price_subtotal @@ -1026,7 +1049,7 @@ class pos_order(osv.osv): 'debit': ((amount<0) and -amount) or 0.0, 'tax_code_id': tax_code_id, 'tax_amount': tax_amount, - 'partner_id': order.partner_id and order.partner_id.id or False + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False }) # For each remaining tax with a code, whe create a move line @@ -1044,6 +1067,7 @@ class pos_order(osv.osv): 'debit': 0.0, 'tax_code_id': tax_code_id, 'tax_amount': tax_amount, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False }) # Create a move for each tax group @@ -1060,6 +1084,7 @@ class pos_order(osv.osv): 'debit': ((tax_amount<0) and -tax_amount) or 0.0, 'tax_code_id': key[tax_code_pos], 'tax_amount': tax_amount, + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False }) # counterpart @@ -1068,14 +1093,17 @@ class pos_order(osv.osv): 'account_id': order_account, 'credit': ((order.amount_total < 0) and -order.amount_total) or 0.0, 'debit': ((order.amount_total > 0) and order.amount_total) or 0.0, - 'partner_id': order.partner_id and order.partner_id.id or False + 'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False }) order.write({'state':'done', 'account_move': move_id}) + all_lines = [] for group_key, group_data in grouped_data.iteritems(): for value in group_data: - account_move_line_obj.create(cr, uid, value, context=context) + all_lines.append((0, 0, value),) + if move_id: #In case no order was changed + self.pool.get("account.move").write(cr, uid, [move_id], {'line_id':all_lines}, context=context) return True @@ -1103,7 +1131,6 @@ class account_bank_statement(osv.osv): _defaults = { 'user_id': lambda self,cr,uid,c={}: uid } -account_bank_statement() class account_bank_statement_line(osv.osv): _inherit = 'account.bank.statement.line' @@ -1111,7 +1138,6 @@ class account_bank_statement_line(osv.osv): 'pos_statement_id': fields.many2one('pos.order', ondelete='cascade'), } -account_bank_statement_line() class pos_order_line(osv.osv): _name = "pos.order.line" @@ -1279,7 +1305,7 @@ class ean_wizard(osv.osv_memory): ean13 = openerp.addons.product.product.sanitize_ean13(r.ean13_pattern) m = context.get('active_model') m_id = context.get('active_id') - self.pool.get(m).write(cr,uid,[m_id],{'ean13':ean13}) + self.pool[m].write(cr,uid,[m_id],{'ean13':ean13}) return { 'type' : 'ir.actions.act_window_close' } class product_product(osv.osv): diff --git a/addons/point_of_sale/point_of_sale_demo.xml b/addons/point_of_sale/point_of_sale_demo.xml index 011c6d81bfc..1c61218a4cb 100644 --- a/addons/point_of_sale/point_of_sale_demo.xml +++ b/addons/point_of_sale/point_of_sale_demo.xml @@ -430,6 +430,7 @@ True 1.49 Coca-Cola Regular 1L + 5449000054227 /9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAIgMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZqYHHfEPXNS0W3sjp0zRGUvvKqD0246g+tc+InKNrHqZVhaVdy5+ljlPCfjPxBf65aW1xevLFJOqOCi8gn6cVnGrPnSPRxuW4elRlJLWx65XWfNENusizXJcMFaUFMngjYo4+Y8ZB7L34/iKW7NKjTjG3b9X5L9fXopqozMPX9Kt9YvtPtp9+AZHbbj7oA9fcrUyipbmtGvOldxdjPPhPS9G1PT7q2EiKbkK3Ixkqdp4H97ApKnFO9jWpja1SNpSOsqzlCgAoArwx77mS6JyGUJH7KOSfxP8hSAW9thd2rRZ2tkMjf3WByp/AgUASROZIwxG1v4l9D3FAD6YHLeN/FqeHFsLSMj7VqMxRSULhEAyzFQRnqB17+1IaKUnjCyaBmHiSOHaMk+SqY/wC+gaGmBHY+NLCWUr/wlUE5AztAjb/0FaSuN2J7PxvAfFNnpj3Kzw3yskcyxFMSDkAknnIyBxT2EdnTEeQ/EzzW8eRSNG7Rw6avl4GQCXbd+PSlzJM1jRqShzJNo4TVZlawmRchihzuBH86pmRz+hXH2bUN57rjHryKSA7RLl7i805rdJfNivoJI2CHghx/TNKTRpCnOeyufQtMzPMvH8ck3jWyjQqubQl2Y4CqNxJPsAP0rlrrmkkfSZI4xw1RvuvvOQ1jRb2S58mGzmmQkKD5RBZiM4xzyKy5ZR0R69OthpQvNxKFt4fZIvtV1ss4wCy7kyzD1A9O2TjJ49cP3t2yFKgp8sIJv5HQabZJb+INMiNys7M+4IsZXCkcE5789O1OEUpIxxc+fC1Go2S8/P8AI9rrtPjThvFMFtc+Kmha4SC+axQ2pkfarne+VyQQD909OcVhUScj2MvlOFHmSvHm1t6b/mcnfalqOk2k8F9ptxb3e3y0meMMm3LHgjA6uemRwOOKxcnFao9inh6NealCacd7Xs+nr28iD/hINMnu/Mj0ySaNGjWNTCPljHDLwTzjAB7jPAycnPG+wLA1oxs5pb9evQ07RpNV8R6fcxyQxrEwHkyTqZXy3zPtHT6HFVH3pI5sRy0MJOLTd+ttF2V2epW/2jz7rzs+X5o8jgfc2Lnp/tbuvP4YrojzXd++n3L9bnztTk5Ycu9tfW7/AEtt+Z458X9Xaw8ZRwTQxvFLYIUZs5U73yQR9KmpT5zuy7M3hNLO1+hx8fim5ih2pqF1GmMbUnOPyBrH2NRdT2f7Wy+erhr5pFV9eS4bDzyyn/bJP86n2M2aRznBw2/I2YPFLat4u0JIIFidbi3t9qvnOJBzxxznpWypu6b6Hj1MypqjOnFN8zbu/P7z6Lrc8c8S/aBtNuoaLegf6yKWIn/dKkf+hGkB5ZkGIY9KBobH1pgdJ8NLM6h8SdJTGRHMZT7bFLD9QKQj6epgeQftAzxCy0W3JBmMkrj2UBQf1I/KkB44n+pH0oGgi5NAHafBiWKL4jQLKQGkglVM/wB7Gf5A0CPo6mB4P8ab42/jy286FZol09Aqt2y75IpDOEuZ7CaycwwBJCOMKP6UwKWnNCs5NwhdMdMZ7ikgOi8J3UMnjrQ/sNsIdt/EC4PJBYAj8iaGB9PUxHkXx50Ca4srDXYI2cWxaCcgZ2q3Kk+2cj8RSYHj39najb6d/aD2c62bSeWJmjIRm54DdCeDQncbTTG2lle6iJhY2k0/kx+ZL5SFiq5AycdByKLgdr8H9BuNS8ZwXJhb7PYjzpHK8A4+UZ9SSD9AaSdxuLW59F1RIjKrqVdQykYIIyDQBR1nRNP13SJdL1C3EtrKACg4xjkEEdCKTApeGPBuieEYpk0i2MZuGBkd3LscdBk9vb3oSA2Y4YoQwijSPcdzbVAyfU0wH0AA/9k= @@ -716,6 +717,7 @@ True 0.44 + 5449000111715 Chaudfontaine 50cl /9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAJAMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZqYHnfj34gaj4a1xdPtYU8vyVkLkckkn1+ld2EwsKsbswq1XF2Oa/4XBqwmUxwqVHVWAwf6/rXQ8DTSIVeVz2KxuGurC3uGXa0sSuR6EjNeVJWbR0rYnpDCgDjvFfhCw8Qaul1PJIJEiEeF6dSR05712Yau6cLW6nLXg5S0ZjS/DLS127pJl7jk5/Umt/rjfQy9jJPc9Ht0WK3jjQ5VVAB9gK8x7neh9AEVxN5SD1PApxVxN2M6Kzja5EsjMxJyd2TWzm7GSpJu5NfWkE6qehHGRkVMJtDnTUh1o/kAQlmYE8Fj0pT11Kh7uheFZllG/fFzChHBBOffitILRkyeqEQ9xxTAVizD5jxRawEEj7GTC7jvUYzzyadrkt2NWsTQp34O6I4yMkGrh1JkInTjiqBDj065pAQMf38YC/xin0F1NIVkWUdVuBbQxOy5UyYJ/u8HmtKUeZsipLlRQj1WFpBGJV3HoCwAP0zWrpSS2M1VVy5HOZc8AAdwwNQ1YtSuRifddQBFMgdyCw6LgHn9KbjowUtUaorA0KGsqr2O1hkFhWtF+8Z1fhOM8Q38+lWEUtoUWR5QnIz8pB/wr08LSjVk1LscdRuK0LHhi9uzqd7b3dzHMsUalWRcA5J/wAKjFwhyRcVbU0otxk02dJFJvvIdvIyc4PHSuGStFnRF3ZrVgaFLVl3WWP9qtKPxGdX4Th/Elk11p8fzJEsEnms7HBAAPAHfqK9TCVFGb63VjkqQukL4fkeTxDqIJP3IhyMdvTJ/nRiElRh8xxbdRnWWg/02PA9ea86psdNPc2KwNSrqS7rNuM4NXS+Iip8JwvimSKKxhMi7gJRhcZ5wSMj0yK9XBpuTt2OOrbQk8NKy+JNRXbjbHFngZ6Z55NTiv4EPVl0v4jOsgx9uiA9D0rz5fCzoW5qViaFe9lSKEbj944qoK7Jk0jkvFGmTahZW4trbzylwruowCVwc9cZ/OvQwdZU5O7toc1aDklZdSPwxp99aahfXN5bvH5qxhS5zkgHOOTxVYurCUIqL2uFGMlJto6W3KLex5IBOQAK4ZX5WbppM1KxNDP1faYUDlQuTkscCtKZnUa6meiyKoUF9o7A5qxaj2LqNyll/wA+9AbDLfbFfRscb5HHfJNNq8WSnaR0Fc5uVdRhaa3wq7sHp61dN2ZM1dGBftLDAipByuemRg4rqppNnJVbtYjmYyuskI81VOMEkD8hjNacqWj0MeZ3utTWsIJXlVzEyqDksx61y1Gl1Oyld62NesDcOtAAQD1FAAFA6AUBYKACgAD/2Q== @@ -830,6 +832,7 @@ True 0.77 + 5410228142027 Jupiler 33cl /9j/4AAQSkZJRgABAAEBLAEsAAD/2wCEAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9PjsBCwoKDg0OHBYWHCgoKCgoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O//AABEIAGQAPQMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APZqYGdqet2+myJE6tJK4yFXgAepNACJqrPD5reTEvbc+aVwKU2vyR7mDx7FOCxUj+dMC3Z61bzxF5LuFAOu4hcfnQBZmv444fNSSKVc4+Vuv40gH2l7Hdq20FXX7yt1FMCduq/X+hrCs7Tp6/a/9tf9df1TXUWtxHlnjvxjFDrotYbWG6jiUBw5Iyc+oNc1XEckrI9vA5OsRS5pNrsT6ZrM2q2izw6LdqgGP9HuQR+RIq41m1sYYjK1Slb2i+af/BM+58UwaTcukkWqW7scssipyfzxSdeK3TFDJ681eLi/n/wCH/hOLa/22wtbmY9gIkGfxzmhYiL2uOWT14q7cV/XoQax4pvbWJ3fT3O8/wDLWXp+A/xode3QKWVqb1kbPwv8YG/1G6sL3y0lmw0RGecdsk06VXm3IxuCVGKcT05vvJ9f6GprtKdP/F/7bLz/AM/TqvPWzKmsySQ6PdyRNtdYjg1tLYuik6kU+58838N4buSW5Us7sSWHIry5KV9T7yg6fIlE9M8JXsen+H4kKHcFy2EJH5gV2UmlE+fzCnKdZtHCeM9QF/rDPECwAxjpzWNWze56GB56VKzgzM0afyNRjlcbcHpmpgkpbmuIc50n7up0Wul9TtnjghZnK/KAh6/lW7SaPLp88HrZfMx9K8O+ILO4g1EW0losci/vJPl6kDp170owknew8RiqDg4tpu3Q+homZoYWJySASfXitat+aFv5v/bX56/j3t1Xz3cr60M6Ld/9cjWstmXQ/iR9TxS6P71vrXDI+upbGTqSkBJF4PQkVnJaHXRe6MwmoNGy3owDa7YqwyDcICD3+YVrRXvo4ce2qEj2Hw8Uj8L6YzFUBtYuTxztFejT+Beh8ljP48/8T/Mi190bTmQMCyzRbh6fOKU/hIpb/I7WH/UQf7o/lWVX46f+L/21+f8An6dVK6kGsDOkXQ/6ZGtpbF0vjXqeXaZ4f1K71CC7TTftNoJfnDFQrKDg9T9a5I05N3sfQVcZShTceazsdhqWhaBoqTakdKNwXQQpaxoH3seyqe5/kK6lSgnseJLHYiUbOTOZ8PQ21zo+owaX4PvLO8jjkMU2pxKMyH7qhm5wM+nQeppRpQjsgqY7EVLc02YGl/D3xDY6nZzSwR+XDKjM3nKTgEZNYRoz57nq1cwoPDezTbdkburo1z8MrAQW0106Jb/u4SQxKldwyBkdDz2rpirRR4uIkp1pNbNsWHUtW1Pw9LcarZRWZNxH5KRybyU3DknJ759OlKfwip7v0PUIR+4i/wB0fyqZptxt3/R+a/X06qSDVf8AkFXX/XI/yq5bFU/jXqeY6tPdJHYQQ3j2sCWrSyuHKqo8x8scdew9+BXM1JtJHuUp0qcJzmk9f0RVXSfEGsWC3OnWV89rIuY557tAz88OEJ4/M8d619k0tGcX1+M5WcUtdLLb17nHajNr2l3T292buKVeShmYE/nXLKLT1bR7sainDmjTjJeVv1sbPgDWLyfxbaRS3VwyP5iPHJITg7CeQT64rSkpRqJNnBjJUK+EnKEbNNdLNanRatb3E3w/ms4LaW4KX8kbwxEgui3DZGe3A611vY+e6lLw1Hcx+D5Eu3jEsd1FGIo5Ffy1XbgEgnBPJx71MvhNIbv0PYoT+4i91H8qUpNOPm/0ZJDqn/ILuf8Ark38qp7Dh8SPJ/EsTXel2VvE+17tUtwScLxLITk+nCn8Kzprr5HbjJWjy/3r/gjF/wCElN5pVroOq2Yc2JKQXK3PkFAOMHPB6Y5x0FanCVNZ8aXceiadp+o6ba3v+jbkmuA/nINzAHII4KhSCRzU1KfMjpweKeHldX+T/wCAxvw/lF740tLhE27mAK5zjbERn8cGs7WlFHWqiqUa89rtafM3NY0qLUXurlrPUrsxySukMEmyJs3Eg5O089z7YrZnlFvQ7c23hSbdY21mjXiYjgm80nkZ3NuPze3H0qJfCaQ+J+h69EMwxc9FB/SpnTcpRd9nf8GvluRfcS72/ZJdy7l2HK+taivY8f8AEjJNbQadE2xlvrhY+efu/uxn/eJH41nT6rzO3Gp+4+8UcncPZalCj30/2K8OS8phJSYdmOOQ3XoMHj3NaI4zIuYF1rWb+5UMIHaTyAeMsQxjTH0XAHtincVjp/hParFqF5qk3ENjAzsfQ4I/kT+VRb3jojLloyXdr/M63V4vtPw8s4xazXLS+TI0UOdzksGb6ZyeardGEo8smjI0611Kz0+7VtOj0/TmuIylv5xkkWT5Ov1HPOOtRPYunu/Q9oh/1Ef+6P5VZmNu/wDj0lz/AHDQB5JNHZ3Pis219lYnaZRMT/qpXOFb8MD8zWF46q+7PWUKzcJqN1GK+fcxNa0ud/EHkajClvDZKwIUZLrlm4Hc/Mfr+NNztp1M44duXtOW8fLr8vzMfyZ7283xwt9onYBIkB+QfwoBWdWbbsj08twsKdN1Zqzf4I7O/wDJ8MaTa6CRG2papKkt+V7LkcH3OMe/zHvW6fKknueNVjGtVnOCtFFzUvL/AOEBsvOe8SPyoQfsY/eNwOBVR+FHPW/iS9WZ+nW32XwpdqlhcWSNfRskdy2ZCMKMngdSD2qanwhT3foewwf6iP8A3R/KrM2Nuji0lPohoA8W17jVrv8A67P/AOhGuGp8TPrcD/Bj6I1LTV9L1uyjsPEJaOWJdsN8n3gPRvX/AD9auMozVpfec1bD1cPJzo6p7x/yIZfEPh7w0jjw7aSXt8wx9ruV+VPp0P5AfWrUqdPY55Ucbi/j91djlbFp9R8R29xeytLLNcKXYnk81EZ80zorYRUsO0tEdwNF1XVf7Nt4dWudP08aZE2Lfgu/RhnIxwR611Q+Feh4GI0qy9WR3nhyPw/bTrbSTSQTmAs0r7maQO2SfwIqanwipbv0PUrf/j2i/wBwfyqzNjpE3xsp/iGKAPHfEqyWus3aXNu2Gkcpu9CSQQT1riqpqTPp8DaVGPK+xjEQvKRuKR46kZNZna3NR7spSQQszE3IHJH3fyoKdSaXwk2kRxrq1kwlDN56ZXB45q6fxI5cbKToyTX9XPXNGH/Eh0//AK9Y/wD0EV2w+FHzGJ/jT/xP8yvr1jcajZx2tsm+Rpk/AZ5NE1dEUmk36HXRp5capnO0AZpkjqAKt/p1tqEJjuIY5R6SKGFJpMuFSUHeLaOUvvAmkuWJtJ4Ae9vJkfrmsnRh2O2nmuIj1T9TDn+H+kBj/p92nsyg/wBKn2MfM6Fndb+VDLXwZpFldxXC3t5K8ThgoQDJH4U404p31M6+bVasHFxSudbYwXn2WG1tLORYoY1jWS5O3gDAPv8AhWqemh5s5OcnJ9Wb9naC1j+Zt8h+82MfkPSqJLFABQAUAFAAQD1GaACgAoAKACgA/9k= diff --git a/addons/point_of_sale/report/pos_order_report.py b/addons/point_of_sale/report/pos_order_report.py index 62250f11e17..f4df5f0f5f6 100644 --- a/addons/point_of_sale/report/pos_order_report.py +++ b/addons/point_of_sale/report/pos_order_report.py @@ -84,6 +84,5 @@ class pos_order_report(osv.osv): having sum(l.qty * u.factor) != 0)""") -pos_order_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/report/pos_report.py b/addons/point_of_sale/report/pos_report.py index 0179c7bcf45..96b056fb0fc 100644 --- a/addons/point_of_sale/report/pos_report.py +++ b/addons/point_of_sale/report/pos_report.py @@ -74,7 +74,6 @@ class report_transaction_pos(osv.osv): """) #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD') #to_char(date_trunc('day',absl.create_date),'YYYY-MM-DD')::text as date_create, -report_transaction_pos() class report_sales_by_user_pos(osv.osv): _name = "report.sales.by.user.pos" @@ -107,7 +106,6 @@ class report_sales_by_user_pos(osv.osv): ) """) -report_sales_by_user_pos() class report_sales_by_user_pos_month(osv.osv): _name = "report.sales.by.user.pos.month" @@ -140,7 +138,6 @@ class report_sales_by_user_pos_month(osv.osv): ) """) -report_sales_by_user_pos_month() class report_sales_by_margin_pos(osv.osv): _name = "report.sales.by.margin.pos" @@ -187,7 +184,6 @@ class report_sales_by_margin_pos(osv.osv): ) """) -report_sales_by_margin_pos() class report_sales_by_margin_pos_month(osv.osv): _name = "report.sales.by.margin.pos.month" @@ -233,6 +229,5 @@ class report_sales_by_margin_pos_month(osv.osv): ) """) -report_sales_by_margin_pos_month() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 660434ba53e..98ba49c8856 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -1,24 +1,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sale var QWeb = instance.web.qweb; - // rounds a value with a fixed number of decimals. - // round(3.141492,2) -> 3.14 - function round(value,decimals){ - var mult = Math.pow(10,decimals || 0); - return Math.round(value*mult)/mult; - } - window.round = round; - - // rounds a value with decimal form precision - // round(3.141592,0.025) ->3.125 - function round_pr(value,precision){ - if(!precision || precision < 0){ - throw new Error('round_pr(): needs a precision greater than zero, got '+precision+' instead'); - } - return Math.round(value / precision) * precision; - } - window.round_pr = round_pr; - + var round_di = instance.web.round_decimals; + var round_pr = instance.web.round_precision // The PosModel contains the Point Of Sale's representation of the backend. // Since the PoS must work in standalone ( Without connection to the server ) @@ -391,7 +375,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal var quant = Math.max(parseFloat(quantity) || 0, 0); var unit = this.get_unit(); if(unit){ - this.quantity = Math.max(unit.rounding, Math.round(quant / unit.rounding) * unit.rounding); + this.quantity = Math.max(unit.rounding, round_pr(quant, unit.rounding)); this.quantityStr = this.quantity.toFixed(Math.max(0,Math.ceil(Math.log(1.0 / unit.rounding) / Math.log(10)))); }else{ this.quantity = quant; @@ -484,7 +468,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal }, // changes the base price of the product for this orderline set_unit_price: function(price){ - this.price = round(parseFloat(price) || 0, 2); + this.price = round_di(parseFloat(price) || 0, 2); this.trigger('change'); }, get_unit_price: function(){ diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index eca4a3c5915..0bd399428ed 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -983,7 +983,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa } if(this.pos_widget.action_bar){ - this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0); + this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0.000001); } }, set_numpad_state: function(numpadState) { diff --git a/addons/point_of_sale/wizard/pos_box.py b/addons/point_of_sale/wizard/pos_box.py index 776d962a3a8..471153695f4 100644 --- a/addons/point_of_sale/wizard/pos_box.py +++ b/addons/point_of_sale/wizard/pos_box.py @@ -16,7 +16,7 @@ class PosBox(CashBox): active_ids = context.get('active_ids', []) or [] if active_model == 'pos.session': - records = self.pool.get(active_model).browse(cr, uid, active_ids, context=context) + records = self.pool[active_model].browse(cr, uid, active_ids, context=context) bank_statements = [record.cash_register_id for record in records if record.cash_register_id] if not bank_statements: @@ -41,7 +41,7 @@ class PosBoxIn(PosBox): active_ids = context.get('active_ids', []) or [] if active_model == 'pos.session': - session = self.pool.get(active_model).browse(cr, uid, active_ids, context=context)[0] + session = self.pool[active_model].browse(cr, uid, active_ids, context=context)[0] values['ref'] = session.name return values @@ -57,7 +57,7 @@ class PosBoxOut(PosBox): active_ids = context.get('active_ids', []) or [] if active_model == 'pos.session': - session = self.pool.get(active_model).browse(cr, uid, active_ids, context=context)[0] + session = self.pool[active_model].browse(cr, uid, active_ids, context=context)[0] values['ref'] = session.name return values diff --git a/addons/point_of_sale/wizard/pos_box_entries.py b/addons/point_of_sale/wizard/pos_box_entries.py index ff6af8e1f62..8ca4f01b006 100644 --- a/addons/point_of_sale/wizard/pos_box_entries.py +++ b/addons/point_of_sale/wizard/pos_box_entries.py @@ -141,7 +141,6 @@ class pos_box_entries(osv.osv_memory): bank_statement.create(cr, uid, vals, context=context) return {} -pos_box_entries() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_box_out.py b/addons/point_of_sale/wizard/pos_box_out.py index 6b0b9ee4755..51c3993183b 100644 --- a/addons/point_of_sale/wizard/pos_box_out.py +++ b/addons/point_of_sale/wizard/pos_box_out.py @@ -100,7 +100,6 @@ class pos_box_out(osv.osv_memory): statement_line_obj.create(cr, uid, vals, context=context) return {} -pos_box_out() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_confirm.py b/addons/point_of_sale/wizard/pos_confirm.py index f37bdd9f8b8..6a031f54f61 100644 --- a/addons/point_of_sale/wizard/pos_confirm.py +++ b/addons/point_of_sale/wizard/pos_confirm.py @@ -48,7 +48,6 @@ class pos_confirm(osv.osv_memory): data_lines += [x.id for x in move.line_id if x.account_id.id == invoice.account_id.id] self.pool.get('account.move.line').reconcile(cr, uid, data_lines, context=context) return {} -pos_confirm() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_details.py b/addons/point_of_sale/wizard/pos_details.py index 3f06dc57a46..bfd6e7c4ca7 100644 --- a/addons/point_of_sale/wizard/pos_details.py +++ b/addons/point_of_sale/wizard/pos_details.py @@ -60,7 +60,6 @@ class pos_details(osv.osv_memory): 'datas': datas, } -pos_details() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_discount.py b/addons/point_of_sale/wizard/pos_discount.py index d2703b21ad7..d60b2766644 100644 --- a/addons/point_of_sale/wizard/pos_discount.py +++ b/addons/point_of_sale/wizard/pos_discount.py @@ -68,6 +68,5 @@ class pos_discount(osv.osv_memory): order_line_ref.write(cr, uid, [x.id for x in order.lines], {'discount':this.discount}, context=context) return {} -pos_discount() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_open_statement.py b/addons/point_of_sale/wizard/pos_open_statement.py index 750a5519865..97ffad805b7 100644 --- a/addons/point_of_sale/wizard/pos_open_statement.py +++ b/addons/point_of_sale/wizard/pos_open_statement.py @@ -86,6 +86,5 @@ class pos_open_statement(osv.osv_memory): 'search_view_id': search_id, } -pos_open_statement() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_payment.py b/addons/point_of_sale/wizard/pos_payment.py index 47814761b20..02223153290 100644 --- a/addons/point_of_sale/wizard/pos_payment.py +++ b/addons/point_of_sale/wizard/pos_payment.py @@ -128,7 +128,6 @@ class pos_make_payment(osv.osv_memory): 'amount': _default_amount, } -pos_make_payment() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_payment_report.py b/addons/point_of_sale/wizard/pos_payment_report.py index b28af5a3fff..a591fd4ce5f 100644 --- a/addons/point_of_sale/wizard/pos_payment_report.py +++ b/addons/point_of_sale/wizard/pos_payment_report.py @@ -43,7 +43,6 @@ class pos_payment_report(osv.osv_memory): 'datas': datas, } -pos_payment_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_payment_report_user.py b/addons/point_of_sale/wizard/pos_payment_report_user.py index 06e224f39bc..37a62783ed2 100644 --- a/addons/point_of_sale/wizard/pos_payment_report_user.py +++ b/addons/point_of_sale/wizard/pos_payment_report_user.py @@ -49,7 +49,6 @@ class pos_payment_report_user(osv.osv_memory): 'user_id': fields.many2many('res.users', 'res_user_sale', 'user_id', 'sale_id', 'Salesperson') } -pos_payment_report_user() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_receipt.py b/addons/point_of_sale/wizard/pos_receipt.py index 2a62b74bb30..3e6714f3ac7 100644 --- a/addons/point_of_sale/wizard/pos_receipt.py +++ b/addons/point_of_sale/wizard/pos_receipt.py @@ -55,6 +55,5 @@ class pos_receipt(osv.osv_memory): 'datas': datas, } -pos_receipt() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_return.py b/addons/point_of_sale/wizard/pos_return.py index b4e0e7b2041..f0624948ed5 100644 --- a/addons/point_of_sale/wizard/pos_return.py +++ b/addons/point_of_sale/wizard/pos_return.py @@ -171,7 +171,6 @@ class pos_return(osv.osv_memory): } return act -pos_return() class add_product(osv.osv_memory): _inherit = 'pos.add.product' @@ -321,5 +320,4 @@ class add_product(osv.osv_memory): } return True -add_product() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_sales_user.py b/addons/point_of_sale/wizard/pos_sales_user.py index f2f56d6e35c..e8ece7a7953 100644 --- a/addons/point_of_sale/wizard/pos_sales_user.py +++ b/addons/point_of_sale/wizard/pos_sales_user.py @@ -55,7 +55,6 @@ class pos_sale_user(osv.osv_memory): 'datas': datas, } -pos_sale_user() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_sales_user_current_user.py b/addons/point_of_sale/wizard/pos_sales_user_current_user.py index d3da6a9ad42..d6178ff6a50 100644 --- a/addons/point_of_sale/wizard/pos_sales_user_current_user.py +++ b/addons/point_of_sale/wizard/pos_sales_user_current_user.py @@ -53,7 +53,6 @@ class pos_sales_user_today_current_user(osv.osv_memory): 'datas': datas, } -pos_sales_user_today_current_user() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_sales_user_today.py b/addons/point_of_sale/wizard/pos_sales_user_today.py index dfc40c863b0..65e4182daed 100644 --- a/addons/point_of_sale/wizard/pos_sales_user_today.py +++ b/addons/point_of_sale/wizard/pos_sales_user_today.py @@ -52,7 +52,6 @@ class pos_sales_user_today(osv.osv_memory): 'datas': datas, } -pos_sales_user_today() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/point_of_sale/wizard/pos_session_opening.py b/addons/point_of_sale/wizard/pos_session_opening.py index e0135de0cdb..039dd90ae6b 100644 --- a/addons/point_of_sale/wizard/pos_session_opening.py +++ b/addons/point_of_sale/wizard/pos_session_opening.py @@ -115,4 +115,3 @@ class pos_session_opening(osv.osv_memory): 'pos_config_id' : result, 'show_config' : show_config, } -pos_session_opening() diff --git a/addons/portal/mail_mail.py b/addons/portal/mail_mail.py index 3810ddd2540..bc982671bfe 100644 --- a/addons/portal/mail_mail.py +++ b/addons/portal/mail_mail.py @@ -45,7 +45,7 @@ class mail_mail(osv.Model): and self.check_access_rights(cr, partner.user_ids[0].id, 'read', raise_exception=False): related_user = partner.user_ids[0] try: - self.pool.get(mail.model).check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) + self.pool[mail.model].check_access_rule(cr, related_user.id, [mail.res_id], 'read', context=context) url = partner_obj._get_signup_url_for_action(cr, related_user.id, [partner.id], action='', res_id=mail.res_id, model=mail.model, context=context)[partner.id] text = _("""Access this document directly in OpenERP""") % url except except_orm, e: diff --git a/addons/portal/wizard/share_wizard.py b/addons/portal/wizard/share_wizard.py index d983bf11ee1..0ffae804f07 100644 --- a/addons/portal/wizard/share_wizard.py +++ b/addons/portal/wizard/share_wizard.py @@ -179,7 +179,6 @@ class share_wizard_portal(osv.TransientModel): super(share_wizard_portal,self)._finish_result_lines(cr, uid, wizard_data, share_group_id, context=context) self.copy_share_group_access_and_delete(cr, wizard_data, share_group_id, context=context) -share_wizard_portal() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal_anonymous/i18n/lt.po b/addons/portal_anonymous/i18n/lt.po new file mode 100644 index 00000000000..60643c7f356 --- /dev/null +++ b/addons/portal_anonymous/i18n/lt.po @@ -0,0 +1,25 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-24 18:34+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: portal_anonymous +#. openerp-web +#: code:addons/portal_anonymous/static/src/xml/portal_anonymous.xml:8 +#, python-format +msgid "Login" +msgstr "Prisijungti" diff --git a/addons/portal_anonymous/i18n/ru.po b/addons/portal_anonymous/i18n/ru.po new file mode 100644 index 00000000000..5ae80dfe490 --- /dev/null +++ b/addons/portal_anonymous/i18n/ru.po @@ -0,0 +1,25 @@ +# Russian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-09 13:04+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Russian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-10 05:21+0000\n" +"X-Generator: Launchpad (build 16550)\n" + +#. module: portal_anonymous +#. openerp-web +#: code:addons/portal_anonymous/static/src/xml/portal_anonymous.xml:8 +#, python-format +msgid "Login" +msgstr "Вход" diff --git a/addons/portal_crm/i18n/lt.po b/addons/portal_crm/i18n/lt.po new file mode 100644 index 00000000000..b2b223c6124 --- /dev/null +++ b/addons/portal_crm/i18n/lt.po @@ -0,0 +1,546 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-29 15:24+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,type:0 +msgid "Lead" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,title:0 +msgid "Title" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,probability:0 +msgid "Success Rate (%)" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Contact us" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_action:0 +msgid "Next Action Date" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,fax:0 +msgid "Fax" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,zip:0 +msgid "Zip" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,message_unread:0 +msgid "Unread Messages" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,company_id:0 +msgid "Company" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,day_open:0 +msgid "Days to Open" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Thank you for your interest, we'll respond to your request shortly." +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,priority:0 +msgid "Highest" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,mobile:0 +msgid "Mobile" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,description:0 +msgid "Notes" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,color:0 +msgid "Color Index" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_latitude:0 +msgid "Geo Latitude" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_name:0 +msgid "Customer Name" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,state:0 +msgid "Cancelled" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,channel_id:0 +msgid "Communication channel (mail, direct, phone, ...)" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,type_id:0 +msgid "Campaign" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,ref:0 +msgid "Reference" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_action_next:0 +#: field:portal_crm.crm_contact_us,title_action:0 +msgid "Next Action" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,message_summary:0 +msgid "" +"Holds the Chatter summary (number of messages, ...). This summary is " +"directly in html format in order to be inserted in kanban views." +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_id:0 +msgid "Partner" +msgstr "" + +#. module: portal_crm +#: model:ir.actions.act_window,name:portal_crm.action_contact_us +msgid "Contact Us" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,name:0 +msgid "Subject" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,opt_out:0 +msgid "Opt-Out" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,priority:0 +msgid "Priority" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,state_id:0 +msgid "State" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,message_follower_ids:0 +msgid "Followers" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,partner_id:0 +msgid "Linked partner (optional). Usually created when converting the lead." +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,payment_mode:0 +msgid "Payment Mode" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,state:0 +msgid "New" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,type:0 +msgid "Type" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,email_from:0 +msgid "Email" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,channel_id:0 +msgid "Channel" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Name" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,priority:0 +msgid "Lowest" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,create_date:0 +msgid "Creation Date" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Close" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,state:0 +msgid "Pending" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,type:0 +msgid "Type is used to separate Leads and Opportunities" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,categ_ids:0 +msgid "Categories" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,stage_id:0 +msgid "Stage" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,user_login:0 +msgid "User Login" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,opt_out:0 +msgid "" +"If opt-out is checked, this contact has refused to receive emails or " +"unsubscribed to a campaign." +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,contact_name:0 +msgid "Contact Name" +msgstr "" + +#. module: portal_crm +#: model:ir.ui.menu,name:portal_crm.portal_company_contact +msgid "Contact" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_address_email:0 +msgid "Partner Contact Email" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,planned_revenue:0 +msgid "Expected Revenue" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,task_ids:0 +msgid "Tasks" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Contact form" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,company_currency:0 +msgid "Currency" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,write_date:0 +msgid "Update Date" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_deadline:0 +msgid "Expected Closing" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,ref2:0 +msgid "Reference 2" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,user_email:0 +msgid "User Email" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_open:0 +msgid "Opened" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,state:0 +msgid "In Progress" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,partner_name:0 +msgid "" +"The name of the future partner company that will be created while converting " +"the lead into opportunity" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,planned_cost:0 +msgid "Planned Costs" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,date_deadline:0 +msgid "Estimate of the date on which the opportunity will be won." +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,email_cc:0 +msgid "" +"These email addresses will be added to the CC field of all inbound and " +"outbound emails for this record before being sent. Separate multiple email " +"addresses with a comma" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,priority:0 +msgid "Low" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_closed:0 +#: selection:portal_crm.crm_contact_us,state:0 +msgid "Closed" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_assign:0 +msgid "Assignation Date" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,state:0 +msgid "Status" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,priority:0 +msgid "Normal" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,email_cc:0 +msgid "Global CC" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,street2:0 +msgid "Street2" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,id:0 +msgid "ID" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,phone:0 +msgid "Phone" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,message_is_follower:0 +msgid "Is a Follower" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,active:0 +msgid "Active" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,user_id:0 +msgid "Salesperson" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,day_close:0 +msgid "Days to Close" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,company_ids:0 +msgid "Companies" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,message_summary:0 +msgid "Summary" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,section_id:0 +msgid "" +"When sending mails, the default email address is taken from the sales team." +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_address_name:0 +msgid "Partner Contact Name" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_longitude:0 +msgid "Geo Longitude" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,date_assign:0 +msgid "Last date this case was forwarded/assigned to a partner" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,email_from:0 +msgid "Email address of the contact" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,city:0 +msgid "City" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Submit" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,function:0 +msgid "Function" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,referred:0 +msgid "Referred By" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,partner_assigned_id:0 +msgid "Assigned Partner" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,type:0 +msgid "Opportunity" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,partner_assigned_id:0 +msgid "Partner this case has been forwarded/assigned to." +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,country_id:0 +msgid "Country" +msgstr "" + +#. module: portal_crm +#: view:portal_crm.crm_contact_us:0 +msgid "Thank you" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,state:0 +msgid "" +"The Status is set to 'Draft', when a case is created. If the case is in " +"progress the Status is set to 'Open'. When the case is over, the Status is " +"set to 'Done'. If the case needs to be reviewed then the Status is set to " +"'Pending'." +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,message_ids:0 +msgid "Messages and communication history" +msgstr "" + +#. module: portal_crm +#: help:portal_crm.crm_contact_us,type_id:0 +msgid "" +"From which campaign (seminar, marketing campaign, mass mailing, ...) did " +"this contact come from?" +msgstr "" + +#. module: portal_crm +#: selection:portal_crm.crm_contact_us,priority:0 +msgid "High" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,section_id:0 +msgid "Sales Team" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,street:0 +msgid "Street" +msgstr "" + +#. module: portal_crm +#: field:portal_crm.crm_contact_us,date_action_last:0 +msgid "Last Action" +msgstr "" + +#. module: portal_crm +#: model:ir.model,name:portal_crm.model_portal_crm_crm_contact_us +msgid "Contact form for the portal" +msgstr "" diff --git a/addons/portal_hr_employees/i18n/lt.po b/addons/portal_hr_employees/i18n/lt.po new file mode 100644 index 00000000000..50a5a872eac --- /dev/null +++ b/addons/portal_hr_employees/i18n/lt.po @@ -0,0 +1,95 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:05+0000\n" +"PO-Revision-Date: 2013-04-29 15:24+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Coach" +msgstr "" + +#. module: portal_hr_employees +#: model:ir.actions.act_window,name:portal_hr_employees.action_team +#: view:portal_crm.crm_contact_us:0 +msgid "Our Team" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Group By..." +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Company" +msgstr "" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Public" +msgstr "" + +#. module: portal_hr_employees +#: help:hr.employee,visibility:0 +msgid "Employee's visibility in the portal's contact page" +msgstr "" + +#. module: portal_hr_employees +#: selection:hr.employee,visibility:0 +msgid "Private" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Manager" +msgstr "" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_hr_employee +msgid "Employee" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Job" +msgstr "" + +#. module: portal_hr_employees +#: field:hr.employee,visibility:0 +msgid "Visibility" +msgstr "" + +#. module: portal_hr_employees +#: field:hr.employee,public_info:0 +msgid "Public Info" +msgstr "" + +#. module: portal_hr_employees +#: model:ir.model,name:portal_hr_employees.model_portal_crm_crm_contact_us +msgid "Contact form for the portal" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +msgid "Department" +msgstr "" + +#. module: portal_hr_employees +#: view:hr.employee:0 +#: field:portal_crm.crm_contact_us,employee_ids:0 +msgid "Employees" +msgstr "" diff --git a/addons/portal_project/__init__.py b/addons/portal_project/__init__.py index 26c654db9dd..164c7826e47 100644 --- a/addons/portal_project/__init__.py +++ b/addons/portal_project/__init__.py @@ -19,3 +19,4 @@ # ############################################################################## +import project diff --git a/addons/portal_project/project.py b/addons/portal_project/project.py new file mode 100644 index 00000000000..fb783c8a531 --- /dev/null +++ b/addons/portal_project/project.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-TODAY OpenERP S.A (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import osv + + +class portal_project(osv.Model): + """ Update of mail_mail class, to add the signin URL to notifications. """ + _inherit = 'project.project' + + def _get_visibility_selection(self, cr, uid, context=None): + """ Override to add portal option. """ + selection = super(portal_project, self)._get_visibility_selection(cr, uid, context=context) + idx = [item[0] for item in selection].index('public') + selection.insert((idx + 1), ('portal', 'Portal Users and Employees')) + return selection + # return [('public', 'All Users'), + # ('portal', 'Portal Users and Employees'), + # ('employees', 'Employees Only'), + # ('followers', 'Followers Only')] diff --git a/addons/portal_project/security/ir.model.access.csv b/addons/portal_project/security/ir.model.access.csv index 0e743050568..a999f5555d9 100644 --- a/addons/portal_project/security/ir.model.access.csv +++ b/addons/portal_project/security/ir.model.access.csv @@ -5,3 +5,9 @@ access_task_type,task_type,project.model_project_task_type,portal.group_portal,1 access_task_work,task_work,project.model_project_task_work,portal.group_portal,1,0,0,0 access_project_category,project_category,project.model_project_category,portal.group_portal,1,0,0,0 access_account_analytic_account,account_analytic_account,analytic.model_account_analytic_account,portal.group_portal,1,0,0,0 +access_project_anonymous,project,project.model_project_project,portal.group_anonymous,1,0,0,0 +access_task_anonymous,task,project.model_project_task,portal.group_anonymous,1,0,0,0 +access_task_type_anonymous,task_type,project.model_project_task_type,portal.group_anonymous,1,0,0,0 +access_task_work_anonymous,task_work,project.model_project_task_work,portal.group_anonymous,1,0,0,0 +access_project_category_anonymous,project_category,project.model_project_category,portal.group_anonymous,1,0,0,0 +access_account_analytic_account_anonymous,account_analytic_account,analytic.model_account_analytic_account,portal.group_anonymous,1,0,0,0 \ No newline at end of file diff --git a/addons/portal_project/security/portal_security.xml b/addons/portal_project/security/portal_security.xml index 0d995cfd70b..8d58ed70aeb 100644 --- a/addons/portal_project/security/portal_security.xml +++ b/addons/portal_project/security/portal_security.xml @@ -2,19 +2,68 @@ + + Project: employees: public, portal, employees or following + ['|', + ('privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + + - Portal Projects - - ['|', ('privacy_visibility', '=', 'public'), ('message_follower_ids', 'in', [user.partner_id.id])] + Project: portal users: public, portal or following + + ['|', + ('privacy_visibility', 'in', ['public', 'portal']), + '&', + ('privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + Project: anonymous users: public only + + [('privacy_visibility', '=', 'public')] + + + + + Project/Task: employees: public, portal, employee or following or assigned + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + - Portal Tasks - - [('message_follower_ids','in', [user.partner_id.id])] + Project/Task: portal users: public or portal and following + + ['|', + ('project_id.privacy_visibility', '=', 'public'), + '&', + ('project_id.privacy_visibility', 'in', ['portal', 'followers']), + '|', + ('message_follower_ids','in', [user.partner_id.id]), + ('user_id', '=', user.id), + ] + + Project/Task: anonymous users: public only + + [('project_id.privacy_visibility', '=', 'public')] + + + diff --git a/addons/portal_project/tests/__init__.py b/addons/portal_project/tests/__init__.py new file mode 100644 index 00000000000..c79181f7710 --- /dev/null +++ b/addons/portal_project/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_access_rights + +checks = [ + test_access_rights, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal_project/tests/test_access_rights.py b/addons/portal_project/tests/test_access_rights.py new file mode 100644 index 00000000000..a7d56cb3895 --- /dev/null +++ b/addons/portal_project/tests/test_access_rights.py @@ -0,0 +1,300 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv.orm import except_orm +from openerp.tests import common +from openerp.tools import mute_logger + + +class TestPortalProject(common.TransactionCase): + + def setUp(self): + super(TestPortalProject, self).setUp() + cr, uid = self.cr, self.uid + + # Useful models + self.project_project = self.registry('project.project') + self.project_task = self.registry('project.task') + self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') + + # Find Employee group + group_employee_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') + self.group_employee_id = group_employee_ref and group_employee_ref[1] or False + + # Find Project User group + group_project_user_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_user') + self.group_project_user_id = group_project_user_ref and group_project_user_ref[1] or False + + # Find Project Manager group + group_project_manager_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_manager') + self.group_project_manager_id = group_project_manager_ref and group_project_manager_ref[1] or False + + # Find Portal group + group_portal_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_portal') + self.group_portal_id = group_portal_ref and group_portal_ref[1] or False + + # Find Anonymous group + group_anonymous_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'portal', 'group_anonymous') + self.group_anonymous_id = group_anonymous_ref and group_anonymous_ref[1] or False + + # Test users to use through the various tests + self.user_alfred_id = self.res_users.create(cr, uid, { + 'name': 'Alfred EmployeeUser', + 'login': 'alfred', + 'alias_name': 'alfred', + 'groups_id': [(6, 0, [self.group_employee_id, self.group_project_user_id])] + }) + self.user_bert_id = self.res_users.create(cr, uid, { + 'name': 'Bert Nobody', + 'login': 'bert', + 'alias_name': 'bert', + 'groups_id': [(6, 0, [])] + }) + self.user_chell_id = self.res_users.create(cr, uid, { + 'name': 'Chell Portal', + 'login': 'chell', + 'alias_name': 'chell', + 'groups_id': [(6, 0, [self.group_portal_id])] + }) + self.user_donovan_id = self.res_users.create(cr, uid, { + 'name': 'Donovan Anonymous', + 'login': 'donovan', + 'alias_name': 'donovan', + 'groups_id': [(6, 0, [self.group_anonymous_id])] + }) + self.user_ernest_id = self.res_users.create(cr, uid, { + 'name': 'Ernest Manager', + 'login': 'ernest', + 'alias_name': 'ernest', + 'groups_id': [(6, 0, [self.group_project_manager_id])] + }) + + # Test 'Pigs' project + self.project_pigs_id = self.project_project.create(cr, uid, + {'name': 'Pigs', 'privacy_visibility': 'public'}, + {'mail_create_nolog': True}) + # Various test tasks + self.task_1_id = self.project_task.create(cr, uid, + {'name': 'Test1', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_2_id = self.project_task.create(cr, uid, + {'name': 'Test2', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_3_id = self.project_task.create(cr, uid, + {'name': 'Test3', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_4_id = self.project_task.create(cr, uid, + {'name': 'Test4', 'user_id': self.user_alfred_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_5_id = self.project_task.create(cr, uid, + {'name': 'Test5', 'user_id': self.user_chell_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.task_6_id = self.project_task.create(cr, uid, + {'name': 'Test6', 'user_id': self.user_donovan_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm') + def test_00_project_access_rights(self): + """ Test basic project access rights, for project and portal_project """ + cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id + + # ---------------------------------------- + # CASE1: public project + # ---------------------------------------- + + # Do: Alfred reads project -> ok (employee ok public) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_alfred_id, task_ids, ['name']) + # Test: all project tasks writable + self.project_task.write(cr, self.user_alfred_id, task_ids, {'description': 'TestDescription'}) + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + # Test: no project task visible + self.assertRaises(except_orm, self.project_task.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + # Test: no project task readable + self.assertRaises(except_orm, self.project_task.read, + cr, self.user_bert_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_bert_id, task_ids, {'description': 'TestDescription'}) + + # Do: Chell reads project -> ok (portal ok public) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_chell_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_chell_id, task_ids, {'description': 'TestDescription'}) + + # Do: Donovan reads project -> ok (anonymous ok public) + self.project_project.read(cr, self.user_donovan_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: anonymous user cannot see all tasks of a public project') + # Test: all project tasks readable + self.project_task.read(cr, self.user_donovan_id, task_ids, ['name']) + # Test: no project task writable + self.assertRaises(except_orm, self.project_task.write, + cr, self.user_donovan_id, task_ids, {'description': 'TestDescription'}) + + # ---------------------------------------- + # CASE2: portal project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'}) + + # Do: Alfred reads project -> ok (employee ok public) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of a portal project') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + # Test: no project task searchable + self.assertRaises(except_orm, self.project_task.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + + # Data: task follower + self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id]) + + # Do: Chell reads project -> ok (portal ok public) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: only followed project tasks visible + assigned + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: portal user should see the followed tasks of a portal project') + + # Do: Donovan reads project -> ko (anonymous ko portal) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a portal project') + + # Data: task follower cleaning + self.project_task.message_unsubscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id]) + + # ---------------------------------------- + # CASE3: employee project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'}) + + # Do: Alfred reads project -> ok (employee ok employee) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: all project tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_2_id, self.task_3_id, self.task_4_id, self.task_5_id, self.task_6_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: project user cannot see all tasks of an employees project') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + + # Do: Chell reads project -> ko (portal ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_chell_id, pigs_id, ['name']) + # Test: no project task visible + assigned + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: portal user should not see tasks of an employees project, even if assigned') + + # Do: Donovan reads project -> ko (anonymous ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of an employees project') + + # ---------------------------------------- + # CASE4: followers project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'}) + + # Do: Alfred reads project -> ko (employee ko followers) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_alfred_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_4_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see tasks of a not-followed followers project, only assigned') + + # Do: Bert reads project -> crash, no group + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_bert_id, pigs_id, ['name']) + + # Do: Chell reads project -> ko (portal ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_chell_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: portal user should not see tasks of a not-followed followers project, only assigned') + + # Do: Donovan reads project -> ko (anonymous ko employee) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) + # Test: no project task visible + task_ids = self.project_task.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertFalse(task_ids, 'access rights: anonymous user should not see tasks of a followers project') + + # Data: subscribe Alfred, Chell and Donovan as follower + self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_alfred_id, self.user_chell_id, self.user_donovan_id]) + self.project_task.message_subscribe_users(cr, self.user_alfred_id, [self.task_1_id, self.task_3_id], [self.user_chell_id, self.user_alfred_id]) + + # Do: Alfred reads project -> ok (follower ok followers) + self.project_project.read(cr, self.user_alfred_id, pigs_id, ['name']) + # Test: followed + assigned tasks visible + task_ids = self.project_task.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_4_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see followed + assigned tasks of a follower project') + + # Do: Chell reads project -> ok (follower ok follower) + self.project_project.read(cr, self.user_chell_id, pigs_id, ['name']) + # Test: followed + assigned tasks visible + task_ids = self.project_task.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_task_ids = set([self.task_1_id, self.task_3_id, self.task_5_id]) + self.assertEqual(set(task_ids), test_task_ids, + 'access rights: employee user should not see followed + assigned tasks of a follower project') + + # Do: Donovan reads project -> ko (anonymous ko follower even if follower) + self.assertRaises(except_orm, self.project_project.read, + cr, self.user_donovan_id, pigs_id, ['name']) diff --git a/addons/portal_project_issue/security/ir.model.access.csv b/addons/portal_project_issue/security/ir.model.access.csv index 80a38366b51..7355f7a9b1d 100644 --- a/addons/portal_project_issue/security/ir.model.access.csv +++ b/addons/portal_project_issue/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_issues,project_issue,project_issue.model_project_issue,portal.group_portal,1,0,0,0 access_case_section,crm_case_section,crm.model_crm_case_section,portal.group_portal,1,0,0,0 +access_issues_anonymous,project_issue,project_issue.model_project_issue,portal.group_anonymous,1,0,0,0 \ No newline at end of file diff --git a/addons/portal_project_issue/security/portal_security.xml b/addons/portal_project_issue/security/portal_security.xml index a121eeb593e..f6cd8d89be7 100644 --- a/addons/portal_project_issue/security/portal_security.xml +++ b/addons/portal_project_issue/security/portal_security.xml @@ -3,14 +3,36 @@ - Portal Personal Issues - - [('message_follower_ids','in', [user.partner_id.id])] + Project/Issue: portal users: public or portal and following + + ['|', + ('project_id.privacy_visibility', '=', 'public'), + '&', + ('project_id.privacy_visibility', 'in', ['portal', 'followers']), + '|', + ('message_follower_ids','in', [user.partner_id.id]), + ('user_id', '=', user.id), + ] - - - - + + + + Project/Issue: employees: public, portal, employee or following or assigned + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'portal', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + + + + Project/Issue: anonymous users: public only + + [('project_id.privacy_visibility', '=', 'public')] + diff --git a/addons/portal_project_issue/tests/__init__.py b/addons/portal_project_issue/tests/__init__.py new file mode 100644 index 00000000000..fa5b47ac535 --- /dev/null +++ b/addons/portal_project_issue/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_access_rights + +checks = [ + test_access_rights, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal_project_issue/tests/test_access_rights.py b/addons/portal_project_issue/tests/test_access_rights.py new file mode 100644 index 00000000000..28d337a4965 --- /dev/null +++ b/addons/portal_project_issue/tests/test_access_rights.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2013-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.portal_project.tests.test_access_rights import TestPortalProject +from openerp.osv.orm import except_orm +from openerp.tools import mute_logger + + +class TestPortalIssueProject(TestPortalProject): + + def setUp(self): + super(TestPortalIssueProject, self).setUp() + cr, uid = self.cr, self.uid + + # Useful models + self.project_issue = self.registry('project.issue') + + # Various test issues + self.issue_1_id = self.project_issue.create(cr, uid, + {'name': 'Test1', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.issue_2_id = self.project_issue.create(cr, uid, + {'name': 'Test2', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.issue_3_id = self.project_issue.create(cr, uid, + {'name': 'Test3', 'user_id': False, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.issue_4_id = self.project_issue.create(cr, uid, + {'name': 'Test4', 'user_id': self.user_alfred_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.issue_5_id = self.project_issue.create(cr, uid, + {'name': 'Test5', 'user_id': self.user_chell_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + self.issue_6_id = self.project_issue.create(cr, uid, + {'name': 'Test6', 'user_id': self.user_donovan_id, 'project_id': self.project_pigs_id}, + {'mail_create_nolog': True}) + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm') + def test_00_project_access_rights(self): + """ Test basic project access rights, for project and portal_project """ + cr, uid, pigs_id = self.cr, self.uid, self.project_pigs_id + + # ---------------------------------------- + # CASE1: public project + # ---------------------------------------- + + # Do: Alfred reads project -> ok (employee ok public) + # Test: all project issues visible + issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_1_id, self.issue_2_id, self.issue_3_id, self.issue_4_id, self.issue_5_id, self.issue_6_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: project user cannot see all issues of a public project') + # Test: all project issues readable + self.project_issue.read(cr, self.user_alfred_id, issue_ids, ['name']) + # Test: all project issues writable + self.project_issue.write(cr, self.user_alfred_id, issue_ids, {'description': 'TestDescription'}) + + # Do: Bert reads project -> crash, no group + # Test: no project issue visible + self.assertRaises(except_orm, self.project_issue.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + # Test: no project issue readable + self.assertRaises(except_orm, self.project_issue.read, + cr, self.user_bert_id, issue_ids, ['name']) + # Test: no project issue writable + self.assertRaises(except_orm, self.project_issue.write, + cr, self.user_bert_id, issue_ids, {'description': 'TestDescription'}) + + # Do: Chell reads project -> ok (portal ok public) + # Test: all project issues visible + issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: project user cannot see all issues of a public project') + # Test: all project issues readable + self.project_issue.read(cr, self.user_chell_id, issue_ids, ['name']) + # Test: no project issue writable + self.assertRaises(except_orm, self.project_issue.write, + cr, self.user_chell_id, issue_ids, {'description': 'TestDescription'}) + + # Do: Donovan reads project -> ok (anonymous ok public) + # Test: all project issues visible + issue_ids = self.project_issue.search(cr, self.user_donovan_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: project user cannot see all issues of a public project') + + # ---------------------------------------- + # CASE2: portal project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'portal'}) + + # Do: Alfred reads project -> ok (employee ok public) + # Test: all project issues visible + issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: project user cannot see all issues of a portal project') + + # Do: Bert reads project -> crash, no group + # Test: no project issue searchable + self.assertRaises(except_orm, self.project_issue.search, + cr, self.user_bert_id, [('project_id', '=', pigs_id)]) + + # Data: issue follower + self.project_issue.message_subscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id]) + + # Do: Chell reads project -> ok (portal ok public) + # Test: only followed project issues visible + assigned + issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_5_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: portal user should see the followed issues of a portal project') + + # Data: issue follower cleaning + self.project_issue.message_unsubscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id]) + + # ---------------------------------------- + # CASE3: employee project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'employees'}) + + # Do: Alfred reads project -> ok (employee ok employee) + # Test: all project issues visible + issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_1_id, self.issue_2_id, self.issue_3_id, self.issue_4_id, self.issue_5_id, self.issue_6_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: project user cannot see all issues of an employees project') + + # Do: Chell reads project -> ko (portal ko employee) + # Test: no project issue visible + assigned + issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + self.assertFalse(issue_ids, 'access rights: portal user should not see issues of an employees project, even if assigned') + + # ---------------------------------------- + # CASE4: followers project + # ---------------------------------------- + self.project_project.write(cr, uid, [pigs_id], {'privacy_visibility': 'followers'}) + + # Do: Alfred reads project -> ko (employee ko followers) + # Test: no project issue visible + issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_4_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: employee user should not see issues of a not-followed followers project, only assigned') + + # Do: Chell reads project -> ko (portal ko employee) + # Test: no project issue visible + issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_5_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: portal user should not see issues of a not-followed followers project, only assigned') + + # Data: subscribe Alfred, Chell and Donovan as follower + self.project_project.message_subscribe_users(cr, uid, [pigs_id], [self.user_alfred_id, self.user_chell_id, self.user_donovan_id]) + self.project_issue.message_subscribe_users(cr, self.user_alfred_id, [self.issue_1_id, self.issue_3_id], [self.user_chell_id, self.user_alfred_id]) + + # Do: Alfred reads project -> ok (follower ok followers) + # Test: followed + assigned issues visible + issue_ids = self.project_issue.search(cr, self.user_alfred_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_4_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: employee user should not see followed + assigned issues of a follower project') + + # Do: Chell reads project -> ok (follower ok follower) + # Test: followed + assigned issues visible + issue_ids = self.project_issue.search(cr, self.user_chell_id, [('project_id', '=', pigs_id)]) + test_issue_ids = set([self.issue_1_id, self.issue_3_id, self.issue_5_id]) + self.assertEqual(set(issue_ids), test_issue_ids, + 'access rights: employee user should not see followed + assigned issues of a follower project') diff --git a/addons/portal_project_long_term/security/ir.model.access.csv b/addons/portal_project_long_term/security/ir.model.access.csv index 738c264a71c..efbc4fc2418 100644 --- a/addons/portal_project_long_term/security/ir.model.access.csv +++ b/addons/portal_project_long_term/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_issues,project_phase,project_long_term.model_project_phase,portal.group_portal,1,0,0,0 +access_issues_anonymous,project_phase_anonymous,project_long_term.model_project_phase,portal.group_anonymous,1,0,0,0 diff --git a/addons/portal_project_long_term/security/portal_security.xml b/addons/portal_project_long_term/security/portal_security.xml index b46d277e4ee..eab70ebe734 100644 --- a/addons/portal_project_long_term/security/portal_security.xml +++ b/addons/portal_project_long_term/security/portal_security.xml @@ -3,14 +3,24 @@ - Portal Personal Long term project - - ['|',('project_id.message_follower_ids','in', [user.partner_id.id]),('task_ids.message_follower_ids','in', [user.partner_id.id])] + Project/Phase: portal users: public or portal and following + + ['|', + ('project_id.privacy_visibility', '=', 'public'), + '&', + ('project_id.privacy_visibility', 'in', ['portal', 'followers']), + '|', + ('message_follower_ids','in', [user.partner_id.id]), + ('user_id', '=', user.id), + ] - - - - + + + + Project/Phase: anonymous users: public only + + [('project_id.privacy_visibility', '=', 'public')] + diff --git a/addons/portal_sale/i18n/lt.po b/addons/portal_sale/i18n/lt.po new file mode 100644 index 00000000000..92db39babce --- /dev/null +++ b/addons/portal_sale/i18n/lt.po @@ -0,0 +1,344 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-29 15:25+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_account_config_settings +msgid "account.config.settings" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.portal_action_invoices +msgid "We haven't sent you any invoice." +msgstr "" + +#. module: portal_sale +#: model:email.template,report_name:portal_sale.email_template_edi_sale +msgid "" +"${(object.name or '').replace('/','_')}_${object.state == 'draft' and " +"'draft' or ''}" +msgstr "" + +#. module: portal_sale +#: model:res.groups,name:portal_sale.group_payment_options +msgid "View Online Payment Options" +msgstr "" + +#. module: portal_sale +#: field:account.config.settings,group_payment_options:0 +msgid "Show payment buttons to employees too" +msgstr "" + +#. module: portal_sale +#: model:email.template,subject:portal_sale.email_template_edi_sale +msgid "" +"${object.company_id.name} ${object.state in ('draft', 'sent') and " +"'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.action_quotations_portal +msgid "We haven't sent you any quotation." +msgstr "" + +#. module: portal_sale +#: model:ir.ui.menu,name:portal_sale.portal_sales_orders +msgid "Sales Orders" +msgstr "" + +#. module: portal_sale +#: model:res.groups,comment:portal_sale.group_payment_options +msgid "" +"Members of this group see the online payment options\n" +"on Sale Orders and Customer Invoices. These options are meant for customers " +"who are accessing\n" +"their documents through the portal." +msgstr "" + +#. module: portal_sale +#: model:email.template,body_html:portal_sale.email_template_edi_sale +msgid "" +"\n" +"
\n" +"\n" +"

Hello ${object.partner_id.name},

\n" +" \n" +"

Here is your ${object.state in ('draft', 'sent') and 'quotation' or " +"'order confirmation'} from ${object.company_id.name}:

\n" +"\n" +"

\n" +"   REFERENCES
\n" +"   Order number: ${object.name}
\n" +"   Order total: ${object.amount_total} " +"${object.pricelist_id.currency_id.name}
\n" +"   Order date: ${object.date_order}
\n" +" % if object.origin:\n" +"   Order reference: ${object.origin}
\n" +" % endif\n" +" % if object.client_order_ref:\n" +"   Your reference: ${object.client_order_ref}
\n" +" % endif\n" +" % if object.user_id:\n" +"   Your contact: ${object.user_id.name}\n" +" % endif\n" +"

\n" +"\n" +" <% set signup_url = object.get_signup_url() %>\n" +" % if signup_url:\n" +"

\n" +" You can access this document and pay online via our Customer Portal:\n" +"

\n" +" View ${object.state in ('draft', 'sent') " +"and 'Quotation' or 'Order'}\n" +" % endif\n" +"\n" +" % if object.paypal_url:\n" +"
\n" +"

It is also possible to directly pay with Paypal:

\n" +" \n" +" \n" +" \n" +" % endif\n" +"\n" +"
\n" +"

If you have any question, do not hesitate to contact us.

\n" +"

Thank you for choosing ${object.company_id.name or 'us'}!

\n" +"
\n" +"
\n" +"
\n" +"

\n" +" ${object.company_id.name}

\n" +"
\n" +"
\n" +" \n" +" % if object.company_id.street:\n" +" ${object.company_id.street}
\n" +" % endif\n" +" % if object.company_id.street2:\n" +" ${object.company_id.street2}
\n" +" % endif\n" +" % if object.company_id.city or object.company_id.zip:\n" +" ${object.company_id.zip} ${object.company_id.city}
\n" +" % endif\n" +" % if object.company_id.country_id:\n" +" ${object.company_id.state_id and ('%s, ' % " +"object.company_id.state_id.name) or ''} ${object.company_id.country_id.name " +"or ''}
\n" +" % endif\n" +"
\n" +" % if object.company_id.phone:\n" +"
\n" +" Phone:  ${object.company_id.phone}\n" +"
\n" +" % endif\n" +" % if object.company_id.website:\n" +"
\n" +" Web : ${object.company_id.website}\n" +"
\n" +" % endif\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" + +#. module: portal_sale +#: model:email.template,report_name:portal_sale.email_template_edi_invoice +msgid "" +"Invoice_${(object.number or '').replace('/','_')}_${object.state == 'draft' " +"and 'draft' or ''}" +msgstr "" + +#. module: portal_sale +#: model:email.template,subject:portal_sale.email_template_edi_invoice +msgid "${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })" +msgstr "" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_mail_mail +msgid "Outgoing Mails" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.action_quotations_portal +#: model:ir.ui.menu,name:portal_sale.portal_quotations +msgid "Quotations" +msgstr "" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: portal_sale +#: field:account.invoice,portal_payment_options:0 +#: field:sale.order,portal_payment_options:0 +msgid "Portal Payment Options" +msgstr "" + +#. module: portal_sale +#: help:account.config.settings,group_payment_options:0 +msgid "" +"Show online payment options on Sale Orders and Customer Invoices to " +"employees. If not checked, these options are only visible to portal users." +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.portal_action_invoices +#: model:ir.ui.menu,name:portal_sale.portal_invoices +msgid "Invoices" +msgstr "" + +#. module: portal_sale +#: view:account.config.settings:0 +msgid "Configure payment acquiring methods" +msgstr "" + +#. module: portal_sale +#: model:email.template,body_html:portal_sale.email_template_edi_invoice +msgid "" +"\n" +"
\n" +"\n" +"

Hello ${object.partner_id.name},

\n" +"\n" +"

A new invoice is available for you:

\n" +" \n" +"

\n" +"   REFERENCES
\n" +"   Invoice number: ${object.number}
\n" +"   Invoice total: ${object.amount_total} " +"${object.currency_id.name}
\n" +"   Invoice date: ${object.date_invoice}
\n" +" % if object.origin:\n" +"   Order reference: ${object.origin}
\n" +" % endif\n" +" % if object.user_id:\n" +"   Your contact: ${object.user_id.name}\n" +" % endif\n" +"

\n" +"\n" +" <% set signup_url = object.get_signup_url() %>\n" +" % if signup_url:\n" +"

\n" +" You can access the invoice document and pay online via our Customer " +"Portal:\n" +"

\n" +" View Invoice\n" +" % endif\n" +" \n" +" % if object.paypal_url:\n" +"
\n" +"

It is also possible to directly pay with Paypal:

\n" +" \n" +" \n" +" \n" +" % endif\n" +" \n" +"
\n" +"

If you have any question, do not hesitate to contact us.

\n" +"

Thank you for choosing ${object.company_id.name or 'us'}!

\n" +"
\n" +"
\n" +"
\n" +"

\n" +" ${object.company_id.name}

\n" +"
\n" +"
\n" +" \n" +" % if object.company_id.street:\n" +" ${object.company_id.street}
\n" +" % endif\n" +" % if object.company_id.street2:\n" +" ${object.company_id.street2}
\n" +" % endif\n" +" % if object.company_id.city or object.company_id.zip:\n" +" ${object.company_id.zip} ${object.company_id.city}
\n" +" % endif\n" +" % if object.company_id.country_id:\n" +" ${object.company_id.state_id and ('%s, ' % " +"object.company_id.state_id.name) or ''} ${object.company_id.country_id.name " +"or ''}
\n" +" % endif\n" +"
\n" +" % if object.company_id.phone:\n" +"
\n" +" Phone:  ${object.company_id.phone}\n" +"
\n" +" % endif\n" +" % if object.company_id.website:\n" +"
\n" +" Web : ${object.company_id.website}\n" +"
\n" +" % endif\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,help:portal_sale.action_orders_portal +msgid "We haven't sent you any sales order." +msgstr "" + +#. module: portal_sale +#: model:ir.model,name:portal_sale.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: portal_sale +#: model:ir.actions.act_window,name:portal_sale.action_orders_portal +msgid "Sale Orders" +msgstr "" diff --git a/addons/portal_stock/security/portal_security.xml b/addons/portal_stock/security/portal_security.xml index 154a7fc9ddd..c5587453e40 100644 --- a/addons/portal_stock/security/portal_security.xml +++ b/addons/portal_stock/security/portal_security.xml @@ -17,19 +17,5 @@
- - Portal Personal Stock Moves - - [('message_follower_ids','in',[user.partner_id.id])] - - - - - Portal Personal Stock Warehouse Orderpoints - - [('message_follower_ids','in',[user.partner_id.id])] - - - diff --git a/addons/process/process.py b/addons/process/process.py index 30a1865af4b..d6a72148fa6 100644 --- a/addons/process/process.py +++ b/addons/process/process.py @@ -288,7 +288,6 @@ class process_process(osv.osv): }) return super(process_process, self).copy(cr, uid, id, default, context) -process_process() class process_node(osv.osv): _name = 'process.node' @@ -323,7 +322,6 @@ class process_node(osv.osv): }) return super(process_node, self).copy_data(cr, uid, id, default, context=context) -process_node() class process_node_condition(osv.osv): _name = 'process.condition' @@ -334,7 +332,6 @@ class process_node_condition(osv.osv): 'model_id': fields.many2one('ir.model', 'Object', ondelete='set null'), 'model_states': fields.char('Expression', required=True, size=128) } -process_node_condition() class process_transition(osv.osv): _name = 'process.transition' @@ -348,7 +345,6 @@ class process_transition(osv.osv): 'group_ids': fields.many2many('res.groups', 'process_transition_group_rel', 'tid', 'rid', string='Required Groups'), 'note': fields.text('Description', translate=True), } -process_transition() class process_transition_action(osv.osv): _name = 'process.transition.action' @@ -381,6 +377,5 @@ class process_transition_action(osv.osv): return super(process_transition_action, self).copy_data(cr, uid, id, default, context) -process_transition_action() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/procurement/company.py b/addons/procurement/company.py index 60e2db873e2..1fd08d967f1 100644 --- a/addons/procurement/company.py +++ b/addons/procurement/company.py @@ -33,6 +33,5 @@ class company(osv.osv): 'schedule_range': 730.0, } -company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/procurement/i18n/lt.po b/addons/procurement/i18n/lt.po new file mode 100644 index 00000000000..6b2ac1a24be --- /dev/null +++ b/addons/procurement/i18n/lt.po @@ -0,0 +1,1005 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-29 15:26+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: procurement +#: model:ir.ui.menu,name:procurement.menu_stock_sched +msgid "Schedulers" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_make_procurement +msgid "Make Procurements" +msgstr "" + +#. module: procurement +#: help:procurement.order.compute.all,automatic:0 +msgid "" +"Triggers an automatic procurement for all products that have a virtual stock " +"under 0. You should probably not use this option, we suggest using a MTO " +"configuration on products." +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Group By..." +msgstr "" + +#. module: procurement +#: help:stock.warehouse.orderpoint,procurement_draft_ids:0 +msgid "Draft procurement of the product and location of that orderpoint" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"required quantities are always\n" +" available" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"If there are not enough quantities available, the delivery order\n" +" will wait for new products. To fulfill the " +"inventory, you should\n" +" create others rules like orderpoints." +msgstr "" + +#. module: procurement +#: field:procurement.order,procure_method:0 +#: field:product.template,procure_method:0 +msgid "Procurement Method" +msgstr "" + +#. module: procurement +#: selection:product.template,supply_method:0 +msgid "Manufacture" +msgstr "" + +#. module: procurement +#: model:process.process,name:procurement.process_process_serviceproductprocess0 +msgid "Service" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.action_procurement_compute +msgid "Compute Stock Minimum Rules Only" +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Rules" +msgstr "" + +#. module: procurement +#: field:procurement.order,company_id:0 +#: field:stock.warehouse.orderpoint,company_id:0 +msgid "Company" +msgstr "" + +#. module: procurement +#: field:procurement.order,product_uos_qty:0 +msgid "UoS Quantity" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Reason" +msgstr "" + +#. module: procurement +#: view:procurement.order.compute:0 +msgid "Compute Procurements" +msgstr "" + +#. module: procurement +#: field:procurement.order,message:0 +msgid "Latest error" +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,product_min_qty:0 +msgid "Minimum Quantity" +msgstr "" + +#. module: procurement +#: help:mrp.property,composition:0 +msgid "Not used in computations, for information purpose only." +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,procurement_id:0 +msgid "Latest procurement" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,help:procurement.action_orderpoint_form +msgid "" +"You can define your minimum stock rules, so that OpenERP will automatically " +"create draft manufacturing orders or purchase quotations according to the " +"stock level. Once the virtual stock of a product (= stock on hand minus all " +"confirmed orders and reservations) is below the minimum quantity, OpenERP " +"will generate a procurement request to increase the stock up to the maximum " +"quantity." +msgstr "" + +#. module: procurement +#: field:procurement.order,message_ids:0 +msgid "Messages" +msgstr "" + +#. module: procurement +#: help:procurement.order,message:0 +msgid "Exception occurred while computing procurement orders." +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "Products" +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Cancelled" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Permanent Procurement Exceptions" +msgstr "" + +#. module: procurement +#: help:procurement.order,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "" + +#. module: procurement +#: view:procurement.order.compute.all:0 +msgid "Scheduler Parameters" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "Stockable products" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:138 +#, python-format +msgid "Invalid Action!" +msgstr "" + +#. module: procurement +#: help:procurement.order,message_summary:0 +msgid "" +"Holds the Chatter summary (number of messages, ...). This summary is " +"directly in html format in order to be inserted in kanban views." +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Ready" +msgstr "" + +#. module: procurement +#: field:procurement.order.compute.all,automatic:0 +msgid "Automatic orderpoint" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,help:procurement.procurement_exceptions +msgid "" +"

\n" +" Procurement Orders represent the need for a certain quantity " +"of products, at a given time, in a given location. Sales Orders are one " +"typical source of Procurement Orders (but these are distinct documents). " +"Depending on the procurement parameters and the product configuration, the " +"procurement engine will attempt to satisfy the need by reserving products " +"from stock, ordering products from a supplier, or passing a manufacturing " +"order, etc. A Procurement Exception occurs when the system cannot find a way " +"to fulfill a procurement. Some exceptions will resolve themselves " +"automatically, but others require manual intervention (those are identified " +"by a specific error message).\n" +"

\n" +" " +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Confirmed" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Retry" +msgstr "" + +#. module: procurement +#: view:procurement.order.compute:0 +#: view:procurement.orderpoint.compute:0 +msgid "Parameters" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Confirm" +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Quantity Multiple" +msgstr "" + +#. module: procurement +#: help:procurement.order,origin:0 +msgid "" +"Reference of the document that created this Procurement.\n" +"This is automatically completed by OpenERP." +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Procurement Orders to Process" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_stock_warehouse_orderpoint +msgid "Minimum Inventory Rule" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:370 +#, python-format +msgid "Procurement '%s' is in exception: " +msgstr "" + +#. module: procurement +#: field:procurement.order,priority:0 +msgid "Priority" +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Reordering Rules Search" +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Waiting" +msgstr "" + +#. module: procurement +#: field:procurement.order,message_follower_ids:0 +msgid "Followers" +msgstr "" + +#. module: procurement +#: field:procurement.order,location_id:0 +#: view:stock.warehouse.orderpoint:0 +#: field:stock.warehouse.orderpoint,location_id:0 +msgid "Location" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_stock_picking +msgid "Picking List" +msgstr "" + +#. module: procurement +#: field:make.procurement,warehouse_id:0 +#: view:stock.warehouse.orderpoint:0 +#: field:stock.warehouse.orderpoint,warehouse_id:0 +msgid "Warehouse" +msgstr "" + +#. module: procurement +#: selection:stock.warehouse.orderpoint,logic:0 +msgid "Best price (not yet active!)" +msgstr "" + +#. module: procurement +#: code:addons/procurement/schedulers.py:110 +#, python-format +msgid "PROC %d: from stock - %3.2f %-5s - %s" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_procurement_order_compute +msgid "Compute Procurement" +msgstr "" + +#. module: procurement +#: field:res.company,schedule_range:0 +msgid "Scheduler Range Days" +msgstr "" + +#. module: procurement +#: view:make.procurement:0 +msgid "Ask New Products" +msgstr "" + +#. module: procurement +#: field:make.procurement,date_planned:0 +msgid "Planned Date" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Group By" +msgstr "" + +#. module: procurement +#: field:make.procurement,qty:0 +#: field:procurement.order,product_qty:0 +msgid "Quantity" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:365 +#, python-format +msgid "Not enough stock and no minimum orderpoint rule defined." +msgstr "" + +#. module: procurement +#: field:make.procurement,uom_id:0 +#: view:procurement.order:0 +msgid "Unit of Measure" +msgstr "" + +#. module: procurement +#: selection:procurement.order,procure_method:0 +#: selection:product.template,procure_method:0 +msgid "Make to Stock" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,help:procurement.procurement_action +msgid "" +"

\n" +" Click to create a procurement order. \n" +"

\n" +" A procurement order is used to record a need for a specific\n" +" product at a specific location. Procurement orders are " +"usually\n" +" created automatically from sales orders, pull logistic rules " +"or\n" +" minimum stock rules.\n" +"

\n" +" When the procurement order is confirmed, it automatically\n" +" creates the necessary operations to fullfil the need: " +"purchase\n" +" order proposition, manufacturing order, etc.\n" +"

\n" +" " +msgstr "" + +#. module: procurement +#: help:procurement.order,procure_method:0 +msgid "" +"If you encode manually a Procurement, you probably want to use a make to " +"order method." +msgstr "" + +#. module: procurement +#: model:ir.ui.menu,name:procurement.menu_stock_procurement +msgid "Automatic Procurements" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"use the available\n" +" inventory" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_procurement_order +#: model:process.process,name:procurement.process_process_procurementprocess0 +#: view:procurement.order:0 +msgid "Procurement" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.procurement_action +msgid "Procurement Orders" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "To Fix" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Exceptions" +msgstr "" + +#. module: procurement +#: model:process.node,note:procurement.process_node_serviceonorder0 +msgid "Assignment from Production or Purchase Order." +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_mrp_property +msgid "Property" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.act_make_procurement +#: view:make.procurement:0 +msgid "Procurement Request" +msgstr "" + +#. module: procurement +#: view:procurement.orderpoint.compute:0 +msgid "Compute Stock" +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,procurement_draft_ids:0 +msgid "Related Procurement Orders" +msgstr "" + +#. module: procurement +#: field:procurement.order,message_unread:0 +msgid "Unread Messages" +msgstr "" + +#. module: procurement +#: selection:mrp.property,composition:0 +msgid "plus" +msgstr "" + +#. module: procurement +#: help:procurement.order,state:0 +msgid "" +"When a procurement is created the status is set to 'Draft'.\n" +" If the procurement is confirmed, the status is set to 'Confirmed'. " +" \n" +"After confirming the status is set to 'Running'.\n" +" If any exception arises in the order then the status is set to " +"'Exception'.\n" +" Once the exception is removed the status becomes 'Ready'.\n" +" It is in 'Waiting'. status when the procurement is waiting for another one " +"to finish." +msgstr "" + +#. module: procurement +#: help:stock.warehouse.orderpoint,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the " +"orderpoint without removing it." +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"When you sell this service, nothing special will be triggered\n" +" to deliver the customer, as you set the " +"procurement method as\n" +" 'Make to Stock'." +msgstr "" + +#. module: procurement +#: help:procurement.orderpoint.compute,automatic:0 +msgid "If the stock of a product is under 0, it will act like an orderpoint" +msgstr "" + +#. module: procurement +#: field:procurement.order,product_uom:0 +#: field:stock.warehouse.orderpoint,product_uom:0 +msgid "Product Unit of Measure" +msgstr "" + +#. module: procurement +#: constraint:stock.warehouse.orderpoint:0 +msgid "" +"You have to select a product unit of measure in the same category than the " +"default unit of measure of the product" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Procurement Lines" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"as it's a consumable (as a result of this, the quantity\n" +" on hand may become negative)." +msgstr "" + +#. module: procurement +#: field:procurement.order,note:0 +msgid "Note" +msgstr "" + +#. module: procurement +#: help:stock.warehouse.orderpoint,product_min_qty:0 +msgid "" +"When the virtual stock goes below the Min Quantity specified for this field, " +"OpenERP generates a procurement to bring the forecasted quantity to the Max " +"Quantity." +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Draft" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.action_compute_schedulers +#: model:ir.ui.menu,name:procurement.menu_stock_proc_schedulers +#: view:procurement.order.compute.all:0 +msgid "Run Schedulers" +msgstr "" + +#. module: procurement +#: view:procurement.order.compute:0 +msgid "This wizard will schedule procurements." +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +#: field:procurement.order,state:0 +msgid "Status" +msgstr "" + +#. module: procurement +#: selection:product.template,supply_method:0 +msgid "Buy" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "for the delivery order." +msgstr "" + +#. module: procurement +#: selection:procurement.order,priority:0 +msgid "Normal" +msgstr "" + +#. module: procurement +#: help:product.template,supply_method:0 +msgid "" +"Manufacture: When procuring the product, a manufacturing order or a task " +"will be generated, depending on the product type. \n" +"Buy: When procuring the product, a purchase order will be generated." +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,product_max_qty:0 +msgid "Maximum Quantity" +msgstr "" + +#. module: procurement +#: field:procurement.order,message_is_follower:0 +msgid "Is a Follower" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:367 +#, python-format +msgid "Not enough stock." +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,active:0 +msgid "Active" +msgstr "" + +#. module: procurement +#: model:process.node,name:procurement.process_node_procureproducts0 +msgid "Procure Products" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:312 +#, python-format +msgid "" +"Please check the quantity in procurement order(s) for the product \"%s\", it " +"should not be 0 or less!" +msgstr "" + +#. module: procurement +#: field:procurement.order,date_planned:0 +msgid "Scheduled date" +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Exception" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "" +"When you sell this product, a delivery order will be created.\n" +" OpenERP will consider that the" +msgstr "" + +#. module: procurement +#: code:addons/procurement/schedulers.py:133 +#, python-format +msgid "Automatic OP: %s" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_procurement_orderpoint_compute +msgid "Automatic Order Point" +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,qty_multiple:0 +msgid "Qty Multiple" +msgstr "" + +#. module: procurement +#: help:stock.warehouse.orderpoint,qty_multiple:0 +msgid "The procurement quantity will be rounded up to this multiple." +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_res_company +msgid "Companies" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Extra Information" +msgstr "" + +#. module: procurement +#: field:procurement.order,message_summary:0 +msgid "Summary" +msgstr "" + +#. module: procurement +#: sql_constraint:stock.warehouse.orderpoint:0 +msgid "Qty Multiple must be greater than zero." +msgstr "" + +#. module: procurement +#: selection:stock.warehouse.orderpoint,logic:0 +msgid "Order to Max" +msgstr "" + +#. module: procurement +#: field:procurement.order,date_close:0 +msgid "Date Closed" +msgstr "" + +#. module: procurement +#: view:res.company:0 +msgid "Logistics" +msgstr "" + +#. module: procurement +#: help:product.template,procure_method:0 +msgid "" +"Make to Stock: When needed, the product is taken from the stock or we wait " +"for replenishment. \n" +"Make to Order: When needed, the product is purchased or produced." +msgstr "" + +#. module: procurement +#: field:mrp.property,composition:0 +msgid "Properties composition" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:311 +#, python-format +msgid "Data Insufficient !" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_mrp_property_group +#: field:mrp.property,group_id:0 +#: field:mrp.property.group,name:0 +msgid "Property Group" +msgstr "" + +#. module: procurement +#: view:stock.warehouse.orderpoint:0 +msgid "Misc" +msgstr "" + +#. module: procurement +#: field:stock.move,procurements:0 +msgid "Procurements" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Run Procurement" +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Done" +msgstr "" + +#. module: procurement +#: view:make.procurement:0 +#: view:procurement.order.compute:0 +#: view:procurement.order.compute.all:0 +#: view:procurement.orderpoint.compute:0 +msgid "Cancel" +msgstr "" + +#. module: procurement +#: field:stock.warehouse.orderpoint,logic:0 +msgid "Reordering Mode" +msgstr "" + +#. module: procurement +#: field:procurement.order,origin:0 +msgid "Source Document" +msgstr "" + +#. module: procurement +#: selection:procurement.order,priority:0 +msgid "Not urgent" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.product_open_orderpoint +#: view:product.product:0 +msgid "Orderpoints" +msgstr "" + +#. module: procurement +#: help:stock.warehouse.orderpoint,product_max_qty:0 +msgid "" +"When the virtual stock goes below the Min Quantity, OpenERP generates a " +"procurement to bring the forecasted quantity to the Quantity specified as " +"Max Quantity." +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_procurement_order_compute_all +msgid "Compute all schedulers" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Late" +msgstr "" + +#. module: procurement +#: view:board.board:0 +msgid "Procurements in Exception" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.procurement_action5 +#: model:ir.actions.act_window,name:procurement.procurement_action_board +#: model:ir.actions.act_window,name:procurement.procurement_exceptions +#: model:ir.ui.menu,name:procurement.menu_stock_procurement_action +#: view:procurement.order:0 +msgid "Procurement Exceptions" +msgstr "" + +#. module: procurement +#: field:product.product,orderpoint_ids:0 +msgid "Minimum Stock Rules" +msgstr "" + +#. module: procurement +#: view:make.procurement:0 +msgid "" +"Fill is this for to launch a procurement request for this\n" +" product. According to the product configuration, " +"this may\n" +" trigger a draft purchase order, a manufacturing " +"order or\n" +" a new task." +msgstr "" + +#. module: procurement +#: field:procurement.order,close_move:0 +msgid "Close Move at end" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Scheduled Date" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_product_product +#: field:make.procurement,product_id:0 +#: view:procurement.order:0 +#: field:procurement.order,product_id:0 +#: field:stock.warehouse.orderpoint,product_id:0 +msgid "Product" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Temporary" +msgstr "" + +#. module: procurement +#: field:mrp.property,description:0 +#: field:mrp.property.group,description:0 +#: field:procurement.order,name:0 +msgid "Description" +msgstr "" + +#. module: procurement +#: selection:procurement.order,priority:0 +msgid "Urgent" +msgstr "" + +#. module: procurement +#: selection:procurement.order,state:0 +msgid "Running" +msgstr "" + +#. module: procurement +#: model:process.node,name:procurement.process_node_serviceonorder0 +#: selection:procurement.order,procure_method:0 +#: selection:product.template,procure_method:0 +msgid "Make to Order" +msgstr "" + +#. module: procurement +#: field:product.template,supply_method:0 +msgid "Supply Method" +msgstr "" + +#. module: procurement +#: field:procurement.order,move_id:0 +msgid "Reservation" +msgstr "" + +#. module: procurement +#: model:process.node,note:procurement.process_node_procureproducts0 +msgid "The way to procurement depends on the product type." +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "When you sell this product, OpenERP will" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Temporary Procurement Exceptions" +msgstr "" + +#. module: procurement +#: field:mrp.property,name:0 +#: field:stock.warehouse.orderpoint,name:0 +msgid "Name" +msgstr "" + +#. module: procurement +#: selection:mrp.property,composition:0 +msgid "max" +msgstr "" + +#. module: procurement +#: model:ir.actions.act_window,name:procurement.act_procurement_2_stock_warehouse_orderpoint +#: model:ir.actions.act_window,name:procurement.act_stock_warehouse_2_stock_warehouse_orderpoint +#: model:ir.actions.act_window,name:procurement.action_orderpoint_form +#: model:ir.ui.menu,name:procurement.menu_stock_order_points +#: view:stock.warehouse.orderpoint:0 +msgid "Reordering Rules" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:139 +#, python-format +msgid "Cannot delete Procurement Order(s) which are in %s state." +msgstr "" + +#. module: procurement +#: field:procurement.order,product_uos:0 +msgid "Product UoS" +msgstr "" + +#. module: procurement +#: model:ir.model,name:procurement.model_product_template +msgid "Product Template" +msgstr "" + +#. module: procurement +#: view:procurement.orderpoint.compute:0 +msgid "" +"Wizard checks all the stock minimum rules and generate procurement order." +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Search Procurement" +msgstr "" + +#. module: procurement +#: help:res.company,schedule_range:0 +msgid "" +"This is the time frame analysed by the scheduler when computing " +"procurements. All procurements that are not between today and today+range " +"are skipped for future computation." +msgstr "" + +#. module: procurement +#: selection:procurement.order,priority:0 +msgid "Very Urgent" +msgstr "" + +#. module: procurement +#: field:procurement.orderpoint.compute,automatic:0 +msgid "Automatic Orderpoint" +msgstr "" + +#. module: procurement +#: help:procurement.order,message_ids:0 +msgid "Messages and communication history" +msgstr "" + +#. module: procurement +#: view:procurement.order:0 +msgid "Procurement started late" +msgstr "" + +#. module: procurement +#: selection:mrp.property,composition:0 +msgid "min" +msgstr "" + +#. module: procurement +#: view:make.procurement:0 +#: view:procurement.order.compute:0 +#: view:procurement.order.compute.all:0 +#: view:procurement.orderpoint.compute:0 +msgid "or" +msgstr "" + +#. module: procurement +#: code:addons/procurement/schedulers.py:134 +#, python-format +msgid "SCHEDULER" +msgstr "" + +#. module: procurement +#: view:product.product:0 +msgid "Request Procurement" +msgstr "" + +#. module: procurement +#: code:addons/procurement/schedulers.py:87 +#, python-format +msgid "PROC %d: on order - %3.2f %-5s - %s" +msgstr "" + +#. module: procurement +#: code:addons/procurement/procurement.py:339 +#, python-format +msgid "Products reserved from stock." +msgstr "" diff --git a/addons/procurement/i18n/pt_BR.po b/addons/procurement/i18n/pt_BR.po index 626e66dc827..9e425149ee3 100644 --- a/addons/procurement/i18n/pt_BR.po +++ b/addons/procurement/i18n/pt_BR.po @@ -8,15 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-12-21 17:06+0000\n" -"PO-Revision-Date: 2012-12-21 00:27+0000\n" -"Last-Translator: Fábio Martinelli - http://zupy.com.br " -"\n" +"PO-Revision-Date: 2013-04-18 17:42+0000\n" +"Last-Translator: Thiago Tognoli \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2013-03-16 05:43+0000\n" -"X-Generator: Launchpad (build 16532)\n" +"X-Launchpad-Export-Date: 2013-04-19 05:24+0000\n" +"X-Generator: Launchpad (build 16567)\n" #. module: procurement #: model:ir.ui.menu,name:procurement.menu_stock_sched @@ -57,7 +56,7 @@ msgid "" " available" msgstr "" "quantidades necessárias estão sempre\n" -"                            disponíveis" +" disponíveis" #. module: procurement #: view:product.product:0 @@ -68,9 +67,9 @@ msgid "" " create others rules like orderpoints." msgstr "" "Se não houver quantidade suficiente disponível, a ordem de entrega\n" -"                            vai esperar por novos produtos. Para cumprir o " +" vai esperar por novos produtos. Para cumprir o " "inventário, você deve\n" -"                            criar outras regras como ponto de compra." +" criar outras regras como ponto de compra." #. module: procurement #: field:procurement.order,procure_method:0 @@ -632,7 +631,7 @@ msgstr "Provisório" #: model:ir.ui.menu,name:procurement.menu_stock_proc_schedulers #: view:procurement.order.compute.all:0 msgid "Run Schedulers" -msgstr "executar Agendadores" +msgstr "Executar Agendadores" #. module: procurement #: view:procurement.order.compute:0 diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index 1009873beb2..7c9d5ec3290 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -155,7 +155,7 @@ class procurement_order(osv.osv): warehouse_obj = self.pool.get('stock.warehouse') warehouse_ids = warehouse_obj.search(cr, uid, [], context=context) - products_ids = product_obj.search(cr, uid, [('purchase_ok', '=', True)], order='id', context=context) + products_ids = product_obj.search(cr, uid, [], order='id', context=context) for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context): context['warehouse'] = warehouse diff --git a/addons/procurement/wizard/make_procurement_product.py b/addons/procurement/wizard/make_procurement_product.py index aab49bf8e80..bf83bc73f1a 100644 --- a/addons/procurement/wizard/make_procurement_product.py +++ b/addons/procurement/wizard/make_procurement_product.py @@ -126,6 +126,5 @@ class make_procurement(osv.osv_memory): return res -make_procurement() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/procurement/wizard/mrp_procurement.py b/addons/procurement/wizard/mrp_procurement.py index 56386bf234e..fd5a9b530b6 100644 --- a/addons/procurement/wizard/mrp_procurement.py +++ b/addons/procurement/wizard/mrp_procurement.py @@ -46,7 +46,6 @@ class procurement_compute(osv.osv_memory): threaded_calculation.start() return {} -procurement_compute() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/procurement/wizard/orderpoint_procurement.py b/addons/procurement/wizard/orderpoint_procurement.py index 2d5b91d4c81..afc8e2a721f 100644 --- a/addons/procurement/wizard/orderpoint_procurement.py +++ b/addons/procurement/wizard/orderpoint_procurement.py @@ -68,6 +68,5 @@ class procurement_compute(osv.osv_memory): threaded_calculation.start() return {'type': 'ir.actions.act_window_close'} -procurement_compute() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/procurement/wizard/schedulers_all.py b/addons/procurement/wizard/schedulers_all.py index c60ac989274..aab9657ae5d 100644 --- a/addons/procurement/wizard/schedulers_all.py +++ b/addons/procurement/wizard/schedulers_all.py @@ -65,6 +65,5 @@ class procurement_compute_all(osv.osv_memory): threaded_calculation.start() return {'type': 'ir.actions.act_window_close'} -procurement_compute_all() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product/partner.py b/addons/product/partner.py index 1d11e909346..fbfee76d0b2 100644 --- a/addons/product/partner.py +++ b/addons/product/partner.py @@ -36,7 +36,9 @@ class res_partner(osv.osv): help="This pricelist will be used, instead of the default one, for sales to the current partner"), } -res_partner() + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_product_pricelist'] + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product/partner_view.xml b/addons/product/partner_view.xml index acb087533ac..5117da6044e 100644 --- a/addons/product/partner_view.xml +++ b/addons/product/partner_view.xml @@ -8,9 +8,12 @@ - + +
+

Pricelists are managed on

diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 757cce2ae95..6fc926980dd 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -64,7 +64,6 @@ class price_type(osv.osv): "currency_id": _get_currency } -price_type() #---------------------------------------------------------- # Price lists @@ -77,7 +76,6 @@ class product_pricelist_type(osv.osv): 'name': fields.char('Name',size=64, required=True, translate=True), 'key': fields.char('Key', size=64, required=True, help="Used in the code to select specific prices based on the context. Keep unchanged."), } -product_pricelist_type() class product_pricelist(osv.osv): @@ -236,7 +234,10 @@ class product_pricelist(osv.osv): qty, context=context)[res['base_pricelist_id']] ptype_src = self.browse(cr, uid, res['base_pricelist_id']).currency_id.id uom_price_already_computed = True - price = currency_obj.compute(cr, uid, ptype_src, res['currency_id'], price_tmp, round=False) + price = currency_obj.compute(cr, uid, + ptype_src, res['currency_id'], + price_tmp, round=False, + context=context) elif res['base'] == -2: # this section could be improved by moving the queries outside the loop: where = [] @@ -305,7 +306,6 @@ class product_pricelist(osv.osv): res.update({'item_id': {ids[-1]: res_multi.get('item_id', ids[-1])}}) return res -product_pricelist() class product_pricelist_version(osv.osv): @@ -363,7 +363,6 @@ class product_pricelist_version(osv.osv): ['date_start', 'date_end']) ] -product_pricelist_version() class product_pricelist_item(osv.osv): def _price_field_get(self, cr, uid, context=None): @@ -443,7 +442,6 @@ class product_pricelist_item(osv.osv): if prod[0]['code']: return {'value': {'name': prod[0]['code']}} return {} -product_pricelist_item() diff --git a/addons/product/product.py b/addons/product/product.py index 82b37f7b9e8..518e455ddc1 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -84,7 +84,6 @@ class product_uom_categ(osv.osv): _columns = { 'name': fields.char('Name', size=64, required=True, translate=True), } -product_uom_categ() class product_uom(osv.osv): _name = 'product.uom' @@ -208,7 +207,6 @@ class product_uom(osv.osv): raise osv.except_osv(_('Warning!'),_("Cannot change the category of existing Unit of Measure '%s'.") % (uom.name,)) return super(product_uom, self).write(cr, uid, ids, vals, context=context) -product_uom() class product_ul(osv.osv): @@ -218,7 +216,6 @@ class product_ul(osv.osv): 'name' : fields.char('Name', size=64,select=True, required=True, translate=True), 'type' : fields.selection([('unit','Unit'),('pack','Pack'),('box', 'Box'), ('pallet', 'Pallet')], 'Type', required=True), } -product_ul() #---------------------------------------------------------- @@ -283,7 +280,6 @@ class product_category(osv.osv): def child_get(self, cr, uid, ids): return [ids] -product_category() #---------------------------------------------------------- @@ -401,7 +397,6 @@ class product_template(osv.osv): pass return super(product_template, self).name_get(cr, user, ids, context) -product_template() class product_product(osv.osv): def view_header_get(self, cr, uid, view_id, view_type, context=None): @@ -523,6 +518,13 @@ class product_product(osv.osv): def _set_image(self, cr, uid, id, name, value, args, context=None): return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context) + def _get_name_template_ids(self, cr, uid, ids, context=None): + result = set() + template_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', 'in', ids)]) + for el in template_ids: + result.add(el) + return list(result) + _defaults = { 'active': lambda *a: 1, 'price_extra': lambda *a: 0.0, @@ -554,7 +556,11 @@ class product_product(osv.osv): 'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Product Price')), 'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Product Price')), 'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'), - 'name_template': fields.related('product_tmpl_id', 'name', string="Template Name", type='char', size=128, store=True, select=True), + 'name_template': fields.related('product_tmpl_id', 'name', string="Template Name", type='char', size=128, store={ + 'product.template': (_get_name_template_ids, ['name'], 10), + 'product.product': (lambda self, cr, uid, ids, c=None: ids, [], 10), + + }, select=True), 'color': fields.integer('Color Index'), # image: all image fields are base64 encoded and PIL-supported 'image': fields.binary("Image", @@ -753,7 +759,6 @@ class product_product(osv.osv): args.append((('categ_id', 'child_of', context['search_default_categ_id']))) return super(product_product, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=False) -product_product() class product_packaging(osv.osv): _name = "product.packaging" @@ -819,7 +824,6 @@ class product_packaging(osv.osv): return (10 - (sum % 10)) % 10 checksum = staticmethod(checksum) -product_packaging() class product_supplierinfo(osv.osv): @@ -893,7 +897,6 @@ class product_supplierinfo(osv.osv): res[supplier.id] = price return res _order = 'sequence' -product_supplierinfo() class pricelist_partnerinfo(osv.osv): @@ -905,7 +908,6 @@ class pricelist_partnerinfo(osv.osv): 'price': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price'), help="This price will be considered as a price for the supplier Unit of Measure if any or the default Unit of Measure of the product otherwise"), } _order = 'min_quantity asc' -pricelist_partnerinfo() class res_currency(osv.osv): _inherit = 'res.currency' diff --git a/addons/product/wizard/product_price.py b/addons/product/wizard/product_price.py index 8989e97d656..5b5c4ccd834 100644 --- a/addons/product/wizard/product_price.py +++ b/addons/product/wizard/product_price.py @@ -60,6 +60,5 @@ class product_price_list(osv.osv_memory): 'report_name': 'product.pricelist', 'datas': datas, } -product_price_list() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product_expiry/product_expiry.py b/addons/product_expiry/product_expiry.py index 457b4428522..4d8fd49ec4f 100644 --- a/addons/product_expiry/product_expiry.py +++ b/addons/product_expiry/product_expiry.py @@ -74,7 +74,6 @@ class stock_production_lot(osv.osv): 'removal_date': _get_date('removal_time'), 'alert_date': _get_date('alert_time'), } -stock_production_lot() class product_product(osv.osv): _inherit = 'product.product' @@ -88,5 +87,4 @@ class product_product(osv.osv): 'alert_time': fields.integer('Product Alert Time', help='When a new a Serial Number is issued, this is the number of days before an alert should be notified.'), } -product_product() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product_manufacturer/product_manufacturer.py b/addons/product_manufacturer/product_manufacturer.py index fdd25133433..ea7acf66670 100644 --- a/addons/product_manufacturer/product_manufacturer.py +++ b/addons/product_manufacturer/product_manufacturer.py @@ -28,7 +28,6 @@ class product_product(osv.osv): 'manufacturer_pref' : fields.char('Manufacturer Product Code', size=64), 'attribute_ids': fields.one2many('product.manufacturer.attribute', 'product_id', 'Attributes'), } -product_product() class product_attribute(osv.osv): _name = "product.manufacturer.attribute" @@ -38,6 +37,5 @@ class product_attribute(osv.osv): 'value' : fields.char('Value', size=64), 'product_id': fields.many2one('product.product', 'Product', ondelete='cascade'), } -product_attribute() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product_margin/product_margin.py b/addons/product_margin/product_margin.py index f9187647f35..1bbfe8c285b 100644 --- a/addons/product_margin/product_margin.py +++ b/addons/product_margin/product_margin.py @@ -128,6 +128,5 @@ class product_product(osv.osv): help="Expected margin * 100 / Expected Sale"), } -product_product() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product_margin/wizard/product_margin.py b/addons/product_margin/wizard/product_margin.py index 6c54bd72be3..7a56ed2e13d 100644 --- a/addons/product_margin/wizard/product_margin.py +++ b/addons/product_margin/wizard/product_margin.py @@ -81,6 +81,5 @@ class product_margin(osv.osv_memory): 'search_view_id': id['res_id'] } -product_margin() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index 3928c2e4676..4f8a7b0b9cc 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -33,7 +33,6 @@ class product_pricelist(osv.osv): 'visible_discount': True, } -product_pricelist() class sale_order_line(osv.osv): _inherit = "sale.order.line" @@ -95,7 +94,6 @@ class sale_order_line(osv.osv): result['discount'] = 0.0 return res -sale_order_line() class account_invoice_line(osv.osv): _inherit = "account.invoice.line" @@ -161,6 +159,5 @@ class account_invoice_line(osv.osv): result['discount']=0.0 return res -account_invoice_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/company.py b/addons/project/company.py index 459c24e7b8a..19f40436070 100644 --- a/addons/project/company.py +++ b/addons/project/company.py @@ -31,7 +31,6 @@ class res_company(osv.osv): "forget to setup the right unit of measure in your employees.", ), } -res_company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/project.py b/addons/project/project.py index eb328007d44..0b99356a9de 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -208,6 +208,12 @@ class project(osv.osv): """Overriden in project_issue to offer more options""" return [('project.task', "Tasks")] + def _get_visibility_selection(self, cr, uid, context=None): + """ Overriden in portal_project to offer more options """ + return [('public', 'All Users'), + ('employees', 'Employees Only'), + ('followers', 'Followers Only')] + def attachment_tree_view(self, cr, uid, ids, context): task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)]) domain = [ @@ -229,6 +235,8 @@ class project(osv.osv): } # Lambda indirection method to avoid passing a copy of the overridable method when declaring the field _alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs) + _visibility_selection = lambda self, *args, **kwargs: self._get_visibility_selection(*args, **kwargs) + _columns = { 'complete_name': fields.function(_complete_name, string="Project Name", type='char', size=250), 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."), @@ -267,7 +275,7 @@ class project(osv.osv): "with Tasks (or optionally Issues if the Issue Tracker module is installed)."), 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, help="The kind of document created when an email is received on this project's email alias"), - 'privacy_visibility': fields.selection([('public','All Users'), ('followers','Followers Only')], 'Privacy / Visibility', required=True), + 'privacy_visibility': fields.selection(_visibility_selection, 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), 'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", type='int') } diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index a016c46e0e0..298355d4c82 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -189,7 +189,7 @@ - + diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py index 8e7dca059f9..006aae1085c 100644 --- a/addons/project/report/project_report.py +++ b/addons/project/report/project_report.py @@ -117,5 +117,4 @@ class report_project_task_user(osv.osv): """) -report_project_task_user() diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml index 10f3f4a8c7c..d9650e165ce 100644 --- a/addons/project/report/project_report_view.xml +++ b/addons/project/report/project_report_view.xml @@ -69,7 +69,7 @@ - + diff --git a/addons/project/res_partner.py b/addons/project/res_partner.py index 97fca8c459f..e4ca63d79d8 100644 --- a/addons/project/res_partner.py +++ b/addons/project/res_partner.py @@ -29,7 +29,6 @@ class res_partner(osv.osv): 'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'), } -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml index f1d8c7e760f..4bbaa9b788e 100644 --- a/addons/project/security/project_security.xml +++ b/addons/project/security/project_security.xml @@ -38,39 +38,62 @@ - Project multi-company + Project: multi-company - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + ['|', + ('company_id', '=', False), + ('company_id', 'child_of', [user.company_id.id]), + ] + + + + Project: project manager: see all + + [(1, '=', 1)] + - Public Members + Project: employees: public, employees or followers - - ['|',('privacy_visibility','in',[False,'public']),('message_follower_ids','in',[user.partner_id.id])] + ['|', + ('privacy_visibility', 'in', ['public', 'employees']), + ('message_follower_ids', 'in', [user.partner_id.id]) + ] + - Task multi-company + Project/Task: multi-company - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + ['|', + ('company_id', '=', False), + ('company_id', 'child_of', [user.company_id.id]), + ] - Tasks According to User and Project + Project/Task: employees: public or employee or following or assigned - - ['|','|','|',('user_id','=',False),('user_id','=',user.id),('project_id.members','in', [user.id]),('project_id.user_id','=',user.id)] - + ['|', + ('user_id', '=', user.id), + '|', + ('project_id.privacy_visibility', 'in', ['public', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.partner_id.id]), + ] + - Project Managers: all tasks from all projects + Project/Task: project manager: see all - [(1,'=',1)] + [(1, '=', 1)] + diff --git a/addons/project/wizard/project_task_delegate.py b/addons/project/wizard/project_task_delegate.py index bb15c62db1f..6a26868557e 100644 --- a/addons/project/wizard/project_task_delegate.py +++ b/addons/project/wizard/project_task_delegate.py @@ -125,13 +125,12 @@ class project_task_delegate(osv.osv_memory): action_model, action_id = models_data.get_object_reference(cr, uid, 'project', 'action_view_task') view_model, task_view_form_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_form2') view_model, task_view_tree_id = models_data.get_object_reference(cr, uid, 'project', 'view_task_tree2') - action = self.pool.get(action_model).read(cr, uid, action_id, context=context) + action = self.pool[action_model].read(cr, uid, action_id, context=context) action['res_id'] = delegated_tasks[task_id] action['view_id'] = False action['views'] = [(task_view_form_id, 'form'), (task_view_tree_id, 'tree')] action['help'] = False return action -project_task_delegate() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/wizard/project_task_reevaluate.py b/addons/project/wizard/project_task_reevaluate.py index 1cbea397d5e..eeb8cccc5d2 100644 --- a/addons/project/wizard/project_task_reevaluate.py +++ b/addons/project/wizard/project_task_reevaluate.py @@ -80,6 +80,5 @@ class project_task_reevaluate(osv.osv_memory): if context.get('button_reactivate'): task_pool.do_reopen(cr, uid, [task_id], context=context) return {'type': 'ir.actions.act_window_close'} -project_task_reevaluate() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_gtd/project_gtd.py b/addons/project_gtd/project_gtd.py index 715544cf4fc..47569e863aa 100644 --- a/addons/project_gtd/project_gtd.py +++ b/addons/project_gtd/project_gtd.py @@ -37,7 +37,6 @@ class project_gtd_context(osv.osv): } _order = "sequence, name" -project_gtd_context() class project_gtd_timebox(osv.osv): @@ -49,7 +48,6 @@ class project_gtd_timebox(osv.osv): 'icon': fields.selection(tools.icons, 'Icon', size=64), } -project_gtd_timebox() class project_task(osv.osv): _inherit = "project.task" @@ -120,6 +118,5 @@ class project_task(osv.osv): return res -project_task() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_gtd/wizard/project_gtd_empty.py b/addons/project_gtd/wizard/project_gtd_empty.py index 26e438b789e..36c64cbc224 100644 --- a/addons/project_gtd/wizard/project_gtd_empty.py +++ b/addons/project_gtd/wizard/project_gtd_empty.py @@ -62,6 +62,5 @@ class project_timebox_empty(osv.osv_memory): obj_task.write(cr, uid, close, {'timebox_id':False}) return {} -project_timebox_empty() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_gtd/wizard/project_gtd_fill.py b/addons/project_gtd/wizard/project_gtd_fill.py index ae8fea61e1c..121bd7efdac 100644 --- a/addons/project_gtd/wizard/project_gtd_fill.py +++ b/addons/project_gtd/wizard/project_gtd_fill.py @@ -56,6 +56,5 @@ class project_timebox_fill(osv.osv_memory): self.pool.get('project.task').write(cr, uid, data[0]['task_ids'], {'timebox_id':data[0]['timebox_to_id'][0]}) return {'type': 'ir.actions.act_window_close'} -project_timebox_fill() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 7aa408a99aa..69792322ab3 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -40,7 +40,6 @@ class project_issue_version(osv.osv): _defaults = { 'active': 1, } -project_issue_version() class project_issue(base_stage, osv.osv): _name = "project.issue" diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index f888ff9da77..f15355e9cc8 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -146,7 +146,7 @@ project.issue - + @@ -154,10 +154,11 @@ - + + diff --git a/addons/project_issue/report/project_issue_report.py b/addons/project_issue/report/project_issue_report.py index 199a971c4ad..5cca0ff8689 100644 --- a/addons/project_issue/report/project_issue_report.py +++ b/addons/project_issue/report/project_issue_report.py @@ -105,6 +105,5 @@ class project_issue_report(osv.osv): WHERE c.active= 'true' )""") -project_issue_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml index e56c6612e0d..a885dcd8a7f 100644 --- a/addons/project_issue/report/project_issue_report_view.xml +++ b/addons/project_issue/report/project_issue_report_view.xml @@ -55,7 +55,7 @@ - + diff --git a/addons/project_issue/security/project_issue_security.xml b/addons/project_issue/security/project_issue_security.xml index c3119073a75..5215a94a8b7 100644 --- a/addons/project_issue/security/project_issue_security.xml +++ b/addons/project_issue/security/project_issue_security.xml @@ -1,5 +1,27 @@ + + + Project/Issue: project manager: see all + + [(1, '=', 1)] + + + + + Project/Issue: employees: public or employee or following or assigned + + ['|', + '|', + ('project_id.privacy_visibility', 'in', ['public', 'employees']), + '&', + ('project_id.privacy_visibility', '=', 'followers'), + ('message_follower_ids', 'in', [user.id]), + ('user_id', '=', user.id), + ] + + + diff --git a/addons/project_issue/test/subscribe_issue.yml b/addons/project_issue/test/subscribe_issue.yml index 9f16dc80b97..af81ff4d295 100644 --- a/addons/project_issue/test/subscribe_issue.yml +++ b/addons/project_issue/test/subscribe_issue.yml @@ -2,8 +2,7 @@ In Order to test process of Issue in OpenERP, Custmer send the issue by email. - !python {model: mail.thread}: | - from openerp import addons - request_file = open(addons.get_module_resource('project_issue','test', 'issue.eml'),'rb') + request_file = open(openerp.modules.module.get_module_resource('project_issue','test', 'issue.eml'),'rb') request_message = request_file.read() self.message_process(cr, uid, 'project.issue', request_message) - @@ -14,4 +13,4 @@ assert issue_ids and len(issue_ids), "issue is not created after getting request" issue = self.browse(cr, uid, issue_ids[0], context=context) assert not issue.partner_id, "Customer should be a new" - assert issue.name == "Error in the account module", "Subject does not match" \ No newline at end of file + assert issue.name == "Error in the account module", "Subject does not match" diff --git a/addons/project_issue_sheet/project_issue_sheet.py b/addons/project_issue_sheet/project_issue_sheet.py index 8fe645e19fe..551ef3c8194 100644 --- a/addons/project_issue_sheet/project_issue_sheet.py +++ b/addons/project_issue_sheet/project_issue_sheet.py @@ -58,7 +58,6 @@ class project_issue(osv.osv): return result -project_issue() class account_analytic_line(osv.osv): _inherit = 'account.analytic.line' @@ -67,7 +66,6 @@ class account_analytic_line(osv.osv): 'create_date' : fields.datetime('Create Date', readonly=True), } -account_analytic_line() class hr_analytic_issue(osv.osv): @@ -77,6 +75,5 @@ class hr_analytic_issue(osv.osv): 'issue_id' : fields.many2one('project.issue', 'Issue'), } -hr_analytic_issue() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_long_term/project_long_term.py b/addons/project_long_term/project_long_term.py index e53ff9ed253..a950907fb69 100644 --- a/addons/project_long_term/project_long_term.py +++ b/addons/project_long_term/project_long_term.py @@ -198,7 +198,6 @@ class project_phase(osv.osv): result += "\n" return result -project_phase() class project_user_allocation(osv.osv): _name = 'project.user.allocation' @@ -211,7 +210,6 @@ class project_user_allocation(osv.osv): 'date_start': fields.datetime('Start Date', help="Starting Date"), 'date_end': fields.datetime('End Date', help="Ending Date"), } -project_user_allocation() class project(osv.osv): _inherit = "project.project" @@ -267,7 +265,6 @@ class project(osv.osv): 'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S') }, context=context) return True -project() class account_analytic_account(osv.osv): _inherit = 'account.analytic.account' @@ -283,17 +280,17 @@ class account_analytic_account(osv.osv): res['value']['use_phases'] = template.use_phases return res - def _trigger_project_creation(self, cr, uid, vals, context=None): - res= super(account_analytic_account, self)._trigger_project_creation(cr, uid, vals, context=context) - return res or vals.get('use_phases') -account_analytic_account() + def _trigger_project_creation(self, cr, uid, vals, context=None): + if context is None: context = {} + res = super(account_analytic_account, self)._trigger_project_creation(cr, uid, vals, context=context) + return res or (vals.get('use_phases') and not 'project_creation_in_progress' in context) + class project_task(osv.osv): _inherit = "project.task" _columns = { 'phase_id': fields.many2one('project.phase', 'Project Phase', domain="[('project_id', '=', project_id)]"), } -project_task() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_long_term/wizard/project_compute_phases.py b/addons/project_long_term/wizard/project_compute_phases.py index c9aa7eb3573..5ac89e2def2 100644 --- a/addons/project_long_term/wizard/project_compute_phases.py +++ b/addons/project_long_term/wizard/project_compute_phases.py @@ -71,6 +71,5 @@ class project_compute_phases(osv.osv_memory): result['context'] = {"search_default_project_id":project_id, "default_project_id":project_id, "search_default_current": 1} return result -project_compute_phases() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_long_term/wizard/project_compute_tasks.py b/addons/project_long_term/wizard/project_compute_tasks.py index f21dab8a6d2..d442aee7790 100644 --- a/addons/project_long_term/wizard/project_compute_tasks.py +++ b/addons/project_long_term/wizard/project_compute_tasks.py @@ -59,6 +59,5 @@ class project_compute_tasks(osv.osv_memory): result['target'] = 'current' return result -project_compute_tasks() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mrp/project_mrp.py b/addons/project_mrp/project_mrp.py index a6f0495a962..bb25b4aae24 100644 --- a/addons/project_mrp/project_mrp.py +++ b/addons/project_mrp/project_mrp.py @@ -45,14 +45,12 @@ class project_task(osv.osv): res = super(project_task, self).do_cancel(cr, uid, ids, *args, **kwargs) self._validate_subflows(cr, uid, ids) return res -project_task() class product_product(osv.osv): _inherit = "product.product" _columns = { 'project_id': fields.many2one('project.project', 'Project', ondelete='set null',) } -product_product() class sale_order(osv.osv): _inherit ='sale.order' @@ -108,5 +106,4 @@ class sale_order(osv.osv): 'picked_rate': fields.function(_picked_rate, method=True, string='Picked', type='float'), } -sale_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mrp/project_procurement.py b/addons/project_mrp/project_procurement.py index 79228fe3285..f4b4a28db46 100644 --- a/addons/project_mrp/project_procurement.py +++ b/addons/project_mrp/project_procurement.py @@ -94,6 +94,5 @@ class procurement_order(osv.osv): if procurement.sale_line_id and procurement.sale_line_id.order_id: procurement.sale_line_id.order_id.message_post(body=body) -procurement_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mrp/test/project_task_procurement.yml b/addons/project_mrp/test/project_task_procurement.yml index 8854ce20c80..967461ca30a 100644 --- a/addons/project_mrp/test/project_task_procurement.yml +++ b/addons/project_mrp/test/project_task_procurement.yml @@ -27,7 +27,7 @@ assert (not project and not account) or project.analytic_account_id == account, "Project does not correspond." planned_hours = self._convert_qty_company_hours(cr, uid, procurement, context=context) assert task.planned_hours == planned_hours, 'Planned Hours do not correspond.' - assert datetime.strptime(task.date_deadline, '%Y-%m-%d') == datetime.strptime(procurement.date_planned, '%Y-%m-%d %H:%M:%S'), 'Deadline does not correspond.' + assert datetime.strptime(task.date_deadline, '%Y-%m-%d') == datetime.strptime(procurement.date_planned[:10], '%Y-%m-%d'), 'Deadline does not correspond.' if procurement.product_id.product_manager: assert task.user_id.id == procurement.product_id.product_manager.id, 'Allocated Person does not correspond with Service Product Manager.' - diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 6c2b2b0e13b..ffee600f5a7 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -69,7 +69,6 @@ class project_project(osv.osv): result['help'] = help return result -project_project() class project_work(osv.osv): _inherit = "project.task.work" @@ -218,7 +217,6 @@ class project_work(osv.osv): 'hr_analytic_timesheet_id':fields.many2one('hr.analytic.timesheet','Related Timeline Id', ondelete='set null'), } -project_work() class task(osv.osv): _inherit = "project.task" @@ -254,7 +252,6 @@ class task(osv.osv): hr_anlytic_timesheet.write(cr, uid, [line_id], vals_line, {}) return super(task,self).write(cr, uid, ids, vals, context) -task() class res_partner(osv.osv): _inherit = 'res.partner' @@ -266,7 +263,6 @@ class res_partner(osv.osv): return super(res_partner,self).unlink(cursor, user, ids, context=context) -res_partner() class account_analytic_line(osv.osv): _inherit = "account.analytic.line" @@ -293,6 +289,5 @@ class account_analytic_line(osv.osv): raise osv.except_osv(_('Invalid Analytic Account !'), _('You cannot select a Analytic Account which is in Close or Cancelled state.')) return res -account_analytic_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_timesheet/report/task_report.py b/addons/project_timesheet/report/task_report.py index c4945e942f6..3411a73a239 100644 --- a/addons/project_timesheet/report/task_report.py +++ b/addons/project_timesheet/report/task_report.py @@ -79,6 +79,5 @@ class report_timesheet_task_user(osv.osv): to_char(to_date(months.name, 'YYYY/MM/DD'),'MM') ) """) -report_timesheet_task_user() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/company.py b/addons/purchase/company.py index 275f5ea200d..cc98c200458 100644 --- a/addons/purchase/company.py +++ b/addons/purchase/company.py @@ -24,13 +24,16 @@ from openerp.osv import osv,fields class company(osv.osv): _inherit = 'res.company' _columns = { - 'po_lead': fields.float('Purchase Lead Time', required=True, - help="This is the leads/security time for each purchase order."), + 'po_lead': fields.float( + 'Purchase Lead Time', required=True, + help="Margin of error for supplier lead times. When the system"\ + "generates Purchase Orders for procuring products,"\ + "they will be scheduled that many days earlier "\ + "to cope with unexpected supplier delays."), } _defaults = { 'po_lead': lambda *a: 1.0, } -company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/partner.py b/addons/purchase/partner.py index 56eaf288fe6..5e5af0815e3 100644 --- a/addons/purchase/partner.py +++ b/addons/purchase/partner.py @@ -43,6 +43,9 @@ class res_partner(osv.osv): super(res_partner, self).copy(cr, uid, id, default=default, context=context) + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_product_pricelist_purchase'] + _columns = { 'property_product_pricelist_purchase': fields.property( 'product.pricelist', @@ -55,7 +58,6 @@ class res_partner(osv.osv): 'purchase_order_count': fields.function(_purchase_order_count, string='# of Purchase Order', type='integer'), 'purchase_order_ids': fields.one2many('purchase.order','partner_id','Purchase Order') } -res_partner() diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 4c18739cc78..26e34048b40 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -20,6 +20,8 @@ ############################################################################## import time +import pytz +from openerp import SUPERUSER_ID from datetime import datetime from dateutil.relativedelta import relativedelta from operator import attrgetter @@ -371,7 +373,7 @@ class purchase_order(osv.osv): pick_ids += [picking.id for picking in po.picking_ids] action_model, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree4')) - action = self.pool.get(action_model).read(cr, uid, action_id, context=context) + action = self.pool[action_model].read(cr, uid, action_id, context=context) ctx = eval(action['context']) ctx.update({ 'search_default_purchase_id': ids[0] @@ -592,11 +594,34 @@ class purchase_order(osv.osv): self.signal_purchase_cancel(cr, uid, ids) return True + def date_to_datetime(self, cr, uid, userdate, context=None): + """ Convert date values expressed in user's timezone to + server-side UTC timestamp, assuming a default arbitrary + time of 12:00 AM - because a time is needed. + + :param str userdate: date string in in user time zone + :return: UTC datetime string for server-side use + """ + # TODO: move to fields.datetime in server after 7.0 + user_date = datetime.strptime(userdate, DEFAULT_SERVER_DATE_FORMAT) + if context and context.get('tz'): + tz_name = context['tz'] + else: + tz_name = self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz'] + if tz_name: + utc = pytz.timezone('UTC') + context_tz = pytz.timezone(tz_name) + user_datetime = user_date + relativedelta(hours=12.0) + local_timestamp = context_tz.localize(user_datetime, is_dst=False) + user_datetime = local_timestamp.astimezone(utc) + return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return user_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + def _prepare_order_picking(self, cr, uid, order, context=None): return { 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in'), 'origin': order.name + ((order.origin and (':' + order.origin)) or ''), - 'date': order.date_order, + 'date': self.date_to_datetime(cr, uid, order.date_order, context), 'partner_id': order.dest_address_id.id or order.partner_id.id, 'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none', 'type': 'in', @@ -614,8 +639,8 @@ class purchase_order(osv.osv): 'product_uos_qty': order_line.product_qty, 'product_uom': order_line.product_uom.id, 'product_uos': order_line.product_uom.id, - 'date': order_line.date_planned, - 'date_expected': order_line.date_planned, + 'date': self.date_to_datetime(cr, uid, order.date_order, context), + 'date_expected': self.date_to_datetime(cr, uid, order.date_order, context), 'location_id': order.partner_id.property_stock_supplier.id, 'location_dest_id': order.location_id.id, 'picking_id': picking_id, @@ -990,7 +1015,6 @@ class purchase_order_line(osv.osv): self.write(cr, uid, ids, {'state': 'confirmed'}, context=context) return True -purchase_order_line() class procurement_order(osv.osv): _inherit = 'procurement.order' diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 056a06f0750..4432d2b3200 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -270,7 +270,7 @@ - + @@ -297,7 +297,7 @@ - + @@ -335,7 +335,7 @@ ir.actions.act_window purchase.order {} - [('state','in',('draft','sent','cancel'))] + [('state','in',('draft','sent','cancel', 'confirmed'))] tree,form,graph,calendar @@ -481,7 +481,7 @@ - + diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py index 1855ed173ca..5af74b23495 100644 --- a/addons/purchase/report/purchase_report.py +++ b/addons/purchase/report/purchase_report.py @@ -136,7 +136,6 @@ class purchase_report(osv.osv): u2.factor ) """) -purchase_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index 60b439a2097..e64c12a2359 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -29,7 +29,6 @@ class stock_move(osv.osv): readonly=True), } -stock_move() # # Inherit of picking to add the link to the PO diff --git a/addons/purchase/wizard/purchase_line_invoice.py b/addons/purchase/wizard/purchase_line_invoice.py index 12dbf4a09f6..f97d82a8456 100644 --- a/addons/purchase/wizard/purchase_line_invoice.py +++ b/addons/purchase/wizard/purchase_line_invoice.py @@ -142,7 +142,6 @@ class purchase_line_invoice(osv.osv_memory): 'context': "{'type':'in_invoice', 'journal_type': 'purchase'}", 'type': 'ir.actions.act_window' } -purchase_line_invoice() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase/wizard/purchase_order_group.py b/addons/purchase/wizard/purchase_order_group.py index f9b68c1fe11..243f27efea4 100644 --- a/addons/purchase/wizard/purchase_order_group.py +++ b/addons/purchase/wizard/purchase_order_group.py @@ -83,6 +83,5 @@ class purchase_order_group(osv.osv_memory): 'search_view_id': id['res_id'] } -purchase_order_group() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase_analytic_plans/purchase_analytic_plans.py b/addons/purchase_analytic_plans/purchase_analytic_plans.py index c80beddd20f..a654de83253 100644 --- a/addons/purchase_analytic_plans/purchase_analytic_plans.py +++ b/addons/purchase_analytic_plans/purchase_analytic_plans.py @@ -29,7 +29,6 @@ class purchase_order_line(osv.osv): 'analytics_id':fields.many2one('account.analytic.plan.instance','Analytic Distribution'), } -purchase_order_line() class purchase_order(osv.osv): _name='purchase.order' @@ -40,6 +39,5 @@ class purchase_order(osv.osv): res['analytics_id'] = order_line.analytics_id.id return res -purchase_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase_analytic_plans/purchase_analytic_plans_view.xml b/addons/purchase_analytic_plans/purchase_analytic_plans_view.xml index bcc67a2a215..d1db411f68c 100644 --- a/addons/purchase_analytic_plans/purchase_analytic_plans_view.xml +++ b/addons/purchase_analytic_plans/purchase_analytic_plans_view.xml @@ -38,5 +38,22 @@
+ + + + + + + + account.invoice.supplier.form.inherit + account.invoice + + 2 + + + + + + diff --git a/addons/purchase_double_validation/i18n/lt.po b/addons/purchase_double_validation/i18n/lt.po new file mode 100644 index 00000000000..7205a152f84 --- /dev/null +++ b/addons/purchase_double_validation/i18n/lt.po @@ -0,0 +1,49 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-29 15:27+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: purchase_double_validation +#: model:ir.model,name:purchase_double_validation.model_purchase_config_settings +msgid "purchase.config.settings" +msgstr "" + +#. module: purchase_double_validation +#: view:purchase.order:0 +msgid "Purchase orders which are not approved yet." +msgstr "" + +#. module: purchase_double_validation +#: field:purchase.config.settings,limit_amount:0 +msgid "limit to require a second approval" +msgstr "" + +#. module: purchase_double_validation +#: view:board.board:0 +#: model:ir.actions.act_window,name:purchase_double_validation.purchase_waiting +msgid "Purchase Orders Waiting Approval" +msgstr "" + +#. module: purchase_double_validation +#: view:purchase.order:0 +msgid "To Approve" +msgstr "" + +#. module: purchase_double_validation +#: help:purchase.config.settings,limit_amount:0 +msgid "Amount after which validation of purchase is required." +msgstr "" diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index b213f958736..c5d32136a7a 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -194,7 +194,6 @@ class purchase_requisition_line(osv.osv): _defaults = { 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition.line', context=c), } -purchase_requisition_line() class purchase_order(osv.osv): _inherit = "purchase.order" @@ -216,7 +215,6 @@ class purchase_order(osv.osv): po.requisition_id.tender_done(context=context) return res -purchase_order() class product_product(osv.osv): _inherit = 'product.product' @@ -228,7 +226,6 @@ class product_product(osv.osv): 'purchase_requisition': False } -product_product() class procurement_order(osv.osv): @@ -261,6 +258,5 @@ class procurement_order(osv.osv): res = super(procurement_order, self).make_po(cr, uid, ids, context=context) return res -procurement_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/purchase_requisition/wizard/purchase_requisition_partner.py b/addons/purchase_requisition/wizard/purchase_requisition_partner.py index 58a72556b22..0fee8745931 100644 --- a/addons/purchase_requisition/wizard/purchase_requisition_partner.py +++ b/addons/purchase_requisition/wizard/purchase_requisition_partner.py @@ -47,7 +47,6 @@ class purchase_requisition_partner(osv.osv_memory): self.pool.get('purchase.requisition').make_purchase_order(cr, uid, active_ids, data.partner_id.id, context=context) return {'type': 'ir.actions.act_window_close'} -purchase_requisition_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/report_intrastat/report_intrastat.py b/addons/report_intrastat/report_intrastat.py index 2d11151cb5b..003aaec8447 100644 --- a/addons/report_intrastat/report_intrastat.py +++ b/addons/report_intrastat/report_intrastat.py @@ -34,7 +34,6 @@ class res_country(osv.osv): 'intrastat': lambda *a: False, } -res_country() class report_intrastat_code(osv.osv): @@ -45,7 +44,6 @@ class report_intrastat_code(osv.osv): 'description': fields.char('Description', size=64), } -report_intrastat_code() class product_template(osv.osv): @@ -55,7 +53,6 @@ class product_template(osv.osv): 'intrastat_id': fields.many2one('report.intrastat.code', 'Intrastat code'), } -product_template() class report_intrastat(osv.osv): _name = "report.intrastat" @@ -122,6 +119,5 @@ class report_intrastat(osv.osv): group by to_char(inv.create_date, 'YYYY'), to_char(inv.create_date, 'MM'),intrastat.id,inv.type,pt.intrastat_id, inv_country.code,inv.number, inv.currency_id )""") -report_intrastat() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/report_webkit/header.py b/addons/report_webkit/header.py index 71deb57c370..9713efc5382 100644 --- a/addons/report_webkit/header.py +++ b/addons/report_webkit/header.py @@ -87,7 +87,6 @@ class HeaderHTML(osv.osv): help="Select Proper Paper size" ) } -HeaderHTML() class HeaderImage(osv.osv): """Logo allows you to define multiple logo per company""" @@ -98,6 +97,5 @@ class HeaderImage(osv.osv): 'name' : fields.char('Name', size=128, required =True, help="Name of Image"), 'type' : fields.char('Type', size=32, required =True, help="Image type(png,gif,jpeg)") } -HeaderImage() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/report_webkit/webkit_report.py b/addons/report_webkit/webkit_report.py index 01536f471fe..3d8e41c42ff 100644 --- a/addons/report_webkit/webkit_report.py +++ b/addons/report_webkit/webkit_report.py @@ -40,8 +40,8 @@ import logging from report_helper import WebKitHelper import openerp +from openerp.modules.module import get_module_resource from openerp.report.report_sxw import * -from openerp import addons from openerp import tools from openerp.tools.translate import _ from openerp.osv.osv import except_osv @@ -280,7 +280,7 @@ class WebKitParser(report_sxw): template = False if report_xml.report_file : - path = addons.get_module_resource(*report_xml.report_file.split(os.path.sep)) + path = get_module_resource(*report_xml.report_file.split(os.path.sep)) if path and os.path.exists(path) : template = file(path).read() if not template and report_xml.report_webkit_data : @@ -296,7 +296,7 @@ class WebKitParser(report_sxw): ) if not report_xml.header : header = '' - default_head = addons.get_module_resource('report_webkit', 'default_header.html') + default_head = get_module_resource('report_webkit', 'default_header.html') with open(default_head,'r') as f: header = f.read() css = report_xml.webkit_header.css diff --git a/addons/report_webkit/wizard/report_webkit_actions.py b/addons/report_webkit/wizard/report_webkit_actions.py index 4080d0a4034..5f91d05d671 100644 --- a/addons/report_webkit/wizard/report_webkit_actions.py +++ b/addons/report_webkit/wizard/report_webkit_actions.py @@ -138,6 +138,5 @@ class report_webkit_actions(osv.osv_memory): 'type': 'ir.actions.act_window', } -report_webkit_actions() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/resource/resource.py b/addons/resource/resource.py index 73a6e2a3ec8..944c90a3749 100644 --- a/addons/resource/resource.py +++ b/addons/resource/resource.py @@ -317,7 +317,6 @@ class resource_calendar(osv.osv): # return timedelta converted to hours return (hours_timedelta.days * 24.0 + hours_timedelta.seconds / 3600.0) -resource_calendar() class resource_calendar_attendance(osv.osv): _name = "resource.calendar.attendance" @@ -337,7 +336,6 @@ class resource_calendar_attendance(osv.osv): _defaults = { 'dayofweek' : '0' } -resource_calendar_attendance() def hours_time_string(hours): """ convert a number of hours (float) into a string with format '%H:%M' """ @@ -475,7 +473,6 @@ class resource_resource(osv.osv): wktime_cal.append((non_working[:-1], time_range)) return wktime_cal -resource_resource() class resource_calendar_leaves(osv.osv): _name = "resource.calendar.leaves" @@ -508,6 +505,5 @@ class resource_calendar_leaves(osv.osv): return {'value': result} return {'value': {'calendar_id': []}} -resource_calendar_leaves() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index 070e9c4336f..80143379247 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -110,6 +110,5 @@ class sale_report(osv.osv): s.project_id ) """) -sale_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index ec2307ca983..bfbaa5f0a6f 100644 --- a/addons/sale/res_config.py +++ b/addons/sale/res_config.py @@ -58,7 +58,7 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""), implied_group='product.group_product_variant', help="""Allow to manage several variants per product. As an example, if you sell T-Shirts, for the same "Linux T-Shirt", you may have variants on sizes or colors; S, M, L, XL, XXL."""), 'module_warning': fields.boolean("Allow configuring alerts by customer or products", - help="""Allow to configure notification on products and trigger them when a user wants to sale a given product or a given customer. + help="""Allow to configure notification on products and trigger them when a user wants to sell a given product or a given customer. Example: Product: this product is deprecated, do not purchase more than 5. Supplier: don't forget to ask for an express delivery."""), 'module_sale_margin': fields.boolean("Display margins on sales orders", diff --git a/addons/sale/res_partner_view.xml b/addons/sale/res_partner_view.xml index 54a9813eea9..40ac119fdee 100644 --- a/addons/sale/res_partner_view.xml +++ b/addons/sale/res_partner_view.xml @@ -63,14 +63,34 @@ sale.group_delivery_invoice_address - - False False + sale.group_delivery_invoice_address - + + False + sale.group_delivery_invoice_address + + + False + sale.group_delivery_invoice_address + + + False + sale.group_delivery_invoice_address + + + False + sale.group_delivery_invoice_address + + + False + sale.group_delivery_invoice_address + + + False sale.group_delivery_invoice_address diff --git a/addons/sale/sale.py b/addons/sale/sale.py index d1b4236cd4e..210e4dd3be6 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -42,7 +42,6 @@ class sale_shop(osv.osv): 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'sale.shop', context=c), } -sale_shop() class sale_order(osv.osv): _name = "sale.order" @@ -211,8 +210,7 @@ class sale_order(osv.osv): 'order_policy': fields.selection([ ('manual', 'On Demand'), ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, - help="""This field controls how invoice and delivery operations are synchronized. - - With 'Before Delivery', a draft invoice is created, and it must be paid before delivery."""), + help="""This field controls how invoice and delivery operations are synchronized."""), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Pricelist for current sales order."), 'currency_id': fields.related('pricelist_id', 'currency_id', type="many2one", relation="res.currency", string="Currency", readonly=True, required=True), 'project_id': fields.many2one('account.analytic.account', 'Contract / Analytic', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a sales order."), @@ -322,10 +320,6 @@ class sale_order(osv.osv): return {'value': {'partner_invoice_id': False, 'partner_shipping_id': False, 'payment_term': False, 'fiscal_position': False}} part = self.pool.get('res.partner').browse(cr, uid, part, context=context) - #if the chosen partner is not a company and has a parent company, use the parent to choose the delivery, the - #invoicing addresses and all the fields related to the partner. - if part.parent_id and not part.is_company: - part = part.parent_id addr = self.pool.get('res.partner').address_get(cr, uid, [part.id], ['delivery', 'invoice', 'contact']) pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False payment_term = part.property_payment_term and part.property_payment_term.id or False @@ -1026,4 +1020,16 @@ class account_invoice(osv.Model): sale_order_obj.message_post(cr, uid, so_ids, body=_("Invoice paid"), context=context) return res + def unlink(self, cr, uid, ids, context=None): + """ Overwrite unlink method of account invoice to send a trigger to the sale workflow upon invoice deletion """ + invoice_ids = self.search(cr, uid, [('id', 'in', ids), ('state', 'in', ['draft', 'cancel'])], context=context) + #if we can't cancel all invoices, do nothing + if len(invoice_ids) == len(ids): + #Cancel invoice(s) first before deleting them so that if any sale order is associated with them + #it will trigger the workflow to put the sale order in an 'invoice exception' state + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_validate(uid, 'account.invoice', id, 'invoice_cancel', cr) + return super(account_invoice, self).unlink(cr, uid, ids, context=context) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 76e2f08aeb3..d8d038eb002 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -156,7 +156,7 @@ - + @@ -308,7 +308,7 @@ - + @@ -329,7 +329,6 @@ tree,form,calendar,graph { - 'show_address': 1, 'search_default_my_sale_orders_filter': 1 } @@ -376,7 +375,7 @@ form tree,form,calendar,graph - {'show_address': 1, 'search_default_my_sale_orders_filter': 1} + {'search_default_my_sale_orders_filter': 1} [('state','in',('draft','sent','cancel'))] @@ -477,7 +476,7 @@ - + @@ -503,7 +502,7 @@ - + diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py index 656e21a2963..83b953cc4d5 100644 --- a/addons/sale/wizard/sale_line_invoice.py +++ b/addons/sale/wizard/sale_line_invoice.py @@ -128,6 +128,5 @@ class sale_order_line_make_invoice(osv.osv_memory): 'type': 'ir.actions.act_window', } -sale_order_line_make_invoice() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/wizard/sale_make_invoice.py b/addons/sale/wizard/sale_make_invoice.py index 47716018caa..3712feda20b 100644 --- a/addons/sale/wizard/sale_make_invoice.py +++ b/addons/sale/wizard/sale_make_invoice.py @@ -67,6 +67,5 @@ class sale_make_invoice(osv.osv_memory): return result -sale_make_invoice() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py index 5d3cc165b46..4cb00245ade 100644 --- a/addons/sale/wizard/sale_make_invoice_advance.py +++ b/addons/sale/wizard/sale_make_invoice_advance.py @@ -210,6 +210,5 @@ class sale_advance_payment_inv(osv.osv_memory): 'type': 'ir.actions.act_window', } -sale_advance_payment_inv() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_analytic_plans/sale_analytic_plans.py b/addons/sale_analytic_plans/sale_analytic_plans.py index 2abbab7ab38..522f6b6f0fb 100644 --- a/addons/sale_analytic_plans/sale_analytic_plans.py +++ b/addons/sale_analytic_plans/sale_analytic_plans.py @@ -37,7 +37,6 @@ class sale_order_line(osv.osv): i = i + 1 return create_ids -sale_order_line() class sale_order(osv.osv): _inherit = "sale.order" diff --git a/addons/sale_analytic_plans/sale_analytic_plans_view.xml b/addons/sale_analytic_plans/sale_analytic_plans_view.xml index 6c788866520..3600d71b7d4 100644 --- a/addons/sale_analytic_plans/sale_analytic_plans_view.xml +++ b/addons/sale_analytic_plans/sale_analytic_plans_view.xml @@ -38,5 +38,19 @@ + + + + + account.invoice.line.form.inherit + account.invoice.line + + + + + + + + diff --git a/addons/sale_crm/sale_crm.py b/addons/sale_crm/sale_crm.py index b865edf6d0f..798c6a0d86b 100644 --- a/addons/sale_crm/sale_crm.py +++ b/addons/sale_crm/sale_crm.py @@ -134,6 +134,21 @@ class res_users(osv.Model): } +class sale_crm_lead(osv.Model): + _inherit = 'crm.lead' + + def on_change_user(self, cr, uid, ids, user_id, context=None): + """ Override of on change user_id on lead/opportunity; when having sale + the new logic is : + - use user.default_section_id + - or fallback on previous behavior """ + if user_id: + user = self.pool.get('res.users').browse(cr, uid, user_id, context=context) + if user.default_section_id and user.default_section_id.id: + return {'value': {'section_id': user.default_section_id.id}} + return super(sale_crm_lead, self).on_change_user(cr, uid, ids, user_id, context=context) + + class account_invoice(osv.osv): _inherit = 'account.invoice' _columns = { diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py index a052f47ca1f..6bea6286456 100644 --- a/addons/sale_crm/wizard/crm_make_sale.py +++ b/addons/sale_crm/wizard/crm_make_sale.py @@ -153,6 +153,5 @@ class crm_make_sale(osv.osv_memory): 'partner_id': _selectPartner, } -crm_make_sale() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_journal/sale_journal.py b/addons/sale_journal/sale_journal.py index 8463ab3f3bd..9e10c826898 100644 --- a/addons/sale_journal/sale_journal.py +++ b/addons/sale_journal/sale_journal.py @@ -34,7 +34,6 @@ class sale_journal_invoice_type(osv.osv): 'active': True, 'invoicing_method': 'simple' } -sale_journal_invoice_type() #============================================== # sale journal inherit @@ -52,28 +51,28 @@ class res_partner(osv.osv): group_name = "Accounting Properties", help = "This invoicing type will be used, by default, to invoice the current partner."), } -res_partner() + + def _commercial_fields(self, cr, uid, context=None): + return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_invoice_type'] + class picking(osv.osv): _inherit = "stock.picking" _columns = { 'invoice_type_id': fields.many2one('sale_journal.invoice.type', 'Invoice Type', readonly=True) } -picking() class stock_picking_in(osv.osv): _inherit = "stock.picking.in" _columns = { 'invoice_type_id': fields.many2one('sale_journal.invoice.type', 'Invoice Type', readonly=True) } -stock_picking_in() class stock_picking_out(osv.osv): _inherit = "stock.picking.out" _columns = { 'invoice_type_id': fields.many2one('sale_journal.invoice.type', 'Invoice Type', readonly=True) } -stock_picking_out() class sale(osv.osv): @@ -95,6 +94,5 @@ class sale(osv.osv): result['value']['invoice_type_id'] = itype.id return result -sale() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_journal/sale_journal_view.xml b/addons/sale_journal/sale_journal_view.xml index bcd83b5be84..9d41103252e 100644 --- a/addons/sale_journal/sale_journal_view.xml +++ b/addons/sale_journal/sale_journal_view.xml @@ -146,7 +146,7 @@ - + diff --git a/addons/sale_margin/sale_margin.py b/addons/sale_margin/sale_margin.py index dc8c4dfff3c..4c2e9ffc4f4 100644 --- a/addons/sale_margin/sale_margin.py +++ b/addons/sale_margin/sale_margin.py @@ -56,7 +56,6 @@ class sale_order_line(osv.osv): 'purchase_price': fields.float('Cost Price', digits=(16,2)) } -sale_order_line() class sale_order(osv.osv): _inherit = "sale.order" @@ -82,6 +81,5 @@ class sale_order(osv.osv): }), } -sale_order() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/addons/sale_mrp/i18n/lt.po b/addons/sale_mrp/i18n/lt.po new file mode 100644 index 00000000000..185753c3663 --- /dev/null +++ b/addons/sale_mrp/i18n/lt.po @@ -0,0 +1,43 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-29 15:30+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: sale_mrp +#: model:ir.model,name:sale_mrp.model_mrp_production +msgid "Manufacturing Order" +msgstr "" + +#. module: sale_mrp +#: help:mrp.production,sale_name:0 +msgid "Indicate the name of sales order." +msgstr "" + +#. module: sale_mrp +#: help:mrp.production,sale_ref:0 +msgid "Indicate the Customer Reference from sales order." +msgstr "" + +#. module: sale_mrp +#: field:mrp.production,sale_ref:0 +msgid "Sale Reference" +msgstr "" + +#. module: sale_mrp +#: field:mrp.production,sale_name:0 +msgid "Sale Name" +msgstr "" diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py index a5043d3b7d5..77a35972638 100644 --- a/addons/sale_mrp/sale_mrp.py +++ b/addons/sale_mrp/sale_mrp.py @@ -74,6 +74,5 @@ class mrp_production(osv.osv): 'sale_ref': fields.function(_ref_calc, multi='sale_name', type='char', string='Sale Reference', help='Indicate the Customer Reference from sales order.'), } -mrp_production() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_order_dates/sale_order_dates.py b/addons/sale_order_dates/sale_order_dates.py index d54eca88ca5..64b939de088 100644 --- a/addons/sale_order_dates/sale_order_dates.py +++ b/addons/sale_order_dates/sale_order_dates.py @@ -59,6 +59,5 @@ class sale_order_dates(osv.osv): 'effective_date': fields.function(_get_effective_date, type='date', store=True, string='Effective Date',help="Date on which picking is created."), } -sale_order_dates() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_stock/company.py b/addons/sale_stock/company.py index 67440ed6c67..5be1dbd524d 100644 --- a/addons/sale_stock/company.py +++ b/addons/sale_stock/company.py @@ -24,13 +24,15 @@ from openerp.osv import fields, osv class company(osv.osv): _inherit = 'res.company' _columns = { - 'security_lead': fields.float('Security Days', required=True, - help="This is the days added to what you promise to customers "\ - "for security purpose"), + 'security_lead': fields.float( + 'Security Days', required=True, + help="Margin of error for dates promised to customers. "\ + "Products will be scheduled for procurement and delivery "\ + "that many days earlier than the actual promised date, to "\ + "cope with unexpected delays in the supply chain."), } _defaults = { 'security_lead': 0.0, } -company() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_stock/process/sale_stock_process.xml b/addons/sale_stock/process/sale_stock_process.xml index 666f6ba6502..c10b34c8866 100644 --- a/addons/sale_stock/process/sale_stock_process.xml +++ b/addons/sale_stock/process/sale_stock_process.xml @@ -64,17 +64,6 @@ - - - - - - - - - @@ -106,7 +95,7 @@ - + diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py index 1f1e4a625be..63641020d98 100644 --- a/addons/sale_stock/report/sale_report.py +++ b/addons/sale_stock/report/sale_report.py @@ -91,6 +91,5 @@ class sale_report(osv.osv): s.project_id ) """) -sale_report() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index bd4eb2b8256..e71fb92f842 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -24,6 +24,8 @@ from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FO from dateutil.relativedelta import relativedelta from openerp.osv import fields, osv from openerp.tools.translate import _ +import pytz +from openerp import SUPERUSER_ID class sale_shop(osv.osv): _inherit = "sale.shop" @@ -31,7 +33,6 @@ class sale_shop(osv.osv): 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'), } -sale_shop() class sale_order(osv.osv): _inherit = "sale.order" @@ -229,6 +230,29 @@ class sale_order(osv.osv): res.append(line.procurement_id.id) return res + def date_to_datetime(self, cr, uid, userdate, context=None): + """ Convert date values expressed in user's timezone to + server-side UTC timestamp, assuming a default arbitrary + time of 12:00 AM - because a time is needed. + + :param str userdate: date string in in user time zone + :return: UTC datetime string for server-side use + """ + # TODO: move to fields.datetime in server after 7.0 + user_date = datetime.strptime(userdate, DEFAULT_SERVER_DATE_FORMAT) + if context and context.get('tz'): + tz_name = context['tz'] + else: + tz_name = self.pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz'] + if tz_name: + utc = pytz.timezone('UTC') + context_tz = pytz.timezone(tz_name) + user_datetime = user_date + relativedelta(hours=12.0) + local_timestamp = context_tz.localize(user_datetime, is_dst=False) + user_datetime = local_timestamp.astimezone(utc) + return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + return user_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + # if mode == 'finished': # returns True if all lines are done, False otherwise # if mode == 'canceled': @@ -311,7 +335,7 @@ class sale_order(osv.osv): return { 'name': pick_name, 'origin': order.name, - 'date': order.date_order, + 'date': self.date_to_datetime(cr, uid, order.date_order, context), 'type': 'out', 'state': 'auto', 'move_type': order.picking_policy, @@ -345,7 +369,8 @@ class sale_order(osv.osv): return True def _get_date_planned(self, cr, uid, order, line, start_date, context=None): - date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=line.delay or 0.0) + start_date = self.date_to_datetime(cr, uid, start_date, context) + date_planned = datetime.strptime(start_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=line.delay or 0.0) date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) return date_planned @@ -605,11 +630,6 @@ class sale_advance_payment_inv(osv.osv_memory): sale_line_obj = self.pool.get('sale.order.line') wizard = self.browse(cr, uid, [result], context) sale = sale_obj.browse(cr, uid, sale_id, context=context) - if sale.order_policy == 'postpaid': - raise osv.except_osv( - _('Error!'), - _("You cannot make an advance on a sales order \ - that is defined as 'Automatic Invoice after delivery'.")) # If invoice on picking: add the cost on the SO # If not, the advance will be deduced when generating the final invoice diff --git a/addons/sale_stock/stock.py b/addons/sale_stock/stock.py index e1070998ae8..ad19684c4cf 100644 --- a/addons/sale_stock/stock.py +++ b/addons/sale_stock/stock.py @@ -54,7 +54,7 @@ class stock_picking(osv.osv): We select the partner of the sales order as the partner of the customer invoice """ if picking.sale_id: - return picking.sale_id.partner_id + return picking.sale_id.partner_invoice_id return super(stock_picking, self)._get_partner_to_invoice(cr, uid, picking, context=context) def _get_comment_invoice(self, cursor, user, picking): diff --git a/addons/sale_stock/test/picking_order_policy.yml b/addons/sale_stock/test/picking_order_policy.yml index 1df3626880f..afffda1ef21 100644 --- a/addons/sale_stock/test/picking_order_policy.yml +++ b/addons/sale_stock/test/picking_order_policy.yml @@ -5,6 +5,12 @@ - !assert {model: sale.order, id: sale.sale_order_6, string: The amount of the Quotation is not correctly computed}: - sum([l.price_subtotal for l in order_line]) == amount_untaxed +- + I set an explicit invoicing partner that is different from the main SO Customer +- + !python {model: sale.order, id: sale.sale_order_6}: | + order = self.browse(cr, uid, ref("sale.sale_order_6")) + order.write({'partner_invoice_id': ref('base.res_partner_address_29')}) - I confirm the quotation with Invoice based on deliveries policy. - @@ -26,7 +32,8 @@ order = self.browse(cr, uid, ref("sale.sale_order_6")) for order_line in order.order_line: procurement = order_line.procurement_id - date_planned = datetime.strptime(order.date_order, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=order_line.delay or 0.0) + sale_order_date = self.date_to_datetime(cr, uid, order.date_order, context) + date_planned = datetime.strptime(sale_order_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0) date_planned = (date_planned - timedelta(days=order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) assert procurement.date_planned == date_planned, "Scheduled date is not correspond." assert procurement.product_id.id == order_line.product_id.id, "Product is not correspond." @@ -60,7 +67,8 @@ output_id = sale_order.shop_id.warehouse_id.lot_output_id.id for move in picking.move_lines: order_line = move.sale_line_id - date_planned = datetime.strptime(sale_order.date_order, DEFAULT_SERVER_DATE_FORMAT) + relativedelta(days=order_line.delay or 0.0) + sale_order_date = self.date_to_datetime(cr, uid, sale_order.date_order, context) + date_planned = datetime.strptime(sale_order_date, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=order_line.delay or 0.0) date_planned = (date_planned - timedelta(days=sale_order.company_id.security_lead)).strftime(DEFAULT_SERVER_DATETIME_FORMAT) assert datetime.strptime(move.date_expected, DEFAULT_SERVER_DATETIME_FORMAT) == datetime.strptime(date_planned, DEFAULT_SERVER_DATETIME_FORMAT), "Excepted Date is not correspond with Planned Date." assert move.product_id.id == order_line.product_id.id,"Product is not correspond." @@ -108,13 +116,13 @@ !python {model: sale.order}: | order = self.browse(cr, uid, ref("sale.sale_order_6")) assert order.invoice_ids, "Invoice is not created." - ac = order.partner_id.property_account_receivable.id + ac = order.partner_invoice_id.property_account_receivable.id journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale'), ('company_id', '=', order.company_id.id)]) for invoice in order.invoice_ids: assert invoice.type == 'out_invoice',"Invoice should be Customer Invoice." assert invoice.account_id.id == ac,"Invoice account is not correspond." assert invoice.reference == order.client_order_ref or order.name,"Reference is not correspond." - assert invoice.partner_id.id == order.partner_id.id,"Customer is not correspond." + assert invoice.partner_id.id == order.partner_invoice_id.id,"Customer does not correspond." assert invoice.currency_id.id == order.pricelist_id.currency_id.id, "Currency is not correspond." assert invoice.comment == (order.note or ''),"Note is not correspond." assert invoice.journal_id.id in journal_ids,"Sales Journal is not link on Invoice." diff --git a/addons/share/i18n/lt.po b/addons/share/i18n/lt.po new file mode 100644 index 00000000000..497de8b405d --- /dev/null +++ b/addons/share/i18n/lt.po @@ -0,0 +1,617 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-29 15:32+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-30 05:29+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:841 +#, python-format +msgid "Invitation to collaborate about %s" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:779 +#, python-format +msgid "" +"The share engine has not been able to fetch a record_id for your invitation." +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Include an Optional Personal Message" +msgstr "" + +#. module: share +#: field:share.wizard,embed_option_title:0 +msgid "Display title" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Access granted!" +msgstr "" + +#. module: share +#: field:share.wizard,record_name:0 +msgid "Record name" +msgstr "" + +#. module: share +#: help:share.wizard,message:0 +msgid "" +"An optional personal message, to be included in the email notification." +msgstr "" + +#. module: share +#: field:share.wizard,user_type:0 +msgid "Sharing method" +msgstr "" + +#. module: share +#: field:share.wizard,name:0 +msgid "Share Title" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:847 +#: code:addons/share/wizard/share_wizard.py:876 +#, python-format +msgid "" +"The documents are not attached, you can view them online directly on my " +"OpenERP server at:\n" +" %s\n" +"\n" +msgstr "" + +#. module: share +#: model:ir.module.category,name:share.module_category_share +msgid "Sharing" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:842 +#: code:addons/share/wizard/share_wizard.py:874 +#, python-format +msgid "" +"Hello,\n" +"\n" +msgstr "" + +#. module: share +#: field:share.wizard,share_root_url:0 +msgid "Share Access URL" +msgstr "" + +#. module: share +#: field:share.wizard,email_1:0 +#: field:share.wizard,email_2:0 +#: field:share.wizard,email_3:0 +msgid "New user email" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:886 +#, python-format +msgid "You may use your current login (%s) and password to view them.\n" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:620 +#, python-format +msgid "(Modified)" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:664 +#, python-format +msgid "You must be a member of the Share/User group to use the share wizard." +msgstr "" + +#. module: share +#. openerp-web +#: code:addons/share/static/src/js/share.js:63 +#, python-format +msgid "Embed" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:598 +#, python-format +msgid "Sharing filter created by user %s (%s) for group %s" +msgstr "" + +#. module: share +#: field:share.wizard,embed_url:0 +#: field:share.wizard.result.line,share_url:0 +msgid "Share URL" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:848 +#: code:addons/share/wizard/share_wizard.py:880 +#, python-format +msgid "These are your credentials to access this protected area:\n" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Access info" +msgstr "" + +#. module: share +#. openerp-web +#: code:addons/share/static/src/js/share.js:60 +#: view:share.wizard:0 +#, python-format +msgid "Share" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:570 +#, python-format +msgid "(Duplicated for modified sharing permissions)" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:668 +#, python-format +msgid "" +"Please indicate the emails of the persons to share with, one per line." +msgstr "" + +#. module: share +#: help:share.wizard,domain:0 +msgid "Optional domain for further data filtering" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Next" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:661 +#, python-format +msgid "Action and Access Mode are required to create a shared access." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:849 +#: code:addons/share/wizard/share_wizard.py:881 +#, python-format +msgid "Username" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Sharing Options" +msgstr "" + +#. module: share +#. openerp-web +#: code:addons/share/static/src/xml/share.xml:9 +#, python-format +msgid "Invite" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Embedded code options" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Configuration" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "" +"Please select the action that opens the screen containing the data you want " +"to share." +msgstr "" + +#. module: share +#: field:res.groups,share:0 +msgid "Share Group" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:834 +#: code:addons/share/wizard/share_wizard.py:865 +#, python-format +msgid "Email required" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "" +"Optionally, you may specify an additional domain restriction that will be " +"applied to the shared data." +msgstr "" + +#. module: share +#: view:res.groups:0 +msgid "Non-Share Groups" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "" +"An email notification with instructions has been sent to the following " +"people:" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:77 +#, python-format +msgid "Direct link or embed code" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:855 +#: code:addons/share/wizard/share_wizard.py:889 +#, python-format +msgid "" +"OpenERP is a powerful and user-friendly suite of Business Applications (CRM, " +"Sales, HR, etc.)\n" +"It is open source and can be found on http://www.openerp.com." +msgstr "" + +#. module: share +#: field:share.wizard,action_id:0 +msgid "Action to share" +msgstr "" + +#. module: share +#: help:share.wizard,record_name:0 +msgid "Name of the shared record, if sharing a precise record" +msgstr "" + +#. module: share +#: field:res.users,share:0 +msgid "Share User" +msgstr "" + +#. module: share +#: field:share.wizard.result.line,user_id:0 +msgid "unknown" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:61 +#: code:addons/share/wizard/share_wizard.py:656 +#, python-format +msgid "Sharing access cannot be created." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:779 +#, python-format +msgid "Record id not found" +msgstr "" + +#. module: share +#: help:res.groups,share:0 +msgid "Group created to set access rights for sharing data with some users." +msgstr "" + +#. module: share +#: view:res.groups:0 +msgid "Share Groups" +msgstr "" + +#. module: share +#: help:share.wizard,action_id:0 +msgid "" +"The action that opens the screen containing the data you wish to share." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:545 +#, python-format +msgid "(Copy for sharing)" +msgstr "" + +#. module: share +#: field:share.wizard.result.line,newly_created:0 +msgid "Newly created" +msgstr "" + +#. module: share +#: help:share.wizard,name:0 +msgid "Title for the share (displayed to users as menu and shortcut name)" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:635 +#, python-format +msgid "Indirect sharing filter created by user %s (%s) for group %s" +msgstr "" + +#. module: share +#: help:share.wizard,share_root_url:0 +msgid "Main access page for users that are granted shared access" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:206 +#, python-format +msgid "" +"You must configure your email address in the user preferences before using " +"the Share button." +msgstr "" + +#. module: share +#: model:res.groups,name:share.group_share_user +msgid "User" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:657 +#, python-format +msgid "" +"Sorry, the current screen and filter you are trying to share are not " +"supported at the moment.\n" +"You may want to try a simpler filter." +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Use this link" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:851 +#: code:addons/share/wizard/share_wizard.py:883 +#, python-format +msgid "Database" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Share with these People (one email per line)" +msgstr "" + +#. module: share +#: field:share.wizard,domain:0 +msgid "Domain" +msgstr "" + +#. module: share +#: view:res.groups:0 +msgid "{'search_default_no_share':1}" +msgstr "" + +#. module: share +#: view:share.wizard:0 +#: field:share.wizard,result_line_ids:0 +msgid "Summary" +msgstr "" + +#. module: share +#: help:share.wizard,embed_code:0 +msgid "" +"Embed this code in your documents to provide a link to the shared document." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:512 +#, python-format +msgid "Copied access for sharing" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:816 +#, python-format +msgid "Invitation" +msgstr "" + +#. module: share +#: model:ir.actions.act_window,name:share.action_share_wizard_step1 +msgid "Share your documents" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Or insert the following code where you want to embed your documents" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:885 +#, python-format +msgid "" +"The documents have been automatically added to your current OpenERP " +"documents.\n" +msgstr "" + +#. module: share +#: model:ir.model,name:share.model_share_wizard_result_line +msgid "share.wizard.result.line" +msgstr "" + +#. module: share +#: field:share.wizard,embed_code:0 +msgid "Code" +msgstr "" + +#. module: share +#: help:share.wizard,user_type:0 +msgid "Select the type of user(s) you would like to share data with." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:843 +#, python-format +msgid "" +"I have shared %s (%s) with you!\n" +"\n" +msgstr "" + +#. module: share +#: field:share.wizard,view_type:0 +msgid "Current View Type" +msgstr "" + +#. module: share +#: selection:share.wizard,access_mode:0 +msgid "Can view" +msgstr "" + +#. module: share +#: selection:share.wizard,access_mode:0 +msgid "Can edit" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Cancel" +msgstr "" + +#. module: share +#: help:res.users,share:0 +msgid "" +"External user with limited access, created only for the purpose of sharing " +"data." +msgstr "" + +#. module: share +#: model:ir.actions.act_window,name:share.action_share_wizard +#: model:ir.model,name:share.model_share_wizard +#: field:share.wizard.result.line,share_wizard_id:0 +msgid "Share Wizard" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:792 +#, python-format +msgid "Shared access created!" +msgstr "" + +#. module: share +#: model:res.groups,comment:share.group_share_user +msgid "" +"\n" +"Members of this groups have access to the sharing wizard, which allows them " +"to invite external users to view or edit some of their documents." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:852 +#, python-format +msgid "" +"The documents have been automatically added to your subscriptions.\n" +"\n" +msgstr "" + +#. module: share +#: model:ir.model,name:share.model_res_users +msgid "Users" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:875 +#, python-format +msgid "" +"I've shared %s with you!\n" +"\n" +msgstr "" + +#. module: share +#: model:ir.model,name:share.model_res_groups +msgid "Access Groups" +msgstr "" + +#. module: share +#: field:share.wizard,invite:0 +msgid "Invite users to OpenSocial record" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:850 +#: code:addons/share/wizard/share_wizard.py:882 +#: field:share.wizard.result.line,password:0 +#, python-format +msgid "Password" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:77 +#: field:share.wizard,new_users:0 +#, python-format +msgid "Emails" +msgstr "" + +#. module: share +#: field:share.wizard,embed_option_search:0 +msgid "Display search view" +msgstr "" + +#. module: share +#: field:share.wizard,message:0 +msgid "Personal Message" +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:834 +#: code:addons/share/wizard/share_wizard.py:865 +#, python-format +msgid "" +"The current user must have an email address configured in User Preferences " +"to be able to send outgoing emails." +msgstr "" + +#. module: share +#: code:addons/share/wizard/share_wizard.py:205 +#, python-format +msgid "No email address configured" +msgstr "" + +#. module: share +#: field:share.wizard.result.line,login:0 +msgid "Login" +msgstr "" + +#. module: share +#: view:res.users:0 +msgid "Regular users only (no share user)" +msgstr "" + +#. module: share +#: field:share.wizard,access_mode:0 +msgid "Access Mode" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "Sharing: preparation" +msgstr "" + +#. module: share +#: model:ir.model,name:share.model_ir_model_access +msgid "ir.model.access" +msgstr "" + +#. module: share +#: view:share.wizard:0 +msgid "or" +msgstr "" + +#. module: share +#: help:share.wizard,access_mode:0 +msgid "Access rights to be granted on the shared documents." +msgstr "" diff --git a/addons/share/res_users.py b/addons/share/res_users.py index 25fd25c234f..30200133f8e 100644 --- a/addons/share/res_users.py +++ b/addons/share/res_users.py @@ -34,7 +34,6 @@ class res_groups(osv.osv): domain.append(('share', '=', False)) return super(res_groups, self).get_application_groups(cr, uid, domain=domain, context=context) -res_groups() class res_users(osv.osv): _name = 'res.users' @@ -43,6 +42,5 @@ class res_users(osv.osv): 'share': fields.boolean('Share User', readonly=True, help="External user with limited access, created only for the purpose of sharing data.") } -res_users() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index 429a1dc2f08..3a26dcb9e6b 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -205,7 +205,7 @@ class share_wizard(osv.TransientModel): raise osv.except_osv(_('No email address configured'), _('You must configure your email address in the user preferences before using the Share button.')) model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'share', 'action_share_wizard_step1') - action = self.pool.get(model).read(cr, uid, res_id, context=context) + action = self.pool[model].read(cr, uid, res_id, context=context) action['res_id'] = ids[0] action.pop('context', '') return action @@ -404,7 +404,7 @@ class share_wizard(osv.TransientModel): local_rel_fields = [] models = [x[1].model for x in relation_fields] model_obj = self.pool.get('ir.model') - model_osv = self.pool.get(model.model) + model_osv = self.pool[model.model] for colinfo in model_osv._all_columns.itervalues(): coldef = colinfo.column coltype = coldef._type @@ -412,7 +412,7 @@ class share_wizard(osv.TransientModel): if coltype in ttypes and colinfo.column._obj not in models: relation_model_id = model_obj.search(cr, UID_ROOT, [('model','=',coldef._obj)])[0] relation_model_browse = model_obj.browse(cr, UID_ROOT, relation_model_id, context=context) - relation_osv = self.pool.get(coldef._obj) + relation_osv = self.pool[coldef._obj] if coltype == 'one2many': # don't record reverse path if it's not a real m2o (that happens, but rarely) dest_model_ci = relation_osv._all_columns @@ -422,7 +422,7 @@ class share_wizard(osv.TransientModel): local_rel_fields.append((relation_field, relation_model_browse)) for parent in relation_osv._inherits: if parent not in models: - parent_model = self.pool.get(parent) + parent_model = self.pool[parent] parent_colinfos = parent_model._all_columns parent_model_browse = model_obj.browse(cr, UID_ROOT, model_obj.search(cr, UID_ROOT, [('model','=',parent)]))[0] @@ -458,7 +458,7 @@ class share_wizard(osv.TransientModel): """ # obj0 class and its parents obj0 = [(None, model)] - model_obj = self.pool.get(model.model) + model_obj = self.pool[model.model] ir_model_obj = self.pool.get('ir.model') for parent in model_obj._inherits: parent_model_browse = ir_model_obj.browse(cr, UID_ROOT, @@ -777,7 +777,7 @@ class share_wizard(osv.TransientModel): # Record id not found: issue if res_id <= 0: raise osv.except_osv(_('Record id not found'), _('The share engine has not been able to fetch a record_id for your invitation.')) - self.pool.get(model.model).message_subscribe(cr, uid, [res_id], new_ids + existing_ids, context=context) + self.pool[model.model].message_subscribe(cr, uid, [res_id], new_ids + existing_ids, context=context) # self.send_invite_email(cr, uid, wizard_data, context=context) # self.send_invite_note(cr, uid, model.model, res_id, wizard_data, context=context) @@ -823,7 +823,7 @@ class share_wizard(osv.TransientModel): elif tmp_idx == len(wizard_data.result_line_ids)-2: body += ' and' body += '.' - return self.pool.get(model_name).message_post(cr, uid, [res_id], body=body, context=context) + return self.pool[model_name].message_post(cr, uid, [res_id], body=body, context=context) def send_invite_email(self, cr, uid, wizard_data, context=None): # TDE Note: not updated because will disappear @@ -902,7 +902,6 @@ class share_wizard(osv.TransientModel): options = dict(title=opt_title, search=opt_search) return {'value': {'embed_code': self._generate_embedded_code(wizard, options)}} -share_wizard() class share_result_line(osv.osv_memory): _name = 'share.wizard.result.line' diff --git a/addons/stock/partner.py b/addons/stock/partner.py index f5f02b7cd19..b8bad4faf95 100644 --- a/addons/stock/partner.py +++ b/addons/stock/partner.py @@ -41,6 +41,5 @@ class res_partner(osv.osv): help="This stock location will be used, instead of the default one, as the source location for goods you receive from the current partner"), } -res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/product.py b/addons/stock/product.py index 1d56ce64988..29445f671ee 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -479,7 +479,6 @@ class product_product(osv.osv): res['fields']['qty_available']['string'] = _('Produced Qty') return res -product_product() class product_template(osv.osv): _name = 'product.template' @@ -528,7 +527,6 @@ class product_template(osv.osv): _defaults = { 'sale_delay': 7, } -product_template() class product_category(osv.osv): @@ -558,6 +556,5 @@ class product_category(osv.osv): help="When real-time inventory valuation is enabled on a product, this account will hold the current value of the products.",), } -product_category() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/report/product_stock.py b/addons/stock/report/product_stock.py index 0315c44281b..d213db673dc 100644 --- a/addons/stock/report/product_stock.py +++ b/addons/stock/report/product_stock.py @@ -30,6 +30,7 @@ from openerp.report.render import render import stock_graph import StringIO +import unicodedata class external_pdf(render): def __init__(self, pdf): @@ -107,7 +108,12 @@ class report_stock(report_int): io = StringIO.StringIO() gt = stock_graph.stock_graph(io) for prod_id in products: - gt.add(prod_id, names.get(prod_id, 'Unknown'), products[prod_id]) + prod_name = names.get(prod_id,'Unknown') + if isinstance(prod_name, str): + prod_name = prod_name.decode('utf-8') + prod_name = unicodedata.normalize('NFKD',prod_name) + prod_name = prod_name.encode('ascii','replace') + gt.add(prod_id, prod_name, products[prod_id]) gt.draw() gt.close() self.obj = external_pdf(io.getvalue()) diff --git a/addons/stock/report/report_stock.py b/addons/stock/report/report_stock.py index 0aac0d6599b..d4bbb3ab127 100644 --- a/addons/stock/report/report_stock.py +++ b/addons/stock/report/report_stock.py @@ -77,7 +77,6 @@ class stock_report_prodlots(osv.osv): def unlink(self, cr, uid, ids, context=None): raise osv.except_osv(_('Error!'), _('You cannot delete any record!')) -stock_report_prodlots() class stock_report_tracklots(osv.osv): _name = "stock.report.tracklots" @@ -133,7 +132,6 @@ class stock_report_tracklots(osv.osv): def unlink(self, cr, uid, ids, context=None): raise osv.except_osv(_('Error!'), _('You cannot delete any record!')) -stock_report_tracklots() class report_stock_lines_date(osv.osv): _name = "report.stock.lines.date" @@ -162,6 +160,5 @@ class report_stock_lines_date(osv.osv): group by p.id )""") -report_stock_lines_date() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/report/report_stock_move.py b/addons/stock/report/report_stock_move.py index 39591021735..b8c22062fa5 100644 --- a/addons/stock/report/report_stock_move.py +++ b/addons/stock/report/report_stock_move.py @@ -142,7 +142,6 @@ class report_stock_move(osv.osv): ) """) -report_stock_move() class report_stock_inventory(osv.osv): @@ -193,6 +192,7 @@ CREATE OR REPLACE view report_stock_inventory AS ( LEFT JOIN product_uom pu2 ON (m.product_uom=pu2.id) LEFT JOIN product_uom u ON (m.product_uom=u.id) LEFT JOIN stock_location l ON (m.location_id=l.id) + WHERE m.state != 'cancel' GROUP BY m.id, m.product_id, m.product_uom, pt.categ_id, m.partner_id, m.location_id, m.location_dest_id, m.prodlot_id, m.date, m.state, l.usage, l.scrap_location, m.company_id, pt.uom_id, to_char(m.date, 'YYYY'), to_char(m.date, 'MM') @@ -216,13 +216,13 @@ CREATE OR REPLACE view report_stock_inventory AS ( LEFT JOIN product_uom pu2 ON (m.product_uom=pu2.id) LEFT JOIN product_uom u ON (m.product_uom=u.id) LEFT JOIN stock_location l ON (m.location_dest_id=l.id) + WHERE m.state != 'cancel' GROUP BY m.id, m.product_id, m.product_uom, pt.categ_id, m.partner_id, m.location_id, m.location_dest_id, m.prodlot_id, m.date, m.state, l.usage, l.scrap_location, m.company_id, pt.uom_id, to_char(m.date, 'YYYY'), to_char(m.date, 'MM') ) ); """) -report_stock_inventory() diff --git a/addons/stock/report/report_stock_move_view.xml b/addons/stock/report/report_stock_move_view.xml index 4f3e256389c..fcf99f6e51e 100644 --- a/addons/stock/report/report_stock_move_view.xml +++ b/addons/stock/report/report_stock_move_view.xml @@ -149,7 +149,7 @@ - + diff --git a/addons/stock/stock.py b/addons/stock/stock.py index be0dd59468c..764d9de453e 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -49,7 +49,6 @@ class stock_incoterms(osv.osv): 'active': True, } -stock_incoterms() class stock_journal(osv.osv): _name = "stock.journal" @@ -62,7 +61,6 @@ class stock_journal(osv.osv): 'user_id': lambda s, c, u, ctx: u } -stock_journal() #---------------------------------------------------------- # Stock Location @@ -472,7 +470,6 @@ class stock_location(osv.osv): continue return False -stock_location() class stock_tracking(osv.osv): @@ -538,7 +535,6 @@ class stock_tracking(osv.osv): """ return self.pool.get('action.traceability').action_traceability(cr,uid,ids,context) -stock_tracking() #---------------------------------------------------------- # Stock Picking @@ -1491,7 +1487,6 @@ class stock_production_lot(osv.osv): default.update(date=time.strftime('%Y-%m-%d %H:%M:%S'), move_ids=[]) return super(stock_production_lot, self).copy(cr, uid, id, default=default, context=context) -stock_production_lot() class stock_production_lot_revision(osv.osv): _name = 'stock.production.lot.revision' @@ -1512,7 +1507,6 @@ class stock_production_lot_revision(osv.osv): 'date': fields.date.context_today, } -stock_production_lot_revision() # ---------------------------------------------------- # Move @@ -2737,7 +2731,6 @@ class stock_move(osv.osv): return [move.id for move in complete] -stock_move() class stock_inventory(osv.osv): _name = "stock.inventory" @@ -2860,7 +2853,6 @@ class stock_inventory(osv.osv): self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context) return True -stock_inventory() class stock_inventory_line(osv.osv): _name = "stock.inventory.line" @@ -2900,7 +2892,6 @@ class stock_inventory_line(osv.osv): result = {'product_qty': amount, 'product_uom': uom, 'prod_lot_id': False} return {'value': result} -stock_inventory_line() #---------------------------------------------------------- # Stock Warehouse @@ -2932,7 +2923,6 @@ class stock_warehouse(osv.osv): 'lot_output_id': _default_lot_output_id, } -stock_warehouse() #---------------------------------------------------------- # "Empty" Classes that are used to vary from the original stock.picking (that are dedicated to the internal pickings) @@ -2944,6 +2934,12 @@ class stock_picking_in(osv.osv): _table = "stock_picking" _description = "Incoming Shipments" + def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): + return self.pool.get('stock.picking').search(cr, user, args, offset, limit, order, context, count) + + def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): + return self.pool.get('stock.picking').read(cr, uid, ids, fields=fields, context=context, load=load) + def check_access_rights(self, cr, uid, operation, raise_exception=True): #override in order to redirect the check of acces rights on the stock.picking object return self.pool.get('stock.picking').check_access_rights(cr, uid, operation, raise_exception=raise_exception) @@ -2999,6 +2995,12 @@ class stock_picking_out(osv.osv): _table = "stock_picking" _description = "Delivery Orders" + def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): + return self.pool.get('stock.picking').search(cr, user, args, offset, limit, order, context, count) + + def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): + return self.pool.get('stock.picking').read(cr, uid, ids, fields=fields, context=context, load=load) + def check_access_rights(self, cr, uid, operation, raise_exception=True): #override in order to redirect the check of acces rights on the stock.picking object return self.pool.get('stock.picking').check_access_rights(cr, uid, operation, raise_exception=raise_exception) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 6162d073fde..6097885175b 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -817,7 +817,7 @@ - + @@ -944,6 +944,7 @@ + @@ -1069,6 +1070,7 @@ + @@ -1380,7 +1382,7 @@ - + diff --git a/addons/stock/wizard/stock_change_standard_price.py b/addons/stock/wizard/stock_change_standard_price.py index 881b5fd9af7..e6614724454 100644 --- a/addons/stock/wizard/stock_change_standard_price.py +++ b/addons/stock/wizard/stock_change_standard_price.py @@ -116,6 +116,5 @@ class change_standard_price(osv.osv_memory): prod_obj.do_change_standard_price(cr, uid, [rec_id], datas, context) return {'type': 'ir.actions.act_window_close'} -change_standard_price() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_fill_inventory.py b/addons/stock/wizard/stock_fill_inventory.py index 237033bb7b0..e3341bd6a80 100644 --- a/addons/stock/wizard/stock_fill_inventory.py +++ b/addons/stock/wizard/stock_fill_inventory.py @@ -143,6 +143,5 @@ class stock_fill_inventory(osv.osv_memory): return {'type': 'ir.actions.act_window_close'} -stock_fill_inventory() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_inventory_merge.py b/addons/stock/wizard/stock_inventory_merge.py index 171a193e173..e2b5cff513d 100644 --- a/addons/stock/wizard/stock_inventory_merge.py +++ b/addons/stock/wizard/stock_inventory_merge.py @@ -86,7 +86,6 @@ class stock_inventory_merge(osv.osv_memory): return {'type': 'ir.actions.act_window_close'} -stock_inventory_merge() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_invoice_onshipping.py b/addons/stock/wizard/stock_invoice_onshipping.py index 7c960c5e617..c95508cd71b 100644 --- a/addons/stock/wizard/stock_invoice_onshipping.py +++ b/addons/stock/wizard/stock_invoice_onshipping.py @@ -39,7 +39,7 @@ class stock_invoice_onshipping(osv.osv_memory): if not model or 'stock.picking' not in model: return [] - model_pool = self.pool.get(model) + model_pool = self.pool[model] journal_obj = self.pool.get('account.journal') res_ids = context and context.get('active_ids', []) vals = [] @@ -119,7 +119,7 @@ class stock_invoice_onshipping(osv.osv_memory): elif inv_type == "in_refund": action_model,action_id = data_pool.get_object_reference(cr, uid, 'account', "action_invoice_tree4") if action_model: - action_pool = self.pool.get(action_model) + action_pool = self.pool[action_model] action = action_pool.read(cr, uid, action_id, context=context) action['domain'] = "[('id','in', ["+','.join(map(str,invoice_ids))+"])]" return action @@ -146,6 +146,5 @@ class stock_invoice_onshipping(osv.osv_memory): context=context) return res -stock_invoice_onshipping() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_location_product.py b/addons/stock/wizard/stock_location_product.py index 59d99167e6a..ef7c9848160 100644 --- a/addons/stock/wizard/stock_location_product.py +++ b/addons/stock/wizard/stock_location_product.py @@ -57,6 +57,5 @@ class stock_location_product(osv.osv_memory): 'domain': [('type', '<>', 'service')], } -stock_location_product() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_move.py b/addons/stock/wizard/stock_move.py index 09f684aa691..a98d0ebda02 100644 --- a/addons/stock/wizard/stock_move.py +++ b/addons/stock/wizard/stock_move.py @@ -78,7 +78,6 @@ class stock_move_consume(osv.osv_memory): context=context) return {'type': 'ir.actions.act_window_close'} -stock_move_consume() class stock_move_scrap(osv.osv_memory): @@ -139,7 +138,6 @@ class stock_move_scrap(osv.osv_memory): context=context) return {'type': 'ir.actions.act_window_close'} -stock_move_scrap() class split_in_production_lot(osv.osv_memory): @@ -255,7 +253,6 @@ class split_in_production_lot(osv.osv_memory): return new_move -split_in_production_lot() class stock_move_split_lines_exist(osv.osv_memory): _name = "stock.move.split.lines" diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index e9bc0f7626f..c51e1415249 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -38,7 +38,6 @@ class stock_return_picking_memory(osv.osv_memory): } -stock_return_picking_memory() class stock_return_picking(osv.osv_memory): @@ -225,6 +224,5 @@ class stock_return_picking(osv.osv_memory): 'context':context, } -stock_return_picking() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_splitinto.py b/addons/stock/wizard/stock_splitinto.py index 3485a6e8264..76a80595be6 100644 --- a/addons/stock/wizard/stock_splitinto.py +++ b/addons/stock/wizard/stock_splitinto.py @@ -80,7 +80,6 @@ class stock_split_into(osv.osv_memory): return {'type': 'ir.actions.act_window_close'} -stock_split_into() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/wizard/stock_traceability.py b/addons/stock/wizard/stock_traceability.py index 8d5fd1ed10d..c13fc1cf7a6 100644 --- a/addons/stock/wizard/stock_traceability.py +++ b/addons/stock/wizard/stock_traceability.py @@ -62,7 +62,6 @@ class action_traceability(osv.osv_memory): } return value -action_traceability() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_invoice_directly/wizard/stock_invoice.py b/addons/stock_invoice_directly/wizard/stock_invoice.py index 5de6490dc0f..ba41de626da 100644 --- a/addons/stock_invoice_directly/wizard/stock_invoice.py +++ b/addons/stock_invoice_directly/wizard/stock_invoice.py @@ -45,6 +45,5 @@ class invoice_directly(osv.osv_memory): } return {'type': 'ir.actions.act_window_close'} -invoice_directly() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_location/stock_location.py b/addons/stock_location/stock_location.py index 1258ddb529f..e22ccf80f8f 100644 --- a/addons/stock_location/stock_location.py +++ b/addons/stock_location/stock_location.py @@ -54,7 +54,6 @@ class stock_location_path(osv.osv): 'invoice_state': 'none', 'picking_type': 'internal', } -stock_location_path() class product_pulled_flow(osv.osv): _name = 'product.pulled.flow' @@ -85,7 +84,6 @@ class product_pulled_flow(osv.osv): 'invoice_state': 'none', 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.pulled.flow', context=c), } -product_pulled_flow() class product_product(osv.osv): _inherit = 'product.product' @@ -96,7 +94,6 @@ class product_product(osv.osv): help="These rules set the right path of the product in the "\ "whole location tree.") } -product_product() class stock_move(osv.osv): _inherit = 'stock.move' @@ -114,7 +111,6 @@ class stock_move(osv.osv): res = super(stock_move, self)._prepare_chained_picking(cr, uid, picking_name, picking, picking_type, moves_todo, context=context) res.update({'invoice_state': moves_todo[0][1][6] or 'none'}) return res -stock_move() class stock_location(osv.osv): _inherit = 'stock.location' @@ -124,5 +120,4 @@ class stock_location(osv.osv): if path.location_from_id.id == location.id: return path.location_dest_id, path.auto, path.delay, path.journal_id and path.journal_id.id or False, path.company_id and path.company_id.id or False, path.picking_type, path.invoice_state return super(stock_location, self).chained_location_get(cr, uid, location, partner, product, context) -stock_location() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock_no_autopicking/stock_no_autopicking.py b/addons/stock_no_autopicking/stock_no_autopicking.py index 904854f3533..dd8fb31f59f 100644 --- a/addons/stock_no_autopicking/stock_no_autopicking.py +++ b/addons/stock_no_autopicking/stock_no_autopicking.py @@ -29,12 +29,10 @@ class product(osv.osv): _defaults = { 'auto_pick': True } -product() class mrp_production(osv.osv): _inherit = "mrp.production" def _get_auto_picking(self, cr, uid, production): return production.product_id.auto_pick -mrp_production() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/subscription/subscription.py b/addons/subscription/subscription.py index 92bde135bc6..b7ae720c187 100644 --- a/addons/subscription/subscription.py +++ b/addons/subscription/subscription.py @@ -38,7 +38,6 @@ class subscription_document(osv.osv): _defaults = { 'active' : lambda *a: True, } -subscription_document() class subscription_document_fields(osv.osv): _name = "subscription.document.fields" @@ -50,7 +49,6 @@ class subscription_document_fields(osv.osv): 'document_id': fields.many2one('subscription.document', 'Subscription Document', ondelete='cascade'), } _defaults = {} -subscription_document_fields() def _get_document_types(self, cr, uid, context=None): cr.execute('select m.model, s.name from subscription_document s, ir_model m WHERE s.model = m.id order by s.name') @@ -115,7 +113,7 @@ class subscription_subscription(osv.osv): try: (model_name, id) = row['doc_source'].split(',') id = int(id) - model = self.pool.get(model_name) + model = self.pool[model_name] except: raise osv.except_osv(_('Wrong Source Document !'), _('Please provide another source document.\nThis one does not exist !')) @@ -136,7 +134,7 @@ class subscription_subscription(osv.osv): # the subscription is over and we mark it as being done if remaining == 1: state = 'done' - id = self.pool.get(model_name).copy(cr, uid, id, default, context) + id = self.pool[model_name].copy(cr, uid, id, default, context) self.pool.get('subscription.subscription.history').create(cr, uid, {'subscription_id': row['id'], 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'document_id': model_name+','+str(id)}) self.write(cr, uid, [row['id']], {'state':state}) return True @@ -157,7 +155,6 @@ class subscription_subscription(osv.osv): def set_draft(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'draft'}) return True -subscription_subscription() class subscription_subscription_history(osv.osv): _name = "subscription.subscription.history" @@ -168,7 +165,6 @@ class subscription_subscription_history(osv.osv): 'subscription_id': fields.many2one('subscription.subscription', 'Subscription', ondelete='cascade'), 'document_id': fields.reference('Source Document', required=True, selection=_get_document_types, size=128), } -subscription_subscription_history() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/survey.py b/addons/survey/survey.py index d0c59159a3d..ad96145741b 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -36,7 +36,6 @@ class survey_type(osv.osv): 'name': fields.char("Name", size=128, required=1, translate=True), 'code': fields.char("Code", size=64), } -survey_type() class survey(osv.osv): _name = 'survey' @@ -196,7 +195,6 @@ class survey(osv.osv): 'context': context } -survey() class survey_history(osv.osv): _name = 'survey.history' @@ -210,7 +208,6 @@ class survey_history(osv.osv): _defaults = { 'date': lambda * a: datetime.datetime.now() } -survey_history() class survey_page(osv.osv): _name = 'survey.page' @@ -260,7 +257,6 @@ class survey_page(osv.osv): vals.update({'title':title}) return super(survey_page, self).copy(cr, uid, ids, vals, context=context) -survey_page() class survey_question(osv.osv): _name = 'survey.question' @@ -561,7 +557,6 @@ class survey_question(osv.osv): data['page_id']= context.get('page_id', False) return data -survey_question() class survey_question_column_heading(osv.osv): @@ -594,7 +589,6 @@ class survey_question_column_heading(osv.osv): 'in_visible_rating_weight': _get_in_visible_rating_weight, 'in_visible_menu_choice': _get_in_visible_menu_choice, } -survey_question_column_heading() class survey_answer(osv.osv): _name = 'survey.answer' @@ -651,7 +645,6 @@ class survey_answer(osv.osv): data = super(survey_answer, self).default_get(cr, uid, fields, context) return data -survey_answer() class survey_response(osv.osv): _name = "survey.response" @@ -684,7 +677,6 @@ class survey_response(osv.osv): def copy(self, cr, uid, id, default=None, context=None): raise osv.except_osv(_('Warning!'),_('You cannot duplicate the resource!')) -survey_response() class survey_response_line(osv.osv): _name = 'survey.response.line' @@ -708,7 +700,6 @@ class survey_response_line(osv.osv): 'state' : lambda * a: "draft", } -survey_response_line() class survey_tbl_column_heading(osv.osv): _name = 'survey.tbl.column.heading' @@ -720,7 +711,6 @@ class survey_tbl_column_heading(osv.osv): 'response_table_id': fields.many2one('survey.response.line', 'Answer', ondelete='cascade'), } -survey_tbl_column_heading() class survey_response_answer(osv.osv): _name = 'survey.response.answer' @@ -736,7 +726,6 @@ class survey_response_answer(osv.osv): 'comment_field': fields.char('Comment', size = 255) } -survey_response_answer() class res_users(osv.osv): _inherit = "res.users" @@ -745,7 +734,6 @@ class res_users(osv.osv): 'survey_id': fields.many2many('survey', 'survey_users_rel', 'uid', 'sid', 'Groups'), } -res_users() class survey_request(osv.osv): _name = "survey.request" @@ -786,6 +774,5 @@ class survey_request(osv.osv): return {'value': {'email': user.email}} return {} -survey_request() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_answer.py b/addons/survey/wizard/survey_answer.py index 903937af441..6d473754988 100644 --- a/addons/survey/wizard/survey_answer.py +++ b/addons/survey/wizard/survey_answer.py @@ -25,7 +25,8 @@ from lxml import etree import os from time import strftime -from openerp import addons, tools +from openerp import tools +from openerp.modules.module import get_module_resource from openerp.osv import fields, osv import openerp.report from openerp.tools import to_xml @@ -402,16 +403,17 @@ class survey_question_wiz(osv.osv_memory): sur_response_obj.write(cr, uid, [sur_name_read.response], {'state' : 'done'}) # mark the survey request as done; call 'survey_req_done' on its actual model - survey_req_obj = self.pool.get(context.get('active_model')) - if survey_req_obj and hasattr(survey_req_obj, 'survey_req_done'): - survey_req_obj.survey_req_done(cr, uid, context.get('active_ids', []), context=context) + if context.get('active_model') in self.pool: + survey_req_obj = self.pool[context.get('active_model')] + if hasattr(survey_req_obj, 'survey_req_done'): + survey_req_obj.survey_req_done(cr, uid, context.get('active_ids', []), context=context) if sur_rec.send_response: survey_data = survey_obj.browse(cr, uid, survey_id) response_id = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False))['response'] report = self.create_report(cr, uid, [survey_id], 'report.survey.browse.response', survey_data.title,context) attachments = {} - pdf_filename = addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf" + pdf_filename = get_module_resource('survey', 'report') + survey_data.title + ".pdf" if os.path.exists(pdf_filename): file = open(pdf_filename) file_data = "" @@ -423,7 +425,7 @@ class survey_question_wiz(osv.osv_memory): attachments[survey_data.title + ".pdf"] = file_data file.close() - os.remove(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") + os.remove(get_module_resource('survey', 'report') + survey_data.title + ".pdf") context.update({'response_id':response_id}) user_email = user_obj.browse(cr, uid, uid, context).email resp_email = survey_data.responsible_id and survey_data.responsible_id.email or False @@ -465,7 +467,7 @@ class survey_question_wiz(osv.osv_memory): try: uid = 1 result, format = openerp.report.render_report(cr, uid, res_ids, report_name[len('report.'):], {}, context) - ret_file_name = addons.get_module_resource('survey', 'report') + file_name + '.pdf' + ret_file_name = get_module_resource('survey', 'report') + file_name + '.pdf' fp = open(ret_file_name, 'wb+'); fp.write(result); fp.close(); @@ -609,10 +611,10 @@ class survey_question_wiz(osv.osv_memory): survey_obj.write(cr, uid, survey_id, {'tot_start_survey' : sur_rec['tot_start_survey'] + 1}) if context.has_key('cur_id'): if context.has_key('request') and context.get('request',False): - self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id}) - self.pool.get(context.get('object',False)).survey_req_done(cr, uid, [int(context.get('cur_id'))], context) + self.pool[context.get('object')].write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id}) + self.pool[context.get('object')].survey_req_done(cr, uid, [int(context.get('cur_id'))], context) else: - self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id}) + self.pool[context.get('object')].write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id}) if sur_name_read['store_ans'] and type(safe_eval(sur_name_read['store_ans'])) == dict: sur_name_read['store_ans'] = safe_eval(sur_name_read['store_ans']) for key,val in sur_name_read['store_ans'].items(): @@ -1252,6 +1254,5 @@ class survey_question_wiz(osv.osv_memory): 'context': context } -survey_question_wiz() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_browse_answer.py b/addons/survey/wizard/survey_browse_answer.py index a37a4e4f7ca..1b461761bb5 100644 --- a/addons/survey/wizard/survey_browse_answer.py +++ b/addons/survey/wizard/survey_browse_answer.py @@ -55,6 +55,5 @@ class survey_browse_answer(osv.osv_memory): 'context' : context } -survey_browse_answer() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_print.py b/addons/survey/wizard/survey_print.py index 9f0b21affa6..f0e39f99cb9 100644 --- a/addons/survey/wizard/survey_print.py +++ b/addons/survey/wizard/survey_print.py @@ -63,6 +63,5 @@ class survey_print(osv.osv_memory): 'report_name': 'survey.form', 'datas': datas, } -survey_print() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_print_answer.py b/addons/survey/wizard/survey_print_answer.py index 0cadd6bf832..417d1bb732f 100644 --- a/addons/survey/wizard/survey_print_answer.py +++ b/addons/survey/wizard/survey_print_answer.py @@ -68,6 +68,5 @@ class survey_print_answer(osv.osv_memory): 'datas': datas, } -survey_print_answer() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_print_statistics.py b/addons/survey/wizard/survey_print_statistics.py index f2e25eb8d53..d005dbee31a 100644 --- a/addons/survey/wizard/survey_print_statistics.py +++ b/addons/survey/wizard/survey_print_statistics.py @@ -45,6 +45,5 @@ class survey_print_statistics(osv.osv_memory): 'datas': datas, } -survey_print_statistics() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/wizard/survey_send_invitation.py b/addons/survey/wizard/survey_send_invitation.py index 5d96d0246f9..ca6a8e4b15d 100644 --- a/addons/survey/wizard/survey_send_invitation.py +++ b/addons/survey/wizard/survey_send_invitation.py @@ -26,7 +26,8 @@ import os import datetime import socket -from openerp import addons, tools +from openerp import tools +from openerp.modules.module import get_module_resource from openerp.osv import fields, osv import openerp.report from openerp.tools.translate import _ @@ -85,7 +86,7 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we if not report_name or not res_ids: return (False, Exception('Report name and Resources ids are required !!!')) try: - ret_file_name = addons.get_module_resource('survey', 'report') + file_name + '.pdf' + ret_file_name = get_module_resource('survey', 'report') + file_name + '.pdf' result, format = openerp.report.render_report(cr, uid, res_ids, report_name[len('report.'):], {}, {}) fp = open(ret_file_name, 'wb+'); fp.write(result); @@ -128,7 +129,7 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we new_user.append(use.id) for id in survey_ref.browse(cr, uid, survey_ids): report = self.create_report(cr, uid, [id.id], 'report.survey.form', id.title) - file = open(addons.get_module_resource('survey', 'report') + id.title +".pdf") + file = open(get_module_resource('survey', 'report') + id.title +".pdf") file_data = "" while 1: line = file.readline() @@ -137,7 +138,7 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we break file.close() attachments[id.title +".pdf"] = file_data - os.remove(addons.get_module_resource('survey', 'report') + id.title +".pdf") + os.remove(get_module_resource('survey', 'report') + id.title +".pdf") for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids): if not partner.email: @@ -220,7 +221,6 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we 'target': 'new', 'context': context } -survey_send_invitation() class survey_send_invitation_log(osv.osv_memory): _name = 'survey.send.invitation.log' @@ -235,6 +235,5 @@ class survey_send_invitation_log(osv.osv_memory): data['note'] = context.get('note', '') return data -survey_send_invitation_log() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/warning/warning.py b/addons/warning/warning.py index 4bdada396bc..e9e9161bacb 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -50,7 +50,6 @@ class res_partner(osv.osv): 'invoice_warn' : 'no-message', } -res_partner() class sale_order(osv.osv): @@ -79,7 +78,6 @@ class sale_order(osv.osv): warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message'] return {'value': result.get('value',{}), 'warning':warning} -sale_order() class purchase_order(osv.osv): @@ -108,7 +106,6 @@ class purchase_order(osv.osv): return {'value': result.get('value',{}), 'warning':warning} -purchase_order() class account_invoice(osv.osv): @@ -145,7 +142,6 @@ class account_invoice(osv.osv): return {'value': result.get('value',{}), 'warning':warning} -account_invoice() class stock_picking(osv.osv): _inherit = 'stock.picking' @@ -173,7 +169,6 @@ class stock_picking(osv.osv): return {'value': result.get('value',{}), 'warning':warning} -stock_picking() # FIXME:(class stock_picking_in and stock_picking_out) this is a temporary workaround because of a framework bug (ref: lp:996816). # It should be removed as soon as the bug is fixed @@ -243,7 +238,6 @@ class product_product(osv.osv): 'purchase_line_warn' : 'no-message', } -product_product() class sale_order_line(osv.osv): _inherit = 'sale.order.line' @@ -279,7 +273,6 @@ class sale_order_line(osv.osv): return {'value': result.get('value',{}), 'warning':warning} -sale_order_line() class purchase_order_line(osv.osv): _inherit = 'purchase.order.line' @@ -311,7 +304,6 @@ class purchase_order_line(osv.osv): return {'value': result.get('value',{}), 'warning':warning} -purchase_order_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/web_shortcuts/i18n/lt.po b/addons/web_shortcuts/i18n/lt.po new file mode 100644 index 00000000000..70fc0a04b6c --- /dev/null +++ b/addons/web_shortcuts/i18n/lt.po @@ -0,0 +1,25 @@ +# Lithuanian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-04-24 18:29+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-04-25 05:20+0000\n" +"X-Generator: Launchpad (build 16580)\n" + +#. module: web_shortcuts +#. openerp-web +#: code:addons/web_shortcuts/static/src/xml/web_shortcuts.xml:21 +#, python-format +msgid "Add / Remove Shortcut..." +msgstr "Pridėti / pašalinti trumpinį..."