diff options
| author | Amit Patel <apa@tinyerp.com> | 2012-09-12 11:13:36 +0530 |
|---|---|---|
| committer | Amit Patel <apa@tinyerp.com> | 2012-09-12 11:13:36 +0530 |
| commit | ec930cd97decfbe1cd138b638d081536bb9632e2 (patch) | |
| tree | 23bcb442d5ff6aaf16d80c28044a16eb8c4c794c | |
| parent | d52e25483482b464c37695abf8d24e94598f3516 (diff) | |
| parent | f1f23c52ea1601b7588a91497ba46e5b826719b7 (diff) | |
[MEREGE]:trunk
bzr revid: apa@tinyerp.com-20120912054336-9qq9zfewjh4f9432
654 files changed, 33162 insertions, 10851 deletions
diff --git a/addons/account/account.py b/addons/account/account.py index 9383913abbb..e5e29f178a3 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -30,6 +30,8 @@ from osv import fields, osv import decimal_precision as dp from tools.translate import _ from tools.float_utils import float_round +from openerp import SUPERUSER_ID + _logger = logging.getLogger(__name__) @@ -109,7 +111,7 @@ class account_payment_term_line(osv.osv): 'days': fields.integer('Number of Days', required=True, help="Number of days to add before computation of the day of month." \ "If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."), 'days2': fields.integer('Day of the Month', required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the beginning of the month)."), - 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True), + 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True, ondelete='cascade'), } _defaults = { 'value': 'balance', @@ -819,7 +821,7 @@ class account_journal(osv.osv): if not 'sequence_id' in vals or not vals['sequence_id']: # if we have the right to create a journal, we should be able to # create it's sequence. - vals.update({'sequence_id': self.create_sequence(cr, 1, vals, context)}) + vals.update({'sequence_id': self.create_sequence(cr, SUPERUSER_ID, vals, context)}) return super(account_journal, self).create(cr, uid, vals, context) def name_get(self, cr, user, ids, context=None): @@ -1373,7 +1375,7 @@ class account_move(osv.osv): balance = 0.0 for line in line_ids: if line[2]: - balance += (line[2]['debit'] or 0.00)- (line[2]['credit'] or 0.00) + balance += (line[2].get('debit',0.00)- (line[2].get('credit',0.00))) return {'value': {'balance': balance}} def write(self, cr, uid, ids, vals, context=None): @@ -1396,7 +1398,7 @@ class account_move(osv.osv): if not l[0]: l[2].update({ 'reconcile_id':False, - 'reconcil_partial_id':False, + 'reconcile_partial_id':False, 'analytic_lines':False, 'invoice':False, 'ref':False, @@ -1858,7 +1860,7 @@ class account_tax(osv.osv): def get_precision_tax(): def change_digit_tax(cr): - res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, 'Account') + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, SUPERUSER_ID, 'Account') return (16, res+2) return change_digit_tax @@ -2514,22 +2516,25 @@ class account_account_template(osv.osv): 'nocreate': False, } - def _check_type(self, cr, uid, ids, context=None): - if context is None: - context = {} - accounts = self.browse(cr, uid, ids, context=context) - for account in accounts: - if account.parent_id and account.parent_id.type != 'view': - return False - return True - _check_recursion = check_cycle _constraints = [ (_check_recursion, 'Error!\nYou cannot create recursive account templates.', ['parent_id']), - (_check_type, 'Configuration Error!\nYou cannot define children to an account that has internal type other than "View".', ['type']), - ] + def create(self, cr, uid, vals, context=None): + if 'parent_id' in vals: + parent = self.read(cr, uid, [vals['parent_id']], ['type']) + if parent and parent[0]['type'] != 'view': + raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'.")) + return super(account_account_template, self).create(cr, uid, vals, context=context) + + def write(self, cr, uid, ids, vals, context=None): + if 'parent_id' in vals: + parent = self.read(cr, uid, [vals['parent_id']], ['type']) + if parent and parent[0]['type'] != 'view': + raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'.")) + return super(account_account_template, self).write(cr, uid, ids, vals, context=context) + def name_get(self, cr, uid, ids, context=None): if not ids: return [] @@ -2997,6 +3002,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): _columns = { 'company_id':fields.many2one('res.company', 'Company', required=True), + 'currency_id': fields.many2one('res.currency', 'Currency', help="Currency as per company's country."), 'only_one_chart_template': fields.boolean('Only One Chart Template Available'), 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True), 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Cash and Banks', required=True), @@ -3007,6 +3013,13 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'purchase_tax_rate': fields.float('Purchase Tax(%)'), 'complete_tax_set': fields.boolean('Complete Set of Taxes', help='This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete'), } + + def onchange_company_id(self, cr, uid, ids, company_id, context=None): + currency_id = False + if company_id: + currency_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.id + return {'value': {'currency_id': currency_id}} + def onchange_tax_rate(self, cr, uid, ids, rate=False, context=None): return {'value': {'purchase_tax_rate': rate or False}} @@ -3037,6 +3050,13 @@ class wizard_multi_charts_accounts(osv.osv_memory): res.update({'bank_accounts_id': [{'acc_name': _('Cash'), 'account_type': 'cash'},{'acc_name': _('Bank'), 'account_type': 'bank'}]}) if 'company_id' in fields: res.update({'company_id': self.pool.get('res.users').browse(cr, uid, [uid], context=context)[0].company_id.id}) + if 'currency_id' in fields: + company_id = res.get('company_id') or False + if company_id: + company_obj = self.pool.get('res.company') + country_id = company_obj.browse(cr, uid, company_id, context=context).country_id.id + currency_id = company_obj.on_change_country(cr, uid, company_id, country_id, context=context)['value']['currency_id'] + res.update({'currency_id': currency_id}) ids = self.pool.get('account.chart.template').search(cr, uid, [('visible', '=', True)], context=context) if ids: @@ -3341,6 +3361,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): ir_values_obj = self.pool.get('ir.values') obj_wizard = self.browse(cr, uid, ids[0]) company_id = obj_wizard.company_id.id + self.pool.get('res.company').write(cr, uid, [company_id], {'currency_id': obj_wizard.currency_id.id}) # If the floats for sale/purchase rates have been filled, create templates from them self._create_tax_templates_from_rates(cr, uid, obj_wizard, company_id, context=context) @@ -3349,9 +3370,9 @@ class wizard_multi_charts_accounts(osv.osv_memory): # write values of default taxes for product as super user if obj_wizard.sale_tax and taxes_ref: - ir_values_obj.set_default(cr, 1, 'product.product', "taxes_id", [taxes_ref[obj_wizard.sale_tax.id]], for_all_users=True, company_id=company_id) + ir_values_obj.set_default(cr, SUPERUSER_ID, 'product.product', "taxes_id", [taxes_ref[obj_wizard.sale_tax.id]], for_all_users=True, company_id=company_id) if obj_wizard.purchase_tax and taxes_ref: - ir_values_obj.set_default(cr, 1, 'product.product', "supplier_taxes_id", [taxes_ref[obj_wizard.purchase_tax.id]], for_all_users=True, company_id=company_id) + ir_values_obj.set_default(cr, SUPERUSER_ID, 'product.product', "supplier_taxes_id", [taxes_ref[obj_wizard.purchase_tax.id]], for_all_users=True, company_id=company_id) # Create Bank journals self._create_bank_journals_from_o2m(cr, uid, obj_wizard, company_id, acc_template_ref, context=context) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 8df291a98d1..095d461ff3b 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -430,7 +430,7 @@ class account_bank_statement(osv.osv): 'name': st_number, 'balance_end_real': st.balance_end }, context=context) - self.message_append_note(cr, uid, [st.id], body=_('Statement %s is confirmed, journal items are created.') % (st_number,), context=context) + self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st_number,), context=context) return self.write(cr, uid, ids, {'state':'confirm'}, context=context) def button_cancel(self, cr, uid, ids, context=None): diff --git a/addons/account/account_bank_view.xml b/addons/account/account_bank_view.xml index 8ceb8a91885..ef793a1bef5 100644 --- a/addons/account/account_bank_view.xml +++ b/addons/account/account_bank_view.xml @@ -15,7 +15,7 @@ <group name="accounting" col="2" colspan="2" attrs="{'invisible': [('company_id','=', False)]}"> <separator string="Accounting Information" colspan="2"/> <field name="journal_id"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </group> </group> </field> @@ -28,7 +28,7 @@ <field name="inherit_id" ref="base.view_partner_bank_tree"/> <field name="arch" type="xml"> <field name="acc_number" position="after"> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </field> </field> </record> diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 7bc9ceaaf61..19af105c950 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -214,7 +214,7 @@ class account_invoice(osv.osv): \n* The \'Cancelled\' state is used when user cancel invoice.'), 'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."), 'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"), - 'date_due': fields.date('Due Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, select=True, + 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="If you use payment terms, the due date will be computed automatically at the generation "\ "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."), 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}), @@ -253,7 +253,7 @@ class account_invoice(osv.osv): 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), - 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}), + 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean', store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ? @@ -767,17 +767,20 @@ class account_invoice(osv.osv): if not key in tax_key: raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.')) - def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines): + def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None): + if context is None: + context={} total = 0 total_currency = 0 cur_obj = self.pool.get('res.currency') for i in invoice_move_lines: if inv.currency_id.id != company_currency: + context.update({'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) i['currency_id'] = inv.currency_id.id i['amount_currency'] = i['price'] i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, i['price'], - context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) + context=context) else: i['amount_currency'] = False i['currency_id'] = False @@ -887,7 +890,7 @@ class account_invoice(osv.osv): # create one move line for the total and possibly adjust the other lines amount total = 0 total_currency = 0 - total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml) + total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx) acc_id = inv.account_id.id name = inv['name'] or '/' @@ -1045,7 +1048,7 @@ class account_invoice(osv.osv): if obj_inv.type in ('out_invoice', 'out_refund'): ctx = self.get_log_context(cr, uid, context=ctx) message = _("Invoice '%s' is validated.") % name - self.message_append_note(cr, uid, [inv_id], body=message, context=context) + self.message_post(cr, uid, [inv_id], body=message, context=context) return True def action_cancel(self, cr, uid, ids, *args): @@ -1275,7 +1278,7 @@ class account_invoice(osv.osv): # TODO: use currency's formatting function msg = _("Invoice '%s' is paid partially: %s%s of %s%s (%s%s remaining).") % \ (name, pay_amount, code, invoice.amount_total, code, total, code) - self.message_append_note(cr, uid, [inv_id], body=msg, context=context) + self.message_post(cr, uid, [inv_id], body=msg, context=context) self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context) # Update the stored value (fields.function), so we write to trigger recompute @@ -1288,24 +1291,25 @@ class account_invoice(osv.osv): def _get_document_type(self, type): type_dict = { - 'out_invoice': 'Customer invoice', - 'in_invoice': 'Supplier invoice', - 'out_refund': 'Customer Refund', - 'in_refund': 'Supplier Refund', + # Translation markers will have no effect at runtime, only used to properly flag export + 'out_invoice': _('Customer invoice'), + 'in_invoice': _('Supplier invoice'), + 'out_refund': _('Customer Refund'), + 'in_refund': _('Supplier Refund'), } return type_dict.get(type, 'Invoice') def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id],body=_("%s <b>created</b>.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s <b>created</b>.") % (_(self._get_document_type(obj.type))), context=context) def confirm_paid_send_note(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (self._get_document_type(obj.type)), context=context) + for obj in self.browse(cr, uid, ids, context=context): + self.message_post(cr, uid, [obj.id], body=_("%s <b>paid</b>.") % (_(self._get_document_type(obj.type))), context=context) def invoice_cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s <b>cancelled</b>.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s <b>cancelled</b>.") % (_(self._get_document_type(obj.type))), context=context) account_invoice() @@ -1361,10 +1365,16 @@ class account_invoice_line(osv.osv): 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), 'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True) } + + def _default_account_id(self, cr, uid, ids, context=None): + prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) + return prop and prop.id or False + _defaults = { 'quantity': 1, 'discount': 0.0, 'price_unit': _price_unit_default, + 'account_id': _default_account_id, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): @@ -1473,10 +1483,11 @@ class account_invoice_line(osv.osv): prod = self.pool.get('product.product').browse(cr, uid, product, context=context) prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context) if prod.uom_id.category_id.id != prod_uom.category_id.id: - warning = { + warning = { 'title': _('Warning!'), 'message': _('The selected unit of measure is not compatible with the unit of measure of the product.') - } + } + res['value'].update({'uos_id': prod.uom_id.id}) return {'value': res['value'], 'warning': warning} return res diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 143c5514141..1c4d83106ca 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -127,7 +127,7 @@ <field name="user_id"/> <field name="date_due"/> <field name="origin"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="residual" sum="Residual Amount"/> <field name="amount_untaxed" sum="Untaxed Amount"/> <field name="amount_total" sum="Total Amount"/> @@ -184,24 +184,30 @@ name="account_id" groups="account.group_account_user"/> <field name="journal_id" groups="account.group_account_user" on_change="onchange_journal_id(journal_id, context)" widget="selection"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </group> </group> <notebook> <page string="Invoice"> <field context="{'partner_id': partner_id, 'price_type': 'price_type' in dir() and price_type or False, 'type': type}" name="invoice_line"> <tree string="Invoice lines" editable="bottom"> - <field name="product_id" on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/> - <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id,parent.partner_id,parent.type,parent.fiscal_position,account_id)" groups="base.group_account_user"/> - <field name="invoice_line_tax_id" view_mode="2" context="{'type':parent.type}" domain="[('parent_id','=',False)]"/> - <field domain="[('type','<>','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/> + <field name="product_id" + on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/> + <field name="name"/> + <field name="company_id" groups="base.group_multi_company" readonly="1"/> + <field name="account_id" groups="account.group_account_user" + domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '!=', 'view')]" + on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/> + <field name="account_analytic_id" groups="analytic.group_analytic_accounting" + domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]"/> <field name="quantity"/> + <field name="uos_id" groups="product.group_uom" + on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/> <field name="price_unit"/> + <field name="discount" groups="sale.group_discount_per_so_line"/> + <field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}" + domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]"/> <field name="price_subtotal"/> - <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id,parent.partner_id,parent.type,parent.fiscal_position,account_id)" invisible="1"/> - <!-- Removed if subtotal is set --> - <field name="name" invisible="1"/> - <field name="uos_id" invisible="1"/> </tree> </field> <group class="oe_subtotal_footer oe_right"> @@ -265,7 +271,7 @@ <field name="debit"/> <field name="credit"/> <field name="amount_currency"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </tree> </field> </page> @@ -326,8 +332,8 @@ <field domain="[('company_id', '=', company_id),('type','=', 'receivable')]" name="account_id" groups="account.group_account_user"/> - <label for="currency_id"/> - <div> + <label for="currency_id" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="currency_id" class="oe_inline"/> <!-- note fp: I don't think we need this feature ? <button name="%(action_account_change_currency)d" type="action" @@ -342,17 +348,22 @@ <page string="Invoice Lines"> <field name="invoice_line" nolabel="1" widget="one2many_list" context="{'type': type}"> <tree string="Invoice Lines" editable="bottom"> - <field name="invoice_line_tax_id" invisible="1"/> - <field name="product_id" /> + <field name="product_id" + on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/> <field name="name"/> + <field name="company_id" groups="base.group_multi_company" readonly="1"/> <field name="account_id" groups="account.group_account_user" - domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" + domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '!=', 'view')]" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)"/> + <field name="account_analytic_id" groups="analytic.group_analytic_accounting" + domain="[('type','!=','view'), ('company_id', '=', parent.company_id), ('parent_id', '!=', False)]"/> <field name="quantity"/> <field name="uos_id" groups="product.group_uom" on_change="uos_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id, parent.fiscal_position, price_unit, parent.currency_id, context, parent.company_id)"/> <field name="price_unit"/> <field name="discount" groups="sale.group_discount_per_so_line"/> + <field name="invoice_line_tax_id" widget="many2many_tags" context="{'type':parent.type}" + domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]"/> <field name="price_subtotal"/> </tree> </field> @@ -418,8 +429,8 @@ <field name="journal_id" groups="base.group_user"/> <field name="debit"/> <field name="credit"/> - <field name="amount_currency"/> - <field name="currency_id"/> + <field name="amount_currency" groups="base.group_multi_currency"/> + <field name="currency_id" groups="base.group_multi_currency"/> </tree> </field> </page> diff --git a/addons/account/account_menuitem.xml b/addons/account/account_menuitem.xml index 9ac6f98661c..480d5ec76a6 100644 --- a/addons/account/account_menuitem.xml +++ b/addons/account/account_menuitem.xml @@ -44,7 +44,7 @@ <menuitem id="menu_finance_periodical_processing_billing" name="Billing" parent="menu_finance_periodical_processing" sequence="35"/> <menuitem id="next_id_22" name="Partners" parent="menu_finance_generic_reporting" sequence="1"/> - <menuitem id="menu_multi_currency" name="Multi-Currencies" parent="menu_finance_generic_reporting" sequence="10"/> + <menuitem id="menu_multi_currency" name="Multi-Currencies" parent="menu_finance_generic_reporting" sequence="10" groups="base.group_multi_currency"/> <menuitem parent="account.menu_finance_legal_statement" id="final_accounting_reports" diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 81e683e1cfd..8c0de73ef8c 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -204,7 +204,7 @@ attrs="{'invisible':[('type','!=','consolidation')]}" widget="many2many_tags"/> </group> - <group> + <group groups="base.group_multi_currency"> <field name="currency_id"/> <field name="currency_mode" attrs="{'readonly': [('currency_id','=',False)]}"/> </group> @@ -489,7 +489,7 @@ <group> <field name="default_debit_account_id" attrs="{'required':[('type','in', ('cash', 'bank'))]}" domain="[('type','<>','view'),('type','<>','consolidation')]"/> <field name="default_credit_account_id" attrs="{'required':[('type','in',('cash', 'bank'))]}" domain="[('type','<>','view'),('type','<>','consolidation')]"/> - <field name="currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="company_id" groups="base.group_multi_company"/> </group> </group> @@ -1068,7 +1068,7 @@ <field name="account_tax_id"/> <field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('parent_id','!=',False)]"/> <field name="amount_currency" attrs="{'readonly':[('state','=','valid')]}"/> - <field name="currency_id" attrs="{'readonly':[('state','=','valid')]}"/> + <field name="currency_id" attrs="{'readonly':[('state','=','valid')]}" groups="base.group_multi_currency"/> <field name="reconcile_partial_id"/> <field name="reconcile_id"/> <field name="state"/> @@ -1119,7 +1119,7 @@ <field name="tax_amount"/> <field name="account_tax_id" domain="[('parent_id','=',False)]"/> </group> - <group attrs="{'readonly':[('state','=','valid')]}" string="Currency"> + <group attrs="{'readonly':[('state','=','valid')]}" string="Currency" groups="base.group_multi_currency"> <field name="currency_id"/> <field name="amount_currency"/> </group> @@ -1167,8 +1167,8 @@ <field name="credit"/> <separator colspan="4" string="Optional Information"/> - <field name="currency_id"/> - <field name="amount_currency"/> + <field name="currency_id" groups="base.group_multi_currency"/> + <field name="amount_currency" groups="base.group_multi_currency"/> <field name="quantity"/> <field name="move_id" required="False"/> <newline/> @@ -1395,7 +1395,7 @@ <field name="account_tax_id" domain="[('parent_id','=',False)]"/> </group> - <group string="Currency"> + <group string="Currency" groups="base.group_multi_currency"> <field name="currency_id"/> <field name="amount_currency"/> </group> @@ -1433,7 +1433,7 @@ <field name="credit" sum="Total Credit"/> <field name="analytic_account_id" domain="[('parent_id','!=',False)]" groups="analytic.group_analytic_accounting"/> <field name="amount_currency"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="tax_code_id"/> <field name="tax_amount"/> <field name="state"/> @@ -2076,7 +2076,7 @@ <field name="type"/> <field name="user_type"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="reconcile"/> <field name="chart_template_id"/> </group> @@ -2369,20 +2369,15 @@ <group string="res_config_contents" position="replace"> <field name="only_one_chart_template" invisible="1"/> <field name="complete_tax_set" invisible="1"/> - <div groups="base.group_multi_company"> - <label for="company_id"/> - <field name="company_id" widget="selection"/> <!-- we assume that this wizard will be run only by administrators and as this field may cause problem if hidden (because of the default company of the user removed from the selection because already configured), we simply choosed to remove the group "multi company" of it --> - </div> - <group> - <div attrs="{'invisible': [('only_one_chart_template','=',True)]}"> - <label for="chart_template_id"/> + <group col="1"> + <group attrs="{'invisible': [('only_one_chart_template','=',True)]}"> <field name="chart_template_id" widget="selection" on_change="onchange_chart_template_id(chart_template_id)" domain="[('visible','=', True)]"/> - </div> - <newline/> - <group groups="account.group_account_user"> - <field name="code_digits"/> + </group> + <group groups="base.group_multi_company"> + <field name="company_id" widget="selection" on_change="onchange_company_id(company_id)"/> <!-- we assume that this wizard will be run only by administrators and as this field may cause problem if hidden (because of the default company of the user removed from the selection because already configured), we simply choosed to remove the group "multi company" of it --> </group> <group> + <field name="currency_id" class="oe_inline"/> <field name="sale_tax" attrs="{'invisible': [('complete_tax_set', '!=', True)]}" domain="[('chart_template_id', '=', chart_template_id),('parent_id','=',False),('type_tax_use','in',('sale','all'))]"/> <label for="sale_tax_rate" string="Sale Tax" attrs="{'invisible': [('complete_tax_set', '=', True)]}"/> <div attrs="{'invisible': [('complete_tax_set', '=', True)]}"> @@ -2394,17 +2389,10 @@ <field name="purchase_tax_rate" class="oe_inline"/> %% </div> </group> + <group groups="account.group_account_user"> + <field name="code_digits"/> + </group> </group> - <div groups="account.group_account_user"> - <label for="bank_accounts_id" string="Bank Information"/> - <field name="bank_accounts_id"> - <tree editable="bottom"> - <field name="acc_name"/> - <field name="account_type"/> - <field name="currency_id" widget="selection"/> - </tree> - </field> - </div> </group> </field> </record> @@ -2566,7 +2554,7 @@ action = pool.get('res.config').next(cr, uid, [], context) <field name="date" attrs="{'readonly':[('state','!=','draft')]}" on_change="onchange_date(date, company_id)"/> <field name="closing_date" readonly="1"/> <field name="period_id" class="oe_inline"/> - <field name="currency" invisible="1"/> + <field name="currency" invisible="1" groups="base.group_multi_currency"/> </group> </group> <notebook> diff --git a/addons/account/board_account_view.xml b/addons/account/board_account_view.xml index 630c551c5ca..563c3f5fc6c 100644 --- a/addons/account/board_account_view.xml +++ b/addons/account/board_account_view.xml @@ -29,7 +29,6 @@ <form string="Account Board" version="7.0"> <board style="2-1"> <column> - <action name="%(account.action_invoice_tree1)d" creatable="true" string="Draft Customer Invoices" domain="[('state','in',('draft','proforma2')), ('type','=','out_invoice')]"/> <action name="%(action_company_analysis_tree)d" string="Company Analysis"/> </column> <column> diff --git a/addons/account/data/configurable_account_chart.xml b/addons/account/data/configurable_account_chart.xml index 18e22a5e4f2..b8b23344ff3 100644 --- a/addons/account/data/configurable_account_chart.xml +++ b/addons/account/data/configurable_account_chart.xml @@ -45,7 +45,6 @@ <record id="conf_chart0" model="account.account.template"> <field name="code">0</field> <field name="name">Configurable Account Chart</field> - <field eval="0" name="parent_id"/> <field name="type">view</field> <field name="user_type" ref="data_account_type_view"/> </record> diff --git a/addons/account/demo/account_minimal.xml b/addons/account/demo/account_minimal.xml index b7bc34a3e35..53f2b54e0ef 100644 --- a/addons/account/demo/account_minimal.xml +++ b/addons/account/demo/account_minimal.xml @@ -19,7 +19,6 @@ <record id="chart0" model="account.account"> <field name="code">X0</field> <field name="name">Chart For Automated Tests</field> - <field eval="0" name="parent_id"/> <field name="type">view</field> <field name="user_type" ref="data_account_type_view"/> </record> @@ -383,7 +382,7 @@ <field name="type">cash</field> <field name="profit_account_id" model="account.account" ref="rsa" /> <field name="loss_account_id" model="account.account" ref="rsa" /> - <field name="internal_account_id" model="account.account" ref="chart0" /> + <field name="internal_account_id" model="account.account" ref="rsa" /> <field name="with_last_closing_balance" eval="True" /> <field name="cash_control" eval="True" /> <field name="view_id" ref="account_journal_bank_view"/> diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 0eec6d27785..53fa03a6902 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -123,64 +123,6 @@ </div> </div> ]]></field> - <field name="body_text"><![CDATA[ -Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''}, - -A new invoice is available for ${object.partner_id.name}: - | Invoice number: *${object.number}* - | Invoice total: *${object.amount_total} ${object.currency_id.name}* - | Invoice date: ${object.date_invoice} - % if object.origin: - | Order reference: ${object.origin} - % endif - | Your contact: ${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} - -You can view the invoice document, download it and pay online using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -% if object.company_id.paypal_account and object.type in ('out_invoice', 'in_refund'): -<% -comp_name = quote(object.company_id.name) -inv_number = quote(object.number) -paypal_account = quote(object.company_id.paypal_account) -inv_amount = quote(str(object.amount_total)) -cur_name = quote(object.currency_id.name) -paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Invoice%%20%s"\ - "&invoice=%s&amount=%s¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Invoice_PayNow_%s" % \ - (paypal_account,comp_name,inv_number,inv_number,inv_amount,cur_name,cur_name) -%> -It is also possible to directly pay with Paypal: - ${paypal_url} -% endif - -If you have any question, do not hesitate to contact us. - - -Thank you for choosing ${object.company_id.name}! - - --- -${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]></field> </record> </data> </openerp> diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index 174002a8d8d..0d5439719e2 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -9538,7 +9538,7 @@ msgid "Refund" msgstr "" #. module: account -#: model:email.template,body_text:account.email_template_edi_invoice +#: model:email.template,body:account.email_template_edi_invoice msgid "\n" "Hello${object.address_invoice_id.name and ' ' or ''}${object.address_invoice_id.name or ''},\n" "\n" diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index e3b902a000f..da1a11b9cb6 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -56,7 +56,7 @@ <field name="credit"/> <field name="balance"/> <field name="state" invisible="1"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="date" invisible="1"/> <field name="user_id" invisible="1"/> <field name="partner_id" invisible="1"/> @@ -137,8 +137,8 @@ </group> <group string="Amount"> <field name="amount"/> - <label for="amount_currency"/> - <div> + <label for="amount_currency" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="amount_currency" class="oe_inline"/> <field name="currency_id" class="oe_inline"/> </div> @@ -186,19 +186,23 @@ <search string="Search Analytic Lines"> <field name="name" string="Analytic Line"/> <field name="date"/> - <filter name="sales" string="Sales" domain="[('journal_id.type','=','sale')]" icon="terp-camera_test" help="Analytic Journal Items related to a sale journal."/> - <filter name="purchases" string="Purchases" domain="[('journal_id.type','=','purchase')]" icon="terp-purchase" help="Analytic Journal Items related to a purchase journal."/> - <filter name="others" string="Others" domain="[('journal_id.type','in',('cash','general','situation'))]" icon="terp-folder-orange"/> + <filter name="sales" string="Sales" domain="[('journal_id.type','=','sale')]" help="Analytic Journal Items related to a sale journal."/> + <filter name="purchases" string="Purchases" domain="[('journal_id.type','=','purchase')]" help="Analytic Journal Items related to a purchase journal."/> + <filter name="others" string="Others" domain="[('journal_id.type','in',('cash','general','situation'))]"/> <separator/> - <filter string="My Entries" domain="[('user_id','=',uid)]" icon="terp-personal"/> + <filter string="My Entries" domain="[('user_id','=',uid)]"/> <field name="account_id"/> <field name="user_id"/> <group string="Group By..." expand="0"> - <filter string="Account" context="{'group_by':'account_id'}" icon="terp-folder-green"/> - <filter string="Journal" context="{'group_by':'journal_id'}" icon="terp-folder-orange"/> - <filter string="User" context="{'group_by':'user_id'}" icon="terp-personal"/> - <filter string="Fin.Account" context="{'group_by':'general_account_id'}" icon="terp-folder-green"/> - <filter string="Product" context="{'group_by':'product_id'}" icon="terp-accessories-archiver"/> + <filter string="Analytic Account" context="{'group_by':'account_id'}"/> + <filter string="Fin. Account" context="{'group_by':'general_account_id'}"/> + <filter string="Journal" context="{'group_by':'journal_id'}" name="group_journal"/> + <separator/> + <filter string="Product" context="{'group_by':'product_id'}"/> + <filter string="User" context="{'group_by':'user_id'}"/> + <separator/> + <filter string="Date" context="{'group_by':'date'}" name="group_date"/> + </group> </search> </field> @@ -247,8 +251,8 @@ </group> <group string="Amount"> <field name="amount"/> - <label for="amount_currency"/> - <div> + <label for="amount_currency" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="amount_currency" class="oe_inline"/> <field name="currency_id" class="oe_inline"/> </div> @@ -357,16 +361,6 @@ action="action_account_analytic_journal_tree" id="account_analytic_journal_print" parent="account.next_id_40"/> - - <act_window - context="{'search_default_account_id': [active_id], 'search_default_user_id': False, 'default_account_id': active_id}" - id="act_acc_analytic_acc_5_report_hr_timesheet_invoice_journal" - name="All Analytic Entries" - res_model="account.analytic.line" - src_model="account.analytic.account" - view_mode="tree,form" - view_type="form"/> - <record id="view_account_journal_1" model="ir.ui.view"> <field name="name">account.journal.form.1</field> <field name="model">account.journal</field> diff --git a/addons/account/res_config.py b/addons/account/res_config.py index 658f87962ff..91f1068d166 100644 --- a/addons/account/res_config.py +++ b/addons/account/res_config.py @@ -43,8 +43,8 @@ class account_config_settings(osv.osv_memory): string='Default company currency', help="Main currency of the company."), 'paypal_account': fields.related('company_id', 'paypal_account', type='char', size=128, string='Paypal account', help="Paypal account (email) for receiving online payments (credit card, etc.) If you set a paypal account, the customer will be able to pay your invoices or quotations with a button \"Pay with Paypal\" in automated emails or through the OpenERP portal."), - 'company_footer': fields.related('company_id', 'rml_footer2', type='char', size=250, readonly=True, - string='Bank accounts on reports will display as followed', help="Bank accounts as printed in the footer of each customer document. This is for information purpose only, you should configure these bank accounts through the above button \"Configure Bank Accounts\"."), + 'company_footer': fields.related('company_id', 'rml_footer', type='text', readonly=True, + string='Bank accounts footer preview', help="Bank accounts as printed in the footer of each printed document"), 'has_chart_of_accounts': fields.boolean('Company has a chart of accounts'), 'chart_template_id': fields.many2one('account.chart.template', 'Template', domain="[('visible','=', True)]"), @@ -114,6 +114,9 @@ class account_config_settings(osv.osv_memory): help="This purchase tax will be assigned by default on new products."), 'decimal_precision': fields.integer('Decimal precision on journal entries', help="""As an example, a decimal precision of 2 will allow journal entries like: 9.99 EUR, whereas a decimal precision of 4 will allow journal entries like: 0.0231 EUR."""), + 'group_multi_currency': fields.boolean('allow multi currencies', + implied_group='base.group_multi_currency', + help="Allows you multi currency environment"), } def _default_company(self, cr, uid, context=None): @@ -145,42 +148,45 @@ class account_config_settings(osv.osv_memory): def onchange_company_id(self, cr, uid, ids, company_id): # update related fields - company = self.pool.get('res.company').browse(cr, uid, company_id) - has_chart_of_accounts = company_id not in self.pool.get('account.installer').get_unconfigured_cmp(cr, uid) - fiscalyear_count = self.pool.get('account.fiscalyear').search_count(cr, uid, - [('date_start', '<=', time.strftime('%Y-%m-%d')), ('date_stop', '>=', time.strftime('%Y-%m-%d')), - ('company_id', '=', company_id)]) - values = { - 'expects_chart_of_accounts': company.expects_chart_of_accounts, - 'currency_id': company.currency_id.id, - 'paypal_account': company.paypal_account, - 'company_footer': company.rml_footer2, - 'has_chart_of_accounts': has_chart_of_accounts, - 'has_fiscal_year': bool(fiscalyear_count), - 'chart_template_id': False, - 'tax_calculation_rounding_method': company.tax_calculation_rounding_method, - } - # update journals and sequences - for journal_type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'): - for suffix in ('_journal_id', '_sequence_prefix', '_sequence_next'): - values[journal_type + suffix] = False - journal_obj = self.pool.get('account.journal') - journal_ids = journal_obj.search(cr, uid, [('company_id', '=', company_id)]) - for journal in journal_obj.browse(cr, uid, journal_ids): - if journal.type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'): - values.update({ - journal.type + '_journal_id': journal.id, - journal.type + '_sequence_prefix': journal.sequence_id.prefix, - journal.type + '_sequence_next': journal.sequence_id.number_next, - }) - # update taxes - ir_values = self.pool.get('ir.values') - taxes_id = ir_values.get_default(cr, uid, 'product.product', 'taxes_id', company_id=company_id) - supplier_taxes_id = ir_values.get_default(cr, uid, 'product.product', 'supplier_taxes_id', company_id=company_id) - values.update({ - 'default_sale_tax': isinstance(taxes_id, list) and taxes_id[0] or taxes_id, - 'default_purchase_tax': isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id, - }) + values = {} + values['currency_id'] = False + if company_id: + company = self.pool.get('res.company').browse(cr, uid, company_id) + has_chart_of_accounts = company_id not in self.pool.get('account.installer').get_unconfigured_cmp(cr, uid) + fiscalyear_count = self.pool.get('account.fiscalyear').search_count(cr, uid, + [('date_start', '<=', time.strftime('%Y-%m-%d')), ('date_stop', '>=', time.strftime('%Y-%m-%d')), + ('company_id', '=', company_id)]) + values = { + 'expects_chart_of_accounts': company.expects_chart_of_accounts, + 'currency_id': company.currency_id.id, + 'paypal_account': company.paypal_account, + 'company_footer': company.rml_footer, + 'has_chart_of_accounts': has_chart_of_accounts, + 'has_fiscal_year': bool(fiscalyear_count), + 'chart_template_id': False, + 'tax_calculation_rounding_method': company.tax_calculation_rounding_method, + } + # update journals and sequences + for journal_type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'): + for suffix in ('_journal_id', '_sequence_prefix', '_sequence_next'): + values[journal_type + suffix] = False + journal_obj = self.pool.get('account.journal') + journal_ids = journal_obj.search(cr, uid, [('company_id', '=', company_id)]) + for journal in journal_obj.browse(cr, uid, journal_ids): + if journal.type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'): + values.update({ + journal.type + '_journal_id': journal.id, + journal.type + '_sequence_prefix': journal.sequence_id.prefix, + journal.type + '_sequence_next': journal.sequence_id.number_next, + }) + # update taxes + ir_values = self.pool.get('ir.values') + taxes_id = ir_values.get_default(cr, uid, 'product.product', 'taxes_id', company_id=company_id) + supplier_taxes_id = ir_values.get_default(cr, uid, 'product.product', 'supplier_taxes_id', company_id=company_id) + values.update({ + 'default_sale_tax': isinstance(taxes_id, list) and taxes_id[0] or taxes_id, + 'default_purchase_tax': isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id, + }) return {'value': values} def onchange_chart_template_id(self, cr, uid, ids, chart_template_id, context=None): diff --git a/addons/account/res_config_view.xml b/addons/account/res_config_view.xml index 79a65f70414..bc8ace5c262 100644 --- a/addons/account/res_config_view.xml +++ b/addons/account/res_config_view.xml @@ -122,6 +122,10 @@ <label for="id" string="Features"/> <div> <div> + <field name="group_multi_currency" class="oe_inline"/> + <label for="group_multi_currency"/> + </div> + <div> <field name="module_account_accountant" class="oe_inline"/> <label for="module_account_accountant"/> </div> @@ -219,12 +223,12 @@ <label for="id" string="Configuration"/> <div> <div> + <label for="company_footer"/> <button name="%(action_bank_tree)d" string="Configure your bank accounts" icon="gtk-go-forward" type="action" class="oe_inline oe_link"/> - <label for="company_footer"/> <field name="company_footer"/> </div> <div> diff --git a/addons/account/wizard/account_report_common.py b/addons/account/wizard/account_report_common.py index 02da9a6ab1f..9acc09e9026 100644 --- a/addons/account/wizard/account_report_common.py +++ b/addons/account/wizard/account_report_common.py @@ -30,9 +30,11 @@ class account_common_report(osv.osv_memory): _description = "Account Common Report" def onchange_chart_id(self, cr, uid, ids, chart_account_id=False, context=None): + res = {} if chart_account_id: company_id = self.pool.get('account.account').browse(cr, uid, chart_account_id, context=context).company_id.id - return {'value': {'company_id': company_id}} + res['value'] = {'company_id': company_id} + return res _columns = { 'chart_account_id': fields.many2one('account.account', 'Chart of Account', help='Select Charts of Accounts', required=True, domain = [('parent_id','=',False)]), diff --git a/addons/account/wizard/pos_box.py b/addons/account/wizard/pos_box.py index b4254b5ba84..b5d1cc37776 100644 --- a/addons/account/wizard/pos_box.py +++ b/addons/account/wizard/pos_box.py @@ -40,6 +40,10 @@ class CashBox(osv.osv_memory): return {} + def _create_bank_statement_line(self, cr, uid, box, record, context=None): + values = self._compute_values_for_statement_line(cr, uid, box, record, context=context) + return self.pool.get('account.bank.statement.line').create(cr, uid, values, context=context) + class CashBoxIn(CashBox): _name = 'cash.box.in' @@ -49,30 +53,24 @@ class CashBoxIn(CashBox): 'ref' : fields.char('Reference', size=32), }) - def _create_bank_statement_line(self, cr, uid, box, record, context=None): - absl_proxy = self.pool.get('account.bank.statement.line') - - values = { + def _compute_values_for_statement_line(self, cr, uid, box, record, context=None): + return { 'statement_id' : record.id, 'journal_id' : record.journal_id.id, 'account_id' : record.journal_id.internal_account_id.id, 'amount' : box.amount or 0.0, - 'ref' : "%s" % (box.ref or ''), + 'ref' : '%s' % (box.ref or ''), 'name' : box.name, } - return absl_proxy.create(cr, uid, values, context=context) - CashBoxIn() class CashBoxOut(CashBox): _name = 'cash.box.out' - def _create_bank_statement_line(self, cr, uid, box, record, context=None): - absl_proxy = self.pool.get('account.bank.statement.line') - + def _compute_values_for_statement_line(self, cr, uid, box, record, context=None): amount = box.amount or 0.0 - values = { + return { 'statement_id' : record.id, 'journal_id' : record.journal_id.id, 'account_id' : record.journal_id.internal_account_id.id, @@ -80,6 +78,4 @@ class CashBoxOut(CashBox): 'name' : box.name, } - return absl_proxy.create(cr, uid, values, context=context) - CashBoxOut() diff --git a/addons/account_accountant/account_accountant_data.xml b/addons/account_accountant/account_accountant_data.xml index 383b53cf49e..5b563017c62 100644 --- a/addons/account_accountant/account_accountant_data.xml +++ b/addons/account_accountant/account_accountant_data.xml @@ -21,13 +21,14 @@ </record> <!-- Notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Accounting and Finance has been installed.</value> - <value>With OpenERP's accounting, you can get an instant access to all your financial data, setup your analytic accounting, forecast your taxes, control your budgets, easily create and send invoices, record bank statements, etc. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Accounting and Finance application installed!</field> + <field name="body">With OpenERP's accounting, you get instant access to your financial data, and can setup analytic accounting, forecast taxes, control budgets, easily create and send invoices, record bank statements, etc. -The accounting features are fully integrated with others OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.</value> - </function> +The accounting features are fully integrated with other OpenERP applications to automate all your processes: creation of customer invoices, control of supplier invoices, point-of-sale integration, automated follow-ups, etc.</field> + </record> </data> </openerp> diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index 63a8e6c7add..12d8fad1b95 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -6,12 +6,11 @@ Analytic Account form --> - <act_window - id="action_sales_order" - name="Sales Orders" - res_model="sale.order" - src_model="account.analytic.account" - /> + <record model="ir.actions.act_window" id="action_sales_order"> + <field name="name">Sales Orders</field> + <field name="res_model">sale.order</field> + <field name="src_model">account.analytic.account</field> + </record> <record id="account_analytic_account_form_form" model="ir.ui.view"> <field name="name">account.analytic.account.invoice.form.inherit</field> diff --git a/addons/account_analytic_plans/i18n/nb.po b/addons/account_analytic_plans/i18n/nb.po new file mode 100644 index 00000000000..27a6c0d9e24 --- /dev/null +++ b/addons/account_analytic_plans/i18n/nb.po @@ -0,0 +1,546 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-09-02 09:46+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-03 05:21+0000\n" +"X-Generator: Launchpad (build 15890)\n" + +#. module: account_analytic_plans +#: view:analytic.plan.create.model:0 +msgid "" +"This distribution model has been saved.You will be able to reuse it later." +msgstr "" +"Denne fordeling modellen har vært lagret. Du vil kunne bruke den senere." + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance.line,plan_id:0 +msgid "Plan Id" +msgstr "Plan ID" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "From Date" +msgstr "Fra dato" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +#: view:account.crossovered.analytic:0 +#: model:ir.actions.act_window,name:account_analytic_plans.action_account_crossovered_analytic +#: model:ir.actions.report.xml,name:account_analytic_plans.account_analytic_account_crossovered_analytic +msgid "Crossovered Analytic" +msgstr "Krysset Analyse" + +#. module: account_analytic_plans +#: view:account.analytic.plan:0 +#: field:account.analytic.plan,name:0 +#: field:account.analytic.plan.line,plan_id:0 +#: model:ir.actions.act_window,name:account_analytic_plans.account_analytic_plan_form_action +#: model:ir.model,name:account_analytic_plans.model_account_analytic_plan +#: model:ir.ui.menu,name:account_analytic_plans.menu_account_analytic_plan_action +msgid "Analytic Plan" +msgstr "Analytisk Plan" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,journal_id:0 +#: view:account.crossovered.analytic:0 +#: field:account.crossovered.analytic,journal_ids:0 +msgid "Analytic Journal" +msgstr "Analytisk journal" + +#. module: account_analytic_plans +#: view:account.analytic.plan.line:0 +#: model:ir.model,name:account_analytic_plans.model_account_analytic_plan_line +msgid "Analytic Plan Line" +msgstr "Analytisk plan linje" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/wizard/account_crossovered_analytic.py:61 +#, python-format +msgid "User Error" +msgstr "Brukerfeil" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_analytic_plan_instance +msgid "Analytic Plan Instance" +msgstr "Analytisk Plan forekomst" + +#. module: account_analytic_plans +#: view:analytic.plan.create.model:0 +msgid "Ok" +msgstr "Ok" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,plan_id:0 +msgid "Model's Plan" +msgstr "Modell Plan" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account2_ids:0 +msgid "Account2 Id" +msgstr "Konto2 ID" + +#. module: account_analytic_plans +#: sql_constraint:account.invoice:0 +msgid "Invoice Number must be unique per Company!" +msgstr "Fakturanummer må være unik pr. firma!" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Amount" +msgstr "Beløp" + +#. module: account_analytic_plans +#: constraint:account.journal:0 +msgid "" +"Configuration error! The currency chosen should be shared by the default " +"accounts too." +msgstr "" +"Konfigurasjon feil! Den valgte valutaen bør deles av standard kontoer også." + +#. module: account_analytic_plans +#: sql_constraint:account.move.line:0 +msgid "Wrong credit or debit value in accounting entry !" +msgstr "Feil kredit eller debet beløp i regnskaps oppføringen !" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account6_ids:0 +msgid "Account6 Id" +msgstr "Konto6 ID" + +#. module: account_analytic_plans +#: model:ir.ui.menu,name:account_analytic_plans.menu_account_analytic_multi_plan_action +msgid "Multi Plans" +msgstr "Multi Planer" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "Kontoutskriftlinje" + +#. module: account_analytic_plans +#: model:ir.actions.act_window,name:account_analytic_plans.account_analytic_plan_form_action_installer +msgid "Define your Analytic Plans" +msgstr "Definer dine analytiske planer." + +#. module: account_analytic_plans +#: constraint:account.invoice:0 +msgid "Invalid BBA Structured Communication !" +msgstr "Ugyldig BBA Strukturert Kommunikasjon!" + +#. module: account_analytic_plans +#: constraint:account.bank.statement:0 +msgid "The journal and period chosen have to belong to the same company." +msgstr "Tidsskriftet og perioden valgt å tilhøre samme selskap." + +#. module: account_analytic_plans +#: constraint:account.move.line:0 +msgid "" +"The date of your Journal Entry is not in the defined period! You should " +"change the date or remove this constraint from the journal." +msgstr "" +"Datoen for din bilagsregistrering ikke er i den definerte perioden! Du bør " +"endre datoen eller fjerne denne begrensningen fra tidsskriftet." + +#. module: account_analytic_plans +#: sql_constraint:account.journal:0 +msgid "The code of the journal must be unique per company !" +msgstr "Journalkoden må være unik pr firma!" + +#. module: account_analytic_plans +#: field:account.crossovered.analytic,ref:0 +msgid "Analytic Account Reference" +msgstr "Analytisk konto Referanse." + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_sale_order_line +msgid "Sales Order Line" +msgstr "Salgsordrelinje" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/wizard/analytic_plan_create_model.py:47 +#: view:analytic.plan.create.model:0 +#, python-format +msgid "Distribution Model Saved" +msgstr "Distribusjon Model Lagret" + +#. module: account_analytic_plans +#: model:ir.actions.act_window,name:account_analytic_plans.account_analytic_plan_instance_action +msgid "Analytic Distribution's Models" +msgstr "Analytisk distribusjon modeller" + +#. module: account_analytic_plans +#: view:account.crossovered.analytic:0 +msgid "Print" +msgstr "Skriv ut" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +#: field:account.analytic.line,percentage:0 +msgid "Percentage" +msgstr "Prosentdel" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:221 +#, python-format +msgid "A model having this name and code already exists !" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/wizard/analytic_plan_create_model.py:41 +#, python-format +msgid "No analytic plan defined !" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance.line,rate:0 +msgid "Rate (%)" +msgstr "" + +#. module: account_analytic_plans +#: view:account.analytic.plan:0 +#: field:account.analytic.plan,plan_ids:0 +#: field:account.journal,plan_id:0 +msgid "Analytic Plans" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Perc(%)" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.line,max_required:0 +msgid "Maximum Allowed (%)" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Printing date" +msgstr "" + +#. module: account_analytic_plans +#: view:account.analytic.plan.line:0 +msgid "Analytic Plan Lines" +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.bank.statement.line:0 +msgid "" +"The amount of the voucher must be the same amount as the one on the " +"statement line" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.move.line:0 +msgid "" +"The selected account of your Journal Entry forces to provide a secondary " +"currency. You should remove the secondary currency on the account or select " +"a multi-currency view on the journal." +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Currency" +msgstr "" + +#. module: account_analytic_plans +#: field:account.crossovered.analytic,date1:0 +msgid "Start Date" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account_ids:0 +msgid "Account Id" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account5_ids:0 +msgid "Account5 Id" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_analytic_plan_instance_line +msgid "Analytic Instance Line" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.line,root_analytic_id:0 +msgid "Root Account" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "To Date" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:341 +#: code:addons/account_analytic_plans/account_analytic_plans.py:485 +#, python-format +msgid "You have to define an analytic journal on the '%s' journal!" +msgstr "" + +#. module: account_analytic_plans +#: field:account.crossovered.analytic,empty_line:0 +msgid "Dont show empty lines" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.actions.act_window,name:account_analytic_plans.action_analytic_plan_create_model +msgid "analytic.plan.create.model.action" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_analytic_line +msgid "Analytic Line" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Analytic Account :" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Analytic Account Reference:" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.line,name:0 +msgid "Plan Name" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan,default_instance_id:0 +msgid "Default Entries" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_move_line +msgid "Journal Items" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account1_ids:0 +msgid "Account1 Id" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.line,min_required:0 +msgid "Minimum Allowed (%)" +msgstr "" + +#. module: account_analytic_plans +#: help:account.analytic.plan.line,root_analytic_id:0 +msgid "Root account of this plan." +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:221 +#: code:addons/account_analytic_plans/wizard/analytic_plan_create_model.py:38 +#: code:addons/account_analytic_plans/wizard/analytic_plan_create_model.py:41 +#, python-format +msgid "Error" +msgstr "" + +#. module: account_analytic_plans +#: view:analytic.plan.create.model:0 +msgid "Save This Distribution as a Model" +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.move.line:0 +msgid "You can not create journal items on an account of type view." +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Quantity" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/wizard/analytic_plan_create_model.py:38 +#, python-format +msgid "Please put a name and a code before saving the model !" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_crossovered_analytic +msgid "Print Crossovered Analytic" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:341 +#: code:addons/account_analytic_plans/account_analytic_plans.py:485 +#, python-format +msgid "No Analytic Journal !" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance.line,analytic_account_id:0 +msgid "Analytic Account" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_bank_statement +msgid "Bank Statement" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account3_ids:0 +msgid "Account3 Id" +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.analytic.line:0 +msgid "You can not create analytic line on view account." +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: account_analytic_plans +#: view:account.crossovered.analytic:0 +#: view:analytic.plan.create.model:0 +msgid "Cancel" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,account4_ids:0 +msgid "Account4 Id" +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.move.line:0 +msgid "Company must be the same for its related account and period." +msgstr "" + +#. module: account_analytic_plans +#: view:account.analytic.plan.instance.line:0 +msgid "Analytic Distribution Lines" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:234 +#, python-format +msgid "The Total Should be Between %s and %s" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "at" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Account Name" +msgstr "" + +#. module: account_analytic_plans +#: view:account.analytic.plan.instance.line:0 +msgid "Analytic Distribution Line" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.instance,code:0 +msgid "Distribution Code" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "%" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "100.00%" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.default,analytics_id:0 +#: view:account.analytic.plan.instance:0 +#: field:account.analytic.plan.instance,name:0 +#: field:account.bank.statement.line,analytics_id:0 +#: field:account.invoice.line,analytics_id:0 +#: field:account.move.line,analytics_id:0 +#: model:ir.model,name:account_analytic_plans.model_account_analytic_default +msgid "Analytic Distribution" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_account_journal +msgid "Journal" +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Code" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.model,name:account_analytic_plans.model_analytic_plan_create_model +msgid "analytic.plan.create.model" +msgstr "" + +#. module: account_analytic_plans +#: field:account.crossovered.analytic,date2:0 +msgid "End Date" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.actions.act_window,name:account_analytic_plans.account_analytic_instance_model_open +msgid "Distribution Models" +msgstr "" + +#. module: account_analytic_plans +#: model:ir.actions.act_window,help:account_analytic_plans.account_analytic_plan_form_action_installer +msgid "" +"To setup a multiple analytic plans environment, you must define the root " +"analytic accounts for each plan set. Then, you must attach a plan set to " +"your account journals." +msgstr "" + +#. module: account_analytic_plans +#: constraint:account.move.line:0 +msgid "You can not create journal items on closed account." +msgstr "" + +#. module: account_analytic_plans +#: report:account.analytic.account.crossovered.analytic:0 +msgid "Company" +msgstr "" + +#. module: account_analytic_plans +#: field:account.analytic.plan.line,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: account_analytic_plans +#: sql_constraint:account.journal:0 +msgid "The name of the journal must be unique per company !" +msgstr "" + +#. module: account_analytic_plans +#: code:addons/account_analytic_plans/account_analytic_plans.py:234 +#, python-format +msgid "Value Error" +msgstr "" diff --git a/addons/account_asset/account_asset_view.xml b/addons/account_asset/account_asset_view.xml index c230f63e204..bb0222d5e4f 100644 --- a/addons/account_asset/account_asset_view.xml +++ b/addons/account_asset/account_asset_view.xml @@ -100,7 +100,7 @@ </group> <group> <field name="purchase_date"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="company_id" widget="selection" groups="base.group_multi_company" on_change="onchange_company_id(company_id)"/> </group> </group> @@ -192,7 +192,7 @@ <field name="partner_id"/> <field name="purchase_value"/> <field name="value_residual"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="company_id" groups="base.group_multi_company"/> <field name="state"/> </tree> @@ -211,7 +211,7 @@ <field name="purchase_date"/> <field name="purchase_value"/> <field name="value_residual"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="company_id" groups="base.group_multi_company"/> <field name="state"/> </tree> diff --git a/addons/account_bank_statement_extensions/i18n/pt_BR.po b/addons/account_bank_statement_extensions/i18n/pt_BR.po new file mode 100644 index 00000000000..ade94336f2a --- /dev/null +++ b/addons/account_bank_statement_extensions/i18n/pt_BR.po @@ -0,0 +1,385 @@ +# Brazilian Portuguese translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-09-11 18:35+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-12 04:36+0000\n" +"X-Generator: Launchpad (build 15930)\n" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Search Bank Transactions" +msgstr "Procurar Transações Bancárias" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: selection:account.bank.statement.line,state:0 +msgid "Confirmed" +msgstr "Confirmado" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement:0 +#: view:account.bank.statement.line:0 +msgid "Glob. Id" +msgstr "ID Global" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "CODA" +msgstr "CODA" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,parent_id:0 +msgid "Parent Code" +msgstr "Código da Conta-pai" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Debit" +msgstr "Débito" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_cancel_statement_line +#: model:ir.model,name:account_bank_statement_extensions.model_cancel_statement_line +msgid "Cancel selected statement lines" +msgstr "Cancelar linhas de instrução selecionadas" + +#. module: account_bank_statement_extensions +#: constraint:res.partner.bank:0 +msgid "The RIB and/or IBAN is not valid" +msgstr "A RIB e/ ou IBAN não é válido." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Group By..." +msgstr "Agrupar Por..." + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,state:0 +msgid "State" +msgstr "Situação" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: selection:account.bank.statement.line,state:0 +msgid "Draft" +msgstr "Provisório" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement" +msgstr "Demonstrativo" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_confirm_statement_line +#: model:ir.model,name:account_bank_statement_extensions.model_confirm_statement_line +msgid "Confirm selected statement lines" +msgstr "Confirme as linhas do demonstrativo selecionadas" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +#: model:ir.actions.report.xml,name:account_bank_statement_extensions.bank_statement_balance_report +msgid "Bank Statement Balances Report" +msgstr "Relatório de Balanço Bancário" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +msgid "Cancel Lines" +msgstr "Cancelar Linhas" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line_global +msgid "Batch Payment Info" +msgstr "Informações de Pagamento em Lote" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Confirm Lines" +msgstr "Confirmar Linhas" + +#. module: account_bank_statement_extensions +#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130 +#, python-format +msgid "" +"Delete operation not allowed ! Please go to the associated bank " +"statement in order to delete and/or modify this bank statement line" +msgstr "" +"Não é permitido excluir! Vá a linha do demonstrativo bancário associada para " +"excluir ou modificar esta linha do demonstrativo" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,type:0 +msgid "Type" +msgstr "Tipo" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: field:account.bank.statement.line,journal_id:0 +#: report:bank.statement.balance.report:0 +msgid "Journal" +msgstr "Diário" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Confirmed Statement Lines." +msgstr "Linhas do Demonstrativo Confirmadas." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Credit Transactions." +msgstr "Transações de Crédito" + +#. module: account_bank_statement_extensions +#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_cancel_statement_line +msgid "cancel selected statement lines." +msgstr "cancelar linhas do demonstrativo selecionadas." + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_number:0 +msgid "Counterparty Number" +msgstr "Número da Contrapartida" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +msgid "Transactions" +msgstr "Transações" + +#. module: account_bank_statement_extensions +#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130 +#, python-format +msgid "Warning" +msgstr "Aviso" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Closing Balance" +msgstr "Saldo final" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Date" +msgstr "Data" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: field:account.bank.statement.line,globalisation_amount:0 +msgid "Glob. Amount" +msgstr "Valor Global" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Debit Transactions." +msgstr "Transações de Débito." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Extended Filters..." +msgstr "Filtros Extendidos..." + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Confirmed lines cannot be changed anymore." +msgstr "Linhas confirmadas não podem ser alteradas." + +#. module: account_bank_statement_extensions +#: constraint:res.partner.bank:0 +msgid "" +"\n" +"Please define BIC/Swift code on bank for bank type IBAN Account to make " +"valid payments" +msgstr "" +"\n" +"Por favor defina o BIC/Swift code no Banco para o tipo de conta IBAN para " +"fazer pagamentos válidos" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,val_date:0 +msgid "Valuta Date" +msgstr "" + +#. module: account_bank_statement_extensions +#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_confirm_statement_line +msgid "Confirm selected statement lines." +msgstr "Confirmar as linhas do demonstrativo." + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +msgid "Are you sure you want to cancel the selected Bank Statement lines ?" +msgstr "" +"Você tem certeza de que deseja cancelar as Linhas de Demonstrativo Bancário " +"selecionadas?" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Name" +msgstr "Nome" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "ISO 20022" +msgstr "ISO 20022" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Notes" +msgstr "Notas" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "Manual" +msgstr "Manual" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Credit" +msgstr "Crédito" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,amount:0 +msgid "Amount" +msgstr "Valor" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Fin.Account" +msgstr "Fin.Account" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_currency:0 +msgid "Counterparty Currency" +msgstr "Moeda da Contrapartida" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_bic:0 +msgid "Counterparty BIC" +msgstr "BIC da Contrapartida" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,child_ids:0 +msgid "Child Codes" +msgstr "Códigos derivados (sub-contas)" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Are you sure you want to confirm the selected Bank Statement lines ?" +msgstr "" +"Você deseja confirmar as Linhas do Demonstrativo Bancário selecionadas?" + +#. module: account_bank_statement_extensions +#: constraint:account.bank.statement.line:0 +msgid "" +"The amount of the voucher must be the same amount as the one on the " +"statement line" +msgstr "" +"O valor do recibo deve ser o mesmo valor da linha equivalente no extrato" + +#. module: account_bank_statement_extensions +#: help:account.bank.statement.line,globalisation_id:0 +msgid "" +"Code to identify transactions belonging to the same globalisation level " +"within a batch payment" +msgstr "" +"Código para identificar transações que pertencem ao nível de globalização " +"dentro de um mesmo lote de pagamento" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Draft Statement Lines." +msgstr "Linhas de Demonstrativo Provisórias." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Glob. Am." +msgstr "" + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "Linha do Demonstrativo Bancário" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,code:0 +msgid "Code" +msgstr "Código" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_name:0 +msgid "Counterparty Name" +msgstr "Nome da Contrapartida" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,name:0 +msgid "Communication" +msgstr "Comunicação" + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank +msgid "Bank Accounts" +msgstr "Contas Bancárias" + +#. module: account_bank_statement_extensions +#: constraint:account.bank.statement:0 +msgid "The journal and period chosen have to belong to the same company." +msgstr "O diário e o período escolhido tem que pertencer à mesma empresa." + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement +msgid "Bank Statement" +msgstr "Extrato Bancário" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement Line" +msgstr "Linha do Demonstrativo" + +#. module: account_bank_statement_extensions +#: sql_constraint:account.bank.statement.line.global:0 +msgid "The code must be unique !" +msgstr "O código precisa ser único!" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,bank_statement_line_ids:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line +#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line +msgid "Bank Statement Lines" +msgstr "Linhas do Demonstrativo Bancário" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +msgid "Child Batch Payments" +msgstr "Lote de Pagamentos Filho" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +#: view:confirm.statement.line:0 +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement Lines" +msgstr "Linhas do Demonstrativo" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Total Amount" +msgstr "Valor Total" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,globalisation_id:0 +msgid "Globalisation ID" +msgstr "ID Globalização" diff --git a/addons/account_check_writing/i18n/es_EC.po b/addons/account_check_writing/i18n/es_EC.po new file mode 100644 index 00000000000..7543458097f --- /dev/null +++ b/addons/account_check_writing/i18n/es_EC.po @@ -0,0 +1,207 @@ +# Spanish (Ecuador) translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-09-12 01:39+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Ecuador) <es_EC@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-12 04:36+0000\n" +"X-Generator: Launchpad (build 15930)\n" + +#. module: account_check_writing +#: selection:res.company,check_layout:0 +msgid "Check on Top" +msgstr "Cheque on Top" + +#. module: account_check_writing +#: model:ir.actions.act_window,help:account_check_writing.action_write_check +msgid "" +"The check payment form allows you to track the payment you do to your " +"suppliers specially by check. When you select a supplier, the payment method " +"and an amount for the payment, OpenERP will propose to reconcile your " +"payment with the open supplier invoices or bills.You can print the check" +msgstr "" +"El pago de cheques permite rastrear el pago a sus proveedores. Cuando " +"selecciona un proveedor, el método de pago y monto, OpenERP propondrá " +"conciliarlo con tu factura, y podrá imprimir el cheque." + +#. module: account_check_writing +#: view:account.voucher:0 +#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_bottom +#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_middle +#: model:ir.actions.report.xml,name:account_check_writing.account_print_check_top +msgid "Print Check" +msgstr "Imprimir Cheque" + +#. module: account_check_writing +#: selection:res.company,check_layout:0 +msgid "Check in middle" +msgstr "Cheque in middle" + +#. module: account_check_writing +#: help:res.company,check_layout:0 +msgid "" +"Check on top is compatible with Quicken, QuickBooks and Microsoft Money. " +"Check in middle is compatible with Peachtree, ACCPAC and DacEasy. Check on " +"bottom is compatible with Peachtree, ACCPAC and DacEasy only" +msgstr "" + +#. module: account_check_writing +#: selection:res.company,check_layout:0 +msgid "Check on bottom" +msgstr "Cheque on bottom" + +#. module: account_check_writing +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "Error! No puede crear compañías recursivas." + +#. module: account_check_writing +#: help:account.journal,allow_check_writing:0 +msgid "Check this if the journal is to be used for writing checks." +msgstr "Activar si este diario es usado para emitir cheques" + +#. module: account_check_writing +#: field:account.journal,allow_check_writing:0 +msgid "Allow Check writing" +msgstr "Permitir emisión de cheques" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Description" +msgstr "Descripción" + +#. module: account_check_writing +#: model:ir.model,name:account_check_writing.model_account_journal +msgid "Journal" +msgstr "Diario" + +#. module: account_check_writing +#: model:ir.actions.act_window,name:account_check_writing.action_write_check +#: model:ir.ui.menu,name:account_check_writing.menu_action_write_check +msgid "Write Checks" +msgstr "Escribir Cheque" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Discount" +msgstr "Descuento" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Original Amount" +msgstr "Monto Inicial" + +#. module: account_check_writing +#: view:res.company:0 +msgid "Configuration" +msgstr "Configuración" + +#. module: account_check_writing +#: field:account.voucher,allow_check:0 +msgid "Allow Check Writing" +msgstr "Permitir Emisión de Cheques" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Payment" +msgstr "Pagos" + +#. module: account_check_writing +#: field:account.journal,use_preprint_check:0 +msgid "Use Preprinted Check" +msgstr "Usar cheque preimpreso" + +#. module: account_check_writing +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "¡El nombre de la compañía debe ser único!" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Due Date" +msgstr "Fecha de vencimiento" + +#. module: account_check_writing +#: model:ir.model,name:account_check_writing.model_res_company +msgid "Companies" +msgstr "Compañias" + +#. module: account_check_writing +#: view:res.company:0 +msgid "Default Check Layout" +msgstr "" + +#. module: account_check_writing +#: constraint:account.journal:0 +msgid "" +"Configuration error! The currency chosen should be shared by the default " +"accounts too." +msgstr "" +"Error de Configuración! La moneda seleccionada debe ser compartida por las " +"cuentas por defecto tambíen" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +msgid "Balance Due" +msgstr "Saldo Deudor" + +#. module: account_check_writing +#: report:account.print.check.bottom:0 +#: report:account.print.check.middle:0 +#: report:account.print.check.top:0 +msgid "Check Amount" +msgstr "Monto Cheque" + +#. module: account_check_writing +#: model:ir.model,name:account_check_writing.model_account_voucher +msgid "Accounting Voucher" +msgstr "Comprobantes de Pago" + +#. module: account_check_writing +#: sql_constraint:account.journal:0 +msgid "The name of the journal must be unique per company !" +msgstr "El nombre del diaro debe ser único por compañía !" + +#. module: account_check_writing +#: sql_constraint:account.journal:0 +msgid "The code of the journal must be unique per company !" +msgstr "El código del diario debe ser único por compañía !" + +#. module: account_check_writing +#: field:account.voucher,amount_in_word:0 +msgid "Amount in Word" +msgstr "Monto en Letras" + +#. module: account_check_writing +#: report:account.print.check.top:0 +msgid "Open Balance" +msgstr "Saldo Inicial" + +#. module: account_check_writing +#: field:res.company,check_layout:0 +msgid "Choose Check layout" +msgstr "Elegir diseño de cheque" + +#~ msgid "Default Check layout" +#~ msgstr "Diseño de cheque por defecto" diff --git a/addons/account_coda/account_coda_view.xml b/addons/account_coda/account_coda_view.xml index 0b42ad6352f..23e63b2a4a4 100644 --- a/addons/account_coda/account_coda_view.xml +++ b/addons/account_coda/account_coda_view.xml @@ -16,11 +16,11 @@ <filter string="Info" domain="[('state','=','info')]" icon="terp-folder-yellow"/> <field name="bank_id"/> <field name="journal"/> - <field name="currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="company_id" groups="base.group_multi_company"/> <group expand="0" string="Group By..."> <filter string="Bank Account" icon="terp-folder-yellow" domain="[]" context="{'group_by':'bank_id'}"/> - <filter string="Currency" icon="terp-dolar" domain="[]" context="{'group_by':'currency'}"/> + <filter string="Currency" icon="terp-dolar" domain="[]" context="{'group_by':'currency'}" groups="base.group_multi_currency"/> </group> </search> </field> @@ -32,7 +32,7 @@ <tree string="CODA Bank Account Configuration"> <field name="name"/> <field name="bank_id"/> - <field name="currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="description1"/> <field name="journal"/> <field name="state"/> @@ -48,7 +48,7 @@ <field name="name" colspan="4"/> <field name="bank_id" domain="[('partner_id.ref_companies', 'in', [company_id])]"/> <field name="description1"/> - <field name="currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="description2"/> <field name="coda_st_naming"/> <field name="state" on_change="onchange_state(state)"/> @@ -305,7 +305,7 @@ <field name="name"/> <field name="date"/> <field name="coda_bank_account_id"/> - <field name="currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="period_id"/> <field name="type"/> <newline/> diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py index d151d2171a4..1e84681b1ea 100644 --- a/addons/account_followup/wizard/account_followup_print.py +++ b/addons/account_followup/wizard/account_followup_print.py @@ -213,8 +213,6 @@ class account_followup_print_all(osv.osv_memory): mod_obj = self.pool.get('ir.model.data') move_obj = self.pool.get('account.move.line') user_obj = self.pool.get('res.users') - line_obj = self.pool.get('account_followup.stat') - mail_message = self.pool.get('mail.message') if context is None: context = {} @@ -235,13 +233,7 @@ class account_followup_print_all(osv.osv_memory): total_amt += line.debit - line.credit dest = False if partner: - if partner.type=='contact': - if adr.email: - dest = [partner.email] - if (not dest) and partner.type=='default': - if partner.email: - dest = [partner.email] - src = tools.config.options['email_from'] + dest = [partner.email] if not data.partner_lang: body = data.email_body else: @@ -281,7 +273,12 @@ class account_followup_print_all(osv.osv_memory): msg = '' if dest: try: - mail_message.schedule_with_attach(cr, uid, src, dest, sub, body, context=context) + vals = {'state': 'outgoing', + 'subject': sub, + 'body_html': '<pre>%s</pre>' % body, + 'email_to': dest, + 'email_from': data_user.email or tools.config.options['email_from']} + self.pool.get('mail.mail').create(cr, uid, vals, context=context) msg_sent += partner.name + '\n' except Exception, e: raise osv.except_osv('Error !', e ) diff --git a/addons/account_payment/account_payment_view.xml b/addons/account_payment/account_payment_view.xml index e3e9b2ae558..4d69c2d7315 100644 --- a/addons/account_payment/account_payment_view.xml +++ b/addons/account_payment/account_payment_view.xml @@ -30,8 +30,8 @@ <field name="debit" sum="Total debit"/> <field name="credit" sum="Total credit"/> <field name="amount_to_pay"/> - <field name="amount_currency"/> - <field name="currency_id"/> + <field name="amount_currency" groups="base.group_multi_currency"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="period_id" invisible="1"/> </tree> </field> @@ -131,8 +131,8 @@ <field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode,parent.date_prefered,parent.date_scheduled,currency,company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/> <separator colspan="4" string="Transaction Information"/> <field name="date"/> - <label for="amount_currency"/> - <div> + <label for="amount_currency" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/> <field name="currency" nolabel="1" class="oe_inline"/> </div> @@ -151,8 +151,8 @@ </page> <page string="Information"> <group col="4" string="General Information"> - <label for="amount"/> - <div> + <label for="amount" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="amount" class="oe_inline"/> <field name="company_currency" class="oe_inline"/> </div> @@ -171,8 +171,8 @@ <field name="bank_id" domain="[('partner_id', '=', partner_id)]"/> <field name="ml_maturity_date"/> <field name="date"/> - <field name="amount_currency" string="Amount"/> - <field name="currency"/> + <field name="amount_currency" string="Amount" groups="base.group_multi_currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="name"/> <field name="amount" sum="Total in Company Currency" invisible="1"/> </tree> @@ -257,8 +257,8 @@ <field name="move_line_id" on_change="onchange_move_line(move_line_id, False, currency, company_currency)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)]"/> <separator colspan="4" string="Transaction Information"/> <field name="date"/> - <label for="amount_currency"/> - <div> + <label for="amount_currency" groups="base.group_multi_currency"/> + <div groups="base.group_multi_currency"> <field name="amount_currency" on_change="onchange_amount(amount_currency,currency,company_currency)" class="oe_inline"/> <field name="currency" class="oe_inline"/> </div> @@ -277,8 +277,11 @@ </page> <page string="Information"> <group col="4" string="General Information"> - <field name="amount"/> - <field name="company_currency" nolabel="1"/> + <label for="amoun"/> + <div> + <field name="amount" class="oe_inline"/> + <field name="company_currency" class="oe_inline"/> + </div> </group> <group col="4" string="Entry Information"> <field name="create_date"/> @@ -302,8 +305,8 @@ <field name="partner_id"/> <field name="communication"/> <field name="amount" sum="Amount Total"/> - <field name="amount_currency" sum="Currency Amount Total"/> - <field name="currency"/> + <field name="amount_currency" sum="Currency Amount Total" groups="base.group_multi_currency"/> + <field name="currency" groups="base.group_multi_currency"/> <field name="bank_id" domain="[('partner_id', '=', partner_id)]"/> <field name="move_line_id" on_change="onchange_move_line(move_line_id,parent.mode)"/> <field name="create_date"/> diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 223705af85e..f75cda8cdde 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1293,17 +1293,17 @@ class account_voucher(osv.osv): def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): message = "%s <b>created</b>." % self._document_type[obj.type or False] - self.message_append_note(cr, uid, [obj.id], body=message, context=context) + self.message_post(cr, uid, [obj.id], body=message, context=context) def post_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): message = "%s '%s' is <b>posted</b>." % (self._document_type[obj.type or False], obj.move_id.name) - self.message_append_note(cr, uid, [obj.id], body=message, context=context) + self.message_post(cr, uid, [obj.id], body=message, context=context) def reconcile_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): message = "%s <b>reconciled</b>." % self._document_type[obj.type or False] - self.message_append_note(cr, uid, [obj.id], body=message, context=context) + self.message_post(cr, uid, [obj.id], body=message, context=context) account_voucher() diff --git a/addons/account_voucher/account_voucher_data.xml b/addons/account_voucher/account_voucher_data.xml index 8be2851a434..656d2c2cd91 100644 --- a/addons/account_voucher/account_voucher_data.xml +++ b/addons/account_voucher/account_voucher_data.xml @@ -2,15 +2,16 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module eInvoicing & Payments has been installed.</value> - <value>OpenERP's electronic invoicing allows to ease and fasten the creation of invoices and collection of customer payments. Invoices are created in a few clicks and your customers receive them by email. They can pay online and/or import them in their own system. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">eInvoicing & Payments application installed!</field> + <field name="body">OpenERP's electronic invoicing accelerates the creation of invoices and collection of customer payments. Invoices are created in a few clicks and your customers receive them by email. They can pay online and/or import them in their own system. -You can track customer payments easily and automate the reminders. You get an overview of the discussion with your customers on each invoice to ensure a full traceability. +You can track customer payments easily and automate follow-ups. You get an overview of the discussion with your customers on each invoice for easier traceability. -If you want to use advanced accounting features, you should install the "Accounting and Finance" module.</value> - </function> +For advanced accounting features, you should install the "Accounting and Finance" module.</field> + </record> </data> </openerp> diff --git a/addons/account_voucher/account_voucher_view.xml b/addons/account_voucher/account_voucher_view.xml index 64e1cf22156..ba9d6433888 100644 --- a/addons/account_voucher/account_voucher_view.xml +++ b/addons/account_voucher/account_voucher_view.xml @@ -75,7 +75,7 @@ </group> <group string="Other Information"> <field name="number"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </group> <group col="4" attrs="{'invisible':[('type','in',['payment', 'receipt', False])]}"> <separator string="Total" colspan="4"/> diff --git a/addons/account_voucher/voucher_payment_receipt_view.xml b/addons/account_voucher/voucher_payment_receipt_view.xml index 503c3aeaa5e..c41ed6d5577 100644 --- a/addons/account_voucher/voucher_payment_receipt_view.xml +++ b/addons/account_voucher/voucher_payment_receipt_view.xml @@ -102,9 +102,9 @@ <field name="narration" nolabel="1" colspan="2"/> </group> <group string="Other Information" col="4"> - <field name="currency_id" colspan="4"/> + <field name="currency_id" colspan="4" groups="base.group_multi_currency"/> <field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/> - <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/> + <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/> <field name="paid_amount_in_company_currency" colspan="4" invisible="1"/> <field name="number" colspan="4"/> </group> @@ -194,7 +194,7 @@ <separator string="Currency Options" colspan="4"/> <field name="is_multi_currency" invisible="1"/> <field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/> - <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/> + <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/> <field name="paid_amount_in_company_currency" colspan="4" invisible="1"/> </group> <group col="2"> @@ -231,8 +231,8 @@ <field name="credit"/> <field name="state"/> <field name="reconcile_id"/> - <field name="amount_currency"/> - <field name="currency_id"/> + <field name="amount_currency" groups="base.group_multi_currency"/> + <field name="currency_id" groups="base.group_multi_currency"/> </tree> </field> </page> @@ -366,7 +366,7 @@ <group col="4" attrs="{'invisible':[('currency_id','=',False),('is_multi_currency','=',False)]}"> <field name="is_multi_currency" invisible="1"/> <field name="payment_rate" required="1" on_change="onchange_rate(payment_rate, amount, currency_id, payment_rate_currency_id, company_id, context)" colspan="3"/> - <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)"/> + <field name="payment_rate_currency_id" colspan="1" nolabel="1" on_change="onchange_payment_rate_currency(currency_id, payment_rate, payment_rate_currency_id, date, amount, company_id, context)" groups="base.group_multi_currency"/> <field name="paid_amount_in_company_currency" colspan="4" invisible="1"/> </group> <group> @@ -401,7 +401,7 @@ <field name="state"/> <field name="reconcile_id"/> <field name="amount_currency"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </tree> </field> </page> diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index 7eeb9266e47..d90e2006a71 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -297,7 +297,7 @@ class account_analytic_account(osv.osv): def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context) + self.message_post(cr, uid, [obj.id], body=_("Contract for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context) account_analytic_account() diff --git a/addons/analytic/analytic_view.xml b/addons/analytic/analytic_view.xml index c1e59cf3ba3..704d9bb8244 100644 --- a/addons/analytic/analytic_view.xml +++ b/addons/analytic/analytic_view.xml @@ -8,12 +8,18 @@ <field name="arch" type="xml"> <form string="Analytic Account" version="7.0"> <sheet string="Analytic Account"> + <div class="oe_right oe_button_box" name="buttons"> + </div> + + <div class="oe_title"> <label for="name" class="oe_edit_only"/> - <h1> - <field name="name" class="oe_inline"/> - </h1> - <div name="project"/> - <group> + <h1> + <field name="name" class="oe_inline"/> + </h1> + <div name="project"/> + </div> + + <group name="main"> <group> <field name="partner_id" on_change="on_change_partner_id(partner_id, name)" attrs="{'required':[('type','=','contract')]}"/> <field name="manager_id"/> diff --git a/addons/audittrail/audittrail.py b/addons/audittrail/audittrail.py index aee9fbd9305..3dff7c1cb00 100644 --- a/addons/audittrail/audittrail.py +++ b/addons/audittrail/audittrail.py @@ -25,6 +25,7 @@ from tools.translate import _ import pooler import time import tools +from openerp import SUPERUSER_ID class audittrail_rule(osv.osv): """ @@ -252,10 +253,10 @@ class audittrail_objects_proxy(object_proxy): pool = pooler.get_pool(cr.dbname) resource_pool = pool.get(model) model_pool = pool.get('ir.model') - model_ids = model_pool.search(cr, 1, [('model', '=', model)]) + model_ids = model_pool.search(cr, SUPERUSER_ID, [('model', '=', model)]) model_id = model_ids and model_ids[0] or False assert model_id, _("'%s' Model does not exist..." %(model)) - model = model_pool.browse(cr, 1, model_id) + model = model_pool.browse(cr, SUPERUSER_ID, model_id) # fields to log. currently only used by log on read() field_list = [] @@ -322,7 +323,7 @@ class audittrail_objects_proxy(object_proxy): data = {} resource_pool = pool.get(model.model) # read all the fields of the given resources in super admin mode - for resource in resource_pool.read(cr, 1, res_ids): + for resource in resource_pool.read(cr, SUPERUSER_ID, res_ids): values = {} values_text = {} resource_id = resource['id'] @@ -332,19 +333,19 @@ class audittrail_objects_proxy(object_proxy): continue values[field] = resource[field] # get the textual value of that field for this record - values_text[field] = self.get_value_text(cr, 1, pool, resource_pool, method, field, resource[field]) + values_text[field] = self.get_value_text(cr, SUPERUSER_ID, pool, resource_pool, method, field, resource[field]) field_obj = resource_pool._all_columns.get(field).column if field_obj._type in ('one2many','many2many'): # check if an audittrail rule apply in super admin mode - if self.check_rules(cr, 1, field_obj._obj, method): + if self.check_rules(cr, SUPERUSER_ID, field_obj._obj, method): # check if the model associated to a *2m field exists, in super admin mode - x2m_model_ids = pool.get('ir.model').search(cr, 1, [('model', '=', field_obj._obj)]) + x2m_model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', field_obj._obj)]) x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj)) - x2m_model = pool.get('ir.model').browse(cr, 1, x2m_model_id) + x2m_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, x2m_model_id) #recursive call on x2m fields that need to be checked too - data.update(self.get_data(cr, 1, pool, resource[field], x2m_model, method)) + data.update(self.get_data(cr, SUPERUSER_ID, pool, resource[field], x2m_model, method)) data[(model.id, resource_id)] = {'text':values_text, 'value': values} return data @@ -389,12 +390,12 @@ class audittrail_objects_proxy(object_proxy): field_obj = field_definition.column if field_obj._type in ('one2many','many2many'): # checking if an audittrail rule apply in super admin mode - if self.check_rules(cr, 1, field_obj._obj, method): + if self.check_rules(cr, SUPERUSER_ID, field_obj._obj, method): # checking if the model associated to a *2m field exists, in super admin mode - x2m_model_ids = pool.get('ir.model').search(cr, 1, [('model', '=', field_obj._obj)]) + x2m_model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', field_obj._obj)]) x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj)) - x2m_model = pool.get('ir.model').browse(cr, 1, x2m_model_id) + x2m_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, x2m_model_id) # the resource_ids that need to be checked are the sum of both old and previous values (because we # need to log also creation or deletion in those lists). x2m_old_values_ids = old_values.get(key, {'value': {}})['value'].get(field_name, []) @@ -402,7 +403,7 @@ class audittrail_objects_proxy(object_proxy): # We use list(set(...)) to remove duplicates. res_ids = list(set(x2m_old_values_ids + x2m_new_values_ids)) for res_id in res_ids: - lines.update(self.prepare_audittrail_log_line(cr, 1, pool, x2m_model, res_id, method, old_values, new_values, field_list)) + lines.update(self.prepare_audittrail_log_line(cr, SUPERUSER_ID, pool, x2m_model, res_id, method, old_values, new_values, field_list)) # if the value value is different than the old value: record the change if key not in old_values or key not in new_values or old_values[key]['value'][field_name] != new_values[key]['value'][field_name]: data = { @@ -459,9 +460,9 @@ class audittrail_objects_proxy(object_proxy): vals.update({'method': 'unlink'}) # create the audittrail log in super admin mode, only if a change has been detected if lines[(model_id, resource_id)]: - log_id = pool.get('audittrail.log').create(cr, 1, vals) + log_id = pool.get('audittrail.log').create(cr, SUPERUSER_ID, vals) model = pool.get('ir.model').browse(cr, uid, model_id) - self.create_log_line(cr, 1, log_id, model, lines[(model_id, resource_id)]) + self.create_log_line(cr, SUPERUSER_ID, log_id, model, lines[(model_id, resource_id)]) return True def check_rules(self, cr, uid, model, method): @@ -475,11 +476,11 @@ class audittrail_objects_proxy(object_proxy): """ pool = pooler.get_pool(cr.dbname) if 'audittrail.rule' in pool.models: - model_ids = pool.get('ir.model').search(cr, 1, [('model', '=', model)]) + model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', model)]) model_id = model_ids and model_ids[0] or False if model_id: - rule_ids = pool.get('audittrail.rule').search(cr, 1, [('object_id', '=', model_id), ('state', '=', 'subscribed')]) - for rule in pool.get('audittrail.rule').read(cr, 1, rule_ids, ['user_id','log_read','log_write','log_create','log_unlink','log_action','log_workflow']): + rule_ids = pool.get('audittrail.rule').search(cr, SUPERUSER_ID, [('object_id', '=', model_id), ('state', '=', 'subscribed')]) + for rule in pool.get('audittrail.rule').read(cr, SUPERUSER_ID, rule_ids, ['user_id','log_read','log_write','log_create','log_unlink','log_action','log_workflow']): if len(rule['user_id']) == 0 or uid in rule['user_id']: if rule.get('log_'+method,0): return True diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index 2959fa45db1..5d939f19db1 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -7,6 +7,7 @@ import werkzeug.utils import openerp.modules.registry import openerp.addons.web.controllers.main import openerp.addons.web.common.http as openerpweb +from openerp import SUPERUSER_ID _logger = logging.getLogger(__name__) @@ -18,7 +19,7 @@ class OAuthController(openerpweb.Controller): registry = openerp.modules.registry.RegistryManager.get(dbname) with registry.cursor() as cr: providers = registry.get('auth.oauth.provider') - l = providers.read(cr, 1, providers.search(cr, 1, [('enabled','=',True)])) + l = providers.read(cr, SUPERUSER_ID, providers.search(cr, SUPERUSER_ID, [('enabled','=',True)])) return l @openerpweb.httprequest @@ -30,7 +31,7 @@ class OAuthController(openerpweb.Controller): with registry.cursor() as cr: try: u = registry.get('res.users') - credentials = u.auth_oauth(cr, 1, provider, kw) + credentials = u.auth_oauth(cr, SUPERUSER_ID, provider, kw) cr.commit() return openerp.addons.web.controllers.main.login_and_redirect(req, *credentials) except AttributeError: diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index 3dbab849942..b4f62612746 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -6,6 +6,7 @@ import simplejson import openerp from openerp.osv import osv, fields +from openerp import SUPERUSER_ID _logger = logging.getLogger(__name__) @@ -63,7 +64,7 @@ class res_users(osv.Model): try: return super(res_users, self).check_credentials(cr, uid, password) except openerp.exceptions.AccessDenied: - res = self.search(cr, 1, [('id','=',uid),('oauth_access_token','=',password)]) + res = self.search(cr, SUPERUSER_ID, [('id','=',uid),('oauth_access_token','=',password)]) if not res: raise diff --git a/addons/auth_openid/controllers/main.py b/addons/auth_openid/controllers/main.py index eba711dae7a..ebcaab4bf25 100644 --- a/addons/auth_openid/controllers/main.py +++ b/addons/auth_openid/controllers/main.py @@ -23,6 +23,7 @@ import logging import os import tempfile import urllib +from openerp import SUPERUSER_ID import werkzeug.urls import werkzeug.exceptions @@ -176,7 +177,7 @@ class OpenIDController(openerpweb.Controller): with registry.cursor() as cr: Modules = registry.get('ir.module.module') - installed = Modules.search_count(cr, 1, ['&', ('name', '=', 'auth_openid'), ('state', '=', 'installed')]) == 1 + installed = Modules.search_count(cr, SUPERUSER_ID, ['&', ('name', '=', 'auth_openid'), ('state', '=', 'installed')]) == 1 if installed: Users = registry.get('res.users') @@ -196,13 +197,13 @@ class OpenIDController(openerpweb.Controller): domain += [('openid_url', '=', openid_url), ('active', '=', True)] - ids = Users.search(cr, 1, domain) + ids = Users.search(cr, SUPERUSER_ID, domain) assert len(ids) < 2 if ids: user_id = ids[0] - login = Users.browse(cr, 1, user_id).login + login = Users.browse(cr, SUPERUSER_ID, user_id).login key = randomString(utils.KEY_LENGTH, '0123456789abcdef') - Users.write(cr, 1, [user_id], {'openid_key': key}) + Users.write(cr, SUPERUSER_ID, [user_id], {'openid_key': key}) # TODO fill empty fields with the ones from sreg/ax cr.commit() diff --git a/addons/auth_openid/static/src/xml/auth_openid.xml b/addons/auth_openid/static/src/xml/auth_openid.xml index aa61d161657..6a40439a012 100644 --- a/addons/auth_openid/static/src/xml/auth_openid.xml +++ b/addons/auth_openid/static/src/xml/auth_openid.xml @@ -4,7 +4,7 @@ <t t-extend="Login"> <t t-jquery=".oe_login .oe_login_logo" t-operation="after"> - <ul class="openid_providers"> + <ul class="openid_providers oe_semantic_html_override"> <li><a href="#login,password" title="Password" data-url="" id="btn_password">Password</a></li> <li><a href="#google" title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li> <li><a href="#googleapps" title="Google Apps" data-url="https://www.google.com/accounts/o8/site-xrds?hd={id}">Google</a></li> diff --git a/addons/auth_reset_password/auth_reset_password.py b/addons/auth_reset_password/auth_reset_password.py index 3d1bcbbfa7b..ff208c89b9c 100644 --- a/addons/auth_reset_password/auth_reset_password.py +++ b/addons/auth_reset_password/auth_reset_password.py @@ -6,6 +6,7 @@ import urlparse from openerp.tools import config from openerp.osv import osv, fields +from openerp import SUPERUSER_ID TWENTY_FOUR_HOURS = 24 * 60 * 60 @@ -76,9 +77,9 @@ class res_users(osv.osv): MailMessage.send(cr, uid, [msg_id], context=context) def send_reset_password_request(self, cr, uid, email, context=None): - ids = self.pool.get('res.users').search(cr, 1, [('user_email', '=', email)], context=context) + ids = self.pool.get('res.users').search(cr, SUPERUSER_ID, [('user_email', '=', email)], context=context) if ids: - self._auth_reset_password_send_email(cr, 1, email, 'reset_password_email', ids[0], context=context) + self._auth_reset_password_send_email(cr, SUPERUSER_ID, email, 'reset_password_email', ids[0], context=context) return True #else: # _m, company_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'main_company') @@ -109,7 +110,7 @@ class auth_reset_password(osv.TransientModel): Users = self.pool.get('res.users') data = Users._auth_reset_password_check_token(cr, uid, values.get('token', '')) if data: - Users.write(cr, 1, data['uid'], {'password': pw}, context=context) + Users.write(cr, SUPERUSER_ID, data['uid'], {'password': pw}, context=context) else: raise osv.except_osv('Error', 'Invalid token') diff --git a/addons/auth_reset_password/auth_reset_password.xml b/addons/auth_reset_password/auth_reset_password.xml index 26ea8878c45..b7d7e0baef6 100644 --- a/addons/auth_reset_password/auth_reset_password.xml +++ b/addons/auth_reset_password/auth_reset_password.xml @@ -9,16 +9,13 @@ <field name="email_from"><![CDATA[${object.company_id.name} <${object.company_id.email}>]]></field> <field name="email_to" eval="False"><!--(set by reset_password module)--></field> <field name="subject">Password reset</field> - <field name="body_text"><![CDATA[ -A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()} + <field name="body_html"><![CDATA[ +<p>A password reset was requested the OpenERP account linked to this email on ${object._auth_reset_password_host()}</p> -You may change your password following this link: +<p>You may change your password following this <a href="${object._auth_reset_password_link()}">link</a>, +or by copy-pasting the following URL in your browser: ${object._auth_reset_password_link()}</p> -${object._auth_reset_password_link()} - -If you don't have asked for password reset, you can safely ignore this email. - -]]></field> +<p>Note: If you did not ask for a password reset, you can safely ignore this email.</p>]]></field> </record> <!-- TODO get own css --> diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 0ed2151d12b..821ef44f459 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -5,6 +5,7 @@ import werkzeug.urls from openerp.modules.registry import RegistryManager from openerp.addons.web.controllers.main import login_and_redirect import openerp.addons.web.common.http as openerpweb +from openerp import SUPERUSER_ID _logger = logging.getLogger(__name__) @@ -18,7 +19,7 @@ class OpenIDController(openerpweb.Controller): with registry.cursor() as cr: try: Users = registry.get('res.users') - credentials = Users.auth_signup(cr, 1, name, login, password) + credentials = Users.auth_signup(cr, SUPERUSER_ID, name, login, password) cr.commit() return login_and_redirect(req, *credentials) except AttributeError: diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 1d77a3f9473..5f6463508b6 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -1,5 +1,6 @@ import openerp from openerp.osv import osv +from openerp import SUPERUSER_ID class res_users(osv.Model): _inherit = 'res.users' @@ -15,9 +16,9 @@ class res_users(osv.Model): # user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0) if user_template_id: - self.pool.get('res.users').copy(cr, 1, user_template_id, new_user, context=context) + self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context) else: - self.pool.get('res.users').create(cr, 1, new_user, context=context) + self.pool.get('res.users').create(cr, SUPERUSER_ID, new_user, context=context) def auth_signup(self, cr, uid, name, login, password, context=None): r = (cr.dbname, login, password) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index b6eacda63c0..7e9bbacab13 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -302,33 +302,27 @@ the rule to mark CC(mail to any other person defined in actions)."), return self.format_body(body % data) def email_send(self, cr, uid, obj, emails, body, emailfrom=None, context=None): - """ send email - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param email: pass the emails - @param emailfrom: Pass name the email From else False - @param context: A standard dictionary for contextual values """ - if not emailfrom: - emailfrom = tools.config.get('email_from', False) - - if context is None: - context = {} - - mail_message = self.pool.get('mail.message') + emailfrom = tools.config.get('email_from') body = self.format_mail(obj, body) - if not emailfrom: - if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email: - emailfrom = obj.user_id.email - - name = '[%d] %s' % (obj.id, tools.ustr(obj.name)) + if not emailfrom and hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email: + emailfrom = obj.user_id.email emailfrom = tools.ustr(emailfrom) reply_to = emailfrom if not emailfrom: raise osv.except_osv(_('Error!'), - _("No email ID found for your company address.")) - return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model='base.action.rule', reply_to=reply_to, res_id=obj.id) + _("Missing default email address or missing email on responsible user")) + return self.pool.get('mail.mail').create(cr, uid, + { 'email_from': emailfrom, + 'email_to': emails.join(','), + 'reply_to': reply_to, + 'state': 'outgoing', + 'subject': '[%d] %s' % (obj.id, tools.ustr(obj.name)), + 'body_html': '<pre>%s</pre>' % body, + 'res_id': obj.id, + 'model': obj._table_name, + 'auto_delete': True + }, context=context) def do_check(self, cr, uid, action, obj, context=None): @@ -438,11 +432,8 @@ the rule to mark CC(mail to any other person defined in actions)."), if len(emails) and action.act_mail_body: emails = list(set(emails)) email_from = safe_eval(action.act_email_from, {}, locals_for_emails) - - def to_email(text): - return re.findall(r'([^ ,<@]+@[^> ,]+)', text or '') - emails = to_email(','.join(filter(None, emails))) - email_froms = to_email(email_from) + emails = tools.email_split(','.join(filter(None, emails))) + email_froms = tools.email_split(email_from) if email_froms: self.email_send(cr, uid, obj, emails, action.act_mail_body, emailfrom=email_froms[0]) return True diff --git a/addons/base_action_rule/i18n/nb.po b/addons/base_action_rule/i18n/nb.po new file mode 100644 index 00000000000..7aae94c417f --- /dev/null +++ b/addons/base_action_rule/i18n/nb.po @@ -0,0 +1,536 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-03 16:43+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_user:0 +msgid "" +"Check this if you want the rule to send an email to the responsible person." +msgstr "" +"Sjekk dette hvis du vil at regelen skal sende en e-post til ansvarlig person." + +#. module: base_action_rule +#: field:base.action.rule,act_remind_partner:0 +msgid "Remind Partner" +msgstr "Påminn partner" + +#. module: base_action_rule +#: field:base.action.rule,trg_partner_categ_id:0 +msgid "Partner Category" +msgstr "Partner Kategori" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_watchers:0 +msgid "Mail to Watchers (CC)" +msgstr "Post til overvåkere (CC)" + +#. module: base_action_rule +#: field:base.action.rule,trg_state_to:0 +msgid "Button Pressed" +msgstr "Knapp trykket" + +#. module: base_action_rule +#: field:base.action.rule,model_id:0 +msgid "Object" +msgstr "Objekt" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_email:0 +msgid "Mail to these Emails" +msgstr "Send mail til disse e-postene" + +#. module: base_action_rule +#: field:base.action.rule,act_state:0 +msgid "Set State to" +msgstr "Still stat til" + +#. module: base_action_rule +#: field:base.action.rule,act_email_from:0 +msgid "Email From" +msgstr "E-post fra" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Body" +msgstr "E-post kropp" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Days" +msgstr "Dager" + +#. module: base_action_rule +#: field:base.action.rule,last_run:0 +msgid "Last Run" +msgstr "Siste kjøring" + +#. module: base_action_rule +#: code:addons/base_action_rule/base_action_rule.py:328 +#, python-format +msgid "Error!" +msgstr "Feil!" + +#. module: base_action_rule +#: field:base.action.rule,act_reply_to:0 +msgid "Reply-To" +msgstr "Svar til" + +#. module: base_action_rule +#: help:base.action.rule,act_email_cc:0 +msgid "" +"These people will receive a copy of the future communication between partner " +"and users by email" +msgstr "" +"Disse menneskene vil motta en kopi av den fremtidige kommunikasjon mellom " +"partner og brukere av e-post" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Minutes" +msgstr "Minutter" + +#. module: base_action_rule +#: field:base.action.rule,name:0 +msgid "Rule Name" +msgstr "Regelnavn" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_partner:0 +msgid "" +"Check this if you want the rule to send a reminder by email to the partner." +msgstr "" +"Sjekk dette hvis du vil at regelen skal sende en påminnelse via e-post til " +"partneren." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Model Partner" +msgstr "Forholdene på Modell Partner" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Deadline" +msgstr "Frist" + +#. module: base_action_rule +#: field:base.action.rule,trg_partner_id:0 +msgid "Partner" +msgstr "Partner" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_subject)s = Object subject" +msgstr "%(object_subject)s = Object subject" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Reminders" +msgstr "E-post påminnelser" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Special Keywords to be Used in the Body" +msgstr "" + +#. module: base_action_rule +#: field:base.action.rule,trg_state_from:0 +msgid "State" +msgstr "Stat" + +#. module: base_action_rule +#: model:ir.actions.act_window,help:base_action_rule.base_action_rule_act +msgid "" +"Use automated actions to automatically trigger actions for various screens. " +"Example: a lead created by a specific user may be automatically set to a " +"specific sales team, or an opportunity which still has status pending after " +"14 days might trigger an automatic reminder email." +msgstr "" +"Bruke automatiserte tiltak for å automatisk utløse tiltak for ulike " +"skjermer. Eksempel: en leder er opprettet av en bestemt bruker kan bli satt " +"automatisk til en bestemt salgsteam, eller en mulighet som fortsatt har " +"status påvente etter 14 dager kan utløse en automatisk påminnelse e-post." + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_email:0 +msgid "Email-id of the persons whom mail is to be sent" +msgstr "E-post ID av personer som post skal sendes" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Action Rule" +msgstr "Handling regel" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Fields to Change" +msgstr "Felter å endre" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Creation Date" +msgstr "Opprettelsesdato" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Last Action Date" +msgstr "Siste handlingsdato" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Hours" +msgstr "Timer" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_id)s = Object ID" +msgstr "%(object_ID)s = Object ID" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Delay After Trigger Date" +msgstr "Forsinkelse Etter utløser Dato" + +#. module: base_action_rule +#: field:base.action.rule,act_remind_attach:0 +msgid "Remind with Attachment" +msgstr "Minn med vedlegg" + +#. module: base_action_rule +#: constraint:ir.cron:0 +msgid "Invalid arguments" +msgstr "Ugyldige argumenter" + +#. module: base_action_rule +#: field:base.action.rule,act_user_id:0 +msgid "Set Responsible to" +msgstr "Satt Ansvarlig for å" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "None" +msgstr "Ingen" + +#. module: base_action_rule +#: help:base.action.rule,act_email_to:0 +msgid "" +"Use a python expression to specify the right field on which one than we will " +"use for the 'To' field of the header" +msgstr "" +"Bruk en python uttrykk for å angi høyre feltet på hvilken enn vi vil bruke " +"for Til-feltet på header." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user_phone)s = Responsible phone" +msgstr "% (object_bruker_telefonen) s = Ansvarlig telefon" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "" +"The rule uses the AND operator. The model must match all non-empty fields so " +"that the rule executes the action described in the 'Actions' tab." +msgstr "" +"Regelen bruker AND-operatoren. Modellen må matche alle-ikke tomme felt, slik " +"at regelen utfører handlingen som er beskrevet i \"Handlinger\"-fanen." + +#. module: base_action_rule +#: field:base.action.rule,trg_date_range_type:0 +msgid "Delay type" +msgstr "forsinkelse typen" + +#. module: base_action_rule +#: help:base.action.rule,regex_name:0 +msgid "" +"Regular expression for matching name of the resource\n" +"e.g.: 'urgent.*' will search for records having name starting with the " +"string 'urgent'\n" +"Note: This is case sensitive search." +msgstr "" +"Regulært uttrykk for matchende navnet på ressursen\n" +"f.eks: \". haster * 'vil søke etter poster som har navn som starter med " +"strengen\" haster \"\n" +"Merk: Dette er små bokstaver søk." + +#. module: base_action_rule +#: field:base.action.rule,act_method:0 +msgid "Call Object Method" +msgstr "Kall objektmetode" + +#. module: base_action_rule +#: field:base.action.rule,act_email_to:0 +msgid "Email To" +msgstr "E-post til." + +#. module: base_action_rule +#: help:base.action.rule,act_mail_to_watchers:0 +msgid "" +"Check this if you want the rule to mark CC(mail to any other person defined " +"in actions)." +msgstr "" +"Sjekk dette hvis du vil at regelen skal merkes CC (mail til en annen person " +"som er definert i handlinger)." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(partner)s = Partner name" +msgstr "%(partner)s = Navn på partner" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Note" +msgstr "Notat" + +#. module: base_action_rule +#: help:base.action.rule,act_email_from:0 +msgid "" +"Use a python expression to specify the right field on which one than we will " +"use for the 'From' field of the header" +msgstr "" +"Bruk en python uttrykk for å angi høyre feltet på hvilken enn vi vil bruke " +"for Fra-feltet på overskriften" + +#. module: base_action_rule +#: field:base.action.rule,trg_date_range:0 +msgid "Delay after trigger date" +msgstr "Forsinkelse etter triggerdato" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions" +msgstr "Betingelser" + +#. module: base_action_rule +#: help:base.action.rule,trg_date_range:0 +msgid "" +"Delay After Trigger Date,specifies you can put a negative number. If you " +"need a delay before the trigger date, like sending a reminder 15 minutes " +"before a meeting." +msgstr "" +"Forsinkelse Etter utløser Dato, spesifiserer du kan sette et negativt tall. " +"Hvis du trenger en forsinkelse før avtrekkeren dato, som å sende en " +"påminnelse 15 minutter før et møte." + +#. module: base_action_rule +#: field:base.action.rule,active:0 +msgid "Active" +msgstr "Aktiv" + +#. module: base_action_rule +#: code:addons/base_action_rule/base_action_rule.py:329 +#, python-format +msgid "No Email ID Found for your Company address!" +msgstr "" + +#. module: base_action_rule +#: field:base.action.rule,act_remind_user:0 +msgid "Remind Responsible" +msgstr "Minn Ansvarlig" + +#. module: base_action_rule +#: help:base.action.rule,sequence:0 +msgid "Gives the sequence order when displaying a list of rules." +msgstr "Gir sekvens ordre når du viser en liste over regler." + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_range_type:0 +msgid "Months" +msgstr "Måneder" + +#. module: base_action_rule +#: field:base.action.rule,filter_id:0 +msgid "Filter" +msgstr "Filtrer" + +#. module: base_action_rule +#: selection:base.action.rule,trg_date_type:0 +msgid "Date" +msgstr "Dato" + +#. module: base_action_rule +#: help:base.action.rule,server_action_id:0 +msgid "" +"Describes the action name.\n" +"eg:on which object which action to be taken on basis of which condition" +msgstr "" +"Beskriver handlingens navn.\n" +"f.eks: på hvilket objekt som tiltak som skal iverksettes på grunnlag av " +"hvilken tilstand" + +#. module: base_action_rule +#: model:ir.model,name:base_action_rule.model_ir_cron +msgid "ir.cron" +msgstr "ir.actions.actions" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_description)s = Object description" +msgstr "% (object_beskrivelse) s = Object beskrivelse" + +#. module: base_action_rule +#: constraint:base.action.rule:0 +msgid "Error: The mail is not well formated" +msgstr "Feil: E-posten er ikke godt nok formatert" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Actions" +msgstr "E-post handlinger" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Email Information" +msgstr "E-post Informasjon" + +#. module: base_action_rule +#: model:ir.model,name:base_action_rule.model_base_action_rule +msgid "Action Rules" +msgstr "Handlingsregler" + +#. module: base_action_rule +#: help:base.action.rule,act_mail_body:0 +msgid "Content of mail" +msgstr "Innholdet av post" + +#. module: base_action_rule +#: field:base.action.rule,trg_user_id:0 +msgid "Responsible" +msgstr "Ansvarlig" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(partner_email)s = Partner Email" +msgstr "% (partner_e-post) s = Partner E-post" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_date)s = Creation date" +msgstr "%(object_dato)s = opprettelsesdato" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user_email)s = Responsible Email" +msgstr "% (object_brukerens_e-post) s = Ansvarlig e-post" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_body:0 +msgid "Mail body" +msgstr "Mail kropp" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_user:0 +msgid "" +"Check this if you want the rule to send a reminder by email to the user." +msgstr "" +"Kryss av her hvis du vil at regelen skal sende en påminnelse til brukeren " +"via e-post." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Server Action to be Triggered" +msgstr "Server Tiltak som skal Utløses" + +#. module: base_action_rule +#: field:base.action.rule,act_mail_to_user:0 +msgid "Mail to Responsible" +msgstr "send mail til ansvarlig" + +#. module: base_action_rule +#: field:base.action.rule,act_email_cc:0 +msgid "Add Watchers (Cc)" +msgstr "Legg til overvåkere (Cc)" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Model Fields" +msgstr "Forholdene på Modell Felter" + +#. module: base_action_rule +#: model:ir.actions.act_window,name:base_action_rule.base_action_rule_act +#: model:ir.ui.menu,name:base_action_rule.menu_base_action_rule_form +msgid "Automated Actions" +msgstr "Automatiserte handinger" + +#. module: base_action_rule +#: field:base.action.rule,server_action_id:0 +msgid "Server Action" +msgstr "Tjenerhandling" + +#. module: base_action_rule +#: field:base.action.rule,regex_name:0 +msgid "Regex on Resource Name" +msgstr "Regex på Ressursnavn" + +#. module: base_action_rule +#: help:base.action.rule,act_remind_attach:0 +msgid "" +"Check this if you want that all documents attached to the object be attached " +"to the reminder email sent." +msgstr "" +"Kryss av her om du vil at alle dokumenter knyttet til objektet festes til " +"påminnelse e-post sendt." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on Timing" +msgstr "Vilkår for timing" + +#. module: base_action_rule +#: field:base.action.rule,sequence:0 +msgid "Sequence" +msgstr "Sekvens" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Actions" +msgstr "Handlinger" + +#. module: base_action_rule +#: help:base.action.rule,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the rule " +"without removing it." +msgstr "" +"Hvis det aktive feltet er satt til False, vil det tillate deg å skjule " +"regelen uten å fjerne den." + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "%(object_user)s = Responsible name" +msgstr "% (object_bruker) s = Ansvarlig navn" + +#. module: base_action_rule +#: field:base.action.rule,create_date:0 +msgid "Create Date" +msgstr "Opprettet dato" + +#. module: base_action_rule +#: view:base.action.rule:0 +msgid "Conditions on States" +msgstr "Vilkår for tilstander" + +#. module: base_action_rule +#: field:base.action.rule,trg_date_type:0 +msgid "Trigger Date" +msgstr "Uttløser dato" + +#~ msgid "Special Keywords to Be Used in The Body" +#~ msgstr "Spesielle nøkkelord for bruk i meldingsinnhold" + +#, python-format +#~ msgid "No E-Mail ID Found for your Company address!" +#~ msgstr "Ingen E-post ID funnet for din firmaadresse!" diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index be3244f8a5b..a6d5f6859af 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -471,18 +471,10 @@ property or property parameter."), def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context=None): """ Send mail for event invitation to event attendees. - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of attendee’s IDs. @param email_from: Email address for user sending the mail - @param context: A standard dictionary for contextual values @return: True """ - if context is None: - context = {} - company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.name - mail_message = self.pool.get('mail.message') for att in self.browse(cr, uid, ids, context=context): sign = att.sent_by_uid and att.sent_by_uid.signature or '' sign = '<br>'.join(sign and sign.split('\n') or []) @@ -508,17 +500,18 @@ property or property parameter."), } body = html_invitation % body_vals if mail_to and email_from: - attach = self.get_ics_file(cr, uid, res_obj, context=context) - mail_message.schedule_with_attach(cr, uid, - email_from, - mail_to, - sub, - body, - attachments=attach and {'invitation.ics': attach} or None, - content_subtype='html', - reply_to=email_from, - context=context - ) + ics_file = self.get_ics_file(cr, uid, res_obj, context=context) + vals = {'email_from': email_from, + 'email_to': mail_to, + 'state': 'outgoing', + 'subject': sub, + 'body_html': body, + 'auto_delete': True} + if ics_file: + vals['attachment_ids'] = [(0,0,{'name': 'invitation.ics', + 'datas_fname': 'invitation.ics', + 'datas': str(ics_file).encode('base64')})] + self.pool.get('mail.mail').create(cr, uid, vals, context=context) return True def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv): @@ -812,7 +805,6 @@ class calendar_alarm(osv.osv): """ if context is None: context = {} - mail_message = self.pool.get('mail.message') current_datetime = datetime.now() alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context) @@ -849,36 +841,10 @@ class calendar_alarm(osv.osv): else: re_dates = [alarm.trigger_date] - for r_date in re_dates: - ref = alarm.model_id.model + ',' + str(alarm.res_id) - - # search for alreay sent requests - #if request_obj.search(cr, uid, [('trigger_date', '=', r_date), ('ref_doc1', '=', ref)], context=context): - #continue - - # Deactivated because of the removing of res.request - # TODO: when cleaning calendar module, re-add this in a new mechanism - #if alarm.action == 'display': - #value = { - #'name': alarm.name, - #'act_from': alarm.user_id.id, - #'act_to': alarm.user_id.id, - #'body': alarm.description, - #'trigger_date': r_date, - #'ref_doc1': ref - #} - #request_id = request_obj.create(cr, uid, value) - #request_ids = [request_id] - #for attendee in res_obj.attendee_ids: - #if attendee.user_id: - #value['act_to'] = attendee.user_id.id - #request_id = request_obj.create(cr, uid, value) - #request_ids.append(request_id) - #request_obj.request_send(cr, uid, request_ids) - + if re_dates: if alarm.action == 'email': - sub = '[Openobject Reminder] %s' % (alarm.name) - body = """ + sub = '[OpenERP Reminder] %s' % (alarm.name) + body = """<pre> Event: %s Event Date: %s Description: %s @@ -888,20 +854,21 @@ From: ---- %s - +</pre> """ % (alarm.name, alarm.trigger_date, alarm.description, \ alarm.user_id.name, alarm.user_id.signature) mail_to = [alarm.user_id.email] for att in alarm.attendee_ids: mail_to.append(att.user_id.email) if mail_to: - mail_message.schedule_with_attach(cr, uid, - tools.config.get('email_from', False), - mail_to, - sub, - body, - context=context - ) + vals = { + 'state': 'outgoing', + 'subject': sub, + 'body_html': body, + 'email_to': mail_to, + 'email_from': tools.config.get('email_from', mail_to), + } + self.pool.get('mail.mail').create(cr, uid, vals, context=context) if next_trigger_date: update_vals.update({'trigger_date': next_trigger_date}) else: @@ -1616,36 +1583,6 @@ class calendar_todo(osv.osv): calendar_todo() -class ir_attachment(osv.osv): - _name = 'ir.attachment' - _inherit = 'ir.attachment' - - def search_count(self, cr, user, args, context=None): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search_count(cr, user, new_args, context) - - def create(self, cr, uid, vals, context=None): - if context: - id = context.get('default_res_id', False) - context.update({'default_res_id' : base_calendar_id2real_id(id)}) - return super(ir_attachment, self).create(cr, uid, vals, context=context) - - def search(self, cr, uid, args, offset=0, limit=None, order=None, - context=None, count=False): - new_args = [] - for domain_item in args: - if isinstance(domain_item, (list, tuple)) and len(domain_item) == 3 and domain_item[0] == 'res_id': - new_args.append((domain_item[0], domain_item[1], base_calendar_id2real_id(domain_item[2]))) - else: - new_args.append(domain_item) - return super(ir_attachment, self).search(cr, uid, new_args, offset=offset, - limit=limit, order=order, context=context, count=False) -ir_attachment() class ir_values(osv.osv): _inherit = 'ir.values' diff --git a/addons/base_calendar/crm_meeting.py b/addons/base_calendar/crm_meeting.py index cb96ff2a4d0..e1c66148ae7 100644 --- a/addons/base_calendar/crm_meeting.py +++ b/addons/base_calendar/crm_meeting.py @@ -43,7 +43,7 @@ class crm_meeting(base_state, osv.Model): _name = 'crm.meeting' _description = "Meeting" _order = "id desc" - _inherit = ["calendar.event", 'ir.needaction_mixin', "mail.thread"] + _inherit = ["calendar.event", "mail.thread", 'ir.needaction_mixin'] _columns = { # base_state required fields 'create_date': fields.datetime('Creation Date', readonly=True), @@ -70,13 +70,17 @@ class crm_meeting(base_state, osv.Model): # OpenChatter # ---------------------------------------- + # shows events of the day for this user + def needaction_domain_get(self, cr, uid, domain=[], context={}): + return [('date','<=',time.strftime('%Y-%M-%D 23:59:59')), ('date_deadline','>=', time.strftime('%Y-%M-%D 00:00:00')), ('user_id','=',uid)] + def case_get_note_msg_prefix(self, cr, uid, id, context=None): return 'Meeting' def case_open_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>confirmed</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting <b>confirmed</b>."), context=context) def case_close_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>done</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Meeting <b>completed</b>."), context=context) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index 6009251a460..928f8d94d5c 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -170,7 +170,8 @@ <page string="Invitation Detail"> <button string="Invite People" name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d" - icon="terp-partner" type="action" + type="action" + attrs="{'readonly': [('state', '=', 'done')]}" context="{'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}" colspan="2"/> <field name="attendee_ids" widget="one2many" mode="tree"> <tree string="Invitation details" editable="top"> @@ -182,17 +183,16 @@ <button name="do_tentative" states="needs-action,declined,accepted" string="Uncertain" type="object" - icon="terp-crm" /> + /> <button name="do_accept" string="Accept" states="needs-action,tentative,declined" - type="object" icon="gtk-apply" /> + type="object" /> <button name="do_decline" string="Decline" states="needs-action,tentative,accepted" - type="object" icon="gtk-cancel" /> + type="object" /> <button name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d" string="Delegate" type="action" - icon="gtk-sort-descending" states="needs-action,tentative,declined,accepted" context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" /> </tree> @@ -200,16 +200,16 @@ <header> <button name="do_tentative" type="object" states="needs-action,declined,accepted" - string="Uncertain" icon="terp-crm" /> + string="Uncertain" /> <button name="do_accept" type="object" states="needs-action,tentative,declined" - string="Accept" icon="gtk-apply" /> + string="Accept" /> <button name="do_decline" type="object" states="needs-action,tentative,accepted" - string="Decline" icon="gtk-cancel" /> + string="Decline" /> <button name="%(base_calendar.action_view_calendar_invite_attendee_wizard)d" type="action" states="needs-action,tentative,declined,accepted" - string="Delegate" icon="gtk-sort-descending" + string="Delegate" context="{'model' : 'calendar.attendee', 'attendee_field' : 'child_ids'}" /> <field name="state" widget="statusbar" statusbar_visible="draft,open,done"/> </header> @@ -244,13 +244,13 @@ <field name="name">CRM - Meetings Tree</field> <field name="model">crm.meeting</field> <field name="arch" type="xml"> - <tree string="Meetings" fonts="bold:needaction_pending==True"> + <tree string="Meetings" fonts="bold:message_unread==True"> <field name="name" string="Subject" /> <field name="user_id"/> <field name="date"/> <field name="state" invisible="True"/> <field name="duration" /> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> </tree> </field> </record> @@ -287,9 +287,9 @@ <field name="arch" type="xml"> <search string="Search Meetings"> <field name="name" string="Meeting" filter_domain="[('name','ilike',self)]"/> - <filter string="Inbox" help="Unread messages" icon="terp-mail-message-new" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> - <filter string="My Meetings" help="My Meetings" icon="terp-personal" domain="[('user_id','=',uid)]"/> + <filter string="My Meetings" help="My Meetings" domain="[('user_id','=',uid)]"/> <field name="user_id"/> <field name="partner_ids"/> </search> diff --git a/addons/base_import/__init__.py b/addons/base_import/__init__.py new file mode 100644 index 00000000000..939a8173e38 --- /dev/null +++ b/addons/base_import/__init__.py @@ -0,0 +1,3 @@ +import controllers +import models +import test_models diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py new file mode 100644 index 00000000000..eff20bc90ab --- /dev/null +++ b/addons/base_import/__openerp__.py @@ -0,0 +1,39 @@ +{ + 'name': 'Base import', + 'description': """ +New extensible file import for OpenERP +====================================== + +Re-implement openerp's file import system: + +* Server side, the previous system forces most of the logic into the + client which duplicates the effort (between clients), makes the + import system much harder to use without a client (direct RPC or + other forms of automation) and makes knowledge about the + import/export system much harder to gather as it is spread over + 3+ different projects. + +* In a more extensible manner, so users and partners can build their + own front-end to import from other file formats (e.g. OpenDocument + files) which may be simpler to handle in their work flow or from + their data production sources. + +* In a module, so that administrators and users of OpenERP who do not + need or want an online import can avoid it being available to users. +""", + 'category': 'Uncategorized', + 'website': 'http://www.openerp.com', + 'author': 'OpenERP SA', + 'depends': ['base'], + 'installable': True, + 'auto_install': False, # set to true and allow uninstall? + 'css': [ + 'static/lib/select2/select2.css', + 'static/src/css/import.css', + ], + 'js': [ + 'static/lib/select2/select2.js', + 'static/src/js/import.js', + ], + 'qweb': ['static/src/xml/import.xml'], +} diff --git a/addons/base_import/controllers.py b/addons/base_import/controllers.py new file mode 100644 index 00000000000..85ff993e92e --- /dev/null +++ b/addons/base_import/controllers.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +import simplejson + +try: + import openerp.addons.web.common.http as openerpweb +except ImportError: + import web.common.http as openerpweb + +class ImportController(openerpweb.Controller): + _cp_path = '/base_import' + + @openerpweb.httprequest + def set_file(self, req, file, import_id, jsonp='callback'): + import_id = int(import_id) + + written = req.session.model('base_import.import').write(import_id, { + 'file': file.read(), + 'file_name': file.filename, + 'file_type': file.content_type, + }, req.session.eval_context(req.context)) + + return 'window.top.%s(%s)' % ( + jsonp, simplejson.dumps({'result': written})) diff --git a/addons/base_import/models.py b/addons/base_import/models.py new file mode 100644 index 00000000000..1e5ad002b84 --- /dev/null +++ b/addons/base_import/models.py @@ -0,0 +1,352 @@ +import csv +import itertools +import logging +import operator + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +import psycopg2 + +from openerp.osv import orm, fields +from openerp.tools.translate import _ + +FIELDS_RECURSION_LIMIT = 2 +ERROR_PREVIEW_BYTES = 200 +_logger = logging.getLogger(__name__) +class ir_import(orm.TransientModel): + _name = 'base_import.import' + # allow imports to survive for 12h in case user is slow + _transient_max_hours = 12.0 + + _columns = { + 'res_model': fields.char('Model', size=64), + 'file': fields.binary( + 'File', help="File to check and/or import, raw binary (not base64)"), + 'file_name': fields.char('File Name', size=None), + 'file_type': fields.char('File Type', size=None), + } + + def get_fields(self, cr, uid, model, context=None, + depth=FIELDS_RECURSION_LIMIT): + """ Recursively get fields for the provided model (through + fields_get) and filter them according to importability + + The output format is a list of ``Field``, with ``Field`` + defined as: + + .. class:: Field + + .. attribute:: id (str) + + A non-unique identifier for the field, used to compute + the span of the ``required`` attribute: if multiple + ``required`` fields have the same id, only one of them + is necessary. + + .. attribute:: name (str) + + The field's logical (OpenERP) name within the scope of + its parent. + + .. attribute:: string (str) + + The field's human-readable name (``@string``) + + .. attribute:: required (bool) + + Whether the field is marked as required in the + model. Clients must provide non-empty import values + for all required fields or the import will error out. + + .. attribute:: fields (list(Field)) + + The current field's subfields. The database and + external identifiers for m2o and m2m fields; a + filtered and transformed fields_get for o2m fields (to + a variable depth defined by ``depth``). + + Fields with no sub-fields will have an empty list of + sub-fields. + + :param str model: name of the model to get fields form + :param int landing: depth of recursion into o2m fields + """ + fields = [{ + 'id': 'id', + 'name': 'id', + 'string': _("External ID"), + 'required': False, + 'fields': [], + }] + fields_got = self.pool[model].fields_get(cr, uid, context=context) + for name, field in fields_got.iteritems(): + if field.get('readonly'): + states = field.get('states') + if not states: + continue + # states = {state: [(attr, value), (attr2, value2)], state2:...} + if not any(attr == 'readonly' and value is False + for attr, value in itertools.chain.from_iterable( + states.itervalues())): + continue + + f = { + 'id': name, + 'name': name, + 'string': field['string'], + # Y U NO ALWAYS HAVE REQUIRED + 'required': bool(field.get('required')), + 'fields': [], + } + + if field['type'] in ('many2many', 'many2one'): + f['fields'] = [ + dict(f, name='id', string=_("External ID")), + dict(f, name='.id', string=_("Database ID")), + ] + elif field['type'] == 'one2many' and depth: + f['fields'] = self.get_fields( + cr, uid, field['relation'], context=context, depth=depth-1) + + fields.append(f) + + # TODO: cache on model? + return fields + + def _read_csv(self, record, options): + """ Returns a CSV-parsed iterator of all empty lines in the file + + :throws csv.Error: if an error is detected during CSV parsing + :throws UnicodeDecodeError: if ``options.encoding`` is incorrect + """ + csv_iterator = csv.reader( + StringIO(record.file), + quotechar=options['quoting'], + delimiter=options['separator']) + csv_nonempty = itertools.ifilter(None, csv_iterator) + # TODO: guess encoding with chardet? Or https://github.com/aadsm/jschardet + encoding = options.get('encoding', 'utf-8') + return itertools.imap( + lambda row: [item.decode(encoding) for item in row], + csv_nonempty) + + def _match_header(self, header, fields, options): + """ Attempts to match a given header to a field of the + imported model. + + :param str header: header name from the CSV file + :param fields: + :param dict options: + :returns: an empty list if the header couldn't be matched, or + all the fields to traverse + :rtype: list(Field) + """ + for field in fields: + # FIXME: should match all translations & original + # TODO: use string distance (levenshtein? hamming?) + if header == field['name'] \ + or header.lower() == field['string'].lower(): + return [field] + + if '/' not in header: + return [] + + # relational field path + traversal = [] + subfields = fields + # Iteratively dive into fields tree + for section in header.split('/'): + # Strip section in case spaces are added around '/' for + # readability of paths + match = self._match_header(section.strip(), subfields, options) + # Any match failure, exit + if not match: return [] + # prep subfields for next iteration within match[0] + field = match[0] + subfields = field['fields'] + traversal.append(field) + return traversal + + def _match_headers(self, rows, fields, options): + """ Attempts to match the imported model's fields to the + titles of the parsed CSV file, if the file is supposed to have + headers. + + Will consume the first line of the ``rows`` iterator. + + Returns a pair of (None, None) if headers were not requested + or the list of headers and a dict mapping cell indices + to key paths in the ``fields`` tree + + :param Iterator rows: + :param dict fields: + :param dict options: + :rtype: (None, None) | (list(str), dict(int: list(str))) + """ + if not options.get('headers'): + return None, None + + headers = next(rows) + return headers, dict( + (index, [field['name'] for field in self._match_header(header, fields, options)] or None) + for index, header in enumerate(headers) + ) + + def parse_preview(self, cr, uid, id, options, count=10, context=None): + """ Generates a preview of the uploaded files, and performs + fields-matching between the import's file data and the model's + columns. + + If the headers are not requested (not options.headers), + ``matches`` and ``headers`` are both ``False``. + + :param id: identifier of the import + :param int count: number of preview lines to generate + :param options: format-specific options. + CSV: {encoding, quoting, separator, headers} + :type options: {str, str, str, bool} + :returns: {fields, matches, headers, preview} | {error, preview} + :rtype: {dict(str: dict(...)), dict(int, list(str)), list(str), list(list(str))} | {str, str} + """ + (record,) = self.browse(cr, uid, [id], context=context) + fields = self.get_fields(cr, uid, record.res_model, context=context) + + try: + rows = self._read_csv(record, options) + + headers, matches = self._match_headers(rows, fields, options) + # Match should have consumed the first row (iif headers), get + # the ``count`` next rows for preview + preview = itertools.islice(rows, count) + return { + 'fields': fields, + 'matches': matches or False, + 'headers': headers or False, + 'preview': list(preview), + } + except Exception, e: + # Due to lazy generators, UnicodeDecodeError (for + # instance) may only be raised when serializing the + # preview to a list in the return. + _logger.debug("Error during CSV parsing preview", exc_info=True) + return { + 'error': str(e), + # iso-8859-1 ensures decoding will always succeed, + # even if it yields non-printable characters. This is + # in case of UnicodeDecodeError (or csv.Error + # compounded with UnicodeDecodeError) + 'preview': record.file[:ERROR_PREVIEW_BYTES] + .decode( 'iso-8859-1'), + } + + def _convert_import_data(self, record, fields, options, context=None): + """ Extracts the input browse_record and fields list (with + ``False``-y placeholders for fields to *not* import) into a + format Model.import_data can use: a fields list without holes + and the precisely matching data matrix + + :param browse_record record: + :param list(str|bool): fields + :returns: (data, fields) + :rtype: (list(list(str)), list(str)) + :raises ValueError: in case the import data could not be converted + """ + # Get indices for non-empty fields + indices = [index for index, field in enumerate(fields) if field] + if not indices: + raise ValueError(_("You must configure at least one field to import")) + # If only one index, itemgetter will return an atom rather + # than a 1-tuple + if len(indices) == 1: mapper = lambda row: [row[indices[0]]] + else: mapper = operator.itemgetter(*indices) + # Get only list of actually imported fields + import_fields = filter(None, fields) + + rows_to_import = self._read_csv(record, options) + if options.get('headers'): + rows_to_import = itertools.islice( + rows_to_import, 1, None) + data = [ + row for row in itertools.imap(mapper, rows_to_import) + # don't try inserting completely empty rows (e.g. from + # filtering out o2m fields) + if any(row) + ] + + return data, import_fields + + def do(self, cr, uid, id, fields, options, dryrun=False, context=None): + """ Actual execution of the import + + :param fields: import mapping: maps each column to a field, + ``False`` for the columns to ignore + :type fields: list(str|bool) + :param dict options: + :param bool dryrun: performs all import operations (and + validations) but rollbacks writes, allows + getting as much errors as possible without + the risk of clobbering the database. + :returns: A list of errors. If the list is empty the import + executed fully and correctly. If the list is + non-empty it contains dicts with 3 keys ``type`` the + type of error (``error|warning``); ``message`` the + error message associated with the error (a string) + and ``record`` the data which failed to import (or + ``false`` if that data isn't available or provided) + :rtype: list({type, message, record}) + """ + cr.execute('SAVEPOINT import') + + (record,) = self.browse(cr, uid, [id], context=context) + try: + data, import_fields = self._convert_import_data( + record, fields, options, context=context) + except ValueError, e: + return [{ + 'type': 'error', + 'message': str(e), + 'record': False, + }] + + try: + _logger.info('importing %d rows...', len(data)) + (code, record, message, _wat) = self.pool[record.res_model].import_data( + cr, uid, import_fields, data, context=context) + _logger.info('done') + + except Exception, e: + _logger.exception("Import failed") + # TODO: remove when exceptions stop being an "expected" + # behavior of import_data on some (most) invalid + # input. + code, record, message = -1, None, str(e) + + # If transaction aborted, RELEASE SAVEPOINT is going to raise + # an InternalError (ROLLBACK should work, maybe). Ignore that. + # TODO: to handle multiple errors, create savepoint around + # write and release it in case of write error (after + # adding error to errors array) => can keep on trying to + # import stuff, and rollback at the end if there is any + # error in the results. + try: + if dryrun: + cr.execute('ROLLBACK TO SAVEPOINT import') + else: + cr.execute('RELEASE SAVEPOINT import') + except psycopg2.InternalError: + pass + + if code != -1: + return [] + + # TODO: add key for error location? + # TODO: error not within normal preview, how to display? Re-preview + # with higher ``count``? + return [{ + 'type': 'error', + 'message': message, + 'record': record or False + }] diff --git a/addons/base_import/static/lib/select2/LICENSE b/addons/base_import/static/lib/select2/LICENSE new file mode 100644 index 00000000000..627fddef413 --- /dev/null +++ b/addons/base_import/static/lib/select2/LICENSE @@ -0,0 +1,12 @@ +Copyright 2012 Igor Vaynberg + +Version: @@ver@@ Timestamp: @@timestamp@@ + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in +compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License.
\ No newline at end of file diff --git a/addons/base_import/static/lib/select2/README.md b/addons/base_import/static/lib/select2/README.md new file mode 100755 index 00000000000..3a6dcaf1ba4 --- /dev/null +++ b/addons/base_import/static/lib/select2/README.md @@ -0,0 +1,68 @@ +Select2 +================= + +Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. Look and feel of Select2 is based on the excellent [Chosen](http://harvesthq.github.com/chosen/) library. + +To get started -- checkout http://ivaynberg.github.com/select2! + +What Does Select2 Support That Chosen Does Not? +------------------------------------------------- + +* Working with large datasets: Chosen requires the entire dataset to be loaded as `option` tags in the DOM, which limits +it to working with small-ish datasets. Select2 uses a function to find results on-the-fly, which allows it to partially +load results. +* Paging of results: Since Select2 works with large datasets and only loads a small amount of matching results at a time +it has to support paging. Select2 will call the search function when the user scrolls to the bottom of currently loaded +result set allowing for the 'infinite scrolling' of results. +* Custom markup for results: Chosen only supports rendering text results because that is the only markup supported by +`option` tags. Select2 provides an extension point which can be used to produce any kind of markup to represent results. +* Ability to add results on the fly: Select2 provides the ability to add results from the search term entered by the user, which allows it to be used for +tagging. + +Browser Compatibility +-------------------- +* IE 8+ (7 mostly works except for [issue with z-index](https://github.com/ivaynberg/select2/issues/37)) +* Chrome 8+ +* Firefox 3.5+ +* Safari 3+ +* Opera 10.6+ + +Integrations +------------ + +* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / Apache Wicket) +* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails) +* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org)) +* [Django](https://github.com/applegrew/django-select2) + +Bug tracker +----------- + +Have a bug? Please create an issue here on GitHub! + +https://github.com/ivaynberg/select2/issues + + +Mailing list +------------ + +Have a question? Ask on our mailing list! + +select2@googlegroups.com + +https://groups.google.com/d/forum/select2 + + +Copyright and License +--------------------- + +Copyright 2012 Igor Vaynberg + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in +compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is +distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and limitations under the License.
\ No newline at end of file diff --git a/addons/base_import/static/lib/select2/release.sh b/addons/base_import/static/lib/select2/release.sh new file mode 100755 index 00000000000..79eaabd621d --- /dev/null +++ b/addons/base_import/static/lib/select2/release.sh @@ -0,0 +1,77 @@ +#!/bin/bash +set -e + +echo -n "Enter the version for this release: " + +read ver + +if [ ! $ver ]; then + echo "Invalid version." + exit +fi + +name="select2" +js="$name.js" +mini="$name.min.js" +css="$name.css" +release="$name-$ver" +releasedir="/tmp/$release" +tag="release-$ver" +branch="build-$ver" +curbranch=`git branch | grep "*" | sed "s/* //"` +timestamp=$(date) +tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g" +remote="github" + +git branch "$branch" +git checkout "$branch" + +echo "Tokenizing..." + +find . -name "$js" | xargs sed -i -e "$tokens" +find . -name "$css" | xargs sed -i -e "$tokens" + +git add "$js" +git add "$css" + +echo "Minifying..." + +echo "/*" > "$mini" +cat LICENSE | sed "$tokens" >> "$mini" +echo "*/" >> "$mini" + +curl -s \ + -d compilation_level=SIMPLE_OPTIMIZATIONS \ + -d output_format=text \ + -d output_info=compiled_code \ + --data-urlencode "js_code@$js" \ + http://closure-compiler.appspot.com/compile \ + >> "$mini" + +git add "$mini" + +git commit -m "release $ver" + +echo "Tagging..." + +git tag -a "$tag" -m "tagged version $ver" +git push "$remote" --tags + +echo "Archiving..." + +rm -rf "$releasedir" +mkdir "$releasedir" + +cp $name.* "$releasedir" +cp spinner.gif "$releasedir" +cp README.* "$releasedir" + +zip -r "$releasedir.zip" "$releasedir" +rm -rf "$releasedir" + +echo "Cleaning Up..." + +git checkout "$curbranch" +git branch -D "$branch" + +echo "Done. Release archive created: $releasedir.zip" diff --git a/addons/base_import/static/lib/select2/select2.css b/addons/base_import/static/lib/select2/select2.css new file mode 100755 index 00000000000..f0bdfb82f04 --- /dev/null +++ b/addons/base_import/static/lib/select2/select2.css @@ -0,0 +1,524 @@ +/* +Version: @@ver@@ Timestamp: @@timestamp@@ +*/ +.select2-container { + position: relative; + display: inline-block; + /* inline-block for ie7 */ + zoom: 1; + *display: inline; + vertical-align: top; +} + +.select2-container, +.select2-drop, +.select2-search, +.select2-search input{ + /* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + + More Info : http://www.quirksmode.org/css/box.html + */ + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ + -webkit-box-sizing: border-box; /* webkit */ + -khtml-box-sizing: border-box; /* konqueror */ + box-sizing: border-box; /* css3 */ +} + +.select2-container .select2-choice { + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%); + background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0); + background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #aaa; + display: block; + overflow: hidden; + white-space: nowrap; + position: relative; + height: 26px; + line-height: 26px; + padding: 0 0 0 8px; + color: #444; + text-decoration: none; +} + +.select2-container.select2-drop-above .select2-choice +{ + border-bottom-color: #aaa; + -webkit-border-radius:0px 0px 4px 4px; + -moz-border-radius:0px 0px 4px 4px; + border-radius:0px 0px 4px 4px; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white)); + background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%); + background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); + background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%); +} + +.select2-container .select2-choice span { + margin-right: 26px; + display: block; + overflow: hidden; + white-space: nowrap; + -o-text-overflow: ellipsis; + -ms-text-overflow: ellipsis; + text-overflow: ellipsis; +} + +.select2-container .select2-choice abbr { + display: block; + position: absolute; + right: 26px; + top: 8px; + width: 12px; + height: 12px; + font-size: 1px; + background: url('select2.png') right top no-repeat; + cursor: pointer; + text-decoration: none; + border:0; + outline: 0; +} +.select2-container .select2-choice abbr:hover { + background-position: right -11px; + cursor: pointer; +} + +.select2-drop { + background: #fff; + color: #000; + border: 1px solid #aaa; + border-top: 0; + position: absolute; + top: 100%; + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + z-index: 9999; + width:100%; + margin-top:-1px; + + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + +.select2-drop.select2-drop-above { + -webkit-border-radius: 4px 4px 0px 0px; + -moz-border-radius: 4px 4px 0px 0px; + border-radius: 4px 4px 0px 0px; + margin-top:1px; + border-top: 1px solid #aaa; + border-bottom: 0; + + -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + -o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); +} + +.select2-container .select2-choice div { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0); + background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%); + border-left: 1px solid #aaa; + position: absolute; + right: 0; + top: 0; + display: block; + height: 100%; + width: 18px; +} + +.select2-container .select2-choice div b { + background: url('select2.png') no-repeat 0 1px; + display: block; + width: 100%; + height: 100%; +} + +.select2-search { + display: inline-block; + white-space: nowrap; + z-index: 10000; + min-height: 26px; + width: 100%; + margin: 0; + padding-left: 4px; + padding-right: 4px; +} + +.select2-search-hidden { + display: block; + position: absolute; + left: -10000px; +} + +.select2-search input { + background: #fff url('select2.png') no-repeat 100% -22px; + background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); + padding: 4px 20px 4px 5px; + outline: 0; + border: 1px solid #aaa; + font-family: sans-serif; + font-size: 1em; + width:100%; + margin:0; + height:auto !important; + min-height: 26px; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; +} + +.select2-drop.select2-drop-above .select2-search input +{ + margin-top:4px; +} + +.select2-search input.select2-active { + background: #fff url('spinner.gif') no-repeat 100%; + background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); + background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%); +} + + +.select2-container-active .select2-choice, +.select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; + outline: none; +} + +.select2-dropdown-open .select2-choice { + border: 1px solid #aaa; + border-bottom-color: transparent; + -webkit-box-shadow: 0 1px 0 #fff inset; + -moz-box-shadow : 0 1px 0 #fff inset; + -o-box-shadow : 0 1px 0 #fff inset; + box-shadow : 0 1px 0 #fff inset; + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); + background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); + -webkit-border-bottom-left-radius : 0; + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomleft : 0; + -moz-border-radius-bottomright: 0; + border-bottom-left-radius : 0; + border-bottom-right-radius: 0; +} + +.select2-dropdown-open .select2-choice div { + background: transparent; + border-left: none; +} +.select2-dropdown-open .select2-choice div b { + background-position: -18px 1px; +} + +/* results */ +.select2-results { + margin: 4px 4px 4px 0; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; + max-height: 200px; +} + +.select2-results ul.select2-result-sub { + margin: 0 0 0 0; +} + +.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px } +.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px } +.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px } + +.select2-results li { + list-style: none; + display: list-item; +} + +.select2-results li.select2-result-with-children > .select2-result-label { + font-weight: bold; +} + +.select2-results .select2-result-label { + padding: 3px 7px 4px; + margin: 0; + cursor: pointer; +} + +.select2-results .select2-highlighted { + background: #3875d7; + color: #fff; +} +.select2-results li em { + background: #feffde; + font-style: normal; +} +.select2-results .select2-highlighted em { + background: transparent; +} +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-selection-limit { + background: #f4f4f4; + display: list-item; +} + +/* +disabled look for already selected choices in the results dropdown +.select2-results .select2-disabled.select2-highlighted { + color: #666; + background: #f4f4f4; + display: list-item; + cursor: default; +} +.select2-results .select2-disabled { + background: #f4f4f4; + display: list-item; + cursor: default; +} +*/ +.select2-results .select2-disabled { + display: none; +} + +.select2-more-results.select2-active { + background: #f4f4f4 url('spinner.gif') no-repeat 100%; +} + +.select2-more-results { + background: #f4f4f4; + display: list-item; +} + +/* disabled styles */ + +.select2-container.select2-container-disabled .select2-choice { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container.select2-container-disabled .select2-choice div { + background-color: #f4f4f4; + background-image: none; + border-left: 0; +} + + +/* multiselect */ + +.select2-container-multi .select2-choices { + background-color: #fff; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); + border: 1px solid #aaa; + margin: 0; + padding: 0; + cursor: text; + overflow: hidden; + height: auto !important; + height: 1%; + position: relative; +} + +.select2-container-multi .select2-choices { + min-height: 26px; +} + +.select2-container-multi.select2-container-active .select2-choices { + -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); + -moz-box-shadow : 0 0 5px rgba(0,0,0,.3); + -o-box-shadow : 0 0 5px rgba(0,0,0,.3); + box-shadow : 0 0 5px rgba(0,0,0,.3); + border: 1px solid #5897fb; + outline: none; +} +.select2-container-multi .select2-choices li { + float: left; + list-style: none; +} +.select2-container-multi .select2-choices .select2-search-field { + white-space: nowrap; + margin: 0; + padding: 0; +} + +.select2-container-multi .select2-choices .select2-search-field input { + color: #666; + background: transparent !important; + font-family: sans-serif; + font-size: 100%; + height: 15px; + padding: 5px; + margin: 1px 0; + outline: 0; + border: 0; + -webkit-box-shadow: none; + -moz-box-shadow : none; + -o-box-shadow : none; + box-shadow : none; +} + +.select2-container-multi .select2-choices .select2-search-field input.select2-active { + background: #fff url('spinner.gif') no-repeat 100% !important; +} + +.select2-default { + color: #999 !important; +} + +.select2-container-multi .select2-choices .select2-search-choice { + -webkit-border-radius: 3px; + -moz-border-radius : 3px; + border-radius : 3px; + -moz-background-clip : padding; + -webkit-background-clip: padding-box; + background-clip : padding-box; + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 ); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + -moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); + color: #333; + border: 1px solid #aaaaaa; + line-height: 13px; + padding: 3px 5px 3px 18px; + margin: 3px 0 3px 5px; + position: relative; + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice span { + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice-focus { + background: #d4d4d4; +} + +.select2-search-choice-close { + display: block; + position: absolute; + right: 3px; + top: 4px; + width: 12px; + height: 13px; + font-size: 1px; + background: url('select2.png') right top no-repeat; + outline: none; +} + +.select2-container-multi .select2-search-choice-close { + left: 3px; +} + + +.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { + background-position: right -11px; +} +.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { + background-position: right -11px; +} + +/* disabled styles */ + +.select2-container-multi.select2-container-disabled .select2-choices{ + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { + background-image: none; + background-color: #f4f4f4; + border: 1px solid #ddd; + padding: 3px 5px 3px 5px; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { + display: none; +} +/* end multiselect */ + +.select2-result-selectable .select2-match, +.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; } +.select2-result-unselectable .select2-match { text-decoration: none; } + +.select2-offscreen { position: absolute; left: -10000px; } + +/* Retina-ize icons */ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5) { + .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b { + background-image: url(select2x2.png) !important; + background-repeat: no-repeat !important; + background-size: 60px 40px !important; + } + .select2-search input { + background-position: 100% -21px !important; + } +}
\ No newline at end of file diff --git a/addons/base_import/static/lib/select2/select2.js b/addons/base_import/static/lib/select2/select2.js new file mode 100755 index 00000000000..42ce0a928b8 --- /dev/null +++ b/addons/base_import/static/lib/select2/select2.js @@ -0,0 +1,2407 @@ +/* + Copyright 2012 Igor Vaynberg + + Version: @@ver@@ Timestamp: @@timestamp@@ + + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in + compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is + distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + */ + (function ($) { + if(typeof $.fn.each2 == "undefined"){ + $.fn.extend({ + /* + * 4-10 times faster .each replacement + * use it carefully, as it overrides jQuery context of element on each iteration + */ + each2 : function (c) { + var j = $([0]), i = -1, l = this.length; + while ( + ++i < l + && (j.context = j[0] = this[i]) + && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object + ); + return this; + } + }); + } +})(jQuery); + +(function ($, undefined) { + "use strict"; + /*global document, window, jQuery, console */ + + if (window.Select2 !== undefined) { + return; + } + + var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer; + + KEY = { + TAB: 9, + ENTER: 13, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + SHIFT: 16, + CTRL: 17, + ALT: 18, + PAGE_UP: 33, + PAGE_DOWN: 34, + HOME: 36, + END: 35, + BACKSPACE: 8, + DELETE: 46, + isArrow: function (k) { + k = k.which ? k.which : k; + switch (k) { + case KEY.LEFT: + case KEY.RIGHT: + case KEY.UP: + case KEY.DOWN: + return true; + } + return false; + }, + isControl: function (e) { + var k = e.which; + switch (k) { + case KEY.SHIFT: + case KEY.CTRL: + case KEY.ALT: + return true; + } + + if (e.metaKey) return true; + + return false; + }, + isFunctionKey: function (k) { + k = k.which ? k.which : k; + return k >= 112 && k <= 123; + } + }; + + nextUid=(function() { var counter=1; return function() { return counter++; }; }()); + + function indexOf(value, array) { + var i = 0, l = array.length, v; + + if (typeof value === "undefined") { + return -1; + } + + if (value.constructor === String) { + for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i; + } else { + for (; i < l; i = i + 1) { + v = array[i]; + if (v.constructor === String) { + if (v.localeCompare(value) === 0) return i; + } else { + if (v === value) return i; + } + } + } + return -1; + } + + /** + * Compares equality of a and b taking into account that a and b may be strings, in which case localeCompare is used + * @param a + * @param b + */ + function equal(a, b) { + if (a === b) return true; + if (a === undefined || b === undefined) return false; + if (a === null || b === null) return false; + if (a.constructor === String) return a.localeCompare(b) === 0; + if (b.constructor === String) return b.localeCompare(a) === 0; + return false; + } + + /** + * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty + * strings + * @param string + * @param separator + */ + function splitVal(string, separator) { + var val, i, l; + if (string === null || string.length < 1) return []; + val = string.split(separator); + for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]); + return val; + } + + function getSideBorderPadding(element) { + return element.outerWidth() - element.width(); + } + + function installKeyUpChangeEvent(element) { + var key="keyup-change-value"; + element.bind("keydown", function () { + if ($.data(element, key) === undefined) { + $.data(element, key, element.val()); + } + }); + element.bind("keyup", function () { + var val= $.data(element, key); + if (val !== undefined && element.val() !== val) { + $.removeData(element, key); + element.trigger("keyup-change"); + } + }); + } + + $(document).delegate("*", "mousemove", function (e) { + $.data(document, "select2-lastpos", {x: e.pageX, y: e.pageY}); + }); + + /** + * filters mouse events so an event is fired only if the mouse moved. + * + * filters out mouse events that occur when mouse is stationary but + * the elements under the pointer are scrolled. + */ + function installFilteredMouseMove(element) { + element.bind("mousemove", function (e) { + var lastpos = $.data(document, "select2-lastpos"); + if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) { + $(e.target).trigger("mousemove-filtered", e); + } + }); + } + + /** + * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made + * within the last quietMillis milliseconds. + * + * @param quietMillis number of milliseconds to wait before invoking fn + * @param fn function to be debounced + * @param ctx object to be used as this reference within fn + * @return debounced version of fn + */ + function debounce(quietMillis, fn, ctx) { + ctx = ctx || undefined; + var timeout; + return function () { + var args = arguments; + window.clearTimeout(timeout); + timeout = window.setTimeout(function() { + fn.apply(ctx, args); + }, quietMillis); + }; + } + + /** + * A simple implementation of a thunk + * @param formula function used to lazily initialize the thunk + * @return {Function} + */ + function thunk(formula) { + var evaluated = false, + value; + return function() { + if (evaluated === false) { value = formula(); evaluated = true; } + return value; + }; + }; + + function installDebouncedScroll(threshold, element) { + var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);}); + element.bind("scroll", function (e) { + if (indexOf(e.target, element.get()) >= 0) notify(e); + }); + } + + function killEvent(event) { + event.preventDefault(); + event.stopPropagation(); + } + + function measureTextWidth(e) { + if (!sizer){ + var style = e[0].currentStyle || window.getComputedStyle(e[0], null); + sizer = $("<div></div>").css({ + position: "absolute", + left: "-10000px", + top: "-10000px", + display: "none", + fontSize: style.fontSize, + fontFamily: style.fontFamily, + fontStyle: style.fontStyle, + fontWeight: style.fontWeight, + letterSpacing: style.letterSpacing, + textTransform: style.textTransform, + whiteSpace: "nowrap" + }); + $("body").append(sizer); + } + sizer.text(e.val()); + return sizer.width(); + } + + function markMatch(text, term, markup) { + var match=text.toUpperCase().indexOf(term.toUpperCase()), + tl=term.length; + + if (match<0) { + markup.push(text); + return; + } + + markup.push(text.substring(0, match)); + markup.push("<span class='select2-match'>"); + markup.push(text.substring(match, match + tl)); + markup.push("</span>"); + markup.push(text.substring(match + tl, text.length)); + } + + /** + * Produces an ajax-based query function + * + * @param options object containing configuration paramters + * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax + * @param options.url url for the data + * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. + * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified + * @param options.traditional a boolean flag that should be true if you wish to use the traditional style of param serialization for the ajax request + * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often + * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2. + * The expected format is an object containing the following keys: + * results array of objects that will be used as choices + * more (optional) boolean indicating whether there are more results available + * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true} + */ + function ajax(options) { + var timeout, // current scheduled but not yet executed request + requestSequence = 0, // sequence used to drop out-of-order responses + handler = null, + quietMillis = options.quietMillis || 100; + + return function (query) { + window.clearTimeout(timeout); + timeout = window.setTimeout(function () { + requestSequence += 1; // increment the sequence + var requestNumber = requestSequence, // this request's sequence number + data = options.data, // ajax data function + transport = options.transport || $.ajax, + traditional = options.traditional || false, + type = options.type || 'GET'; // set type of request (GET or POST) + + data = data.call(this, query.term, query.page, query.context); + + if( null !== handler) { handler.abort(); } + + handler = transport.call(null, { + url: options.url, + dataType: options.dataType, + data: data, + type: type, + traditional: traditional, + success: function (data) { + if (requestNumber < requestSequence) { + return; + } + // TODO 3.0 - replace query.page with query so users have access to term, page, etc. + var results = options.results(data, query.page); + query.callback(results); + } + }); + }, quietMillis); + }; + } + + /** + * Produces a query function that works with a local array + * + * @param options object containing configuration parameters. The options parameter can either be an array or an + * object. + * + * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys. + * + * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain + * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text' + * key can either be a String in which case it is expected that each element in the 'data' array has a key with the + * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract + * the text. + */ + function local(options) { + var data = options, // data elements + dataText, + text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search + + if (!$.isArray(data)) { + text = data.text; + // if text is not a function we assume it to be a key name + if (!$.isFunction(text)) { + dataText = data.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available + text = function (item) { return item[dataText]; }; + } + data = data.results; + } + + return function (query) { + var t = query.term, filtered = { results: [] }, process; + if (t === "") { + query.callback({results: data}); + return; + } + + process = function(datum, collection) { + var group, attr; + datum = datum[0]; + if (datum.children) { + group = {}; + for (attr in datum) { + if (datum.hasOwnProperty(attr)) group[attr]=datum[attr]; + } + group.children=[]; + $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); }); + if (group.children.length) { + collection.push(group); + } + } else { + if (query.matcher(t, text(datum))) { + collection.push(datum); + } + } + }; + + $(data).each2(function(i, datum) { process(datum, filtered.results); }); + query.callback(filtered); + }; + } + + // TODO javadoc + function tags(data) { + // TODO even for a function we should probably return a wrapper that does the same object/string check as + // the function for arrays. otherwise only functions that return objects are supported. + if ($.isFunction(data)) { + return data; + } + + // if not a function we assume it to be an array + + return function (query) { + var t = query.term, filtered = {results: []}; + $(data).each(function () { + var isObject = this.text !== undefined, + text = isObject ? this.text : this; + if (t === "" || query.matcher(t, text)) { + filtered.results.push(isObject ? this : {id: this, text: this}); + } + }); + query.callback(filtered); + }; + } + + /** + * Checks if the formatter function should be used. + * + * Throws an error if it is not a function. Returns true if it should be used, + * false if no formatting should be performed. + * + * @param formatter + */ + function checkFormatter(formatter, formatterName) { + if ($.isFunction(formatter)) return true; + if (!formatter) return false; + throw new Error("formatterName must be a function or a falsy value"); + } + + function evaluate(val) { + return $.isFunction(val) ? val() : val; + } + + function countResults(results) { + var count = 0; + $.each(results, function(i, item) { + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + }); + return count; + } + + /** + * Default tokenizer. This function uses breaks the input on substring match of any string from the + * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those + * two options have to be defined in order for the tokenizer to work. + * + * @param input text user has typed so far or pasted into the search field + * @param selection currently selected choices + * @param selectCallback function(choice) callback tho add the choice to selection + * @param opts select2's opts + * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value + */ + function defaultTokenizer(input, selection, selectCallback, opts) { + var original = input, // store the original so we can compare and know if we need to tell the search to update its text + dupe = false, // check for whether a token we extracted represents a duplicate selected choice + token, // token + index, // position at which the separator was found + i, l, // looping variables + separator; // the matched separator + + if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined; + + while (true) { + index = -1; + + for (i = 0, l = opts.tokenSeparators.length; i < l; i++) { + separator = opts.tokenSeparators[i]; + index = input.indexOf(separator); + if (index >= 0) break; + } + + if (index < 0) break; // did not find any token separator in the input string, bail + + token = input.substring(0, index); + input = input.substring(index + separator.length); + + if (token.length > 0) { + token = opts.createSearchChoice(token, selection); + if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) { + dupe = false; + for (i = 0, l = selection.length; i < l; i++) { + if (equal(opts.id(token), opts.id(selection[i]))) { + dupe = true; break; + } + } + + if (!dupe) selectCallback(token); + } + } + } + + if (original.localeCompare(input) != 0) return input; + } + + /** + * blurs any Select2 container that has focus when an element outside them was clicked or received focus + * + * also takes care of clicks on label tags that point to the source element + */ + $(document).ready(function () { + $(document).delegate("*", "mousedown touchend", function (e) { + var target = $(e.target).closest("div.select2-container").get(0), attr; + if (target) { + $(document).find("div.select2-container-active").each(function () { + if (this !== target) $(this).data("select2").blur(); + }); + } else { + target = $(e.target).closest("div.select2-drop").get(0); + $(document).find("div.select2-drop-active").each(function () { + if (this !== target) $(this).data("select2").blur(); + }); + } + + target=$(e.target); + attr = target.attr("for"); + if ("LABEL" === e.target.tagName && attr && attr.length > 0) { + target = $("#"+attr); + target = target.data("select2"); + if (target !== undefined) { target.focus(); e.preventDefault();} + } + }); + }); + + /** + * Creates a new class + * + * @param superClass + * @param methods + */ + function clazz(SuperClass, methods) { + var constructor = function () {}; + constructor.prototype = new SuperClass; + constructor.prototype.constructor = constructor; + constructor.prototype.parent = SuperClass.prototype; + constructor.prototype = $.extend(constructor.prototype, methods); + return constructor; + } + + AbstractSelect2 = clazz(Object, { + + // abstract + bind: function (func) { + var self = this; + return function () { + func.apply(self, arguments); + }; + }, + + // abstract + init: function (opts) { + var results, search, resultsSelector = ".select2-results"; + + // prepare options + this.opts = opts = this.prepareOpts(opts); + + this.id=opts.id; + + // destroy if called on an existing component + if (opts.element.data("select2") !== undefined && + opts.element.data("select2") !== null) { + this.destroy(); + } + + this.enabled=true; + this.container = this.createContainer(); + + this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); + this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); + this.container.attr("id", this.containerId); + + // cache the body so future lookups are cheap + this.body = thunk(function() { return opts.element.closest("body"); }); + + if (opts.element.attr("class") !== undefined) { + this.container.addClass(opts.element.attr("class").replace(/validate\[[\S ]+] ?/, '')); + } + + this.container.css(evaluate(opts.containerCss)); + this.container.addClass(evaluate(opts.containerCssClass)); + + // swap container for the element + this.opts.element + .data("select2", this) + .hide() + .before(this.container); + this.container.data("select2", this); + + this.dropdown = this.container.find(".select2-drop"); + this.dropdown.addClass(evaluate(opts.dropdownCssClass)); + this.dropdown.data("select2", this); + + this.results = results = this.container.find(resultsSelector); + this.search = search = this.container.find("input.select2-input"); + + search.attr("tabIndex", this.opts.element.attr("tabIndex")); + + this.resultsPage = 0; + this.context = null; + + // initialize the container + this.initContainer(); + this.initContainerWidth(); + + installFilteredMouseMove(this.results); + this.dropdown.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent)); + + installDebouncedScroll(80, this.results); + this.dropdown.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded)); + + // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel + if ($.fn.mousewheel) { + results.mousewheel(function (e, delta, deltaX, deltaY) { + var top = results.scrollTop(), height; + if (deltaY > 0 && top - deltaY <= 0) { + results.scrollTop(0); + killEvent(e); + } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) { + results.scrollTop(results.get(0).scrollHeight - results.height()); + killEvent(e); + } + }); + } + + installKeyUpChangeEvent(search); + search.bind("keyup-change", this.bind(this.updateResults)); + search.bind("focus", function () { search.addClass("select2-focused"); if (search.val() === " ") search.val(""); }); + search.bind("blur", function () { search.removeClass("select2-focused");}); + + this.dropdown.delegate(resultsSelector, "mouseup", this.bind(function (e) { + if ($(e.target).closest(".select2-result-selectable:not(.select2-disabled)").length > 0) { + this.highlightUnderEvent(e); + this.selectHighlighted(e); + } else { + this.focusSearch(); + } + killEvent(e); + })); + + // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening + // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's + // dom it will trigger the popup close, which is not what we want + this.dropdown.bind("click mouseup mousedown", function (e) { e.stopPropagation(); }); + + if ($.isFunction(this.opts.initSelection)) { + // initialize selection based on the current value of the source element + this.initSelection(); + + // if the user has provided a function that can set selection based on the value of the source element + // we monitor the change event on the element and trigger it, allowing for two way synchronization + this.monitorSource(); + } + + if (opts.element.is(":disabled") || opts.element.is("[readonly='readonly']")) this.disable(); + }, + + // abstract + destroy: function () { + var select2 = this.opts.element.data("select2"); + if (select2 !== undefined) { + select2.container.remove(); + select2.dropdown.remove(); + select2.opts.element + .removeData("select2") + .unbind(".select2") + .show(); + } + }, + + // abstract + prepareOpts: function (opts) { + var element, select, idKey, ajaxUrl; + + element = opts.element; + + if (element.get(0).tagName.toLowerCase() === "select") { + this.select = select = opts.element; + } + + if (select) { + // these options are not allowed when attached to a select because they are picked up off the element itself + $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () { + if (this in opts) { + throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element."); + } + }); + } + + opts = $.extend({}, { + populateResults: function(container, results, query) { + var populate, data, result, children, id=this.opts.id, self=this; + + populate=function(results, container, depth) { + + var i, l, result, selectable, compound, node, label, innerContainer, formatted; + for (i = 0, l = results.length; i < l; i = i + 1) { + + result=results[i]; + selectable=id(result) !== undefined; + compound=("children" in result) && result.children.length > 0; + + node=$("<li></li>"); + node.addClass("select2-results-dept-"+depth); + node.addClass("select2-result"); + node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable"); + if (compound) { node.addClass("select2-result-with-children"); } + node.addClass(self.opts.formatResultCssClass(result)); + + label=$("<div></div>"); + label.addClass("select2-result-label"); + + formatted=opts.formatResult(result, label, query); + if (formatted!==undefined) { + label.html(self.opts.escapeMarkup(formatted)); + } + + node.append(label); + + if (compound) { + + innerContainer=$("<ul></ul>"); + innerContainer.addClass("select2-result-sub"); + populate(result.children, innerContainer, depth+1); + node.append(innerContainer); + } + + node.data("select2-data", result); + container.append(node); + } + }; + + populate(results, container, 0); + } + }, $.fn.select2.defaults, opts); + + if (typeof(opts.id) !== "function") { + idKey = opts.id; + opts.id = function (e) { return e[idKey]; }; + } + + if (select) { + opts.query = this.bind(function (query) { + var data = { results: [], more: false }, + term = query.term, + children, firstChild, process; + + process=function(element, collection) { + var group; + if (element.is("option")) { + if (query.matcher(term, element.text(), element)) { + collection.push({id:element.attr("value"), text:element.text(), element: element.get(), css: element.attr("class")}); + } + } else if (element.is("optgroup")) { + group={text:element.attr("label"), children:[], element: element.get(), css: element.attr("class")}; + element.children().each2(function(i, elm) { process(elm, group.children); }); + if (group.children.length>0) { + collection.push(group); + } + } + }; + + children=element.children(); + + // ignore the placeholder option if there is one + if (this.getPlaceholder() !== undefined && children.length > 0) { + firstChild = children[0]; + if ($(firstChild).text() === "") { + children=children.not(firstChild); + } + } + + children.each2(function(i, elm) { process(elm, data.results); }); + + query.callback(data); + }); + // this is needed because inside val() we construct choices from options and there id is hardcoded + opts.id=function(e) { return e.id; }; + opts.formatResultCssClass = function(data) { return data.css; } + } else { + if (!("query" in opts)) { + if ("ajax" in opts) { + ajaxUrl = opts.element.data("ajax-url"); + if (ajaxUrl && ajaxUrl.length > 0) { + opts.ajax.url = ajaxUrl; + } + opts.query = ajax(opts.ajax); + } else if ("data" in opts) { + opts.query = local(opts.data); + } else if ("tags" in opts) { + opts.query = tags(opts.tags); + opts.createSearchChoice = function (term) { return {id: term, text: term}; }; + opts.initSelection = function (element, callback) { + var data = []; + $(splitVal(element.val(), opts.separator)).each(function () { + var id = this, text = this, tags=opts.tags; + if ($.isFunction(tags)) tags=tags(); + $(tags).each(function() { if (equal(this.id, id)) { text = this.text; return false; } }); + data.push({id: id, text: text}); + }); + + callback(data); + }; + } + } + } + if (typeof(opts.query) !== "function") { + throw "query function not defined for Select2 " + opts.element.attr("id"); + } + + return opts; + }, + + /** + * Monitor the original element for changes and update select2 accordingly + */ + // abstract + monitorSource: function () { + this.opts.element.bind("change.select2", this.bind(function (e) { + if (this.opts.element.data("select2-change-triggered") !== true) { + this.initSelection(); + } + })); + }, + + /** + * Triggers the change event on the source element + */ + // abstract + triggerChange: function (details) { + + details = details || {}; + details= $.extend({}, details, { type: "change", val: this.val() }); + // prevents recursive triggering + this.opts.element.data("select2-change-triggered", true); + this.opts.element.trigger(details); + this.opts.element.data("select2-change-triggered", false); + + // some validation frameworks ignore the change event and listen instead to keyup, click for selects + // so here we trigger the click event manually + this.opts.element.click(); + + // ValidationEngine ignorea the change event and listens instead to blur + // so here we trigger the blur event manually if so desired + if (this.opts.blurOnChange) + this.opts.element.blur(); + }, + + + // abstract + enable: function() { + if (this.enabled) return; + + this.enabled=true; + this.container.removeClass("select2-container-disabled"); + }, + + // abstract + disable: function() { + if (!this.enabled) return; + + this.close(); + + this.enabled=false; + this.container.addClass("select2-container-disabled"); + }, + + // abstract + opened: function () { + return this.container.hasClass("select2-dropdown-open"); + }, + + // abstract + positionDropdown: function() { + var offset = this.container.offset(), + height = this.container.outerHeight(), + width = this.container.outerWidth(), + dropHeight = this.dropdown.outerHeight(), + viewportBottom = $(window).scrollTop() + document.documentElement.clientHeight, + dropTop = offset.top + height, + dropLeft = offset.left, + enoughRoomBelow = dropTop + dropHeight <= viewportBottom, + enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(), + aboveNow = this.dropdown.hasClass("select2-drop-above"), + bodyOffset, + above, + css; + + // console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); + // console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body().scrollTop(), "enough?", enoughRoomAbove); + + // fix positioning when body has an offset and is not position: static + + if (this.body().css('position') !== 'static') { + bodyOffset = this.body().offset(); + dropTop -= bodyOffset.top; + dropLeft -= bodyOffset.left; + } + + // always prefer the current above/below alignment, unless there is not enough room + + if (aboveNow) { + above = true; + if (!enoughRoomAbove && enoughRoomBelow) above = false; + } else { + above = false; + if (!enoughRoomBelow && enoughRoomAbove) above = true; + } + + if (above) { + dropTop = offset.top - dropHeight; + this.container.addClass("select2-drop-above"); + this.dropdown.addClass("select2-drop-above"); + } + else { + this.container.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); + } + + css = $.extend({ + top: dropTop, + left: dropLeft, + width: width + }, evaluate(this.opts.dropdownCss)); + + this.dropdown.css(css); + }, + + // abstract + shouldOpen: function() { + var event; + + if (this.opened()) return false; + + event = jQuery.Event("open"); + this.opts.element.trigger(event); + return !event.isDefaultPrevented(); + }, + + // abstract + clearDropdownAlignmentPreference: function() { + // clear the classes used to figure out the preference of where the dropdown should be opened + this.container.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); + }, + + /** + * Opens the dropdown + * + * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example, + * the dropdown is already open, or if the 'open' event listener on the element called preventDefault(). + */ + // abstract + open: function () { + + if (!this.shouldOpen()) return false; + + window.setTimeout(this.bind(this.opening), 1); + + return true; + }, + + /** + * Performs the opening of the dropdown + */ + // abstract + opening: function() { + var cid = this.containerId, selector = this.containerSelector, + scroll = "scroll." + cid, resize = "resize." + cid; + + this.container.parents().each(function() { + $(this).bind(scroll, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(this).unbind(scroll); + } + s2.select2("close"); + }); + }); + + $(window).bind(resize, function() { + var s2 = $(selector); + if (s2.length == 0) { + $(window).unbind(resize); + } + s2.select2("close"); + }); + + this.clearDropdownAlignmentPreference(); + + if (this.search.val() === " ") { this.search.val(""); } + + this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); + + this.updateResults(true); + + if(this.dropdown[0] !== this.body().children().last()[0]) { + this.dropdown.detach().appendTo(this.body()); + } + + this.dropdown.show(); + + this.positionDropdown(); + this.dropdown.addClass("select2-drop-active"); + + this.ensureHighlightVisible(); + + this.focusSearch(); + }, + + // abstract + close: function () { + if (!this.opened()) return; + + var self = this; + + this.container.parents().each(function() { + $(this).unbind("scroll." + self.containerId); + }); + $(window).unbind("resize." + this.containerId); + + this.clearDropdownAlignmentPreference(); + + this.dropdown.hide(); + this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); + this.results.empty(); + this.clearSearch(); + + this.opts.element.trigger(jQuery.Event("close")); + }, + + // abstract + clearSearch: function () { + + }, + + // abstract + ensureHighlightVisible: function () { + var results = this.results, children, index, child, hb, rb, y, more; + + index = this.highlight(); + + if (index < 0) return; + + if (index == 0) { + + // if the first element is highlighted scroll all the way to the top, + // that way any unselectable headers above it will also be scrolled + // into view + + results.scrollTop(0); + return; + } + + children = results.find(".select2-result-selectable"); + + child = $(children[index]); + + hb = child.offset().top + child.outerHeight(); + + // if this is the last child lets also make sure select2-more-results is visible + if (index === children.length - 1) { + more = results.find("li.select2-more-results"); + if (more.length > 0) { + hb = more.offset().top + more.outerHeight(); + } + } + + rb = results.offset().top + results.outerHeight(); + if (hb > rb) { + results.scrollTop(results.scrollTop() + (hb - rb)); + } + y = child.offset().top - results.offset().top; + + // make sure the top of the element is visible + if (y < 0) { + results.scrollTop(results.scrollTop() + y); // y is negative + } + }, + + // abstract + moveHighlight: function (delta) { + var choices = this.results.find(".select2-result-selectable"), + index = this.highlight(); + + while (index > -1 && index < choices.length) { + index += delta; + var choice = $(choices[index]); + if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled")) { + this.highlight(index); + break; + } + } + }, + + // abstract + highlight: function (index) { + var choices = this.results.find(".select2-result-selectable").not(".select2-disabled"); + + if (arguments.length === 0) { + return indexOf(choices.filter(".select2-highlighted")[0], choices.get()); + } + + if (index >= choices.length) index = choices.length - 1; + if (index < 0) index = 0; + + choices.removeClass("select2-highlighted"); + + $(choices[index]).addClass("select2-highlighted"); + this.ensureHighlightVisible(); + + }, + + // abstract + countSelectableResults: function() { + return this.results.find(".select2-result-selectable").not(".select2-disabled").length; + }, + + // abstract + highlightUnderEvent: function (event) { + var el = $(event.target).closest(".select2-result-selectable"); + if (el.length > 0 && !el.is(".select2-highlighted")) { + var choices = this.results.find('.select2-result-selectable'); + this.highlight(choices.index(el)); + } else if (el.length == 0) { + // if we are over an unselectable item remove al highlights + this.results.find(".select2-highlighted").removeClass("select2-highlighted"); + } + }, + + // abstract + loadMoreIfNeeded: function () { + var results = this.results, + more = results.find("li.select2-more-results"), + below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible + offset = -1, // index of first element without data + page = this.resultsPage + 1, + self=this, + term=this.search.val(), + context=this.context; + + if (more.length === 0) return; + below = more.offset().top - results.offset().top - results.height(); + + if (below <= 0) { + more.addClass("select2-active"); + this.opts.query({ + term: term, + page: page, + context: context, + matcher: this.opts.matcher, + callback: this.bind(function (data) { + + // ignore a response if the select2 has been closed before it was received + if (!self.opened()) return; + + + self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context}); + + if (data.more===true) { + more.detach().appendTo(results).text(self.opts.formatLoadMore(page+1)); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } else { + more.remove(); + } + self.positionDropdown(); + self.resultsPage = page; + })}); + } + }, + + /** + * Default tokenizer function which does nothing + */ + tokenize: function() { + + }, + + /** + * @param initial whether or not this is the call to this method right after the dropdown has been opened + */ + // abstract + updateResults: function (initial) { + var search = this.search, results = this.results, opts = this.opts, data, self=this, input; + + // if the search is currently hidden we do not alter the results + if (initial !== true && (this.showSearchInput === false || !this.opened())) { + return; + } + + search.addClass("select2-active"); + + function postRender() { + results.scrollTop(0); + search.removeClass("select2-active"); + self.positionDropdown(); + } + + function render(html) { + results.html(self.opts.escapeMarkup(html)); + postRender(); + } + + if (opts.maximumSelectionSize >=1) { + data = this.data(); + if ($.isArray(data) && data.length >= opts.maximumSelectionSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { + render("<li class='select2-selection-limit'>" + opts.formatSelectionTooBig(opts.maximumSelectionSize) + "</li>"); + return; + } + } + + if (search.val().length < opts.minimumInputLength && checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) { + render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>"); + return; + } + else { + render("<li class='select2-searching'>" + opts.formatSearching() + "</li>"); + } + + // give the tokenizer a chance to pre-process the input + input = this.tokenize(); + if (input != undefined && input != null) { + search.val(input); + } + + this.resultsPage = 1; + opts.query({ + term: search.val(), + page: this.resultsPage, + context: null, + matcher: opts.matcher, + callback: this.bind(function (data) { + var def; // default choice + + // ignore a response if the select2 has been closed before it was received + if (!this.opened()) return; + + // save context, if any + this.context = (data.context===undefined) ? null : data.context; + + // create a default choice and prepend it to the list + if (this.opts.createSearchChoice && search.val() !== "") { + def = this.opts.createSearchChoice.call(null, search.val(), data.results); + if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) { + if ($(data.results).filter( + function () { + return equal(self.id(this), self.id(def)); + }).length === 0) { + data.results.unshift(def); + } + } + } + + if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) { + render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>"); + return; + } + + results.empty(); + self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null}); + + if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) { + results.append("<li class='select2-more-results'>" + self.opts.escapeMarkup(opts.formatLoadMore(this.resultsPage)) + "</li>"); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } + + this.postprocessResults(data, initial); + + postRender(); + })}); + }, + + // abstract + cancel: function () { + this.close(); + }, + + // abstract + blur: function () { + this.close(); + this.container.removeClass("select2-container-active"); + this.dropdown.removeClass("select2-drop-active"); + // synonymous to .is(':focus'), which is available in jquery >= 1.6 + if (this.search[0] === document.activeElement) { this.search.blur(); } + this.clearSearch(); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + }, + + // abstract + focusSearch: function () { + // need to do it here as well as in timeout so it works in IE + this.search.show(); + this.search.focus(); + + /* we do this in a timeout so that current event processing can complete before this code is executed. + this makes sure the search field is focussed even if the current event would blur it */ + window.setTimeout(this.bind(function () { + // reset the value so IE places the cursor at the end of the input box + this.search.show(); + this.search.focus(); + this.search.val(this.search.val()); + }), 10); + }, + + // abstract + selectHighlighted: function () { + var index=this.highlight(), + highlighted=this.results.find(".select2-highlighted").not(".select2-disabled"), + data = highlighted.closest('.select2-result-selectable').data("select2-data"); + if (data) { + highlighted.addClass("select2-disabled"); + this.highlight(index); + this.onSelect(data); + } + }, + + // abstract + getPlaceholder: function () { + return this.opts.element.attr("placeholder") || + this.opts.element.attr("data-placeholder") || // jquery 1.4 compat + this.opts.element.data("placeholder") || + this.opts.placeholder; + }, + + /** + * Get the desired width for the container element. This is + * derived first from option `width` passed to select2, then + * the inline 'style' on the original element, and finally + * falls back to the jQuery calculated element width. + */ + // abstract + initContainerWidth: function () { + function resolveContainerWidth() { + var style, attrs, matches, i, l; + + if (this.opts.width === "off") { + return null; + } else if (this.opts.width === "element"){ + return this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'; + } else if (this.opts.width === "copy" || this.opts.width === "resolve") { + // check if there is inline style on the element that contains width + style = this.opts.element.attr('style'); + if (style !== undefined) { + attrs = style.split(';'); + for (i = 0, l = attrs.length; i < l; i = i + 1) { + matches = attrs[i].replace(/\s/g, '') + .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/); + if (matches !== null && matches.length >= 1) + return matches[1]; + } + } + + if (this.opts.width === "resolve") { + // next check if css('width') can resolve a width that is percent based, this is sometimes possible + // when attached to input type=hidden or elements hidden via css + style = this.opts.element.css('width'); + if (style.indexOf("%") > 0) return style; + + // finally, fallback on the calculated width of the element + return (this.opts.element.outerWidth() === 0 ? 'auto' : this.opts.element.outerWidth() + 'px'); + } + + return null; + } else if ($.isFunction(this.opts.width)) { + return this.opts.width(); + } else { + return this.opts.width; + } + }; + + var width = resolveContainerWidth.call(this); + if (width !== null) { + this.container.attr("style", "width: "+width); + } + } + }); + + SingleSelect2 = clazz(AbstractSelect2, { + + // single + + createContainer: function () { + var container = $("<div></div>", { + "class": "select2-container" + }).html([ + " <a href='#' onclick='return false;' class='select2-choice'>", + " <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>", + " <div><b></b></div>" , + "</a>", + " <div class='select2-drop select2-offscreen'>" , + " <div class='select2-search'>" , + " <input type='text' autocomplete='off' class='select2-input'/>" , + " </div>" , + " <ul class='select2-results'>" , + " </ul>" , + "</div>"].join("")); + return container; + }, + + // single + opening: function () { + this.search.show(); + this.parent.opening.apply(this, arguments); + this.dropdown.removeClass("select2-offscreen"); + }, + + // single + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show(); + }, + + // single + focus: function () { + this.close(); + this.selection.focus(); + }, + + // single + isFocused: function () { + return this.selection[0] === document.activeElement; + }, + + // single + cancel: function () { + this.parent.cancel.apply(this, arguments); + this.selection.focus(); + }, + + // single + initContainer: function () { + + var selection, + container = this.container, + dropdown = this.dropdown, + clickingInside = false; + + this.selection = selection = container.find(".select2-choice"); + + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + return; + } + + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.TAB: + case KEY.ENTER: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } else { + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + this.open(); + + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes + return; + } + } + })); + + this.search.bind("focus", this.bind(function() { + this.selection.attr("tabIndex", "-1"); + })); + this.search.bind("blur", this.bind(function() { + if (!this.opened()) this.container.removeClass("select2-container-active"); + window.setTimeout(this.bind(function() { this.selection.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); + + selection.bind("mousedown", this.bind(function (e) { + clickingInside = true; + + if (this.opened()) { + this.close(); + this.selection.focus(); + } else if (this.enabled) { + this.open(); + } + + clickingInside = false; + })); + + dropdown.bind("mousedown", this.bind(function() { this.search.focus(); })); + + selection.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + // hide the search so the tab key does not focus on it + this.search.attr("tabIndex", "-1"); + })); + + selection.bind("blur", this.bind(function() { + if (!this.opened()) { + this.container.removeClass("select2-container-active"); + } + window.setTimeout(this.bind(function() { this.search.attr("tabIndex", this.opts.element.attr("tabIndex")); }), 10); + })); + + selection.bind("keydown", this.bind(function(e) { + if (!this.enabled) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + return; + } + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + if (e.which == KEY.DELETE) { + if (this.opts.allowClear) { + this.clear(); + } + return; + } + + this.open(); + + if (e.which === KEY.ENTER) { + // do not propagate the event otherwise we open, and propagate enter which closes + killEvent(e); + return; + } + + // do not set the search input value for non-alpha-numeric keys + // otherwise pressing down results in a '(' being set in the search field + if (e.which < 48 ) { // '0' == 48 + killEvent(e); + return; + } + + var keyWritten = String.fromCharCode(e.which).toLowerCase(); + + if (e.shiftKey) { + keyWritten = keyWritten.toUpperCase(); + } + + // focus the field before calling val so the cursor ends up after the value instead of before + this.search.focus(); + this.search.val(keyWritten); + + // prevent event propagation so it doesnt replay on the now focussed search field and result in double key entry + killEvent(e); + })); + + selection.delegate("abbr", "mousedown", this.bind(function (e) { + if (!this.enabled) return; + this.clear(); + killEvent(e); + this.close(); + this.triggerChange(); + this.selection.focus(); + })); + + this.setPlaceholder(); + + this.search.bind("focus", this.bind(function() { + this.container.addClass("select2-container-active"); + })); + }, + + // single + clear: function() { + this.opts.element.val(""); + this.selection.find("span").empty(); + this.selection.removeData("select2-data"); + this.setPlaceholder(); + }, + + /** + * Sets selection based on source element's value + */ + // single + initSelection: function () { + var selected; + if (this.opts.element.val() === "") { + this.close(); + this.setPlaceholder(); + } else { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(selected){ + if (selected !== undefined && selected !== null) { + self.updateSelection(selected); + self.close(); + self.setPlaceholder(); + } + }); + } + }, + + // single + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments); + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install the selection initializer + opts.initSelection = function (element, callback) { + var selected = element.find(":selected"); + // a single select box always has a value, no need to null check 'selected' + if ($.isFunction(callback)) + callback({id: selected.attr("value"), text: selected.text()}); + }; + } + + return opts; + }, + + // single + setPlaceholder: function () { + var placeholder = this.getPlaceholder(); + + if (this.opts.element.val() === "" && placeholder !== undefined) { + + // check for a first blank option if attached to a select + if (this.select && this.select.find("option:first").text() !== "") return; + + this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); + + this.selection.addClass("select2-default"); + + this.selection.find("abbr").hide(); + } + }, + + // single + postprocessResults: function (data, initial) { + var selected = 0, self = this, showSearchInput = true; + + // find the selected element in the result list + + this.results.find(".select2-result-selectable").each2(function (i, elm) { + if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { + selected = i; + return false; + } + }); + + // and highlight it + + this.highlight(selected); + + // hide the search box if this is the first we got the results and there are a few of them + + if (initial === true) { + showSearchInput = this.showSearchInput = countResults(data.results) >= this.opts.minimumResultsForSearch; + this.dropdown.find(".select2-search")[showSearchInput ? "removeClass" : "addClass"]("select2-search-hidden"); + + //add "select2-with-searchbox" to the container if search box is shown + $(this.dropdown, this.container)[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox"); + } + + }, + + // single + onSelect: function (data) { + var old = this.opts.element.val(); + + this.opts.element.val(this.id(data)); + this.updateSelection(data); + this.close(); + this.selection.focus(); + + if (!equal(old, this.id(data))) { this.triggerChange(); } + }, + + // single + updateSelection: function (data) { + + var container=this.selection.find("span"), formatted; + + this.selection.data("select2-data", data); + + container.empty(); + formatted=this.opts.formatSelection(data, container); + if (formatted !== undefined) { + container.append(this.opts.escapeMarkup(formatted)); + } + + this.selection.removeClass("select2-default"); + + if (this.opts.allowClear && this.getPlaceholder() !== undefined) { + this.selection.find("abbr").show(); + } + }, + + // single + val: function () { + var val, data = null, self = this; + + if (arguments.length === 0) { + return this.opts.element.val(); + } + + val = arguments[0]; + + if (this.select) { + this.select + .val(val) + .find(":selected").each2(function (i, elm) { + data = {id: elm.attr("value"), text: elm.text()}; + return false; + }); + this.updateSelection(data); + this.setPlaceholder(); + } else { + if (this.opts.initSelection === undefined) { + throw new Error("cannot call val() if initSelection() is not defined"); + } + // val is an id. !val is true for [undefined,null,''] + if (!val) { + this.clear(); + return; + } + this.opts.element.val(val); + this.opts.initSelection(this.opts.element, function(data){ + self.opts.element.val(!data ? "" : self.id(data)); + self.updateSelection(data); + self.setPlaceholder(); + }); + } + }, + + // single + clearSearch: function () { + this.search.val(""); + }, + + // single + data: function(value) { + var data; + + if (arguments.length === 0) { + data = this.selection.data("select2-data"); + if (data == undefined) data = null; + return data; + } else { + if (!value || value === "") { + this.clear(); + } else { + this.opts.element.val(!value ? "" : this.id(value)); + this.updateSelection(value); + } + } + } + }); + + MultiSelect2 = clazz(AbstractSelect2, { + + // multi + createContainer: function () { + var container = $("<div></div>", { + "class": "select2-container select2-container-multi" + }).html([ + " <ul class='select2-choices'>", + //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" , + " <li class='select2-search-field'>" , + " <input type='text' autocomplete='off' class='select2-input'>" , + " </li>" , + "</ul>" , + "<div class='select2-drop select2-drop-multi' style='display:none;'>" , + " <ul class='select2-results'>" , + " </ul>" , + "</div>"].join("")); + return container; + }, + + // multi + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments); + + // TODO validate placeholder is a string if specified + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install sthe selection initializer + opts.initSelection = function (element,callback) { + + var data = []; + element.find(":selected").each2(function (i, elm) { + data.push({id: elm.attr("value"), text: elm.text()}); + }); + + if ($.isFunction(callback)) + callback(data); + }; + } + + return opts; + }, + + // multi + initContainer: function () { + + var selector = ".select2-choices", selection; + + this.searchContainer = this.container.find(".select2-search-field"); + this.selection = selection = this.container.find(selector); + + this.search.bind("keydown", this.bind(function (e) { + if (!this.enabled) return; + + if (e.which === KEY.BACKSPACE && this.search.val() === "") { + this.close(); + + var choices, + selected = selection.find(".select2-search-choice-focus"); + if (selected.length > 0) { + this.unselect(selected.first()); + this.search.width(10); + killEvent(e); + return; + } + + choices = selection.find(".select2-search-choice"); + if (choices.length > 0) { + choices.last().addClass("select2-search-choice-focus"); + } + } else { + selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + } + + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.ENTER: + case KEY.TAB: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + return; + } + + this.open(); + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + } + })); + + this.search.bind("keyup", this.bind(this.resizeSearch)); + + this.search.bind("blur", this.bind(function(e) { + this.container.removeClass("select2-container-active"); + this.search.removeClass("select2-focused"); + this.clearSearch(); + e.stopImmediatePropagation(); + })); + + this.container.delegate(selector, "mousedown", this.bind(function (e) { + if (!this.enabled) return; + if ($(e.target).closest(".select2-search-choice").length > 0) { + // clicked inside a select2 search choice, do not open + return; + } + this.clearPlaceholder(); + this.open(); + this.focusSearch(); + e.preventDefault(); + })); + + this.container.delegate(selector, "focus", this.bind(function () { + if (!this.enabled) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + this.clearPlaceholder(); + })); + + // set the placeholder if necessary + this.clearSearch(); + }, + + // multi + enable: function() { + if (this.enabled) return; + + this.parent.enable.apply(this, arguments); + + this.search.removeAttr("disabled"); + }, + + // multi + disable: function() { + if (!this.enabled) return; + + this.parent.disable.apply(this, arguments); + + this.search.attr("disabled", true); + }, + + // multi + initSelection: function () { + var data; + if (this.opts.element.val() === "") { + this.updateSelection([]); + this.close(); + // set the placeholder if necessary + this.clearSearch(); + } + if (this.select || this.opts.element.val() !== "") { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(data){ + if (data !== undefined && data !== null) { + self.updateSelection(data); + self.close(); + // set the placeholder if necessary + self.clearSearch(); + } + }); + } + }, + + // multi + clearSearch: function () { + var placeholder = this.getPlaceholder(); + + if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { + this.search.val(placeholder).addClass("select2-default"); + // stretch the search box to full width of the container so as much of the placeholder is visible as possible + this.resizeSearch(); + } else { + // we set this to " " instead of "" and later clear it on focus() because there is a firefox bug + // that does not properly render the caret when the field starts out blank + this.search.val(" ").width(10); + } + }, + + // multi + clearPlaceholder: function () { + if (this.search.hasClass("select2-default")) { + this.search.val("").removeClass("select2-default"); + } else { + // work around for the space character we set to avoid firefox caret bug + if (this.search.val() === " ") this.search.val(""); + } + }, + + // multi + opening: function () { + this.parent.opening.apply(this, arguments); + + this.clearPlaceholder(); + this.resizeSearch(); + this.focusSearch(); + }, + + // multi + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + }, + + // multi + focus: function () { + this.close(); + this.search.focus(); + }, + + // multi + isFocused: function () { + return this.search.hasClass("select2-focused"); + }, + + // multi + updateSelection: function (data) { + var ids = [], filtered = [], self = this; + + // filter out duplicates + $(data).each(function () { + if (indexOf(self.id(this), ids) < 0) { + ids.push(self.id(this)); + filtered.push(this); + } + }); + data = filtered; + + this.selection.find(".select2-search-choice").remove(); + $(data).each(function () { + self.addSelectedChoice(this); + }); + self.postprocessResults(); + }, + + tokenize: function() { + var input = this.search.val(); + input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); + if (input != null && input != undefined) { + this.search.val(input); + if (input.length > 0) { + this.open(); + } + } + + }, + + // multi + onSelect: function (data) { + this.addSelectedChoice(data); + if (this.select) { this.postprocessResults(); } + + if (this.opts.closeOnSelect) { + this.close(); + this.search.width(10); + } else { + if (this.countSelectableResults()>0) { + this.search.width(10); + this.resizeSearch(); + this.positionDropdown(); + } else { + // if nothing left to select close + this.close(); + } + } + + // since its not possible to select an element that has already been + // added we do not need to check if this is a new element before firing change + this.triggerChange({ added: data }); + + this.focusSearch(); + }, + + // multi + cancel: function () { + this.close(); + this.focusSearch(); + }, + + // multi + addSelectedChoice: function (data) { + var choice=$( + "<li class='select2-search-choice'>" + + " <div></div>" + + " <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a>" + + "</li>"), + id = this.id(data), + val = this.getVal(), + formatted; + + formatted=this.opts.formatSelection(data, choice); + choice.find("div").replaceWith("<div>"+this.opts.escapeMarkup(formatted)+"</div>"); + choice.find(".select2-search-choice-close") + .bind("mousedown", killEvent) + .bind("click dblclick", this.bind(function (e) { + if (!this.enabled) return; + + $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ + this.unselect($(e.target)); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + this.close(); + this.focusSearch(); + })).dequeue(); + killEvent(e); + })).bind("focus", this.bind(function () { + if (!this.enabled) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + })); + + choice.data("select2-data", data); + choice.insertBefore(this.searchContainer); + + val.push(id); + this.setVal(val); + }, + + // multi + unselect: function (selected) { + var val = this.getVal(), + data, + index; + + selected = selected.closest(".select2-search-choice"); + + if (selected.length === 0) { + throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; + } + + data = selected.data("select2-data"); + + index = indexOf(this.id(data), val); + + if (index >= 0) { + val.splice(index, 1); + this.setVal(val); + if (this.select) this.postprocessResults(); + } + selected.remove(); + this.triggerChange({ removed: data }); + }, + + // multi + postprocessResults: function () { + var val = this.getVal(), + choices = this.results.find(".select2-result-selectable"), + compound = this.results.find(".select2-result-with-children"), + self = this; + + choices.each2(function (i, choice) { + var id = self.id(choice.data("select2-data")); + if (indexOf(id, val) >= 0) { + choice.addClass("select2-disabled").removeClass("select2-result-selectable"); + } else { + choice.removeClass("select2-disabled").addClass("select2-result-selectable"); + } + }); + + compound.each2(function(i, e) { + if (e.find(".select2-result-selectable").length==0) { + e.addClass("select2-disabled"); + } else { + e.removeClass("select2-disabled"); + } + }); + + choices.each2(function (i, choice) { + if (!choice.hasClass("select2-disabled") && choice.hasClass("select2-result-selectable")) { + self.highlight(0); + return false; + } + }); + + }, + + // multi + resizeSearch: function () { + + var minimumWidth, left, maxWidth, containerLeft, searchWidth, + sideBorderPadding = getSideBorderPadding(this.search); + + minimumWidth = measureTextWidth(this.search) + 10; + + left = this.search.offset().left; + + maxWidth = this.selection.width(); + containerLeft = this.selection.offset().left; + + searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; + if (searchWidth < minimumWidth) { + searchWidth = maxWidth - sideBorderPadding; + } + + if (searchWidth < 40) { + searchWidth = maxWidth - sideBorderPadding; + } + this.search.width(searchWidth); + }, + + // multi + getVal: function () { + var val; + if (this.select) { + val = this.select.val(); + return val === null ? [] : val; + } else { + val = this.opts.element.val(); + return splitVal(val, this.opts.separator); + } + }, + + // multi + setVal: function (val) { + var unique; + if (this.select) { + this.select.val(val); + } else { + unique = []; + // filter out duplicates + $(val).each(function () { + if (indexOf(this, unique) < 0) unique.push(this); + }); + this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); + } + }, + + // multi + val: function () { + var val, data = [], self=this; + + if (arguments.length === 0) { + return this.getVal(); + } + + val = arguments[0]; + + if (!val) { + this.opts.element.val(""); + this.updateSelection([]); + this.clearSearch(); + return; + } + + // val is a list of ids + this.setVal(val); + + if (this.select) { + this.select.find(":selected").each(function () { + data.push({id: $(this).attr("value"), text: $(this).text()}); + }); + this.updateSelection(data); + } else { + if (this.opts.initSelection === undefined) { + throw new Error("val() cannot be called if initSelection() is not defined") + } + + this.opts.initSelection(this.opts.element, function(data){ + var ids=$(data).map(self.id); + self.setVal(ids); + self.updateSelection(data); + self.clearSearch(); + }); + } + this.clearSearch(); + }, + + // multi + onSortStart: function() { + if (this.select) { + throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead."); + } + + // collapse search field into 0 width so its container can be collapsed as well + this.search.width(0); + // hide the container + this.searchContainer.hide(); + }, + + // multi + onSortEnd:function() { + + var val=[], self=this; + + // show search and move it to the end of the list + this.searchContainer.show(); + // make sure the search container is the last item in the list + this.searchContainer.appendTo(this.searchContainer.parent()); + // since we collapsed the width in dragStarted, we resize it here + this.resizeSearch(); + + // update selection + + this.selection.find(".select2-search-choice").each(function() { + val.push(self.opts.id($(this).data("select2-data"))); + }); + this.setVal(val); + this.triggerChange(); + }, + + // multi + data: function(values) { + var self=this, ids; + if (arguments.length === 0) { + return this.selection + .find(".select2-search-choice") + .map(function() { return $(this).data("select2-data"); }) + .get(); + } else { + if (!values) { values = []; } + ids = $.map(values, function(e) { return self.opts.id(e)}); + this.setVal(ids); + this.updateSelection(values); + this.clearSearch(); + } + } + }); + + $.fn.select2 = function () { + + var args = Array.prototype.slice.call(arguments, 0), + opts, + select2, + value, multiple, allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "disable", "positionDropdown", "data"]; + + this.each(function () { + if (args.length === 0 || typeof(args[0]) === "object") { + opts = args.length === 0 ? {} : $.extend({}, args[0]); + opts.element = $(this); + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + multiple = opts.element.attr("multiple"); + } else { + multiple = opts.multiple || false; + if ("tags" in opts) {opts.multiple = multiple = true;} + } + + select2 = multiple ? new MultiSelect2() : new SingleSelect2(); + select2.init(opts); + } else if (typeof(args[0]) === "string") { + + if (indexOf(args[0], allowedMethods) < 0) { + throw "Unknown method: " + args[0]; + } + + value = undefined; + select2 = $(this).data("select2"); + if (select2 === undefined) return; + if (args[0] === "container") { + value=select2.container; + } else { + value = select2[args[0]].apply(select2, args.slice(1)); + } + if (value !== undefined) {return false;} + } else { + throw "Invalid arguments to select2 plugin: " + args; + } + }); + return (value === undefined) ? this : value; + }; + + // plugin defaults, accessible to users + $.fn.select2.defaults = { + width: "copy", + closeOnSelect: true, + openOnEnter: true, + containerCss: {}, + dropdownCss: {}, + containerCssClass: "", + dropdownCssClass: "", + formatResult: function(result, container, query) { + var markup=[]; + markMatch(result.text, query.term, markup); + return markup.join(""); + }, + formatSelection: function (data, container) { + return data.text; + }, + formatResultCssClass: function(data) {return undefined;}, + formatNoMatches: function () { return "No matches found"; }, + formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; }, + formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, + formatLoadMore: function (pageNumber) { return "Loading more results..."; }, + formatSearching: function () { return "Searching..."; }, + minimumResultsForSearch: 0, + minimumInputLength: 0, + maximumSelectionSize: 0, + id: function (e) { return e.id; }, + matcher: function(term, text) { + return text.toUpperCase().indexOf(term.toUpperCase()) >= 0; + }, + separator: ",", + tokenSeparators: [], + tokenizer: defaultTokenizer, + escapeMarkup: function (markup) { + if (markup && typeof(markup) === "string") { + return markup.replace(/&/g, "&"); + } + return markup; + }, + blurOnChange: false + }; + + // exports + window.Select2 = { + query: { + ajax: ajax, + local: local, + tags: tags + }, util: { + debounce: debounce, + markMatch: markMatch + }, "class": { + "abstract": AbstractSelect2, + "single": SingleSelect2, + "multi": MultiSelect2 + } + }; + +}(jQuery)); diff --git a/addons/base_import/static/lib/select2/select2.png b/addons/base_import/static/lib/select2/select2.png Binary files differnew file mode 100644 index 00000000000..1d804ffb996 --- /dev/null +++ b/addons/base_import/static/lib/select2/select2.png diff --git a/addons/base_import/static/lib/select2/select2x2.png b/addons/base_import/static/lib/select2/select2x2.png Binary files differnew file mode 100644 index 00000000000..4bdd5c961d4 --- /dev/null +++ b/addons/base_import/static/lib/select2/select2x2.png diff --git a/addons/base_import/static/lib/select2/spinner.gif b/addons/base_import/static/lib/select2/spinner.gif Binary files differnew file mode 100755 index 00000000000..5b33f7e54f4 --- /dev/null +++ b/addons/base_import/static/lib/select2/spinner.gif diff --git a/addons/base_import/static/src/css/import.css b/addons/base_import/static/src/css/import.css new file mode 100644 index 00000000000..f6258f5aa50 --- /dev/null +++ b/addons/base_import/static/src/css/import.css @@ -0,0 +1,57 @@ +.openerp .oe_list_buttons .oe_alternative { + visibility: visible; +} +.openerp .oe_list_buttons.oe_editing .oe_list_button_import { + display: none; +} + +.oe_import dd, +.oe_import .oe_import_toggled, +.oe_import .oe_import_grid, +.oe_import .oe_import_error_report, +.oe_import .oe_import_with_file, +.oe_import .oe_import_noheaders { + display: none; +} + +.oe_import.oe_import_preview .oe_import_grid { + display: table; +} +.oe_import.oe_import_error .oe_import_error_report, +.oe_import.oe_import_with_file .oe_import_with_file, +.oe_import.oe_import_noheaders .oe_import_noheaders { + display: block; +} + +.oe_import .oe_import_error_report ul .oe_import_report_error { + background-color: #FFD9DB; +} +.oe_import .oe_import_error_report ul .oe_import_report_warning { + background-color: #FEFFD9; +} + +.oe_import .oe_import_noheaders { + color: #888; +} + +.oe_import a.oe_import_toggle { + display: block; +} +.oe_import a.oe_import_toggle:before { + content: '> ' +} + +.oe_import .oe_import_options p { + margin: 0; + padding: 0; +} +.oe_import .oe_import_options label { + display: inline-block; + width: 10em; + text-align: right; +} + +.oe_import_selector ul, +.oe_import_selector li { + margin: 0; padding: 0; +} diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js new file mode 100644 index 00000000000..c2deb660ff2 --- /dev/null +++ b/addons/base_import/static/src/js/import.js @@ -0,0 +1,291 @@ +openerp.base_import = function (instance) { + var QWeb = instance.web.qweb; + var _t = instance.web._t; + var _lt = instance.web._lt; + + /** + * Safari does not deal well at all with raw JSON data being + * returned. As a result, we're going to cheat by using a + * pseudo-jsonp: instead of getting JSON data in the iframe, we're + * getting a ``script`` tag which consists of a function call and + * the returned data (the json dump). + * + * The function is an auto-generated name bound to ``window``, + * which calls back into the callback provided here. + * + * @param {Object} form the form element (DOM or jQuery) to use in the call + * @param {Object} attributes jquery.form attributes object + * @param {Function} callback function to call with the returned data + */ + function jsonp(form, attributes, callback) { + attributes = attributes || {}; + var options = {jsonp: _.uniqueId('import_callback_')}; + window[options.jsonp] = function () { + delete window[options.jsonp]; + callback.apply(null, arguments); + }; + if ('data' in attributes) { + _.extend(attributes.data, options); + } else { + _.extend(attributes, {data: options}); + } + _.extend(attributes, { + dataType: 'script', + }); + $(form).ajaxSubmit(attributes); + } + + // if true, the 'Import', 'Export', etc... buttons will be shown + instance.web.ListView.prototype.defaults.import_enabled = true; + instance.web.ListView.include({ + on_loaded: function () { + var self = this; + var add_button = false; + if (!this.$buttons) { + add_button = true; + } + this._super.apply(this, arguments); + if(add_button) { + this.$buttons.on('click', '.oe_list_button_import', function() { + new instance.web.DataImport(self, self.dataset).open(); + return false; + }); + } + } + }); + + instance.web.DataImport = instance.web.Dialog.extend({ + template: 'ImportView', + dialog_title: _lt("Import Data"), + opts: [ + {name: 'encoding', label: _lt("Encoding:"), value: 'utf-8'}, + {name: 'separator', label: _lt("Separator:"), value: ','}, + {name: 'quoting', label: _lt("Quoting:"), value: '"'} + ], + events: { + 'change .oe_import_grid input': 'import_dryrun', + 'change input.oe_import_file': 'file_update', + 'change input.oe_import_has_header, .oe_import_options input': 'settings_updated', + 'click a.oe_import_csv': function (e) { + e.preventDefault(); + }, + 'click a.oe_import_export': function (e) { + e.preventDefault(); + }, + 'click a.oe_import_toggle': function (e) { + e.preventDefault(); + var $el = $(e.target); + ($el.next().length + ? $el.next() + : $el.parent().next()) + .toggle(); + } + }, + init: function (parent, dataset) { + var self = this; + this._super(parent, { + buttons: [ + {text: _t("Import File"), click: function () { + self.do_import(); + }, 'class': 'oe_import_dialog_button'} + ] + }); + this.res_model = parent.model; + // import object id + this.id = null; + this.Import = new instance.web.Model('base_import.import'); + }, + start: function () { + var self = this; + return this.Import.call('create', [{ + 'res_model': this.res_model + }]).then(function (id) { + self.id = id; + self.$('input[name=import_id]').val(id); + }); + }, + + import_options: function () { + var self = this; + var options = { + headers: this.$('input.oe_import_has_header').prop('checked') + }; + _(this.opts).each(function (opt) { + options[opt.name] = + self.$('input.oe_import_' + opt.name).val(); + }); + return options; + }, + + //- File & settings change section + file_update: function (e) { + if (!this.$('input.oe_import_file').val()) { return; } + + this.$el.removeClass('oe_import_preview oe_import_error'); + jsonp(this.$el, { + url: '/base_import/set_file' + }, this.proxy('settings_updated')); + }, + settings_updated: function () { + this.$el.addClass('oe_import_with_file'); + // TODO: test that write // succeeded? + this.Import.call( + 'parse_preview', [this.id, this.import_options()]) + .then(this.proxy('preview')); + }, + preview: function (result) { + this.$el.toggleClass( + 'oe_import_noheaders', + !this.$('input.oe_import_has_header').prop('checked')); + if (result.error) { + this.$el.addClass('oe_import_error'); + this.$('.oe_import_error_report').html( + QWeb.render('ImportView.preview.error', result)); + return; + } + this.$el.addClass('oe_import_preview'); + this.$('table').html(QWeb.render('ImportView.preview', result)); + + var $fields = this.$('.oe_import_fields input'); + this.render_fields_matches(result, $fields); + var data = this.generate_fields_completion(result); + var item_finder = function (id, items) { + items = items || data; + for (var i=0; i < items.length; ++i) { + var item = items[i]; + if (item.id === id) { + return item; + } + var val; + if (item.children && (val = item_finder(id, item.children))) { + return val; + } + } + return ''; + }; + $fields.select2({ + allowClear: true, + minimumInputLength: 0, + data: data, + initSelection: function (element, callback) { + var default_value = element.val(); + if (!default_value) { + callback(''); + return; + } + + callback(item_finder(default_value)); + }, + + width: 'resolve', + dropdownCssClass: 'oe_import_selector' + }); + this.import_dryrun(); + }, + generate_fields_completion: function (root) { + var basic = []; + var regulars = []; + var o2m = []; + function traverse(field, ancestors, collection) { + var subfields = field.fields; + var field_path = ancestors.concat(field); + var label = _(field_path).pluck('string').join(' / '); + var id = _(field_path).pluck('name').join('/'); + + // If non-relational, m2o or m2m, collection is regulars + if (!collection) { + if (field.name === 'id') { + collection = basic + } else if (_.isEmpty(subfields) + || _.isEqual(_.pluck(subfields, 'name'), ['id', '.id'])) { + collection = regulars; + } else { + collection = o2m; + } + } + + collection.push({ + id: id, + text: label, + required: field.required + }); + + for(var i=0, end=subfields.length; i<end; ++i) { + traverse(subfields[i], field_path, collection); + } + } + _(root.fields).each(function (field) { + traverse(field, []); + }); + + var cmp = function (field1, field2) { + return field1.text.localeCompare(field2.text); + + }; + regulars.sort(cmp); + o2m.sort(cmp); + return basic.concat([ + { text: _t("Normal Fields"), children: regulars }, + { text: _t("Relation Fields"), children: o2m } + ]); + }, + render_fields_matches: function (result, $fields) { + if (_(result.matches).isEmpty()) { return; } + $fields.each(function (index, input) { + var match = result.matches[index]; + if (!match) { return; } + + var current_field = result; + input.value = _(match).chain() + .map(function (name) { + // WARNING: does both mapping and folding (over the + // ``field`` iterator variable) + return current_field = _(current_field.fields).find(function (subfield) { + return subfield.name === name; + }); + }) + .pluck('name') + .value() + .join('/'); + }); + }, + + //- import itself + call_import: function (options) { + var self = this; + var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { + return $(el).select2('val') || false; + }).get(); + return this.Import.call( + 'do', [this.id, fields, this.import_options()], options); + }, + import_dryrun: function () { +// this.call_import({ dryrun: true }) +// .then(this.proxy('render_import_errors')); + }, + do_import: function () { + var self = this; + this.call_import({ dryrun: false }).then(function (errors) { + if (_.isEmpty(errors)) { + if (self.getParent().reload_content) { + self.getParent().reload_content(); + } + self.close(); + return; + } + self.render_import_errors(errors); + }); + }, + render_import_errors: function (errors) { + if (_.isEmpty(errors)) { + this.$el.removeClass('oe_import_error'); + return; + } + // import failed (or maybe just warnings, if we ever get + // warnings?) + this.$el.addClass('oe_import_error'); + this.$('.oe_import_error_report').html( + QWeb.render('ImportView.error', {errors: errors})); + }, + }); +}; diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml new file mode 100644 index 00000000000..386348bea4b --- /dev/null +++ b/addons/base_import/static/src/xml/import.xml @@ -0,0 +1,103 @@ +<templates> + <t t-name="ImportView"> + <t t-set="_id" t-value="_.uniqueId('export')"/> + <form action="" method="post" enctype="multipart/form-data" class="oe_import"> + <input type="hidden" name="session_id" + t-att-value="widget.session.session_id"/> + <input type="hidden" name="import_id"/> + <h2>Upload your file</h2> + <p>Select the <a href="#" class="oe_import_csv">.CSV</a> + file to import. If you need a sample importable file, you + can use <a href="#" class="oe_import_export">the export + tool</a> to generate one.</p> + <label t-attf-for="file_#{_id}" autofocus="autofocus">CSV File:</label> + <input type="file" id-attf-id="file_#{_id}" + name="file" class="oe_import_file"/> + + <div class="oe_import_with_file"> + <h2>Map your data to OpenERP</h2> + <input type="checkbox" class="oe_import_has_header" + id="oe_import_has_header" checked="checked"/> + <label for="oe_import_has_header">The first row of the + file contains the label of the column</label> + <p class="oe_import_noheaders">If the file contains + the column names, OpenERP can try auto-detecting the + field corresponding to the column. This makes imports + simpler especially when the file has many columns.</p> + + <div class="oe_import_error_report"></div> + <table class="oe_import_grid" width="100%"/> + <a href="#" class="oe_import_toggle"> + File Format Options…</a> + <div class="oe_import_toggled oe_import_options"> + <p t-foreach="widget.opts" t-as="option"> + <!-- no @name, avoid submission when file_update called --> + <label t-attf-for="#{option.name}_#{_id}"> + <t t-esc="option.label"/></label> + <input t-attf-id="#{option.name}_#{_id}" + t-attf-class="oe_import_#{option.name}" + t-att-value="option.value"/> + </p> + </div> + + <h2>Frequently Asked Questions</h2> + <dl> + <dt><a href="#" class="oe_import_toggle"> + Need to import data from an other application?</a></dt> + <dd> + <p>In order to re-create relationships between + different records, you should use the unique + identifier from the original application and + map it to the <abbr title="External ID">ID</abbr> + column in OpenERP. When you + import an other record that links to the first + one, use <abbr title="XXX/External ID">XXX/ID</abbr> + to the original unique identifier.</p> + <p>The <abbr title="External ID">ID</abbr> + will also be used to update the original + import if you need to re-import modified data + later, it's thus good practice to specify it + whenever possible</p> + </dd> + </dl> + </div> + </form> + </t> + <t t-name="ImportView.preview"> + <tr t-if="headers" class="oe_import_grid-header"> + <td t-foreach="headers" t-as="header" class="oe_import_grid-cell" + ><t t-esc="header"/></td> + </tr> + <tr class="oe_import_fields"> + <!-- Iterate on first row to ensure we have all columns --> + <td t-foreach="preview[0]" t-as="column"> + <input placeholder="Don't Import" + class="oe_import_match_field"/> + </td> + </tr> + <tr t-foreach="preview" t-as="row" class="oe_import_grid-row"> + <td t-foreach="row" t-as="cell" class="oe_import_grid-cell" + ><t t-esc="cell"/></td> + </tr> + </t> + <t t-name="ImportView.preview.error"> + <p>Import preview failed due to: <t t-esc="error"/></p> + <p>Here is the start of the file we could not import:</p> + <pre><t t-esc="preview"/></pre> + </t> + <ul t-name="ImportView.error"> + <li t-foreach="errors" t-as="error" t-attf-class="oe_import_report_#{error.type}"> + <!-- can also have error.record, but may be *huge* if + e.g. has image fields --> + <t t-esc="error.message"/> + </li> + </ul> + <t t-extend="ListView.buttons"> + <t t-jquery="span.oe_alternative"> + this.attr('t-if', 'widget.options.import_enabled'); + </t> + <t t-jquery="span.oe_alternative" t-operation="append"> + <a href="#" class="oe_bold oe_list_button_import">Import</a> + </t> + </t> +</templates> diff --git a/addons/base_import/test_models.py b/addons/base_import/test_models.py new file mode 100644 index 00000000000..0720ae11eda --- /dev/null +++ b/addons/base_import/test_models.py @@ -0,0 +1,101 @@ +from openerp.osv import orm, fields + +def name(n): return 'base_import.tests.models.%s' % n + +class char(orm.Model): + _name = name('char') + + _columns = { + 'value': fields.char('unknown', size=None) + } + +class char_required(orm.Model): + _name = name('char.required') + + _columns = { + 'value': fields.char('unknown', size=None, required=True) + } + +class char_readonly(orm.Model): + _name = name('char.readonly') + + _columns = { + 'value': fields.char('unknown', size=None, readonly=True) + } + +class char_states(orm.Model): + _name = name('char.states') + + _columns = { + 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', False)]}) + } + +class char_noreadonly(orm.Model): + _name = name('char.noreadonly') + + _columns = { + 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('invisible', True)]}) + } + +class char_stillreadonly(orm.Model): + _name = name('char.stillreadonly') + + _columns = { + 'value': fields.char('unknown', size=None, readonly=True, states={'draft': [('readonly', True)]}) + } + +# TODO: complex field (m2m, o2m, m2o) +class m2o(orm.Model): + _name = name('m2o') + + _columns = { + 'value': fields.many2one(name('m2o.related')) + } +class m2o_related(orm.Model): + _name = name('m2o.related') + + _columns = { + 'value': fields.integer() + } + _defaults = { + 'value': 42 + } + +class m2o_required(orm.Model): + _name = name('m2o.required') + + _columns = { + 'value': fields.many2one(name('m2o.required.related'), required=True) + } +class m2o_required_related(orm.Model): + _name = name('m2o.required.related') + + _columns = { + 'value': fields.integer() + } + _defaults = { + 'value': 42 + } + +class o2m(orm.Model): + _name = name('o2m') + + _columns = { + 'value': fields.one2many(name('o2m.child'), 'parent_id') + } +class o2m_child(orm.Model): + _name = name('o2m.child') + + _columns = { + 'parent_id': fields.many2one(name('o2m')), + 'value': fields.integer() + } + +class preview_model(orm.Model): + _name = name('preview') + + _columns = { + 'name': fields.char('Name', size=None), + 'somevalue': fields.integer('Some Value', required=True), + 'othervalue': fields.integer('Other Variable'), + } diff --git a/addons/base_import/tests/__init__.py b/addons/base_import/tests/__init__.py new file mode 100644 index 00000000000..abef28c8ca1 --- /dev/null +++ b/addons/base_import/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_cases + +checks = [test_cases] diff --git a/addons/base_import/tests/test_cases.py b/addons/base_import/tests/test_cases.py new file mode 100644 index 00000000000..5479ae94428 --- /dev/null +++ b/addons/base_import/tests/test_cases.py @@ -0,0 +1,342 @@ +# -*- encoding: utf-8 -*- +import unittest2 +from openerp.tests.common import TransactionCase + +from .. import models + +ID_FIELD = {'id': 'id', 'name': 'id', 'string': "External ID", 'required': False, 'fields': []} +def make_field(name='value', string='unknown', required=False, fields=[]): + return [ + ID_FIELD, + {'id': name, 'name': name, 'string': string, 'required': required, 'fields': fields}, + ] + +class test_basic_fields(TransactionCase): + def get_fields(self, field): + return self.registry('base_import.import')\ + .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field) + + def test_base(self): + """ A basic field is not required """ + self.assertEqual(self.get_fields('char'), make_field()) + + def test_required(self): + """ Required fields should be flagged (so they can be fill-required) """ + self.assertEqual(self.get_fields('char.required'), make_field(required=True)) + + def test_readonly(self): + """ Readonly fields should be filtered out""" + self.assertEqual(self.get_fields('char.readonly'), [ID_FIELD]) + + def test_readonly_states(self): + """ Readonly fields with states should not be filtered out""" + self.assertEqual(self.get_fields('char.states'), make_field()) + + def test_readonly_states_noreadonly(self): + """ Readonly fields with states having nothing to do with + readonly should still be filtered out""" + self.assertEqual(self.get_fields('char.noreadonly'), [ID_FIELD]) + + def test_readonly_states_stillreadonly(self): + """ Readonly fields with readonly states leaving them readonly + always... filtered out""" + self.assertEqual(self.get_fields('char.stillreadonly'), [ID_FIELD]) + + def test_m2o(self): + """ M2O fields should allow import of themselves (name_get), + their id and their xid""" + self.assertEqual(self.get_fields('m2o'), make_field(fields=[ + {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []}, + {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []}, + ])) + + def test_m2o_required(self): + """ If an m2o field is required, its three sub-fields are + required as well (the client has to handle that: requiredness + is id-based) + """ + self.assertEqual(self.get_fields('m2o.required'), make_field(required=True, fields=[ + {'id': 'value', 'name': 'id', 'string': 'External ID', 'required': True, 'fields': []}, + {'id': 'value', 'name': '.id', 'string': 'Database ID', 'required': True, 'fields': []}, + ])) + +class test_o2m(TransactionCase): + def get_fields(self, field): + return self.registry('base_import.import')\ + .get_fields(self.cr, self.uid, 'base_import.tests.models.' + field) + + def test_shallow(self): + self.assertEqual(self.get_fields('o2m'), make_field(fields=[ + {'id': 'id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []}, + # FIXME: should reverse field be ignored? + {'id': 'parent_id', 'name': 'parent_id', 'string': 'unknown', 'required': False, 'fields': [ + {'id': 'parent_id', 'name': 'id', 'string': 'External ID', 'required': False, 'fields': []}, + {'id': 'parent_id', 'name': '.id', 'string': 'Database ID', 'required': False, 'fields': []}, + ]}, + {'id': 'value', 'name': 'value', 'string': 'unknown', 'required': False, 'fields': []}, + ])) + +class test_match_headers_single(TransactionCase): + def test_match_by_name(self): + match = self.registry('base_import.import')._match_header( + 'f0', [{'name': 'f0'}], {}) + + self.assertEqual(match, [{'name': 'f0'}]) + + def test_match_by_string(self): + match = self.registry('base_import.import')._match_header( + 'some field', [{'name': 'bob', 'string': "Some Field"}], {}) + + self.assertEqual(match, [{'name': 'bob', 'string': "Some Field"}]) + + def test_nomatch(self): + match = self.registry('base_import.import')._match_header( + 'should not be', [{'name': 'bob', 'string': "wheee"}], {}) + + self.assertEqual(match, []) + + def test_recursive_match(self): + f = { + 'name': 'f0', + 'string': "My Field", + 'fields': [ + {'name': 'f0', 'string': "Sub field 0", 'fields': []}, + {'name': 'f1', 'string': "Sub field 2", 'fields': []}, + ] + } + match = self.registry('base_import.import')._match_header( + 'f0/f1', [f], {}) + + self.assertEqual(match, [f, f['fields'][1]]) + + def test_recursive_nomatch(self): + """ Match first level, fail to match second level + """ + f = { + 'name': 'f0', + 'string': "My Field", + 'fields': [ + {'name': 'f0', 'string': "Sub field 0", 'fields': []}, + {'name': 'f1', 'string': "Sub field 2", 'fields': []}, + ] + } + match = self.registry('base_import.import')._match_header( + 'f0/f2', [f], {}) + + self.assertEqual(match, []) + +class test_match_headers_multiple(TransactionCase): + def test_noheaders(self): + self.assertEqual( + self.registry('base_import.import')._match_headers( + [], [], {}), + (None, None) + ) + def test_nomatch(self): + self.assertEqual( + self.registry('base_import.import')._match_headers( + iter([ + ['foo', 'bar', 'baz', 'qux'], + ['v1', 'v2', 'v3', 'v4'], + ]), + [], + {'headers': True}), + ( + ['foo', 'bar', 'baz', 'qux'], + dict.fromkeys(range(4)) + ) + ) + + def test_mixed(self): + self.assertEqual( + self.registry('base_import.import')._match_headers( + iter(['foo bar baz qux/corge'.split()]), + [ + {'name': 'bar', 'string': 'Bar'}, + {'name': 'bob', 'string': 'Baz'}, + {'name': 'qux', 'string': 'Qux', 'fields': [ + {'name': 'corge', 'fields': []}, + ]} + ], + {'headers': True}), + (['foo', 'bar', 'baz', 'qux/corge'], { + 0: None, + 1: ['bar'], + 2: ['bob'], + 3: ['qux', 'corge'], + }) + ) + +class test_preview(TransactionCase): + def make_import(self): + Import = self.registry('base_import.import') + id = Import.create(self.cr, self.uid, { + 'res_model': 'res.users', + 'file': u"로그인,언어\nbob,1\n".encode('euc_kr'), + }) + return Import, id + + def test_encoding(self): + Import, id = self.make_import() + result = Import.parse_preview(self.cr, self.uid, id, { + 'quoting': '"', + 'separator': ',', + }) + self.assertTrue('error' in result) + + def test_csv_errors(self): + Import, id = self.make_import() + + result = Import.parse_preview(self.cr, self.uid, id, { + 'quoting': 'foo', + 'separator': ',', + 'encoding': 'euc_kr', + }) + self.assertTrue('error' in result) + + def test_csv_errors(self): + Import, id = self.make_import() + + result = Import.parse_preview(self.cr, self.uid, id, { + 'quoting': '"', + 'separator': 'bob', + 'encoding': 'euc_kr', + }) + self.assertTrue('error' in result) + + def test_success(self): + Import = self.registry('base_import.import') + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + 'bar,3,4\n' + 'qux,5,6\n' + }) + + result = Import.parse_preview(self.cr, self.uid, id, { + 'quoting': '"', + 'separator': ',', + 'headers': True, + }) + + self.assertEqual(result['matches'], {0: ['name'], 1: ['somevalue'], 2: None}) + self.assertEqual(result['headers'], ['name', 'Some Value', 'Counter']) + # Order depends on iteration order of fields_get + self.assertItemsEqual(result['fields'], [ + {'id': 'id', 'name': 'id', 'string': 'External ID', 'required':False, 'fields': []}, + {'id': 'name', 'name': 'name', 'string': 'Name', 'required':False, 'fields': []}, + {'id': 'somevalue', 'name': 'somevalue', 'string': 'Some Value', 'required':True, 'fields': []}, + {'id': 'othervalue', 'name': 'othervalue', 'string': 'Other Variable', 'required':False, 'fields': []}, + ]) + self.assertEqual(result['preview'], [ + ['foo', '1', '2'], + ['bar', '3', '4'], + ['qux', '5', '6'], + ]) + # Ensure we only have the response fields we expect + self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview']) + +class test_convert_import_data(TransactionCase): + """ Tests conversion of base_import.import input into data which + can be fed to Model.import_data + """ + def test_all(self): + Import = self.registry('base_import.import') + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + 'bar,3,4\n' + 'qux,5,6\n' + }) + record = Import.browse(self.cr, self.uid, id) + data, fields = Import._convert_import_data( + record, ['name', 'somevalue', 'othervalue'], + {'quoting': '"', 'separator': ',', 'headers': True,}) + + self.assertItemsEqual(fields, ['name', 'somevalue', 'othervalue']) + self.assertItemsEqual(data, [ + ('foo', '1', '2'), + ('bar', '3', '4'), + ('qux', '5', '6'), + ]) + + def test_filtered(self): + """ If ``False`` is provided as field mapping for a column, + that column should be removed from importable data + """ + Import = self.registry('base_import.import') + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + 'bar,3,4\n' + 'qux,5,6\n' + }) + record = Import.browse(self.cr, self.uid, id) + data, fields = Import._convert_import_data( + record, ['name', False, 'othervalue'], + {'quoting': '"', 'separator': ',', 'headers': True,}) + + self.assertItemsEqual(fields, ['name', 'othervalue']) + self.assertItemsEqual(data, [ + ('foo', '2'), + ('bar', '4'), + ('qux', '6'), + ]) + + def test_norow(self): + """ If a row is composed only of empty values (due to having + filtered out non-empty values from it), it should be removed + """ + Import = self.registry('base_import.import') + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + ',3,\n' + ',5,6\n' + }) + record = Import.browse(self.cr, self.uid, id) + data, fields = Import._convert_import_data( + record, ['name', False, 'othervalue'], + {'quoting': '"', 'separator': ',', 'headers': True,}) + + self.assertItemsEqual(fields, ['name', 'othervalue']) + self.assertItemsEqual(data, [ + ('foo', '2'), + ('', '6'), + ]) + + def test_nofield(self): + Import = self.registry('base_import.import') + + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + }) + + record = Import.browse(self.cr, self.uid, id) + self.assertRaises( + ValueError, + Import._convert_import_data, + record, [], + {'quoting': '"', 'separator': ',', 'headers': True,}) + + def test_falsefields(self): + Import = self.registry('base_import.import') + + id = Import.create(self.cr, self.uid, { + 'res_model': 'base_import.tests.models.preview', + 'file': 'name,Some Value,Counter\n' + 'foo,1,2\n' + }) + + record = Import.browse(self.cr, self.uid, id) + self.assertRaises( + ValueError, + Import._convert_import_data, + record, [False, False, False], + {'quoting': '"', 'separator': ',', 'headers': True,}) diff --git a/addons/base_module_record/i18n/nb.po b/addons/base_module_record/i18n/nb.po new file mode 100644 index 00000000000..8465596f21b --- /dev/null +++ b/addons/base_module_record/i18n/nb.po @@ -0,0 +1,271 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-04 13:40+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-05 04:46+0000\n" +"X-Generator: Launchpad (build 15901)\n" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,category:0 +msgid "Category" +msgstr "Kategori" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,save:0 +msgid "Information" +msgstr "Informasjon" + +#. module: base_module_record +#: model:ir.model,name:base_module_record.model_ir_module_record +msgid "ir.module.record" +msgstr "ir.modul.opptak" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_data,info,end:0 +#: wizard_button:base_module_record.module_record_data,save_yaml,end:0 +msgid "End" +msgstr "Slutt" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_data,init:0 +#: wizard_view:base_module_record.module_record_objects,init:0 +msgid "Choose objects to record" +msgstr "Velg objekter til opptak" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,author:0 +msgid "Author" +msgstr "Forfatter" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,directory_name:0 +msgid "Directory Name" +msgstr "Navn på katalog" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_data,init,filter_cond:0 +#: wizard_field:base_module_record.module_record_objects,init,filter_cond:0 +msgid "Records only" +msgstr "Bare opptak" + +#. module: base_module_record +#: selection:base_module_record.module_record_objects,info,data_kind:0 +msgid "Demo Data" +msgstr "Demo data" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,save,module_filename:0 +msgid "Filename" +msgstr "Filnavn" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,version:0 +msgid "Version" +msgstr "Versjon" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_data,info:0 +#: wizard_view:base_module_record.module_record_data,init:0 +#: wizard_view:base_module_record.module_record_data,save_yaml:0 +#: wizard_view:base_module_record.module_record_objects,init:0 +msgid "Objects Recording" +msgstr "objekter Innspilling" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,save:0 +msgid "" +"If you think your module could interest other people, we'd like you to " +"publish it on http://www.openerp.com, in the 'Modules' section. You can do " +"it through the website or using features of the 'base_module_publish' module." +msgstr "" +"Hvis du tror din modul kan interessere andre mennesker, vil vi gjerne at du " +"publisere den på http://www.openerp.com, i 'Moduler-delen. Du kan gjøre det " +"gjennom nettstedet eller bruke funksjonene i «base_module_publish»-modulen." + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_data,init,check_date:0 +#: wizard_field:base_module_record.module_record_objects,init,check_date:0 +msgid "Record from Date" +msgstr "Dato fra opptak" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_data,end:0 +#: wizard_view:base_module_record.module_record_objects,end:0 +#: wizard_view:base_module_record.module_record_objects,info:0 +#: wizard_view:base_module_record.module_record_objects,save:0 +#: wizard_view:base_module_record.module_record_objects,save_yaml:0 +msgid "Module Recording" +msgstr "Modul innspilling" + +#. module: base_module_record +#: model:ir.actions.wizard,name:base_module_record.wizard_base_module_record_objects +#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_module_record_objects +msgid "Export Customizations As a Module" +msgstr "Eksporter Tilpasninger som en modul" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,save:0 +msgid "Thanks in advance for your contribution." +msgstr "Takk på forhånd for ditt bidrag." + +#. module: base_module_record +#: help:base_module_record.module_record_data,init,objects:0 +#: help:base_module_record.module_record_objects,init,objects:0 +msgid "List of objects to be recorded" +msgstr "Liste over objekter som skal spilles inn" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,description:0 +msgid "Full Description" +msgstr "Full beskrivelse" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,name:0 +msgid "Module Name" +msgstr "Modulnavn" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_data,init,objects:0 +#: wizard_field:base_module_record.module_record_objects,init,objects:0 +msgid "Objects" +msgstr "Objekter" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,save,module_file:0 +#: wizard_field:base_module_record.module_record_objects,save_yaml,yaml_file:0 +msgid "Module .zip File" +msgstr "Modul .zip fil" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,save:0 +msgid "Module successfully created!" +msgstr "" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,save_yaml:0 +msgid "YAML file successfully created !" +msgstr "YAML fil opprettet!" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_data,info:0 +#: wizard_view:base_module_record.module_record_data,save_yaml:0 +msgid "Result, paste this to your module's xml" +msgstr "Resultatet, lim denne til modulen xml" + +#. module: base_module_record +#: selection:base_module_record.module_record_data,init,filter_cond:0 +#: selection:base_module_record.module_record_objects,init,filter_cond:0 +msgid "Created" +msgstr "Opprettet" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_data,end:0 +#: wizard_view:base_module_record.module_record_objects,end:0 +msgid "Thanks For using Module Recorder" +msgstr "Takk for at du brukte Modul opptaker." + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,website:0 +msgid "Documentation URL" +msgstr "Dokumentasjon URL" + +#. module: base_module_record +#: selection:base_module_record.module_record_data,init,filter_cond:0 +#: selection:base_module_record.module_record_objects,init,filter_cond:0 +msgid "Modified" +msgstr "Modifisert" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_data,init,record:0 +#: wizard_button:base_module_record.module_record_objects,init,record:0 +msgid "Record" +msgstr "Opptak" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_objects,info,save:0 +msgid "Continue" +msgstr "Fortsett" + +#. module: base_module_record +#: model:ir.actions.wizard,name:base_module_record.wizard_base_module_record_data +#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_module_record_data +msgid "Export Customizations As Data File" +msgstr "Eksport Tilpasninger Som datafil" + +#. module: base_module_record +#: code:addons/base_module_record/wizard/base_module_save.py:129 +#, python-format +msgid "Error" +msgstr "Feil" + +#. module: base_module_record +#: selection:base_module_record.module_record_objects,info,data_kind:0 +msgid "Normal Data" +msgstr "Normal data" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_data,end,end:0 +#: wizard_button:base_module_record.module_record_objects,end,end:0 +msgid "OK" +msgstr "Ok" + +#. module: base_module_record +#: model:ir.ui.menu,name:base_module_record.menu_wizard_base_mod_rec +msgid "Module Creation" +msgstr "modul Skapelsen" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_objects,info,data_kind:0 +msgid "Type of Data" +msgstr "Type data" + +#. module: base_module_record +#: wizard_view:base_module_record.module_record_objects,info:0 +msgid "Module Information" +msgstr "Modul informasjon" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_data,init,info_yaml:0 +#: wizard_field:base_module_record.module_record_objects,init,info_yaml:0 +msgid "YAML" +msgstr "YAML" + +#. module: base_module_record +#: wizard_field:base_module_record.module_record_data,info,res_text:0 +#: wizard_field:base_module_record.module_record_data,save_yaml,res_text:0 +msgid "Result" +msgstr "Resultat" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_data,init,end:0 +#: wizard_button:base_module_record.module_record_objects,info,end:0 +#: wizard_button:base_module_record.module_record_objects,init,end:0 +msgid "Cancel" +msgstr "Kanseller" + +#. module: base_module_record +#: wizard_button:base_module_record.module_record_objects,save,end:0 +#: wizard_button:base_module_record.module_record_objects,save_yaml,end:0 +msgid "Close" +msgstr "Lukke" + +#. module: base_module_record +#: selection:base_module_record.module_record_data,init,filter_cond:0 +#: selection:base_module_record.module_record_objects,init,filter_cond:0 +msgid "Created & Modified" +msgstr "Laget & Modifisert" + +#~ msgid "Module successfully created !" +#~ msgstr "Modulen opprettet!" diff --git a/addons/base_report_designer/i18n/nb.po b/addons/base_report_designer/i18n/nb.po new file mode 100644 index 00000000000..9bf2b572a5f --- /dev/null +++ b/addons/base_report_designer/i18n/nb.po @@ -0,0 +1,204 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-04 13:59+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-05 04:46+0000\n" +"X-Generator: Launchpad (build 15901)\n" + +#. module: base_report_designer +#: model:ir.model,name:base_report_designer.model_base_report_sxw +msgid "base.report.sxw" +msgstr "basen.rapport.sxw" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "OpenERP Report Designer Configuration" +msgstr "OpenERP Rapport Designer Konfigurasjon" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "" +"This plug-in allows you to create/modify OpenERP Reports into OpenOffice " +"Writer." +msgstr "" +"Denne plug-in tillater deg å lage / endre OpenERP rapporter i OpenOffice " +"Writer." + +#. module: base_report_designer +#: view:base.report.file.sxw:0 +msgid "Upload the modified report" +msgstr "Laste opp den endrede rapporten" + +#. module: base_report_designer +#: view:base.report.file.sxw:0 +msgid "The .SXW report" +msgstr ".SXW rapport" + +#. module: base_report_designer +#: model:ir.model,name:base_report_designer.model_base_report_designer_installer +msgid "base_report_designer.installer" +msgstr "basen.rapport.designer.installatør" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "_Close" +msgstr "_Lukk" + +#. module: base_report_designer +#: view:base.report.rml.save:0 +msgid "The RML Report" +msgstr "" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "Configure" +msgstr "Konfigurer" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "title" +msgstr "tittel" + +#. module: base_report_designer +#: field:base.report.file.sxw,report_id:0 +#: field:base.report.sxw,report_id:0 +msgid "Report" +msgstr "Rapport" + +#. module: base_report_designer +#: model:ir.model,name:base_report_designer.model_base_report_rml_save +msgid "base.report.rml.save" +msgstr "Basen.rapport.rml.lagre" + +#. module: base_report_designer +#: model:ir.ui.menu,name:base_report_designer.menu_action_report_designer_wizard +msgid "Report Designer" +msgstr "Rapportdesigner" + +#. module: base_report_designer +#: field:base_report_designer.installer,name:0 +msgid "File name" +msgstr "Filnavn" + +#. module: base_report_designer +#: view:base.report.file.sxw:0 +#: view:base.report.sxw:0 +msgid "Get a report" +msgstr "Få en rapport" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_wizard +msgid "OpenERP Report Designer" +msgstr "OpenERP Rapport designer" + +#. module: base_report_designer +#: view:base.report.sxw:0 +msgid "Continue" +msgstr "Fortsett" + +#. module: base_report_designer +#: field:base.report.rml.save,file_rml:0 +msgid "Save As" +msgstr "Lagre som" + +#. module: base_report_designer +#: help:base_report_designer.installer,plugin_file:0 +msgid "" +"OpenObject Report Designer plug-in file. Save as this file and install this " +"plug-in in OpenOffice." +msgstr "" +"OpenObject Report Designer plug-in-filen. Lagre som denne filen og " +"installere denne plugin-modulen i OpenOffice." + +#. module: base_report_designer +#: view:base.report.rml.save:0 +msgid "Save RML FIle" +msgstr "Lagre RML fil" + +#. module: base_report_designer +#: field:base.report.file.sxw,file_sxw:0 +#: field:base.report.file.sxw,file_sxw_upload:0 +msgid "Your .SXW file" +msgstr "Din .SXW fil" + +#. module: base_report_designer +#: view:base_report_designer.installer:0 +msgid "Installation and Configuration Steps" +msgstr "Installasjon og Konfigurasjon trinn" + +#. module: base_report_designer +#: field:base_report_designer.installer,description:0 +msgid "Description" +msgstr "Beskrivelse:" + +#. module: base_report_designer +#: view:base.report.file.sxw:0 +msgid "" +"This is the template of your requested report.\n" +"Save it as a .SXW file and open it with OpenOffice.\n" +"Don't forget to install the OpenERP SA OpenOffice package to modify it.\n" +"Once it is modified, re-upload it in OpenERP using this wizard." +msgstr "" +"Dette er malen for den forespurte rapporten.\n" +"Lagre det som en. Sxw fil og åpne den med OpenOffice.\n" +"Ikke glem å installere OpenERP SA OpenOffice-pakken til å endre det.\n" +"Når den er modifisert, laste opp det i OpenERP bruke denne veiviseren." + +#. module: base_report_designer +#: field:base_report_designer.installer,config_logo:0 +msgid "Image" +msgstr "Bilde" + +#. module: base_report_designer +#: model:ir.actions.act_window,name:base_report_designer.action_view_base_report_sxw +msgid "Base Report sxw" +msgstr "Basen rapport sxw" + +#. module: base_report_designer +#: model:ir.model,name:base_report_designer.model_base_report_file_sxw +msgid "base.report.file.sxw" +msgstr "basen.rapport.fil.sxw" + +#. module: base_report_designer +#: field:base_report_designer.installer,plugin_file:0 +msgid "OpenObject Report Designer Plug-in" +msgstr "OpenObject Rapport Designer Plug-in" + +#. module: base_report_designer +#: model:ir.actions.act_window,name:base_report_designer.action_report_designer_installer +msgid "OpenERP Report Designer Installation" +msgstr "OpenERP Rapport Designer Installasjon" + +#. module: base_report_designer +#: view:base.report.file.sxw:0 +#: view:base.report.rml.save:0 +#: view:base.report.sxw:0 +#: view:base_report_designer.installer:0 +msgid "Cancel" +msgstr "Kanseller" + +#. module: base_report_designer +#: model:ir.model,name:base_report_designer.model_ir_actions_report_xml +msgid "ir.actions.report.xml" +msgstr "ir.handlinger.rapport.xml" + +#. module: base_report_designer +#: view:base.report.sxw:0 +msgid "Select your report" +msgstr "Velg din rapport" + +#~ msgid "The RML report" +#~ msgstr "RML rapport" diff --git a/addons/base_setup/res_config.py b/addons/base_setup/res_config.py index 4826a50400d..e1920fc05d9 100644 --- a/addons/base_setup/res_config.py +++ b/addons/base_setup/res_config.py @@ -35,6 +35,7 @@ class base_config_settings(osv.osv_memory): 'module_auth_anonymous': fields.boolean('activate the public portal', help="""Enable the public part of openerp, openerp becomes a public website."""), 'module_auth_oauth': fields.boolean('use external authentication providers, sign in with google, facebook, ...'), + 'module_base_import': fields.boolean("Allow users to import data from CSV files"), } def open_company(self, cr, uid, ids, context=None): diff --git a/addons/base_setup/res_config_view.xml b/addons/base_setup/res_config_view.xml index 512c0a3f52e..2b717b6e0b1 100644 --- a/addons/base_setup/res_config_view.xml +++ b/addons/base_setup/res_config_view.xml @@ -14,9 +14,10 @@ </header> <separator string="General Settings"/> <div> - <label string="You will also find several configuration options on your company data: - address for the header and footer, overdue payments texts, etc."/> - <button type="object" name="open_company" string="Configure Your Company Data" icon="gtk-execute" class="oe_inline oe_link"/> + <p> + <label string="You will find more options in your company details: address for the header and footer, overdue payments texts, etc."/> + <button type="object" name="open_company" string="Configure Your Company Data" icon="gtk-execute" class="oe_inline oe_link"/> + </p> </div> <group> <label for="id" string="Options"/> @@ -59,6 +60,15 @@ </div> </div> </group> + <group> + <label for="id" string="Import / Export"/> + <div> + <div> + <field name="module_base_import" class="oe_inline"/> + <label for="module_base_import"/> + </div> + </div> + </group> </form> </field> </record> diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py index 775de51394c..529e70fdb49 100644 --- a/addons/base_status/base_stage.py +++ b/addons/base_status/base_stage.py @@ -297,55 +297,31 @@ class base_stage(object): destination=False) def remind_user(self, cr, uid, ids, context=None, attach=False, destination=True): - mail_message = self.pool.get('mail.message') - for case in self.browse(cr, uid, ids, context=context): - if not destination and not case.email_from: - return False - if not case.user_id.email: - return False - if destination and case.section_id.user_id: - case_email = case.section_id.user_id.email - else: - case_email = case.user_id.email - - src = case_email - dest = case.user_id.email or "" - body = case.description or "" - for message in case.message_ids: - if message.email_from and message.body_text: - body = message.body_text - break - - if not destination: - src, dest = dest, case.email_from - if body and case.user_id.signature: - if body: - body += '\n\n%s' % (case.user_id.signature) - else: - body = '\n\n%s' % (case.user_id.signature) - - body = self.format_body(body) - - attach_to_send = {} - - if attach: - attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)]) - attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas']) - attach_to_send = dict(map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)) - - # Send an email - subject = "Reminder: [%s] %s" % (str(case.id), case.name, ) - mail_message.schedule_with_attach(cr, uid, - src, - [dest], - subject, - body, - model=self._name, - reply_to=case.section_id.reply_to, - res_id=case.id, - attachments=attach_to_send, - context=context - ) + if 'message_post' in self: + for case in self.browse(cr, uid, ids, context=context): + if destination: + recipient_id = case.user_id.partner_id.id + else: + if not case.email_from: + return False + recipient_id = self.pool.get('res.partner').find_or_create(cr, uid, case.email_from, context=context) + + body = case.description or "" + for message in case.message_ids: + if message.type == 'email' and message.body: + body = message.body + break + body = self.format_body(body) + attach_to_send = {} + if attach: + attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)]) + attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas']) + attach_to_send = dict(map(lambda x: (x['datas_fname'], x['datas'].decode('base64')), attach_to_send)) + + subject = "Reminder: [%s] %s" % (case.id, case.name) + self.message_post(cr, uid, case.id, body=body, + subject=subject, attachments=attach_to_send, + partner_ids=[recipient_id], context=context) return True def _check(self, cr, uid, ids=False, context=None): @@ -360,17 +336,6 @@ class base_stage(object): def format_mail(self, obj, body): return self.pool.get('base.action.rule').format_mail(obj, body) - def message_thread_followers(self, cr, uid, ids, context=None): - res = {} - for case in self.browse(cr, uid, ids, context=context): - l=[] - if case.email_cc: - l.append(case.email_cc) - if case.user_id and case.user_id.email: - l.append(case.user_id.email) - res[case.id] = l - return res - # ****************************** # Notifications # ****************************** @@ -395,31 +360,31 @@ class base_stage(object): def case_open_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_close_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_cancel_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_pending_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_reset_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None): @@ -428,5 +393,5 @@ class base_stage(object): msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) else: msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py index 8fa2f92e22a..cebf5537485 100644 --- a/addons/base_status/base_state.py +++ b/addons/base_status/base_state.py @@ -179,13 +179,13 @@ class base_state(object): # Notifications # ****************************** - def case_get_note_msg_prefix(self, cr, uid, id, context=None): - return '' - + def case_get_note_msg_prefix(self, cr, uid, id, context=None): + return '' + def case_open_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None): @@ -194,29 +194,29 @@ class base_state(object): msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name) else: msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_close_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_cancel_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_pending_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True def case_reset_send_note(self, cr, uid, ids, context=None): for id in ids: msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=msg, context=context) + self.message_post(cr, uid, [id], body=msg, context=context) return True diff --git a/addons/base_vat/i18n/es_MX.po b/addons/base_vat/i18n/es_MX.po index 57d269be72d..cd511c528b0 100644 --- a/addons/base_vat/i18n/es_MX.po +++ b/addons/base_vat/i18n/es_MX.po @@ -1,70 +1,54 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * base_vat +# Spanish (Mexico) translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 6.0dev\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2010-12-25 18:58+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"<jesteve@zikzakmedia.com>\n" -"Language-Team: \n" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 00:31+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Spanish (Mexico) <es_MX@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:10+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: base_vat -#: code:addons/base_vat/base_vat.py:87 +#: code:addons/base_vat/base_vat.py:141 #, python-format msgid "" -"The Vat does not seems to be correct. You should have entered something like " -"this %s" +"This VAT number does not seem to be valid.\n" +"Note: the expected format is %s" +msgstr "El RFC no es válido. El formato esperado es %s" + +#. module: base_vat +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" msgstr "" -"El CIF/NIF parece que no sea correcto. Debería haber introducido algo como " -"esto %s" #. module: base_vat -#: model:ir.module.module,description:base_vat.module_meta_information -msgid "" -"\n" -" Enable the VAT Number for the partner. Check the validity of that VAT " -"Number.\n" -"\n" -" This module follows the methods stated at http://sima-pc.com/nif.php " -"for\n" -" checking the validity of VAT Number assigned to partners in European " -"countries.\n" -" " +#: constraint:res.partner:0 +msgid "Error ! You cannot create recursive associated members." msgstr "" -"\n" -" Permite la validación del CIF/NIF de las empresas. Comprueba si el " -"CIF/NIF es un número válido.\n" -"\n" -" Este módulo usa los métodos especificados en http://sima-pc.com/nif.php " -"para\n" -" la validación del CIF/NIF asignado a las empresas de los países " -"europeos.\n" -" " #. module: base_vat -#: model:ir.module.module,shortdesc:base_vat.module_meta_information -msgid "Base VAT - To check VAT number validity" -msgstr "Base CIF/NIF - Para comprobar la validez de los CIF/NIF" +#: field:res.company,vat_check_vies:0 +msgid "VIES VAT Check" +msgstr "" #. module: base_vat -#: constraint:res.partner:0 -msgid "Error ! You can not create recursive associated members." -msgstr "¡Error! No puede crear miembros asociados recursivos." +#: model:ir.model,name:base_vat.model_res_company +msgid "Companies" +msgstr "" #. module: base_vat -#: code:addons/base_vat/base_vat.py:88 -#, python-format -msgid "The VAT is invalid, It should begin with the country code" -msgstr "El CIF/NIF no es válido, debería empezar con el código del país" +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" #. module: base_vat #: help:res.partner,vat_subjected:0 @@ -72,27 +56,20 @@ msgid "" "Check this box if the partner is subjected to the VAT. It will be used for " "the VAT legal statement." msgstr "" -"Marque esta opción si la empresa está sujeta al IVA. Será utilizado para la " -"declaración legal del IVA." #. module: base_vat #: model:ir.model,name:base_vat.model_res_partner msgid "Partner" -msgstr "Empresa" +msgstr "" + +#. module: base_vat +#: help:res.company,vat_check_vies:0 +msgid "" +"If checked, Partners VAT numbers will be fully validated against EU's VIES " +"service rather than via a simple format validation (checksum)." +msgstr "" #. module: base_vat #: field:res.partner,vat_subjected:0 msgid "VAT Legal Statement" -msgstr "Sujeto a IVA" - -#~ msgid "Invalid XML for View Architecture!" -#~ msgstr "¡XML inválido para la definición de la vista!" - -#~ msgid "" -#~ "Enable the VAT Number for the partner. Check the validity of that VAT Number." -#~ msgstr "" -#~ "Activa el IVA (Impuesto Valor Añadido) para la empresa. Comprueba la validez " -#~ "del CIF/NIF." - -#~ msgid "VAT" -#~ msgstr "IVA" +msgstr "" diff --git a/addons/board/__openerp__.py b/addons/board/__openerp__.py index f07878717fd..2690012f3b4 100644 --- a/addons/board/__openerp__.py +++ b/addons/board/__openerp__.py @@ -28,9 +28,7 @@ Lets the user create a custom dashboard. ======================================== -This module also creates the Administration Dashboard. - -The user can also publish notes. +Allows users to create custom dashboard. """, 'author': 'OpenERP SA', 'depends': ['base'], diff --git a/addons/board/i18n/nb.po b/addons/board/i18n/nb.po new file mode 100644 index 00000000000..2d759af27d1 --- /dev/null +++ b/addons/board/i18n/nb.po @@ -0,0 +1,348 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-06 14:01+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: board +#: view:res.log.report:0 +msgid " Year " +msgstr " År " + +#. module: board +#: model:ir.model,name:board.model_board_menu_create +msgid "Menu Create" +msgstr "Meny laget" + +#. module: board +#: view:board.menu.create:0 +msgid "Menu Information" +msgstr "Meny informasjon" + +#. module: board +#: view:res.users:0 +msgid "Latest Connections" +msgstr "Siste Tilkoblinger" + +#. module: board +#: view:res.log.report:0 +msgid "Log created in last month" +msgstr "Logg opprettet i forrige måned" + +#. module: board +#: view:board.board:0 +#: model:ir.actions.act_window,name:board.open_board_administration_form +msgid "Administration Dashboard" +msgstr "Administrasjon kontrollpanel" + +#. module: board +#: view:res.log.report:0 +msgid "Group By..." +msgstr "Grupper etter ..." + +#. module: board +#: view:res.log.report:0 +msgid "Log created in current year" +msgstr "Logg opprettet i gjeldende år." + +#. module: board +#: model:ir.model,name:board.model_board_board +msgid "Board" +msgstr "Brett" + +#. module: board +#: field:board.menu.create,menu_name:0 +msgid "Menu Name" +msgstr "Menynavn" + +#. module: board +#: model:ir.actions.act_window,name:board.board_weekly_res_log_report_action +#: view:res.log.report:0 +msgid "Weekly Global Activity" +msgstr "Ukentlig global aktivitet" + +#. module: board +#: field:board.board.line,name:0 +msgid "Title" +msgstr "Tittel" + +#. module: board +#: field:res.log.report,nbr:0 +msgid "# of Entries" +msgstr "# av oppføringer" + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,month:0 +msgid "Month" +msgstr "Måned" + +#. module: board +#: view:res.log.report:0 +msgid "Log created in current month" +msgstr "Logg opprettet i gjeldende måned." + +#. module: board +#: model:ir.actions.act_window,name:board.board_monthly_res_log_report_action +#: view:res.log.report:0 +msgid "Monthly Activity per Document" +msgstr "Månedlig aktivitet per dokument" + +#. module: board +#: view:board.board:0 +msgid "Configuration Overview" +msgstr "Konfigurasjonsoversikt" + +#. module: board +#: model:ir.actions.act_window,name:board.action_view_board_list_form +#: model:ir.ui.menu,name:board.menu_view_board_form +msgid "Dashboard Definition" +msgstr "Kontrollpanel Definisjon" + +#. module: board +#: selection:res.log.report,month:0 +msgid "March" +msgstr "Mars" + +#. module: board +#: selection:res.log.report,month:0 +msgid "August" +msgstr "August" + +#. module: board +#: model:ir.actions.act_window,name:board.action_user_connection_tree +msgid "User Connections" +msgstr "Brukertilkoblinger" + +#. module: board +#: field:res.log.report,creation_date:0 +msgid "Creation Date" +msgstr "Opprettelsesdato" + +#. module: board +#: view:res.log.report:0 +msgid "Log Analysis" +msgstr "Logg analyse" + +#. module: board +#: field:res.log.report,res_model:0 +msgid "Object" +msgstr "Objekt" + +#. module: board +#: selection:res.log.report,month:0 +msgid "June" +msgstr "Juni" + +#. module: board +#: field:board.board,line_ids:0 +msgid "Action Views" +msgstr "Handling Visninger" + +#. module: board +#: model:ir.model,name:board.model_res_log_report +msgid "Log Report" +msgstr "Logg rapport" + +#. module: board +#: code:addons/board/wizard/board_menu_create.py:46 +#, python-format +msgid "Please Insert Dashboard View(s) !" +msgstr "Vennligst Sett Kontrollpanel Vinsning (er)!" + +#. module: board +#: selection:res.log.report,month:0 +msgid "July" +msgstr "juli" + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,day:0 +msgid "Day" +msgstr "Dag" + +#. module: board +#: view:board.menu.create:0 +msgid "Create Menu For Dashboard" +msgstr "Opprett meny for Kontrollpanel." + +#. module: board +#: selection:res.log.report,month:0 +msgid "February" +msgstr "Februar" + +#. module: board +#: selection:res.log.report,month:0 +msgid "October" +msgstr "Oktober" + +#. module: board +#: model:ir.model,name:board.model_board_board_line +msgid "Board Line" +msgstr "bord Linje" + +#. module: board +#: field:board.menu.create,menu_parent_id:0 +msgid "Parent Menu" +msgstr "Overordnet meny" + +#. module: board +#: view:res.log.report:0 +msgid " Month-1 " +msgstr " Måned-1 " + +#. module: board +#: selection:res.log.report,month:0 +msgid "January" +msgstr "Januar" + +#. module: board +#: view:board.board:0 +msgid "Users" +msgstr "Brukere" + +#. module: board +#: selection:res.log.report,month:0 +msgid "November" +msgstr "November" + +#. module: board +#: help:board.board.line,sequence:0 +msgid "" +"Gives the sequence order when displaying a list of " +"board lines." +msgstr "Gir rekkefølgen av når du viser en liste over bord linjer." + +#. module: board +#: selection:res.log.report,month:0 +msgid "April" +msgstr "April" + +#. module: board +#: view:board.board:0 +#: field:board.board,name:0 +#: field:board.board.line,board_id:0 +#: model:ir.ui.menu,name:board.menu_dasboard +msgid "Dashboard" +msgstr "Kontrollpanel" + +#. module: board +#: code:addons/board/wizard/board_menu_create.py:45 +#, python-format +msgid "User Error!" +msgstr "Bruker feil!" + +#. module: board +#: field:board.board.line,action_id:0 +msgid "Action" +msgstr "Handling" + +#. module: board +#: field:board.board.line,position:0 +msgid "Position" +msgstr "Posisjon" + +#. module: board +#: view:res.log.report:0 +msgid "Model" +msgstr "Modell" + +#. module: board +#: model:ir.actions.act_window,name:board.board_homepage_action +msgid "Home Page" +msgstr "Hjemmeside" + +#. module: board +#: model:ir.actions.act_window,name:board.action_latest_activities_tree +msgid "Latest Activities" +msgstr "Senest aktiveter" + +#. module: board +#: selection:board.board.line,position:0 +msgid "Left" +msgstr "Venstre" + +#. module: board +#: field:board.board,view_id:0 +msgid "Board View" +msgstr "Bord visning" + +#. module: board +#: selection:board.board.line,position:0 +msgid "Right" +msgstr "Høyre" + +#. module: board +#: field:board.board.line,width:0 +msgid "Width" +msgstr "Bredde" + +#. module: board +#: view:res.log.report:0 +msgid " Month " +msgstr " Måned " + +#. module: board +#: field:board.board.line,sequence:0 +msgid "Sequence" +msgstr "Sekvens" + +#. module: board +#: selection:res.log.report,month:0 +msgid "September" +msgstr "September" + +#. module: board +#: selection:res.log.report,month:0 +msgid "December" +msgstr "Desember" + +#. module: board +#: view:board.board:0 +#: view:board.menu.create:0 +msgid "Create Menu" +msgstr "Opprett meny" + +#. module: board +#: field:board.board.line,height:0 +msgid "Height" +msgstr "Høyde" + +#. module: board +#: model:ir.actions.act_window,name:board.action_board_menu_create +msgid "Create Board Menu" +msgstr "Opprett bord meny" + +#. module: board +#: selection:res.log.report,month:0 +msgid "May" +msgstr "Mai" + +#. module: board +#: view:res.log.report:0 +#: field:res.log.report,name:0 +msgid "Year" +msgstr "År" + +#. module: board +#: view:board.menu.create:0 +msgid "Cancel" +msgstr "Kanseller" + +#. module: board +#: view:board.board:0 +msgid "Dashboard View" +msgstr "Kontrollpanel visning" diff --git a/addons/board/static/src/css/Makefile b/addons/board/static/src/css/Makefile new file mode 100644 index 00000000000..0cb5ae70f4a --- /dev/null +++ b/addons/board/static/src/css/Makefile @@ -0,0 +1,3 @@ +dashboard.css: dashboard.sass + sass --trace -t expanded dashboard.sass dashboard.css + diff --git a/addons/board/static/src/css/dashboard.css b/addons/board/static/src/css/dashboard.css index 6907b4271a0..ed66e338c81 100644 --- a/addons/board/static/src/css/dashboard.css +++ b/addons/board/static/src/css/dashboard.css @@ -1,3 +1,17 @@ +.openerp .oe_dashboard_layout_selector ul { + white-space: nowrap; +} +.openerp .oe_dashboard_layout_selector li { + margin: 0; + padding: 0; + list-style-type: none; + float: left; +} +.openerp .oe_dashboard_layout_selector li .oe_dashboard_selected_layout { + margin-left: -30px; + vertical-align: bottom; + margin-bottom: 10px; +} .openerp .oe_dashboard_links { text-align: right; margin: 0 4px 6px 0; diff --git a/addons/board/static/src/css/dashboard.sass b/addons/board/static/src/css/dashboard.sass index 4bacc7bd892..32f0e3b4f3a 100644 --- a/addons/board/static/src/css/dashboard.sass +++ b/addons/board/static/src/css/dashboard.sass @@ -9,6 +9,18 @@ box-shadow: $bsval .openerp + .oe_dashboard_layout_selector + ul + white-space: nowrap + li + margin: 0 + padding: 0 + list-style-type: none + float: left + .oe_dashboard_selected_layout + margin-left: -30px + vertical-align: bottom + margin-bottom: 10px .oe_dashboard_links text-align: right margin: 0 4px 6px 0 diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 0333690aa57..9c37924144d 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -220,36 +220,40 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({ am.do_action = function (action) { self.do_action(action); }; - if (action_attrs.creatable && action_attrs.creatable !== 'false') { - var action_id = parseInt(action_attrs.creatable, 10); - $action.parent().find('button.oe_dashboard_button_create').click(function() { - if (isNaN(action_id)) { - action_orig.flags.default_view = 'form'; - self.do_action(action_orig); - } else { - self.rpc('/web/action/load', { - action_id: action_id - }, function(result) { - result.result.flags = result.result.flags || {}; - result.result.flags.default_view = 'form'; - self.do_action(result.result); - }); - } - }); - } if (am.inner_widget) { - am.inner_widget.on_mode_switch.add(function(mode) { + var new_form_action = function(id, editable) { var new_views = []; _.each(action_orig.views, function(view) { - new_views[view[1] === mode ? 'unshift' : 'push'](view); + new_views[view[1] === 'form' ? 'unshift' : 'push'](view); }); - if (!new_views.length || new_views[0][1] !== mode) { - new_views.unshift([false, mode]); + if (!new_views.length || new_views[0][1] !== 'form') { + new_views.unshift([false, 'form']); } action_orig.views = new_views; - action_orig.res_id = am.inner_widget.dataset.ids[am.inner_widget.dataset.index]; + action_orig.res_id = id; + action_orig.flags = { + form: { + "initial_mode": editable ? "edit" : "view", + } + }; self.do_action(action_orig); - }); + }; + var list = am.inner_widget.views.list; + if (list) { + list.deferred.then(function() { + $(list.controller.groups).off('row_link').on('row_link', function(e, id) { + new_form_action(id); + }); + }); + } + var kanban = am.inner_widget.views.kanban; + if (kanban) { + kanban.deferred.then(function() { + kanban.controller.open_record = function(id, editable) { + new_form_action(id, editable); + }; + }); + } } }, renderElement: function() { diff --git a/addons/board/static/src/xml/board.xml b/addons/board/static/src/xml/board.xml index b2363cafd53..e13fa707776 100644 --- a/addons/board/static/src/xml/board.xml +++ b/addons/board/static/src/xml/board.xml @@ -26,7 +26,6 @@ <span class="oe_header_txt"> <t t-esc="action.attrs.string"/> </span> <input class = "oe_header_text" type="text"/> <t t-if="!action.attrs.string">&nbsp;</t> - <button t-if="action.attrs.creatable and action.attrs.creatable !== 'false'" class="oe_button oe_button_create">Create</button> <span class='oe_icon oe_close'></span> <span class='oe_icon oe_minimize oe_fold' t-if="!action.attrs.fold"></span> <span class='oe_icon oe_maximize oe_fold' t-if="action.attrs.fold"></span> diff --git a/addons/crm/board_crm_view.xml b/addons/crm/board_crm_view.xml index 16ed1e4c5ad..d80183b80c9 100644 --- a/addons/crm/board_crm_view.xml +++ b/addons/crm/board_crm_view.xml @@ -2,22 +2,6 @@ <openerp> <data> <!-- CRM dashboard --> - <record model="ir.actions.act_window" id="act_my_oppor"> - <field name="name">My Opportunities</field> - <field name="res_model">crm.lead</field> - <field name="view_mode">tree</field> - <field name="domain">[('user_id','=',uid),('type', '=', 'opportunity'),('state','not in',('cancel','done'))]</field> - <field name="view_id" ref="crm.crm_case_tree_view_oppor"/> - </record> - - <record model="ir.actions.act_window" id="act_crm_tag_tree_view_leads_all"> - <field name="name">New Leads</field> - <field name="res_model">crm.lead</field> - <field name="view_mode">tree</field> - <field name="domain">[('user_id','=',uid),('state','=','draft'),('type','=','lead')]</field> - <field name="view_id" ref="crm.crm_case_tree_view_leads"/> - </record> - <record model="ir.ui.view" id="view_crm_opportunity_stage_graph"> <field name="name">Opportunities By Stage - Graph</field> <field name="model">crm.lead.report</field> @@ -70,11 +54,9 @@ <form string="Statistics Dashboard" version="7.0"> <board style="1-1"> <column> - <action string="New Leads" name="%(act_crm_tag_tree_view_leads_all)d"/> - <action string="My Opportunities" name="%(act_my_oppor)d"/> + <action string="Planned Revenue by Stage and User" name="%(act_oppor_stage_user)d"/> </column> <column> - <action string="Planned Revenue by Stage and User" name="%(act_oppor_stage_user)d"/> <action string="Opportunities by Stage" name="%(act_opportunity_stage)d"/> </column> </board> diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 3e3bdc90d08..8b1ba655afc 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -46,21 +46,11 @@ class base_action_rule(osv.osv): } def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from', False), context=None): - mail_message = self.pool.get('mail.message') - body = self.format_mail(obj, body) - if not emailfrom: - if hasattr(obj, 'user_id') and obj.user_id and obj.user_id.email: - emailfrom = obj.user_id.email - - name = '[%d] %s' % (obj.id, tools.ustr(obj.name)) - emailfrom = tools.ustr(emailfrom) - if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.alias_id: + mail_id = super(base_action_rule, self).email_send(cr, uid, obj, emails, body, emailfrom=emailfrom, context=context) + if mail_id and hasattr(obj, 'section_id') and obj.section_id and obj.section_id.alias_id: reply_to = obj.section_id.alias_id.name_get()[0][1] - else: - reply_to = emailfrom - if not emailfrom: - raise osv.except_osv(_('Error!'), _("There is no email for your company address.")) - return mail_message.schedule_with_attach(cr, uid, emailfrom, emails, name, body, model=obj._name, reply_to=reply_to, res_id=obj.id) + self.pool.get('mail.mail').write(cr, uid, [mail_id], {'reply_to': reply_to}, context=context) + return mail_id def do_check(self, cr, uid, action, obj, context=None): ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context) @@ -105,8 +95,8 @@ class base_action_rule(osv.osv): write['email_cc'] = obj.act_email_cc # Put state change by rule in communication history - if hasattr(obj, 'state') and hasattr(obj, 'message_append') and action.act_state: - model_obj.message_append(cr, uid, [obj], _(action.act_state)) + if hasattr(obj, 'state') and hasattr(obj, 'message_post') and action.act_state: + model_obj.message_post(cr, uid, [obj], _(action.act_state), context=context) model_obj.write(cr, uid, [obj.id], write, context) super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index c5671385e56..5bf4b5ffbf9 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -55,21 +55,20 @@ </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module CRM has been installed</value> - <value>From the top menu Sales, you can: trace leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">CRM application installed!</field> + <field name="body">From the top Sales menu you can track leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects. +To manage quotations and sale orders, install the "Sales Management" application.</field> + </record> -To manage quotations and sale orders, install the module "Sales Management".</value> - </function> - <record model="mail.alias" id="default_sales_alias"> <field name="alias_name">sales</field> <field name="alias_model_id" ref="model_crm_lead"/> <field name="alias_user_id" ref="base.user_root"/> <field name="alias_defaults">{'type':'lead'}</field> </record> - </data> </openerp> diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 25a47ecbadc..ddfdb05ca66 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -19,11 +19,9 @@ # ############################################################################## -import binascii from base_status.base_stage import base_stage import crm from datetime import datetime -from mail.mail_message import to_email from osv import fields, osv import time import tools @@ -40,8 +38,7 @@ class crm_lead(base_stage, osv.osv): _name = "crm.lead" _description = "Lead/Opportunity" _order = "priority,date_action,id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] - _mail_compose_message = True + _inherit = ['mail.thread','ir.needaction_mixin'] def _get_default_section_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ @@ -90,8 +87,8 @@ class crm_lead(base_stage, osv.osv): search_domain = [] section_id = self._resolve_section_id_from_context(cr, uid, context=context) if section_id: - search_domain += ['|', '&', ('section_ids', '=', section_id), ('fold', '=', False)] - search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)] + search_domain += ['|', ('section_ids', '=', section_id)] + search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] # retrieve type from the context (if set: choose 'type' or 'both') type = self._resolve_type_from_context(cr, uid, context=context) if type: @@ -101,7 +98,12 @@ class crm_lead(base_stage, osv.osv): result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) - return result + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + + return result, fold _group_by_full = { 'stage_id': _read_group_stage_ids @@ -175,16 +177,6 @@ class crm_lead(base_stage, osv.osv): else: return [('id', '=', '0')] - def _get_email_subject(self, cr, uid, ids, fields, args, context=None): - res = {} - for obj in self.browse(cr, uid, ids, context=context): - res[obj.id] = '' - for msg in obj.message_ids: - if msg.email_from: - res[obj.id] = msg.subject - break - return res - _columns = { 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', select=True, help="Optional linked partner, usually after conversion of the lead"), @@ -213,7 +205,7 @@ class crm_lead(base_stage, osv.osv): 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'date_closed': fields.datetime('Closed', readonly=True), 'stage_id': fields.many2one('crm.case.stage', 'Stage', - domain="['&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), + domain="['&', ('fold', '=', False), '&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"), 'user_id': fields.many2one('res.users', 'Salesperson'), 'referred': fields.char('Referred By', size=64), 'date_open': fields.datetime('Opened', readonly=True), @@ -228,7 +220,6 @@ class crm_lead(base_stage, osv.osv): When the case is over, the state is set to \'Done\'.\ If the case needs to be reviewed then the state is \ set to \'Pending\'.'), - 'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', type='char', size=64), # Only used for type opportunity 'probability': fields.float('Success Rate (%)',group_operator="avg"), @@ -449,7 +440,7 @@ class crm_lead(base_stage, osv.osv): oldest_id = opportunity_ids[0] return self.browse(cr, uid, oldest_id, context=context) - def _mail_body_text(self, cr, uid, lead, fields, title=False, context=None): + def _mail_body(self, cr, uid, lead, fields, title=False, context=None): body = [] if title: body.append("%s\n" % (title)) @@ -486,11 +477,11 @@ class crm_lead(base_stage, osv.osv): for opportunity in opportunities: subject.append(opportunity.name) title = "%s : %s" % (merge_message, opportunity.name) - details.append(self._mail_body_text(cr, uid, opportunity, fields, title=title, context=context)) + details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context)) subject = subject[0] + ", ".join(subject[1:]) details = "\n\n".join(details) - return self.message_append_note(cr, uid, [opportunity_id], subject=subject, body=details) + return self.message_post(cr, uid, [opportunity_id], body=details, subject=subject, context=context) def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None): message = self.pool.get('mail.message') @@ -546,7 +537,7 @@ class crm_lead(base_stage, osv.osv): oldest = self._merge_find_oldest(cr, uid, ids, context=context) if ctx_opportunities : first_opportunity = ctx_opportunities[0] - tail_opportunities = opportunities_list + tail_opportunities = opportunities_list + ctx_opportunities[1:] else: first_opportunity = opportunities_list[0] tail_opportunities = opportunities_list[1:] @@ -606,19 +597,13 @@ class crm_lead(base_stage, osv.osv): for lead in self.browse(cr, uid, ids, context=context): if lead.state in ('done', 'cancel'): continue - if user_ids or section_id: - self.allocate_salesman(cr, uid, [lead.id], user_ids, section_id, context=context) - vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) self.write(cr, uid, [lead.id], vals, context=context) - self.convert_opportunity_send_note(cr, uid, lead, context=context) - #TOCHECK: why need to change partner details in all messages of lead ? - if lead.partner_id: - msg_ids = [ x.id for x in lead.message_ids] - mail_message.write(cr, uid, msg_ids, { - 'partner_id': lead.partner_id.id - }, context=context) + + if user_ids or section_id: + self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context) + return True def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None): @@ -630,7 +615,7 @@ class crm_lead(base_stage, osv.osv): 'parent_id': parent_id, 'phone': lead.phone, 'mobile': lead.mobile, - 'email': lead.email_from and to_email(lead.email_from)[0], + 'email': lead.email_from and tools.email_split(lead.email_from)[0], 'fax': lead.fax, 'title': lead.title and lead.title.id or False, 'function': lead.function, @@ -650,7 +635,7 @@ class crm_lead(base_stage, osv.osv): partner_id = False if lead.partner_name and lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) - self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) + partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context) elif lead.partner_name and not lead.contact_name: partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context) elif not lead.partner_name and lead.contact_name: @@ -678,32 +663,16 @@ class crm_lead(base_stage, osv.osv): if context is None: context = {} partner_ids = {} + force_partner_id = partner_id for lead in self.browse(cr, uid, ids, context=context): if action == 'create': if not partner_id: partner_id = self._create_lead_partner(cr, uid, lead, context) + partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context=context) self._lead_set_partner(cr, uid, lead, partner_id, context=context) partner_ids[lead.id] = partner_id return partner_ids - def _send_mail_to_salesman(self, cr, uid, lead, context=None): - """ - Send mail to salesman with updated Lead details. - @ lead: browse record of 'crm.lead' object. - """ - #TOFIX: mail template should be used here instead of fix subject, body text. - message = self.pool.get('mail.message') - email_to = lead.user_id and lead.user_id.email - if not email_to: - return False - - email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.email or email_to - partner = lead.partner_id and lead.partner_id.name or lead.partner_name - subject = "lead %s converted into opportunity" % lead.name - body = "Info \n Id : %s \n Subject: %s \n Partner: %s \n Description : %s " % (lead.id, lead.name, lead.partner_id.name, lead.description) - return message.schedule_with_attach(cr, uid, email_from, [email_to], subject, body) - - def allocate_salesman(self, cr, uid, ids, user_ids, team_id=False, context=None): index = 0 for lead_id in ids: @@ -821,14 +790,13 @@ class crm_lead(base_stage, osv.osv): if custom_values is None: custom_values = {} custom_values.update({ 'name': msg.get('subject') or _("No Subject"), - 'description': msg.get('body_text'), + 'description': msg.get('body'), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, }) if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) return super(crm_lead, self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): @@ -841,18 +809,18 @@ class crm_lead(base_stage, osv.osv): if update_vals is None: update_vals = {} if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - vals['priority'] = msg.get('priority') + update_vals['priority'] = msg.get('priority') maps = { 'cost':'planned_cost', 'revenue': 'planned_revenue', 'probability':'probability', } - for line in msg.get('body_text', '').split('\n'): + for line in msg.get('body', '').split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): key = maps.get(res.group(1).lower()) - vals[key] = res.group(2).lower() + update_vals[key] = res.group(2).lower() return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) @@ -860,15 +828,10 @@ class crm_lead(base_stage, osv.osv): # OpenChatter methods and notifications # ---------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(crm_lead, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) + return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) def case_get_note_msg_prefix(self, cr, uid, lead, context=None): if isinstance(lead, (int, long)): @@ -878,33 +841,33 @@ class crm_lead(base_stage, osv.osv): def create_send_note(self, cr, uid, ids, context=None): for id in ids: message = _("%s has been <b>created</b>.")% (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=message, context=context) + self.message_post(cr, uid, [id], body=message, context=context) return True def case_mark_lost_send_note(self, cr, uid, ids, context=None): message = _("Opportunity has been <b>lost</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def case_mark_won_send_note(self, cr, uid, ids, context=None): message = _("Opportunity has been <b>won</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] if action == 'log': prefix = 'Logged' else: prefix = 'Scheduled' message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date) - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def _lead_set_partner_send_note(self, cr, uid, ids, context=None): for lead in self.browse(cr, uid, ids, context=context): message = _("%s <b>partner</b> is now set to <em>%s</em>." % (self.case_get_note_msg_prefix(cr, uid, lead, context=context), lead.partner_id.name)) - lead.message_append_note(body=message) + lead.message_post(body=message) return True def convert_opportunity_send_note(self, cr, uid, lead, context=None): message = _("Lead has been <b>converted to an opportunity</b>.") - lead.message_append_note(body=message) + lead.message_post(body=message) return True crm_lead() diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index 04f91933f66..f734a839ff4 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -55,7 +55,7 @@ <record model="crm.case.stage" id="stage_lead7"> <field name="name">Dead</field> <field eval="1" name="case_default"/> - <field eval="True" name="fold"/> + <field eval="False" name="fold"/> <field name="state">cancel</field> <field eval="'0'" name="probability"/> <field eval="'16'" name="sequence"/> diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml index a274dadfe01..b3c00256b06 100644 --- a/addons/crm/crm_lead_demo.xml +++ b/addons/crm/crm_lead_demo.xml @@ -23,7 +23,7 @@ <field name="channel_id" ref="crm_case_channel_email"/> <field name="priority">1</field> <field name="section_id" ref="crm_case_section_4"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="stage_lead1"/> <field name="description">Hello, I am Jason from Le Club SARL, @@ -47,7 +47,7 @@ Can you send details,</field> <field name="channel_id" ref="crm_case_channel_website"/> <field name="priority">4</field> <field name="section_id" ref="crm_case_section_3"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="stage_lead1"/> <field eval="1" name="active"/> </record> @@ -108,7 +108,7 @@ Can you send details,</field> <field name="channel_id" ref="crm_case_channel_website"/> <field name="priority">3</field> <field name="section_id" ref="section_sales_department"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="stage_lead1"/> <field name="description">Hi, Can you send a quotation for 20 Computers with speakers? Regards, @@ -135,7 +135,7 @@ Contact: +1 813 494 5005</field> <field name="channel_id" ref=""/> <field name="priority">3</field> <field name="section_id" ref="crm_case_section_4"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="stage_lead1"/> <field eval="1" name="active"/> </record> @@ -194,7 +194,7 @@ Contact: +1 813 494 5005</field> <field name="channel_id" ref="crm_case_channel_phone"/> <field name="priority">2</field> <field name="section_id" ref="section_sales_department"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="stage_lead1"/> <field eval="1" name="active"/> </record> @@ -297,7 +297,7 @@ Andrew</field> <field eval="time.strftime('%Y-%m-12')" name="date_action"/> <field name="title_action">Meeting for pricing information.</field> <field name="section_id" ref="crm_case_section_3"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead3"/> <field eval="1" name="active"/> </record> @@ -342,7 +342,7 @@ Andrew</field> <field eval="time.strftime('%Y-%m-10')" name="date_action"/> <field name="title_action">Call to ask system requirement</field> <field name="section_id" ref="crm_case_section_3"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead4"/> <field eval="1" name="active"/> </record> @@ -411,7 +411,7 @@ Andrew</field> <field eval="time.strftime('%Y-%m-4')" name="date_action"/> <field name="title_action">Call to define real needs about training</field> <field name="section_id" ref="crm_case_section_2"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead3"/> <field eval="1" name="active"/> </record> @@ -434,7 +434,7 @@ Andrew</field> <field eval="time.strftime('%Y-%m-5')" name="date_action"/> <field name="title_action">Ask for the good receprion of the proposition</field> <field name="section_id" ref="crm_case_section_1"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead4"/> <field eval="1" name="active"/> </record> @@ -466,7 +466,7 @@ Andrew</field> <field name="channel_id" ref="crm_case_channel_phone"/> <field name="priority">3</field> <field name="section_id" ref="crm_case_section_4"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead8"/> <field eval="1" name="active"/> </record> @@ -484,7 +484,7 @@ Andrew</field> <field name="channel_id" ref="crm_case_channel_email"/> <field name="priority">3</field> <field name="section_id" ref="crm_case_section_4"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead8"/> <field eval="1" name="active"/> </record> @@ -519,7 +519,7 @@ Andrew</field> <field name="priority">5</field> <field eval="time.strftime('%Y-%m-6')" name="date_deadline"/> <field name="section_id" ref="section_sales_department"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead6"/> <field eval="1" name="active"/> </record> @@ -542,7 +542,7 @@ Andrew</field> <field name="priority">2</field> <field name="title_action">Conf call with technical service</field> <field name="section_id" ref="crm_case_section_2"/> - <field name="user_id" ref="base.user_admin"/> + <field name="user_id" ref="base.user_root"/> <field name="stage_id" ref="crm.stage_lead4"/> <field eval="1" name="active"/> </record> @@ -552,86 +552,67 @@ Andrew</field> <field name="subject">Plan to buy a Laptop</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_15"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Email0 inquiry]]><div><font size="2">Hello,</font></div><div><font size="2"><br></font></div><div><font size="2">I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.</font></div><div><font size="2">Can you please send me product catalogue?</font></div></field> + <field name="body"><![CDATA[Email0 inquiry]]><div><font size="2">Hello,</font></div><div><font size="2"><br></font></div><div><font size="2">I am interested in your company's product and I plan to buy a new laptop having latest technologies and affordable price.</font></div><div><font size="2">Can you please send me product catalogue?</font></div></field> <field name="type">email</field> - <field name="state">received</field> - <field name="user_id" ref="base.user_demo"/> </record> <record id="message_note0" model="mail.message"> <field name="subject">Re: Plan to buy a Laptop</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_15"/> - <field name="parent_id" ref="message_email0"/> - <field name="content_subtype">plain</field> - <field name="body_text">Dear Customer, + <field name="type">comment</field> + <field name="body">Dear Customer, Thanks for showing interest in our products. We have attached the catalogue, We would like to know your interests, Let us know if we can call you for more details. Thanks</field> - <field name="type">email</field> - <field name="user_id" ref="base.user_root"/> - <field name="state">sent</field> + <field name="parent_id" ref="message_email0"/> + <field name="author_id" ref="base.partner_root"/> </record> <record id="message_note0_comment0" model="mail.message"> <field name="subject">Re: Plan to buy a Laptop</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_15"/> - <field name="content_subtype">html</field> - <field name="body_html"><div>Thanks for the information,</div><div>I will visit the store soon.</div></field> + <field name="type">comment</field> + <field name="body"><div>Thanks for the information,</div><div>I will visit the store soon.</div></field> <field name="parent_id" ref="message_note0"/> - <field name="type">email</field> - <field name="user_id" ref="base.user_demo"/> - <field name="state">received</field> + <field name="author_id" ref="base.partner_demo"/> </record> <record id="message_note0_comment1" model="mail.message"> <field name="subject">Re: Plan to buy a Laptop</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_15"/> - <field name="content_subtype">html</field> - <field name="body_html"><font color="#1f1f1f">Can you tell me if the store is open at 9:00 PM?</b></font></field> + <field name="type">comment</field> + <field name="body"><font color="#1f1f1f">Can you tell me if the store is open at 9:00 PM?</b></font></field> <field name="parent_id" ref="message_note0"/> - <field name="type">email</field> - <field name="state">received</field> - <field name="user_id" ref="base.user_demo"/> + <field name="author_id" ref="base.partner_demo"/> </record> <record id="message_email1" model="mail.message"> <field name="subject">Re: Plan to buy a Laptop</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_15"/> - <field name="content_subtype">plain</field> - <field name="body_text">Yes, its open till 10:00 PM, you are welcome!</field> + <field name="body">Yes, its open till 10:00 PM, you are welcome!</field> <field name="type">email</field> - <field name="state">sent</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_root"/> </record> - <record id="message_email_12" model="mail.message"> <field name="subject">Inquiry</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_1"/> - <field name="content_subtype">plain</field> - <field name="body_text">Hello, + <field name="body">Hello, I am Jason from Le Club SARL, I am intertested to attend Training organized in your company, Can you send details,</field> <field name="type">email</field> - <field name="state">received</field> - <field name="user_id" ref="base.user_demo"/> </record> - <record id="message_email_13" model="mail.message"> <field name="subject">Need Details</field> <field name="model">crm.lead</field> <field name="res_id" ref="crm_case_2"/> - <field name="content_subtype">plain</field> - <field name="body_text">Want to know features and benifits to use the new software.</field> + <field name="body">Want to know features and benifits to use the new software.</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_demo"/> </record> - <!-- Call Function to set the opportunities as Unread --> <function model="crm.lead" name="message_mark_as_unread" eval="[ ref('crm_case_15'), ref('crm_case_16'), diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 6a715893a17..f018a355579 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -246,7 +246,7 @@ <field name="name">Leads</field> <field name="model">crm.lead</field> <field name="arch" type="xml"> - <tree string="Leads" fonts="bold:needaction_pending==True" colors="grey:state in ('cancel', 'done')"> + <tree string="Leads" fonts="bold:message_unread==True" colors="grey:state in ('cancel', 'done')"> <field name="date_deadline" invisible="1"/> <field name="create_date" groups="base.group_no_one"/> <field name="name"/> @@ -257,12 +257,11 @@ <field name="stage_id"/> <field name="user_id" invisible="1"/> <field name="section_id" invisible="context.get('invisible_section', True)" /> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="type_id" invisible="1"/> <field name="referred" invisible="1"/> <field name="channel_id" invisible="1"/> - <field name="subjects" invisible="1"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> </tree> </field> </record> @@ -296,23 +295,16 @@ <field name="user_id"/> <field name="partner_address_email"/> <field name="message_summary"/> - <field name="needaction_pending"/> + <field name="message_unread"/> <templates> - <t t-name="lead_details"> - <ul class="oe_kanban_tooltip"> - <li t-if="record.phone.raw_value"><b>Phone:</b> <field name="phone"/></li> - <li><b>Probability:</b> <field name="probability"/>%%</li> - <li><b>Creation date:</b> <field name="create_date"/></li> - <li t-if="record.date_deadline.raw_value"><b>Date Deadline:</b> <field name="date_deadline"/></li> - </ul> - </t> + <field name="date_deadline"/> <t t-name="kanban-box"> <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">í</span> <ul class="oe_dropdown_menu"> - <li><a type="edit" >Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li><a name="%(mail.action_email_compose_message_wizard)d" type="action">Send Email</a></li> <li><a name="%(opportunity2phonecall_act)d" type="action">Log Call</a></li> <li><a name="action_makeMeeting" type="object">Schedule Meeting</a></li> @@ -342,7 +334,6 @@ <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/> </div> <div class="oe_kanban_footer_left"> - <t t-if="record.needaction_pending.raw_value"><span class="oe_kanban_mail_new">New</span></t> <t t-raw="record.message_summary.raw_value"/> </div> </div> @@ -362,10 +353,8 @@ <search string="Search Leads"> <field name="name" string="Lead / Customer" filter_domain="['|','|',('partner_name','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/> <field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike',self)]" /> - <!-- subjects is not set as store=True so, it is placed outside filter_domain--> - <field name="subjects"/> <field name="create_date"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-check" string="New" name="new" help="New Leads" domain="[('state','=','draft')]"/> <filter icon="terp-camera_test" string="Open" name="open" domain="[('state','=','open')]"/> @@ -386,7 +375,6 @@ <filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" /> <separator orientation="vertical"/> <filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Creation" help="Create date" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/> </group> <group string="Display"> @@ -435,7 +423,7 @@ <label for="planned_revenue" class="oe_edit_only"/> <h2> <field name="planned_revenue" class="oe_inline"/> - <field name="company_currency" class="oe_inline"/> at + <field name="company_currency" class="oe_inline" groups="base.group_multi_currency"/> at <field name="probability" class="oe_inline" widget="integer"/>%% success rate </h2> </div> @@ -515,7 +503,7 @@ <field name="day_open" groups="base.group_no_one"/> <field name="day_close" groups="base.group_no_one"/> <field name="referred"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="type" invisible="1"/> </group> <group string="References"> @@ -545,7 +533,7 @@ <field name="name">Opportunities Tree</field> <field name="model">crm.lead</field> <field name="arch" type="xml"> - <tree string="Opportunities" fonts="bold:needaction_pending==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline < current_date)"> + <tree string="Opportunities" fonts="bold:message_unread==True" colors="gray:state in ('cancel', 'done');red:date_deadline and (date_deadline < current_date)"> <field name="date_deadline" invisible="1"/> <field name="create_date" groups="base.group_no_one"/> <field name="name" string="Opportunity"/> @@ -555,7 +543,6 @@ <field name="title_action" /> <field name="channel_id" invisible="1"/> <field name="type_id" invisible="1"/> - <field name="subjects" invisible="1"/> <field name="stage_id"/> <field name="planned_revenue" sum="Expected Revenues"/> <field name="probability" widget="progressbar" avg="Avg. of Probability"/> @@ -563,7 +550,7 @@ <field name="user_id"/> <field name="priority" invisible="1"/> <field name="state" groups="base.group_no_one"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> </tree> </field> </record> @@ -578,7 +565,7 @@ <field name="name" string="Opportunity / Customer" filter_domain="['|','|','|',('partner_id','ilike',self),('partner_name','ilike',self),('email_from','ilike',self),('name', 'ilike', self)]"/> <field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike', self)]" /> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-check" string="New" help="New Opportunities" name="new" domain="[('state','=','draft')]"/> <filter icon="terp-camera_test" string="Open" help="Open Opportunities" name="open" domain="[('state','=','open')]"/> @@ -600,7 +587,6 @@ <filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" /> <filter string="Campaign" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'type_id'}"/> <filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" /> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Creation" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/> <filter string="Exp.Closing" icon="terp-go-month" help="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}" /> </group> diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py index 13171d95b9b..62fc53364ee 100644 --- a/addons/crm/crm_meeting.py +++ b/addons/crm/crm_meeting.py @@ -44,7 +44,7 @@ class crm_meeting(osv.Model): def create_send_note(self, cr, uid, ids, context=None): if context is None: context = {} - # update context: if come from phonecall, default state values can make the message_append_note crash + # update context: if come from phonecall, default state values can make the message_post crash context.pop('default_state', False) for meeting in self.browse(cr, uid, ids, context=context): # in the message, transpose meeting.date to the timezone of the current user @@ -53,14 +53,14 @@ class crm_meeting(osv.Model): if meeting.opportunity_id: # meeting can be create from phonecalls or opportunities, therefore checking for the parent lead = meeting.opportunity_id message = _("Meeting linked to the opportunity <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (lead.name, meeting_date_tz) - lead.message_append_note(_('System Notification'), message) + lead.message_post(body=message) elif meeting.phonecall_id: phonecall = meeting.phonecall_id message = _("Meeting linked to the phonecall <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (phonecall.name, meeting_date_tz) - phonecall.message_append_note(body=message) + phonecall.message_post(body=message) else: message = _("A meeting has been <b>scheduled</b> on <em>%s</em>.") % (meeting_date_tz) - meeting.message_append_note(body=message) + meeting.message_post(body=message) return True class calendar_attendee(osv.osv): diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py index da08f97250a..44894fc881d 100644 --- a/addons/crm/crm_phonecall.py +++ b/addons/crm/crm_phonecall.py @@ -32,7 +32,7 @@ class crm_phonecall(base_state, osv.osv): _name = "crm.phonecall" _description = "Phonecall" _order = "id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread'] _columns = { # base_state required fields 'date_action_last': fields.datetime('Last Action', readonly=1), @@ -177,11 +177,11 @@ class crm_phonecall(base_state, osv.osv): if context is None: context = {} partner_ids = {} + force_partner_id = partner_id for call in self.browse(cr, uid, ids, context=context): if action == 'create': - if not partner_id: - partner_id = self._call_create_partner(cr, uid, call, context=context) - self._call_create_partner_address(cr, uid, call, partner_id, context=context) + partner_id = force_partner_id or self._call_create_partner(cr, uid, call, context=context) + self._call_create_partner_address(cr, uid, call, partner_id, context=context) self._call_set_partner(cr, uid, [call.id], partner_id, context=context) partner_ids[call.id] = partner_id return partner_ids @@ -266,7 +266,7 @@ class crm_phonecall(base_state, osv.osv): def case_reset_send_note(self, cr, uid, ids, context=None): message = _('Phonecall has been <b>reset and set as open</b>.') - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def case_open_send_note(self, cr, uid, ids, context=None): lead_obj = self.pool.get('crm.lead') @@ -280,11 +280,11 @@ class crm_phonecall(base_state, osv.osv): message = _("Phonecall linked to the opportunity <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (lead.name, phonecall_date_str) else: message = _("Phonecall has been <b>created and opened</b>.") - phonecall.message_append_note(body=message) + phonecall.message_post(body=message) return True def _call_set_partner_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index 3b5162288a4..2f885e09b61 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -66,8 +66,7 @@ <field name="name">CRM - Phone Calls Tree</field> <field name="model">crm.phonecall</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="gray:state in ('cancel','done');blue:state in ('pending',)" string="Phone Calls"> - <field name="needaction_pending" invisible="1"/> + <tree colors="gray:state in ('cancel','done');blue:state in ('pending',)" string="Phone Calls"> <field name="date"/> <field name="name"/> <field name="partner_id"/> @@ -76,7 +75,6 @@ <field name="categ_id" invisible="1"/> <field name="create_date" invisible="1"/> <field name="opportunity_id" invisible="1"/> - <field name="needaction_pending" invisible="1"/> <button string="Convert to Opportunity" name="%(phonecall2opportunity_act)d" states="open,pending" @@ -166,7 +164,7 @@ <field name="name">CRM - Logged Phone Calls Tree</field> <field name="model">crm.phonecall</field> <field name="arch" type="xml"> - <tree string="Phone Calls" fonts="bold:needaction_pending==True" editable="top"> + <tree string="Phone Calls" editable="top"> <field name="date"/> <field name="name"/> <field name="partner_id" @@ -180,7 +178,6 @@ <field name="state" invisible="1"/> <field name="create_date" invisible="1"/> <field name="opportunity_id" invisible="1"/> - <field name="needaction_pending" invisible="1"/> <button string="Schedule Other Call" icon="terp-call-start" name="%(phonecall_to_phonecall_act)d" @@ -219,7 +216,6 @@ <search string="Search Phonecalls"> <field name="name" string="Phonecalls"/> <field name="date"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> <separator/> <filter icon="terp-gtk-go-back-rtl" string="To Do" name="current" domain="[('state','=','open')]"/> <separator/> diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index 4283dd8fb3c..35ed9411215 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -105,21 +105,11 @@ <filter string="Company" icon="terp-go-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> <filter string="Stage" name="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> <filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" /> - <filter string="Campaign" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'type_id'}" /> - <filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" /> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}" /> - <filter string="Year" icon="terp-go-year" domain="[]" context="{'group_by':'creation_year'}"/> - <filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'creation_month'}"/> - <filter string="Day" icon="terp-go-today" domain="[]" context="{'group_by':'creation_day'}"/> - <filter string="Exp. Closing" icon="terp-go-month" domain="[]" context="{'group_by':'deadline_month'}"/> <filter string="Campaign" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'type_id'}" /> <filter string="Channel" icon="terp-call-start" domain="[]" context="{'group_by':'channel_id'}" /> <separator orientation="vertical" /> - <filter string="Status" icon="terp-stock_effects-object-colorize" - domain="[]" context="{'group_by':'state'}" /> - <separator orientation="vertical" /> <filter string="Year" icon="terp-go-year" domain="[]" context="{'group_by':'creation_year'}"/> <filter string="Month" icon="terp-go-month" diff --git a/addons/crm/res_config.py b/addons/crm/res_config.py index c6d33b4bec2..1d30abdc33c 100644 --- a/addons/crm/res_config.py +++ b/addons/crm/res_config.py @@ -38,7 +38,7 @@ class crm_configuration(osv.osv_memory): 'module_import_google': fields.boolean("Google (Contacts and Calendar)", help="""Import google contact in partner address and add google calendar events details in Meeting. This installs the module import_google."""), - 'module_google_map': fields.boolean("add google maps on customer", + 'module_google_map': fields.boolean("add google maps on customers", help="""Locate customers on Google Map. This installs the module google_map."""), 'group_fund_raising': fields.boolean("Manage Fund Raising", diff --git a/addons/crm/test/process/communication_with_customer.yml b/addons/crm/test/process/communication_with_customer.yml index 1653be10763..d18fa5495d9 100644 --- a/addons/crm/test/process/communication_with_customer.yml +++ b/addons/crm/test/process/communication_with_customer.yml @@ -23,7 +23,7 @@ !python {model: mail.compose.message}: | lead_ids = self.pool.get('crm.lead').search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')]) context.update({'active_model': 'crm.lead','active_id': lead_ids[0]}) - id = self.create(cr, uid, {'body_text': "Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci", 'email_from': 'sales@mycompany.com'}, context=context) + id = self.create(cr, uid, {'body': "Merci à l'intérêt pour notre produit.nous vous contacterons bientôt. Merci", 'email_from': 'sales@mycompany.com'}, context=context) try: self.send_mail(cr, uid, [id], context=context) except: @@ -35,12 +35,6 @@ lead_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')]) self.convert_partner(cr, uid, lead_ids, context=context) - - Now, I search customer in regular customer list. -- - !python {model: crm.lead}: | - partner_ids = self.message_partner_by_email(cr, uid, 'Mr. John Right <info@customer.com>') - assert partner_ids.get('partner_id'), "Customer is not found in regular customer list." -- I convert one phonecall request as a customer and put into regular customer list. - !python {model: crm.phonecall2partner}: | diff --git a/addons/crm/test/process/lead2opportunity2win.yml b/addons/crm/test/process/lead2opportunity2win.yml index 23713dfca2f..ed552421cf0 100644 --- a/addons/crm/test/process/lead2opportunity2win.yml +++ b/addons/crm/test/process/lead2opportunity2win.yml @@ -55,7 +55,7 @@ After communicated with customer, I put some notes with contract details. - !python {model: crm.lead}: | - self.message_append_note(cr, uid, [ref('crm_case_4')], subject='Test note', body='ces détails envoyés par le client sur le FAX pour la qualité') + self.message_post(cr, uid, [ref('crm_case_4')], subject='Test note', body='ces détails envoyés par le client sur le FAX pour la qualité') - I win this opportunity - @@ -73,7 +73,7 @@ I convert mass lead into opportunity customer. - !python {model: crm.lead2opportunity.partner.mass}: | - context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_4")}) + context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_11")}) id = self.create(cr, uid, {'user_ids': [ref('base.user_root')], 'section_id': ref('crm.section_sales_department')}, context=context) self.mass_convert(cr, uid, [id], context=context) - @@ -83,7 +83,8 @@ opp = self.browse(cr, uid, ref('crm_case_11')) assert opp.name == "Need estimated cost for new project", "Opportunity name not correct" assert opp.type == 'opportunity', 'Lead is not converted to opportunity!' - assert opp.partner_id.name == "Thomas Passot", 'Partner mismatch!' + expected_partner = "Thomas Passot" + assert opp.partner_id.name == expected_partner, 'Partner mismatch! %s vs %s' % (opp.partner_id.name, expected_partner) assert opp.stage_id.id == ref("stage_lead1"), 'Stage of probability is incorrect!' - Then check for second lead converted on opportunity. diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 547d3ed299e..e90303dda66 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -24,8 +24,6 @@ from tools.translate import _ import tools import re -import time - class crm_lead2opportunity_partner(osv.osv_memory): _name = 'crm.lead2opportunity.partner' _description = 'Lead To Opportunity Partner' @@ -35,8 +33,8 @@ class crm_lead2opportunity_partner(osv.osv_memory): 'action': fields.selection([('exist', 'Link to an existing partner'), \ ('create', 'Create a new partner'), \ ('nothing', 'Do not link to a partner')], \ - 'Action', required=True), - 'name': fields.selection([('convert', 'Convert to Opportunity'), ('merge', 'Merge with existing Opportunity')],'Select Action', required=True), + 'Related Partner', required=True), + 'name': fields.selection([('convert', 'Convert to Opportunities'), ('merge', 'Merge with existing Opportunities')], 'Conversion Action', required=True), 'opportunity_ids': fields.many2many('crm.lead', string='Opportunities', domain=[('type', '=', 'opportunity')]), } @@ -68,8 +66,6 @@ class crm_lead2opportunity_partner(osv.osv_memory): if ids: opportunities.append(ids[0]) if not partner_id: - label = False - opp_ids = [] if email: # Find email of existing opportunity matches the email_from of the lead cr.execute("""select id from crm_lead where type='opportunity' and @@ -106,23 +102,36 @@ class crm_lead2opportunity_partner(osv.osv_memory): if context is None: context = {} lead = self.pool.get('crm.lead') - partner_id = self._create_partner(cr, uid, ids, context=context) + res = False + partner_ids_map = self._create_partner(cr, uid, ids, context=context) lead_ids = vals.get('lead_ids', []) - user_ids = vals.get('user_ids', False) team_id = vals.get('section_id', False) - return lead.convert_opportunity(cr, uid, lead_ids, partner_id, user_ids, team_id, context=context) + for lead_id in lead_ids: + partner_id = partner_ids_map.get(lead_id, False) + # FIXME: cannot pass user_ids as the salesman allocation only works in batch + res = lead.convert_opportunity(cr, uid, [lead_id], partner_id, [], team_id, context=context) + # FIXME: must perform salesman allocation in batch separately here + user_ids = vals.get('user_ids', False) + if user_ids: + lead.allocate_salesman(cr, uid, lead_ids, user_ids, team_id=team_id, context=context) + return res def _merge_opportunity(self, cr, uid, ids, opportunity_ids, action='merge', context=None): - #TOFIX: is it usefully ? if context is None: context = {} - merge_opportunity = self.pool.get('crm.merge.opportunity') res = False - #If we convert in mass, don't merge if there is no other opportunity but no warning - if action == 'merge' and (len(opportunity_ids) > 1 or not context.get('mass_convert') ): - self.write(cr, uid, ids, {'opportunity_ids' : [(6,0, [opportunity_ids[0].id])]}, context=context) - context.update({'lead_ids' : record_id, "convert" : True}) - res = merge_opportunity.merge(cr, uid, data.opportunity_ids, context=context) + # Expected: all newly-converted leads (active_ids) will be merged with the opportunity(ies) + # that have been selected in the 'opportunity_ids' m2m, with all these records + # merged into the first opportunity (and the rest deleted) + opportunity_ids = [o.id for o in opportunity_ids] + lead_ids = context.get('active_ids', []) + if action == 'merge' and lead_ids and opportunity_ids: + # Add the leads in the to-merge list, next to other opps + # (the fact that they're passed in context['lead_ids'] means that + # they cannot be selected to contain the result of the merge. + opportunity_ids.extend(lead_ids) + context.update({'lead_ids': lead_ids, "convert" : True}) + res = self.pool.get('crm.lead').merge_opportunity(cr, uid, opportunity_ids, context=context) return res def action_apply(self, cr, uid, ids, context=None): @@ -131,27 +140,37 @@ class crm_lead2opportunity_partner(osv.osv_memory): """ if not context: context = {} - lead = self.pool.get('crm.lead') lead_ids = context.get('active_ids', []) data = self.browse(cr, uid, ids, context=context)[0] self._convert_opportunity(cr, uid, ids, {'lead_ids': lead_ids}, context=context) - self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.action, context=context) + self._merge_opportunity(cr, uid, ids, data.opportunity_ids, data.name, context=context) return lead.redirect_opportunity_view(cr, uid, lead_ids[0], context=context) -crm_lead2opportunity_partner() class crm_lead2opportunity_mass_convert(osv.osv_memory): _name = 'crm.lead2opportunity.partner.mass' _description = 'Mass Lead To Opportunity Partner' _inherit = 'crm.lead2opportunity.partner' - _columns = { - 'user_ids': fields.many2many('res.users', string='Salesmans'), + 'user_ids': fields.many2many('res.users', string='Salesmen'), 'section_id': fields.many2one('crm.case.section', 'Sales Team'), - } + + def default_get(self, cr, uid, fields, context=None): + res = super(crm_lead2opportunity_mass_convert, self).default_get(cr, uid, fields, context) + if 'partner_id' in fields: + # avoid forcing the partner of the first lead as default + res['partner_id'] = False + if 'action' in fields: + res['action'] = 'create' + if 'name' in fields: + res['name'] = 'convert' + if 'opportunity_ids' in fields: + res['opportunity_ids'] = False + return res + def _convert_opportunity(self, cr, uid, ids, vals, context=None): data = self.browse(cr, uid, ids, context=context)[0] salesteam_id = data.section_id and data.section_id.id or False @@ -162,9 +181,6 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): return super(crm_lead2opportunity_mass_convert, self)._convert_opportunity(cr, uid, ids, vals, context=context) def mass_convert(self, cr, uid, ids, context=None): - value = self.default_get(cr, uid, ['partner_id', 'opportunity_ids'], context=context) - value['opportunity_ids'] = [(6, 0, value['opportunity_ids'])] - self.write(cr, uid, ids, value, context=context) return self.action_apply(cr, uid, ids, context=context) -crm_lead2opportunity_mass_convert() + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/wizard/crm_lead_to_opportunity_view.xml b/addons/crm/wizard/crm_lead_to_opportunity_view.xml index 8829749efa3..6a826f7bc0e 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity_view.xml +++ b/addons/crm/wizard/crm_lead_to_opportunity_view.xml @@ -37,24 +37,38 @@ <field name="model">crm.lead2opportunity.partner.mass</field> <field name="arch" type="xml"> <form string="Convert to Opportunity" version="7.0"> - <field name="action"/> - <field name="name" colspan="4"/> - <group string="Assigned Opportunities to"> - <field name="section_id"/> + <group string="Conversion Options"> + <field name="action"/> + <field name="partner_id" attrs="{'invisible':[('action','!=','exist')],'required': [('action', '=', 'exist')]}"/> + <field name="name"/> </group> - <separator string="Select Salesman"/> - <field name="user_ids"> + <group string="Select Opportunities" attrs="{'invisible': [('name', '=', 'convert')]}"> + <field name="opportunity_ids" colspan="4" nolabel="1" attrs="{'invisible': [('name', '=', 'convert')]}"> <tree> - <field name="name"/> + <field name="name" /> + <field name="partner_id" /> + <field name="user_id" /> + <field name="section_id" /> </tree> </field> + </group> + + <group string="Assign opportunities to"> + <field name="section_id" /> + <field name="user_ids" colspan="4"> + <tree> + <field name="name" /> + </tree> + </field> + </group> + <footer> - <button name="mass_convert" string="Convert into Opportunities" type="object" class="oe_highlight"/> + <button name="mass_convert" string="Convert to Opportunities" type="object" class="oe_highlight"/> or <button string="Cancel" class="oe_link" special="cancel"/> - </footer> + </footer> </form> - </field> + </field> </record> <record id="action_crm_lead2opportunity_partner" model="ir.actions.act_window"> diff --git a/addons/crm/wizard/crm_lead_to_partner.py b/addons/crm/wizard/crm_lead_to_partner.py index 98f114b4088..9b239bab7b9 100644 --- a/addons/crm/wizard/crm_lead_to_partner.py +++ b/addons/crm/wizard/crm_lead_to_partner.py @@ -50,20 +50,20 @@ class crm_lead2partner(osv.osv_memory): def _select_partner(self, cr, uid, context=None): if context is None: context = {} - lead = self.pool.get('crm.lead') - partner = self.pool.get('res.partner') - lead_ids = list(context and context.get('active_ids', []) or []) - if not len(lead_ids): + if not context.get('active_model') == 'crm.lead' or not context.get('active_id'): return False - this = lead.browse(cr, uid, lead_ids[0], context=context) - # Find partner address matches the email_from of the lead - res = lead.message_partner_by_email(cr, uid, this.email_from, context=context) - partner_id = res.get('partner_id', False) - # Find partner name that matches the name of the lead - if not partner_id and this.partner_name: + partner = self.pool.get('res.partner') + lead = self.pool.get('crm.lead') + this = lead.browse(cr, uid, context.get('active_id'), context=context) + partner_id = False + if this.email_from: + partner_ids = partner.search(cr, uid, [('email', '=', this.email_from)], context=context) + if partner_ids: + partner_id = partner_ids[0] + if not this.partner_id and this.partner_name: partner_ids = partner.search(cr, uid, [('name', '=', this.partner_name)], context=context) - if partner_ids and len(partner_ids): - partner_id = partner_ids[0] + if partner_ids: + partner_id = partner_ids[0] return partner_id def default_get(self, cr, uid, fields, context=None): @@ -107,15 +107,16 @@ class crm_lead2partner(osv.osv_memory): lead_ids = context and context.get('active_ids') or [] data = self.browse(cr, uid, ids, context=context)[0] partner_id = data.partner_id and data.partner_id.id or False - partner_ids = lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context) - return partner_ids[lead_ids[0]] + return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context) def make_partner(self, cr, uid, ids, context=None): """ This function Makes partner based on action. """ - partner_id = self._create_partner(cr, uid, ids, context=context) - return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_id, context=context) + # Only called from Form view, so only meant to convert one Lead. + lead_id = context and context.get('active_id') or False + partner_ids_map = self._create_partner(cr, uid, ids, context=context) + return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context) crm_lead2partner() diff --git a/addons/crm/wizard/crm_partner_to_opportunity_view.xml b/addons/crm/wizard/crm_partner_to_opportunity_view.xml index 765a22de0a6..7f0b5957071 100644 --- a/addons/crm/wizard/crm_partner_to_opportunity_view.xml +++ b/addons/crm/wizard/crm_partner_to_opportunity_view.xml @@ -23,23 +23,5 @@ </field> </record> -<!-- partner To Opportunity Action --> - - <record model="ir.actions.act_window" id="action_view_crm_partner2opportunity"> - <field name="name">Create Opportunity</field> - <field name="res_model">crm.partner2opportunity</field> - <field name="view_type">form</field> - <field name="view_mode">form,tree,kanban,calendar</field> - <field name="target">new</field> - </record> - - <!-- partner To Opportunity wizard --> - - <act_window id="crm_partner2opportunity" - key2="client_action_multi" name="Create Opportunity" - res_model="crm.partner2opportunity" src_model="res.partner" - view_id="view_crm_partner2opportunity" - view_mode="form" target="new" view_type="form" - groups="base.group_sale_salesman"/> </data> </openerp> diff --git a/addons/crm/wizard/crm_phonecall_to_partner.py b/addons/crm/wizard/crm_phonecall_to_partner.py index 422af02f99d..715b7396c66 100644 --- a/addons/crm/wizard/crm_phonecall_to_partner.py +++ b/addons/crm/wizard/crm_phonecall_to_partner.py @@ -57,12 +57,10 @@ class crm_phonecall2partner(osv.osv_memory): if context is None: context = {} phonecall = self.pool.get('crm.phonecall') - data = self.browse(cr, uid, ids, context=context)[0] call_ids = context and context.get('active_ids') or [] partner_id = data.partner_id and data.partner_id.id or False - partner_ids = phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context) - return partner_ids[call_ids[0]] + return phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context) crm_phonecall2partner() diff --git a/addons/crm_caldav/i18n/nb.po b/addons/crm_caldav/i18n/nb.po new file mode 100644 index 00000000000..c5a1c3cfe37 --- /dev/null +++ b/addons/crm_caldav/i18n/nb.po @@ -0,0 +1,33 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-04 14:04+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-05 04:46+0000\n" +"X-Generator: Launchpad (build 15901)\n" + +#. module: crm_caldav +#: model:ir.actions.act_window,name:crm_caldav.action_caldav_browse +msgid "Caldav Browse" +msgstr "CalDAV Bla" + +#. module: crm_caldav +#: model:ir.ui.menu,name:crm_caldav.menu_caldav_browse +msgid "Synchronize This Calendar" +msgstr "Synkroniser denne kalenderen" + +#. module: crm_caldav +#: model:ir.model,name:crm_caldav.model_crm_meeting +msgid "Meeting" +msgstr "Møte" diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index ec6252bcdca..535f6d20367 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -72,7 +72,7 @@ class crm_claim(base_stage, osv.osv): _description = "Claim" _order = "priority,date desc" _inherit = ['mail.thread'] - _mail_compose_message = True + _columns = { 'id': fields.integer('ID', readonly=True), 'name': fields.char('Claim Subject', size=128, required=True), @@ -194,13 +194,12 @@ class crm_claim(base_stage, osv.osv): if custom_values is None: custom_values = {} custom_values.update({ 'name': msg.get('subject') or _("No Subject"), - 'description': msg.get('body_text'), + 'description': msg.get('body'), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) return super(crm_claim,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): @@ -220,7 +219,7 @@ class crm_claim(base_stage, osv.osv): 'revenue': 'planned_revenue', 'probability':'probability' } - for line in msg['body_text'].split('\n'): + for line in msg['body'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): @@ -239,16 +238,16 @@ class crm_claim(base_stage, osv.osv): def create_send_note(self, cr, uid, ids, context=None): msg = _('Claim has been <b>created</b>.') - return self.message_append_note(cr, uid, ids, body=msg, context=context) + return self.message_post(cr, uid, ids, body=msg, context=context) def case_refuse_send_note(self, cr, uid, ids, context=None): msg = _('Claim has been <b>refused</b>.') - return self.message_append_note(cr, uid, ids, body=msg, context=context) + return self.message_post(cr, uid, ids, body=msg, context=context) def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('crm.claim.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) + return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) class res_partner(osv.osv): diff --git a/addons/crm_claim/crm_claim_menu.xml b/addons/crm_claim/crm_claim_menu.xml index 46f8760387a..bb881d62a58 100644 --- a/addons/crm_claim/crm_claim_menu.xml +++ b/addons/crm_claim/crm_claim_menu.xml @@ -50,9 +50,9 @@ parent="base.menu_aftersale" action="crm_case_categ_claim0" sequence="1"/> <!-- Claim Stages --> + <menuitem id="base.menu_definitions" name="Configuration" parent="base.menu_main_pm" sequence="60"/> <menuitem id="base.menu_project_config_project" name="Stages" parent="base.menu_definitions" sequence="1"/> <menuitem id="menu_claim_stage_view" name="Claim Stages" action="crm_claim_stage_act" parent="base.menu_project_config_project" sequence="20"/> - <menuitem id="base.menu_definitions" name="Configuration" parent="base.menu_main_pm" sequence="60"/> </data> </openerp> diff --git a/addons/crm_claim/test/ui/claim_demo.yml b/addons/crm_claim/test/ui/claim_demo.yml index ad8fdf3aa44..044e0482e93 100644 --- a/addons/crm_claim/test/ui/claim_demo.yml +++ b/addons/crm_claim/test/ui/claim_demo.yml @@ -9,7 +9,6 @@ - !python {model: crm.claim}: | try: - self.message_update(cr, uid,[ref('crm_claim_4')], {'subject': 'Claim Update record','body_text': 'first training session completed',}) + self.message_update(cr, uid,[ref('crm_claim_4')], {'subject': 'Claim Update record','body': 'first training session completed',}) except: pass -
\ No newline at end of file diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index 4b0cb2e3ffa..612537bdb77 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -38,7 +38,7 @@ class crm_helpdesk(base_state, osv.osv): _description = "Helpdesk" _order = "id desc" _inherit = ['mail.thread'] - _mail_compose_message = True + _columns = { 'id': fields.integer('ID', readonly=True), 'name': fields.char('Name', size=128, required=True), @@ -105,12 +105,11 @@ class crm_helpdesk(base_state, osv.osv): if custom_values is None: custom_values = {} custom_values.update({ 'name': msg.get('subject') or _("No Subject"), - 'description': msg.get('body_text'), + 'description': msg.get('body'), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, }) - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): @@ -130,7 +129,7 @@ class crm_helpdesk(base_state, osv.osv): 'revenue': 'planned_revenue', 'probability':'probability' } - for line in msg['body_text'].split('\n'): + for line in msg['body'].split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower()): @@ -149,7 +148,7 @@ class crm_helpdesk(base_state, osv.osv): def create_send_note(self, cr, uid, ids, context=None): msg = _('Case has been <b>created</b>.') - self.message_append_note(cr, uid, ids, body=msg, context=context) + self.message_post(cr, uid, ids, body=msg, context=context) return True diff --git a/addons/crm_helpdesk/report/crm_helpdesk_report.py b/addons/crm_helpdesk/report/crm_helpdesk_report.py index 93a33d62819..6496935c2d2 100644 --- a/addons/crm_helpdesk/report/crm_helpdesk_report.py +++ b/addons/crm_helpdesk/report/crm_helpdesk_report.py @@ -97,7 +97,7 @@ class crm_helpdesk_report(osv.osv): c.planned_cost, count(*) as nbr, extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close, - (SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND email_from IS NOT NULL) AS email, + (SELECT count(id) FROM mail_message WHERE model='crm.helpdesk' AND res_id=c.id AND type = 'email') AS email, abs(avg(extract('epoch' from (c.date_deadline - c.date_closed)))/(3600*24)) as delay_expected from crm_helpdesk c diff --git a/addons/crm_helpdesk/test/process/help-desk.yml b/addons/crm_helpdesk/test/process/help-desk.yml index 320bd61c2af..4fd6a9ee9d7 100644 --- a/addons/crm_helpdesk/test/process/help-desk.yml +++ b/addons/crm_helpdesk/test/process/help-desk.yml @@ -23,7 +23,7 @@ !python {model: crm.helpdesk}: | question_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')]) try: - self.message_update(cr, uid, question_ids, {'subject': 'Link of product', 'body_text': 'www.openerp.com'}) + self.message_update(cr, uid, question_ids, {'subject': 'Link of product', 'body': 'www.openerp.com'}) except: pass diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index d4ff93d52fd..cd2e3f08c9a 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -19,8 +19,7 @@ attrs="{'invisible':[('partner_assigned_id','=',False)]}" name="%(crm_lead_forward_to_partner_act)d" icon="terp-mail-forward" type="action" - context="{'default_name': 'partner', 'default_partner_id': partner_assigned_id}" - /> + context="{'default_composition_mode': 'forward', 'default_partner_ids': [partner_assigned_id]}"/> </div> </group> <group string="Geo Assignation"> @@ -37,8 +36,6 @@ </field> </record> - - <record id="view_crm_opportunity_geo_assign_tree" model="ir.ui.view"> <field name="name">crm.lead.geo_assign.tree.inherit</field> <field name="model">crm.lead</field> @@ -49,6 +46,7 @@ </field> </field> </record> + <record model="ir.ui.view" id="crm_opportunity_partner_filter"> <field name="name">crm.opportunity.partner.filter.assigned</field> <field name="model">crm.lead</field> @@ -63,5 +61,6 @@ </field> </field> </record> + </data> </openerp> diff --git a/addons/crm_partner_assign/i18n/ar.po b/addons/crm_partner_assign/i18n/ar.po index de0f43af5a6..53d88fd259a 100644 --- a/addons/crm_partner_assign/i18n/ar.po +++ b/addons/crm_partner_assign/i18n/ar.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:39+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "التمركز الجغرافي" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -134,7 +134,7 @@ msgid "Highest" msgstr "أعلى" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/bg.po b/addons/crm_partner_assign/i18n/bg.po index 8d34ce6a73c..cb670286522 100644 --- a/addons/crm_partner_assign/i18n/bg.po +++ b/addons/crm_partner_assign/i18n/bg.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:39+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Най-висок" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/ca.po b/addons/crm_partner_assign/i18n/ca.po index 1ff3ec07048..1f73a19b35a 100644 --- a/addons/crm_partner_assign/i18n/ca.po +++ b/addons/crm_partner_assign/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo localitzar" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "El més alt" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/crm_partner_assign.pot b/addons/crm_partner_assign/i18n/crm_partner_assign.pot index 3fb9fc0e915..c3ddc66fa3b 100644 --- a/addons/crm_partner_assign/i18n/crm_partner_assign.pot +++ b/addons/crm_partner_assign/i18n/crm_partner_assign.pot @@ -72,7 +72,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -129,7 +129,7 @@ msgid "Highest" msgstr "" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/da.po b/addons/crm_partner_assign/i18n/da.po index 4e249458019..5bb8265399f 100644 --- a/addons/crm_partner_assign/i18n/da.po +++ b/addons/crm_partner_assign/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/de.po b/addons/crm_partner_assign/i18n/de.po index 9257f3c329e..20a3352bc3f 100644 --- a/addons/crm_partner_assign/i18n/de.po +++ b/addons/crm_partner_assign/i18n/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geogr. Lokalisierung" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Text Version der Mitteilung" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Sehr Hoch" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Text Inhalt" diff --git a/addons/crm_partner_assign/i18n/el.po b/addons/crm_partner_assign/i18n/el.po index d621855b061..edad942b005 100644 --- a/addons/crm_partner_assign/i18n/el.po +++ b/addons/crm_partner_assign/i18n/el.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Υψηλότερο" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/es.po b/addons/crm_partner_assign/i18n/es.po index cfcbb629923..f4e6d399e15 100644 --- a/addons/crm_partner_assign/i18n/es.po +++ b/addons/crm_partner_assign/i18n/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo localizar" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Más alta" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/es_CR.po b/addons/crm_partner_assign/i18n/es_CR.po index 4315ece4f3b..383f2669379 100644 --- a/addons/crm_partner_assign/i18n/es_CR.po +++ b/addons/crm_partner_assign/i18n/es_CR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" "Language: es\n" #. module: crm_partner_assign @@ -75,7 +75,7 @@ msgid "Geo Localize" msgstr "Geo localizar" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Versión en texto plano del mensaje" @@ -136,7 +136,7 @@ msgid "Highest" msgstr "Más alta" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Contenido del Texto" diff --git a/addons/crm_partner_assign/i18n/es_PY.po b/addons/crm_partner_assign/i18n/es_PY.po index b3e52bffc08..b250c96e070 100644 --- a/addons/crm_partner_assign/i18n/es_PY.po +++ b/addons/crm_partner_assign/i18n/es_PY.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo localizar" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Muy alto" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/fi.po b/addons/crm_partner_assign/i18n/fi.po index cd7065f8fd6..9c237cf5495 100644 --- a/addons/crm_partner_assign/i18n/fi.po +++ b/addons/crm_partner_assign/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Alueellista" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Korkein" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/fr.po b/addons/crm_partner_assign/i18n/fr.po index 770f58f72a5..0d3f4e07808 100644 --- a/addons/crm_partner_assign/i18n/fr.po +++ b/addons/crm_partner_assign/i18n/fr.po @@ -15,8 +15,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -75,7 +75,7 @@ msgid "Geo Localize" msgstr "Géolocalisation" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Version plein texte du message" @@ -136,7 +136,7 @@ msgid "Highest" msgstr "La Plus haute" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Contenu du texte" diff --git a/addons/crm_partner_assign/i18n/gl.po b/addons/crm_partner_assign/i18n/gl.po index 5bf10d25f16..184f3a25386 100644 --- a/addons/crm_partner_assign/i18n/gl.po +++ b/addons/crm_partner_assign/i18n/gl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Xeolocalizar" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "A máis alta" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/hr.po b/addons/crm_partner_assign/i18n/hr.po index e18b6c5188e..3236f0bd3ac 100644 --- a/addons/crm_partner_assign/i18n/hr.po +++ b/addons/crm_partner_assign/i18n/hr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Najviši" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/hu.po b/addons/crm_partner_assign/i18n/hu.po index 816f93f1203..daaeca2363e 100644 --- a/addons/crm_partner_assign/i18n/hu.po +++ b/addons/crm_partner_assign/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Földrajzi fekvés meghatározása" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Legmagasabb" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/it.po b/addons/crm_partner_assign/i18n/it.po index c008216c5d3..21d3c30d82b 100644 --- a/addons/crm_partner_assign/i18n/it.po +++ b/addons/crm_partner_assign/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo localizzazione" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Maggiore" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/ja.po b/addons/crm_partner_assign/i18n/ja.po index d6a739dc2b1..93dcbe346ca 100644 --- a/addons/crm_partner_assign/i18n/ja.po +++ b/addons/crm_partner_assign/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "ジオロカライゼーション" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "メッセージのテキスト版" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "最高" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "テキストの内容" diff --git a/addons/crm_partner_assign/i18n/lt.po b/addons/crm_partner_assign/i18n/lt.po index 7c2f427260a..8e89e14575c 100644 --- a/addons/crm_partner_assign/i18n/lt.po +++ b/addons/crm_partner_assign/i18n/lt.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/lv.po b/addons/crm_partner_assign/i18n/lv.po index a51b096e0a3..182f8990341 100644 --- a/addons/crm_partner_assign/i18n/lv.po +++ b/addons/crm_partner_assign/i18n/lv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Ģeo. lokalizācija" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "Augstākā" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/nl.po b/addons/crm_partner_assign/i18n/nl.po index ebb0cf33f10..ac9dc276e82 100644 --- a/addons/crm_partner_assign/i18n/nl.po +++ b/addons/crm_partner_assign/i18n/nl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo lokaliseren" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Platte tekst versie van het bericht" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Hoogste" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Tekst inhoud" diff --git a/addons/crm_partner_assign/i18n/pl.po b/addons/crm_partner_assign/i18n/pl.po index 9645def6e96..31936f2794c 100644 --- a/addons/crm_partner_assign/i18n/pl.po +++ b/addons/crm_partner_assign/i18n/pl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Lokalizacja geograficzna" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Najwyższy" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/pt.po b/addons/crm_partner_assign/i18n/pt.po index a1b159fe387..ba250d4519b 100644 --- a/addons/crm_partner_assign/i18n/pt.po +++ b/addons/crm_partner_assign/i18n/pt.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Localizar Geograficamente" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Formatação do texto da versão da mensagem" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Maior" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Conteúdos do texto" diff --git a/addons/crm_partner_assign/i18n/pt_BR.po b/addons/crm_partner_assign/i18n/pt_BR.po index 10e51041d3f..fa7a7d0561c 100644 --- a/addons/crm_partner_assign/i18n/pt_BR.po +++ b/addons/crm_partner_assign/i18n/pt_BR.po @@ -15,8 +15,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -75,7 +75,7 @@ msgid "Geo Localize" msgstr "Localização Geográfica" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Versão em texto" @@ -136,7 +136,7 @@ msgid "Highest" msgstr "Mais Alta" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Conteúdo" diff --git a/addons/crm_partner_assign/i18n/ro.po b/addons/crm_partner_assign/i18n/ro.po index d4e43523d6e..c385587a99b 100644 --- a/addons/crm_partner_assign/i18n/ro.po +++ b/addons/crm_partner_assign/i18n/ro.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Localizare Geo" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Versiune text-simplu a mesajului" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Cel mai ridicat (cea mai ridicata)" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Cuprins text" diff --git a/addons/crm_partner_assign/i18n/ru.po b/addons/crm_partner_assign/i18n/ru.po index 4a5229b59b2..0a354515bf8 100644 --- a/addons/crm_partner_assign/i18n/ru.po +++ b/addons/crm_partner_assign/i18n/ru.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Геолоцировать" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Высший" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/sq.po b/addons/crm_partner_assign/i18n/sq.po index 56d7f386635..7d2f01a6693 100644 --- a/addons/crm_partner_assign/i18n/sq.po +++ b/addons/crm_partner_assign/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:39+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/sr@latin.po b/addons/crm_partner_assign/i18n/sr@latin.po index 6e9aab21f1c..0605ee65706 100644 --- a/addons/crm_partner_assign/i18n/sr@latin.po +++ b/addons/crm_partner_assign/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "" diff --git a/addons/crm_partner_assign/i18n/sv.po b/addons/crm_partner_assign/i18n/sv.po index 345d61d10b3..0605d54bcaa 100644 --- a/addons/crm_partner_assign/i18n/sv.po +++ b/addons/crm_partner_assign/i18n/sv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geoplacering" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Textversion av meddelandet" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "Högsta" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Textinnehåll" diff --git a/addons/crm_partner_assign/i18n/tr.po b/addons/crm_partner_assign/i18n/tr.po index da879a6156b..11fcc743002 100644 --- a/addons/crm_partner_assign/i18n/tr.po +++ b/addons/crm_partner_assign/i18n/tr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Coğrafi konumlama" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "Mesajın düz-metin versiyonu" @@ -135,7 +135,7 @@ msgid "Highest" msgstr "En yüksek" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "Metin içeriği" diff --git a/addons/crm_partner_assign/i18n/zh_CN.po b/addons/crm_partner_assign/i18n/zh_CN.po index 67f4a4e74b8..ff25feafd25 100644 --- a/addons/crm_partner_assign/i18n/zh_CN.po +++ b/addons/crm_partner_assign/i18n/zh_CN.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "geolocalization定位" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "内容的纯文本版本" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "最高" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "文本内容" diff --git a/addons/crm_partner_assign/i18n/zh_TW.po b/addons/crm_partner_assign/i18n/zh_TW.po index 3c77816d71b..4870b464035 100644 --- a/addons/crm_partner_assign/i18n/zh_TW.po +++ b/addons/crm_partner_assign/i18n/zh_TW.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-31 04:56+0000\n" -"X-Generator: Launchpad (build 15887)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: crm_partner_assign #: field:crm.lead.forward.to.partner,send_to:0 @@ -74,7 +74,7 @@ msgid "Geo Localize" msgstr "Geo 定位" #. module: crm_partner_assign -#: help:crm.lead.forward.to.partner,body_text:0 +#: help:crm.lead.forward.to.partner,body:0 msgid "Plain-text version of the message" msgstr "內容的純文字版本" @@ -133,7 +133,7 @@ msgid "Highest" msgstr "最高" #. module: crm_partner_assign -#: field:crm.lead.forward.to.partner,body_text:0 +#: field:crm.lead.forward.to.partner,body:0 msgid "Text contents" msgstr "文本內容" diff --git a/addons/crm_partner_assign/test/partner_assign.yml b/addons/crm_partner_assign/test/partner_assign.yml index bc4cd0d6e57..a055fc6cd6d 100644 --- a/addons/crm_partner_assign/test/partner_assign.yml +++ b/addons/crm_partner_assign/test/partner_assign.yml @@ -29,8 +29,8 @@ I forward this opportunity to its nearest partner. - !python {model: crm.lead.forward.to.partner}: | - context.update({'active_model': 'crm.lead', 'active_id': ref('crm.crm_case_19'), 'active_ids': [ref('crm.crm_case_19')]}) - forward_id = self.create(cr, uid, {'email_from': 'test@openerp.com', 'send_to': 'partner'}, context=context) + context.update({'default_model': 'crm.lead', 'default_res_id': ref('crm.crm_case_19'), 'active_ids': [ref('crm.crm_case_19')]}) + forward_id = self.create(cr, uid, {}, context=context) try: self.action_forward(cr, uid, [forward_id], context=context) except: diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py index d479f8b1bd5..0cac38e0163 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner.py +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner.py @@ -20,186 +20,153 @@ # ############################################################################## -import time import re +import time +import tools + from osv import osv, fields from tools.translate import _ -from mail.mail_message import to_email class crm_lead_forward_to_partner(osv.osv_memory): - """Forwards lead history""" + """ Forward info history to partners. """ _name = 'crm.lead.forward.to.partner' _inherit = "mail.compose.message" + def default_get(self, cr, uid, fields, context=None): + if context is None: + context = {} + # set as comment, perform overrided document-like action that calls get_record_data + old_mode = context.get('default_composition_mode', 'forward') + context['default_composition_mode'] = 'comment' + res = super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context) + # back to forward mode + context['default_composition_mode'] = old_mode + res['composition_mode'] = context['default_composition_mode'] + return res + + def _get_composition_mode_selection(self, cr, uid, context=None): + composition_mode = super(crm_lead_forward_to_partner, self)._get_composition_mode_selection(cr, uid, context=context) + composition_mode.append(('forward', 'Forward')) + return composition_mode + _columns = { - 'send_to': fields.selection([('user', 'User'), ('partner', 'Partner'), \ - ('email', 'Email Address')], 'Send to', required=True), - 'user_id': fields.many2one('res.users', "User"), - 'attachment_ids': fields.many2many('ir.attachment','lead_forward_to_partner_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), - 'partner_id' : fields.many2one('res.partner', 'Partner'), - 'history': fields.selection([('info', 'Case Information'), ('latest', 'Latest email'), ('whole', 'Whole Story')], 'Send history', required=True), + 'partner_ids': fields.many2many('res.partner', + 'lead_forward_to_partner_res_partner_rel', + 'wizard_id', 'partner_id', 'Additional contacts'), + 'attachment_ids': fields.many2many('ir.attachment', + 'lead_forward_to_partner_attachment_rel', + 'wizard_id', 'attachment_id', 'Attachments'), + 'history_mode': fields.selection([('info', 'Case Information'), + ('latest', 'Latest email'), ('whole', 'Whole Story')], + 'Send history', required=True), } _defaults = { - 'send_to' : 'email', - 'history': 'latest', - 'email_from': lambda s, cr, uid, c: s.pool.get('res.users').browse(cr, uid, uid, c).email, + 'history_mode': 'latest', + 'content_subtype': lambda self,cr, uid, context={}: 'html', } - def on_change_email(self, cr, uid, ids, user, context=None): - if not user: - return {'value': {'email_to': False}} - return {'value': {'email_to': self.pool.get('res.users').browse(cr, uid, uid, context=context).email}} - - def on_change_history(self, cr, uid, ids, history_type, context=None): - """Gives message body according to type of history selected - * info: Forward the case information - * whole: Send the whole history - * latest: Send the latest histoy + def get_record_data(self, cr, uid, model, res_id, context=None): + """ Override of mail.compose.message, to add default values coming + form the related lead. """ - #TODO: ids and context are not comming - res = {} - res_id = context.get('active_id') - model = context.get('active_model') - lead = self.pool.get(model).browse(cr, uid, res_id, context) - body_text = self._get_message_body_text(cr, uid, lead, history_type, context=context) - if body_text: - res = {'value': {'body_text' : body_text}} + res = super(crm_lead_forward_to_partner, self).get_record_data(cr, uid, model, res_id, context=context) + if model not in ('crm.lead') or not res_id: + return res + lead_obj = self.pool.get(model) + lead = lead_obj.browse(cr, uid, res_id, context=context) + subject = '%s: %s - %s' % (_('Fwd'), _('Lead forward'), lead.name) + body = self._get_message_body(cr, uid, lead, 'info', context=context) + res.update({ + 'subject': subject, + 'body': body, + }) return res - - def on_change_partner(self, cr, uid, ids, partner_id): - """This function fills address information based on partner/user selected - """ - if not partner_id: - return {'value' : {'email_to' : False}} - partner_obj = self.pool.get('res.partner') - data = {} - partner = partner_obj.browse(cr, uid, [partner_id]) - user_id = partner and partner[0].user_id or False - data.update({'email_from': partner and partner[0].email or "", - 'email_cc' : user_id and user_id.user or '', - 'user_id': user_id and user_id.id or False}) - return {'value' : data} + + def on_change_history_mode(self, cr, uid, ids, history_mode, model, res_id, context=None): + """ Update body when changing history_mode """ + if model and model == 'crm.lead' and res_id: + lead = self.pool.get(model).browse(cr, uid, res_id, context=context) + body = self._get_message_body(cr, uid, lead, history_mode, context=context) + return {'value': {'body': body}} + + def create(self, cr, uid, values, context=None): + """ TDE-HACK: remove 'type' from context, because when viewing an + opportunity form view, a default_type is set and propagated + to the wizard, that has a not matching type field. """ + default_type = context.pop('default_type', None) + new_id = super(crm_lead_forward_to_partner, self).create(cr, uid, values, context=context) + if default_type: + context['default_type'] = default_type + return new_id def action_forward(self, cr, uid, ids, context=None): - """ - Forward the lead to a partner - """ + """ Forward the lead to a partner """ if context is None: context = {} res = {'type': 'ir.actions.act_window_close'} - model = context.get('active_model') - if model not in ('crm.lead'): + wizard = self.browse(cr, uid, ids[0], context=context) + if wizard.model not in ('crm.lead'): return res - this = self.browse(cr, uid, ids[0], context=context) - lead = self.pool.get(model) - lead_id = context and context.get('active_id', False) or False - lead_ids = lead_id and [lead_id] or [] - mode = context.get('mail.compose.message.mode') - if mode == 'mass_mail': + lead = self.pool.get(wizard.model) + lead_ids = wizard.res_id and [wizard.res_id] or [] + + if wizard.composition_mode == 'mass_mail': lead_ids = context and context.get('active_ids', []) or [] - value = self.default_get(cr, uid, ['body_text', 'email_to', 'email_cc', 'subject', 'history'], context=context) + value = self.default_get(cr, uid, ['body', 'email_to', 'email_cc', 'subject', 'history_mode'], context=context) self.write(cr, uid, ids, value, context=context) - context['mail.compose.message.mode'] = mode self.send_mail(cr, uid, ids, context=context) - for case in lead.browse(cr, uid, lead_ids, context=context): - if (this.send_to == 'partner' and this.partner_id): - lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context) - - if this.send_to == 'user': - lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context) - - email_cc = to_email(case.email_cc) - email_cc = email_cc and email_cc[0] or '' - new_cc = [] - if email_cc: - new_cc.append(email_cc) - for to in this.email_to.split(','): - email_to = to_email(to) - email_to = email_to and email_to[0] or '' - if email_to not in new_cc: - new_cc.append(to) - update_vals = {'email_cc' : ', '.join(new_cc) } - lead.write(cr, uid, [case.id], update_vals, context=context) + # for case in lead.browse(cr, uid, lead_ids, context=context): + # TODO: WHAT TO DO WITH THAT ? + # if (this.send_to == 'partner' and this.partner_id): + # lead.assign_partner(cr, uid, [case.id], this.partner_id.id, context=context) + + # if this.send_to == 'user': + # lead.allocate_salesman(cr, uid, [case.id], [this.user_id.id], context=context) return res - def _get_info_body_text(self, cr, uid, lead, context=None): + def _get_info_body(self, cr, uid, lead, context=None): field_names = [] proxy = self.pool.get(lead._name) if lead.type == 'opportunity': field_names += ['partner_id'] field_names += [ - 'partner_name' , 'title', 'function', 'street', 'street2', + 'partner_name' , 'title', 'function', 'street', 'street2', 'zip', 'city', 'country_id', 'state_id', 'email_from', 'phone', 'fax', 'mobile', 'categ_id', 'description', ] - return proxy._mail_body_text(cr, uid, lead, field_names, context=context) + return proxy._mail_body(cr, uid, lead, field_names, context=context) - def _get_message_body_text(self, cr, uid, lead, mode='whole', context=None): - """This function gets whole communication history and returns as top posting style + def _get_message_body(self, cr, uid, lead, history_mode='whole', context=None): + """ This function gets whole communication history and returns as top + posting style + #1: form a body, based on the lead + #2: append to the body the communication history, based on the + history_mode parameter + + - info: Forward the case information + - latest: Send the latest history + - whole: Send the whole history + + :param lead: browse_record on crm.lead + :param history_mode: 'whole' or 'latest' """ mail_message = self.pool.get('mail.message') - message_ids = [] - body = self._get_info_body_text(cr, uid, lead, context=context) - if mode in ('whole', 'latest'): - message_ids = lead.message_ids - message_ids = map(lambda x: x.id, filter(lambda x: x.email_from, message_ids)) - if mode == 'latest' and len(message_ids): - message_ids = [message_ids[0]] - for message in mail_message.browse(cr, uid, message_ids, context=context): - header = '-------- Original Message --------' - sender = 'From: %s' %(message.email_from or '') - to = 'To: %s' % (message.email_to or '') - sentdate = 'Date: %s' % (message.date or '') - desc = '\n%s'%(message.body_text) - original = [header, sender, to, sentdate, desc, '\n'] - original = '\n'.join(original) - body += original + body = self._get_info_body(cr, uid, lead, context=context) + if history_mode not in ('whole', 'latest'): + return body or '' + for message in lead.message_ids: + header = '-------- Original Message --------' + sentdate = 'Date: %s' % (message.date or '') + desc = '\n%s'%(message.body) + original = [header, sentdate, desc, '\n'] + original = '\n'.join(original) + body += original + if history_mode == 'latest': + break return body or '' - def get_value(self, cr, uid, model, res_id, context=None): - if context is None: - context = {} - res = super(crm_lead_forward_to_partner, self).get_value(cr, uid, model, res_id, context=context) - if model not in ("crm.lead"): - return res - proxy = self.pool.get(model) - partner = self.pool.get('res.partner') - lead = proxy.browse(cr, uid, res_id, context=context) - mode = context.get('mail.compose.message.mode') - if mode == "forward": - body_type = context.get('mail.compose.message.body') - email_cc = res.get('email_cc', "") - email = res.get('email_to', "") - subject = '%s: %s - %s' % (_('Fwd'), 'Lead forward', lead.name) - body = self._get_message_body_text(cr, uid, lead, body_type, context=context) - partner_assigned_id = lead.partner_assigned_id and lead.partner_assigned_id.id or False - user_id = False - if not partner_assigned_id: - partner_assigned_id = proxy.search_geo_partner(cr, uid, [lead.id], context=None).get(lead.id, False) - if partner_assigned_id: - assigned_partner = partner.browse(cr, uid, partner_assigned_id, context=context) - user_id = assigned_partner.user_id and assigned_partner.user_id.id or False - email_cc = assigned_partner.user_id and assigned_partner.user_id.email or '' - email = assigned_partner.email - - res.update({ - 'subject' : subject, - 'body_text' : body, - 'email_cc' : email_cc, - 'email_to' : email, - 'partner_assigned_id': partner_assigned_id, - 'user_id': user_id, - }) - return res - - def default_get(self, cr, uid, fields, context=None): - if context is None: - context = {} - context['mail.compose.message.mode'] = 'forward' - context['mail.compose.message.body'] = 'info' - return super(crm_lead_forward_to_partner, self).default_get(cr, uid, fields, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml index cecf73954e5..0d8f27eec74 100644 --- a/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml +++ b/addons/crm_partner_assign/wizard/crm_forward_to_partner_view.xml @@ -7,42 +7,31 @@ <field name="model">crm.lead.forward.to.partner</field> <field name="arch" type="xml"> <form string="Send Mail" version="7.0"> + <field name="composition_mode" colspan="2" nolabel="1" invisible="1"/> + <field name="model" colspan="2" nolabel="1" invisible="1"/> + <field name="res_id" colspan="2" nolabel="1" invisible="1"/> <separator string="Forward to Partner" colspan="4"/> - <group col="4" colspan="6"> - <field name="history" colspan="2" on_change="on_change_history(history, context)"/> - <field name="send_to" colspan="2"/> - <group col="2" colspan="2" attrs="{ 'invisible' : [('send_to','!=','user')]}"> - <field name="user_id" - attrs="{ 'required' : [('send_to','=','user')]}" - on_change="on_change_email(user_id)"/> - </group> - <group col="4" colspan="4" attrs="{'invisible' : [('send_to','!=','partner')]}"> - <field name="partner_id" attrs="{'required' : [('send_to','=','partner')]}" on_change="on_change_partner(partner_id)" colspan="2"/> - </group> + <group col="4"> + <field name="history_mode" colspan="4" + on_change="on_change_history_mode(history_mode, model, res_id)"/> + <field name="subject" colspan="4"/> + <field name="partner_ids" colspan="4" widget="many2many_tags" + on_change="onchange_partner_ids(partner_ids)"/> + <notebook colspan="4"> + <page string="Body"> + <field name="body"/> + </page> + <page string="Attachments"> + <field name="attachment_ids"/> + </page> + </notebook> </group> - <separator string="" colspan="4"/> - <group col="6" colspan="4"> - <field name="email_from" colspan="4" required="1"/> - <field name="email_to" colspan="4" required="1"/> - <field name="email_cc" colspan="4"/> - <field name="email_bcc" colspan="4"/> - <field name="reply_to" colspan="4"/> - <field name="subject" colspan="4" widget="char" size="512"/> - </group> - <separator string="" colspan="4"/> - <notebook colspan="4"> - <page string="Body"> - <field name="body_text" colspan="4" nolabel="1"/> - </page> - <page string="Attachments"> - <label string="Add here all attachments of the current document you want to include in the Email." colspan="4"/> - <field name="attachment_ids" colspan="4" nolabel="1"/> - </page> - </notebook> <footer> - <button name="action_forward" string="Send" type="object" class="oe_highlight"/> + <button name="action_forward" string="Send" type="object" + class="oe_highlight"/> or - <button string="Cancel" class="oe_link" special="cancel"/> + <button string="Cancel" special="cancel" + class="oe_link"/> </footer> </form> </field> @@ -57,18 +46,14 @@ <field name="target">new</field> </record> - - <act_window id="action_crm_send_mass_forward" multi="True" key2="client_action_multi" name="Mass forward to partner" res_model="crm.lead.forward.to.partner" src_model="crm.lead" view_mode="form" target="new" view_type="form" - context="{'mail.compose.message.mode' : 'mass_mail'}" + context="{'default_composition_mode' : 'mass_mail'}" view_id="crm_lead_forward_to_partner_form" - /> - - + /> </data> </openerp> diff --git a/addons/crm_profiling/i18n/nb.po b/addons/crm_profiling/i18n/nb.po new file mode 100644 index 00000000000..653dc05522e --- /dev/null +++ b/addons/crm_profiling/i18n/nb.po @@ -0,0 +1,212 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-06 14:26+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: crm_profiling +#: view:crm_profiling.questionnaire:0 +msgid "Questions List" +msgstr "Spørsmål liste." + +#. module: crm_profiling +#: model:ir.actions.act_window,help:crm_profiling.open_questionnaires +msgid "" +"You can create specific topic-related questionnaires to guide your team(s) " +"in the sales cycle by helping them to ask the right questions. The " +"segmentation tool allows you to automatically assign a partner to a category " +"according to his answers to the different questionnaires." +msgstr "" +"Du kan opprette spesifikke tema-relaterte spørreskjemaer for å styre lag (e) " +"i salgsperioden ved å hjelpe dem til å stille de riktige spørsmålene. " +"Segmentering verktøyet lar deg automatisk tildele en partner til en kategori " +"i henhold til hans svar til de ulike spørreskjemaer." + +#. module: crm_profiling +#: field:crm_profiling.answer,question_id:0 +#: field:crm_profiling.question,name:0 +#: model:ir.model,name:crm_profiling.model_crm_profiling_question +#: field:open.questionnaire.line,question_id:0 +msgid "Question" +msgstr "Spørsmål" + +#. module: crm_profiling +#: model:ir.actions.act_window,name:crm_profiling.action_open_questionnaire +#: view:open.questionnaire:0 +msgid "Open Questionnaire" +msgstr "åpen Spørreskjema" + +#. module: crm_profiling +#: field:crm.segmentation,child_ids:0 +msgid "Child Profiles" +msgstr "Barn profiler" + +#. module: crm_profiling +#: view:crm.segmentation:0 +msgid "Partner Segmentations" +msgstr "Partner segmenteringer" + +#. module: crm_profiling +#: field:crm_profiling.answer,name:0 +#: model:ir.model,name:crm_profiling.model_crm_profiling_answer +#: field:open.questionnaire.line,answer_id:0 +msgid "Answer" +msgstr "Svar" + +#. module: crm_profiling +#: model:ir.model,name:crm_profiling.model_open_questionnaire_line +msgid "open.questionnaire.line" +msgstr "Åpen.Spørreskjema.Linje" + +#. module: crm_profiling +#: model:ir.model,name:crm_profiling.model_crm_segmentation +msgid "Partner Segmentation" +msgstr "Partnersegmentering" + +#. module: crm_profiling +#: view:res.partner:0 +msgid "Profiling" +msgstr "Profilering" + +#. module: crm_profiling +#: view:crm_profiling.questionnaire:0 +#: field:crm_profiling.questionnaire,description:0 +msgid "Description" +msgstr "Beskrivelse:" + +#. module: crm_profiling +#: field:crm.segmentation,answer_no:0 +msgid "Excluded Answers" +msgstr "Ekskluderte svar" + +#. module: crm_profiling +#: view:crm_profiling.answer:0 +#: view:crm_profiling.question:0 +#: field:res.partner,answers_ids:0 +msgid "Answers" +msgstr "Svar" + +#. module: crm_profiling +#: model:ir.model,name:crm_profiling.model_open_questionnaire +msgid "open.questionnaire" +msgstr "Åpen.Spørresjema" + +#. module: crm_profiling +#: field:open.questionnaire,questionnaire_id:0 +msgid "Questionnaire name" +msgstr "Spørresjema navn." + +#. module: crm_profiling +#: view:res.partner:0 +msgid "Use a questionnaire" +msgstr "Bruk et spørresjema." + +#. module: crm_profiling +#: view:open.questionnaire:0 +msgid "_Cancel" +msgstr "_Avbryt" + +#. module: crm_profiling +#: field:open.questionnaire,question_ans_ids:0 +msgid "Question / Answers" +msgstr "Spørsmål / Svar" + +#. module: crm_profiling +#: view:crm_profiling.questionnaire:0 +#: model:ir.actions.act_window,name:crm_profiling.open_questionnaires +#: model:ir.ui.menu,name:crm_profiling.menu_segm_questionnaire +#: view:open.questionnaire:0 +msgid "Questionnaires" +msgstr "Spørreskjemaer" + +#. module: crm_profiling +#: help:crm.segmentation,profiling_active:0 +msgid "" +"Check this box if you want to use this tab as " +"part of the segmentation rule. If not checked, " +"the criteria beneath will be ignored" +msgstr "" +"Kryss av her hvis du ønsker å bruke denne kategorien som en del av " +"segmentering regelen. Hvis det ikke er merket, vil kriteriene under bli " +"ignorert." + +#. module: crm_profiling +#: constraint:crm.segmentation:0 +msgid "Error ! You can not create recursive profiles." +msgstr "Feil ! Du kan ikke lage rekursive profiler." + +#. module: crm_profiling +#: field:crm.segmentation,profiling_active:0 +msgid "Use The Profiling Rules" +msgstr "Bruk profileringen Regler" + +#. module: crm_profiling +#: constraint:res.partner:0 +msgid "Error ! You cannot create recursive associated members." +msgstr "Feil! Du kan ikke opprette rekursive tilknyttede medlemmer." + +#. module: crm_profiling +#: view:crm_profiling.question:0 +#: field:crm_profiling.question,answers_ids:0 +msgid "Avalaible Answers" +msgstr "" + +#. module: crm_profiling +#: field:crm.segmentation,answer_yes:0 +msgid "Included Answers" +msgstr "Inkluderte svar." + +#. module: crm_profiling +#: view:crm_profiling.question:0 +#: field:crm_profiling.questionnaire,questions_ids:0 +#: model:ir.actions.act_window,name:crm_profiling.open_questions +#: model:ir.ui.menu,name:crm_profiling.menu_segm_answer +msgid "Questions" +msgstr "Spørsmål" + +#. module: crm_profiling +#: field:crm.segmentation,parent_id:0 +msgid "Parent Profile" +msgstr "Overordnede profil" + +#. module: crm_profiling +#: view:open.questionnaire:0 +msgid "Cancel" +msgstr "Kanseller" + +#. module: crm_profiling +#: model:ir.model,name:crm_profiling.model_res_partner +msgid "Partner" +msgstr "Partner" + +#. module: crm_profiling +#: code:addons/crm_profiling/wizard/open_questionnaire.py:77 +#: field:crm_profiling.questionnaire,name:0 +#: model:ir.model,name:crm_profiling.model_crm_profiling_questionnaire +#: view:open.questionnaire:0 +#: view:open.questionnaire.line:0 +#: field:open.questionnaire.line,wizard_id:0 +#, python-format +msgid "Questionnaire" +msgstr "Spørreskjemaet" + +#. module: crm_profiling +#: view:open.questionnaire:0 +msgid "Save Data" +msgstr "Lagre data" + +#~ msgid "Avalaible answers" +#~ msgstr "Tilgjengelige svar." diff --git a/addons/crm_todo/i18n/nb.po b/addons/crm_todo/i18n/nb.po new file mode 100644 index 00000000000..44a49165e40 --- /dev/null +++ b/addons/crm_todo/i18n/nb.po @@ -0,0 +1,95 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-04 14:07+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-05 04:46+0000\n" +"X-Generator: Launchpad (build 15901)\n" + +#. module: crm_todo +#: model:ir.model,name:crm_todo.model_project_task +msgid "Task" +msgstr "Oppgave" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Timebox" +msgstr "Timeboks" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "For cancelling the task" +msgstr "For å avbryte oppgaven" + +#. module: crm_todo +#: constraint:project.task:0 +msgid "Error ! Task end-date must be greater then task start-date" +msgstr "Feil! Oppgave sluttdato må være større enn oppgave startdato" + +#. module: crm_todo +#: model:ir.model,name:crm_todo.model_crm_lead +msgid "crm.lead" +msgstr "crm.lead" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Next" +msgstr "Neste" + +#. module: crm_todo +#: model:ir.actions.act_window,name:crm_todo.crm_todo_action +#: model:ir.ui.menu,name:crm_todo.menu_crm_todo +msgid "My Tasks" +msgstr "Mine oppgaver" + +#. module: crm_todo +#: view:crm.lead:0 +#: field:crm.lead,task_ids:0 +msgid "Tasks" +msgstr "Oppgaver" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Done" +msgstr "Utført" + +#. module: crm_todo +#: constraint:project.task:0 +msgid "Error ! You cannot create recursive tasks." +msgstr "Feil! Du kan ikke lage en rekursive oppgaver." + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Cancel" +msgstr "Kanseller" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Extra Info" +msgstr "Ekstra informasjon" + +#. module: crm_todo +#: field:project.task,lead_id:0 +msgid "Lead / Opportunity" +msgstr "Lede / mulighet" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "For changing to done state" +msgstr "For å bytte til ferdig tilstand" + +#. module: crm_todo +#: view:crm.lead:0 +msgid "Previous" +msgstr "Tidligere" diff --git a/addons/decimal_precision/decimal_precision.py b/addons/decimal_precision/decimal_precision.py index d9f6aa7b8db..93d69eb3093 100644 --- a/addons/decimal_precision/decimal_precision.py +++ b/addons/decimal_precision/decimal_precision.py @@ -22,6 +22,7 @@ from osv import osv, fields import tools import pooler +from openerp import SUPERUSER_ID class decimal_precision(osv.osv): _name = 'decimal.precision' @@ -56,7 +57,7 @@ decimal_precision() def get_precision(application): def change_digit(cr): - res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, application) + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, SUPERUSER_ID, application) return (16, res) return change_digit diff --git a/addons/document/board_document_view.xml b/addons/document/board_document_view.xml index a802d788e05..986b054383f 100644 --- a/addons/document/board_document_view.xml +++ b/addons/document/board_document_view.xml @@ -8,9 +8,6 @@ <form string="Document board" version="7.0"> <board style="2-1" > <column> - <action string="New Files" - name="%(document.action_view_all_document_tree1)d" - view_mode="tree,form"/> <action string="Files by Month" name="%(document.action_view_files_by_month_graph)d" view_mode="graph,tree"/> diff --git a/addons/document_ftp/i18n/nb.po b/addons/document_ftp/i18n/nb.po new file mode 100644 index 00000000000..3598b00f54c --- /dev/null +++ b/addons/document_ftp/i18n/nb.po @@ -0,0 +1,121 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-06 14:45+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: document_ftp +#: model:ir.model,name:document_ftp.model_document_ftp_configuration +msgid "Auto Directory Configuration" +msgstr "Automatisk Katalog Konfigurasjon." + +#. module: document_ftp +#: view:document.ftp.configuration:0 +msgid "" +"Indicate the network address on which your OpenERP server should be " +"reachable for end-users. This depends on your network topology and " +"configuration, and will only affect the links displayed to the users. The " +"format is HOST:PORT and the default host (localhost) is only suitable for " +"access from the server machine itself.." +msgstr "" +"Indikere nettverksadressen som din OpenERP server bør være tilgjengelige for " +"sluttbrukerne. Dette avhenger av nettverkstopologi og konfigurasjon, og vil " +"bare påvirke lenker som vises til brukerne. Formatet er HOST: PORT og " +"standard verten (lokalhost) er bare egnet for tilgang fra serveren maskinen " +"selv .." + +#. module: document_ftp +#: model:ir.actions.url,name:document_ftp.action_document_browse +msgid "Browse Files" +msgstr "Bla i filer" + +#. module: document_ftp +#: field:document.ftp.configuration,config_logo:0 +msgid "Image" +msgstr "Bilde" + +#. module: document_ftp +#: field:document.ftp.configuration,host:0 +msgid "Address" +msgstr "Adresse" + +#. module: document_ftp +#: field:document.ftp.browse,url:0 +msgid "FTP Server" +msgstr "FTP-tjener" + +#. module: document_ftp +#: model:ir.actions.act_window,name:document_ftp.action_config_auto_directory +msgid "FTP Server Configuration" +msgstr "FTP Serverkonfigurasjon" + +#. module: document_ftp +#: view:document.ftp.browse:0 +msgid "_Browse" +msgstr "_Bla gjennom" + +#. module: document_ftp +#: help:document.ftp.configuration,host:0 +msgid "" +"Server address or IP and port to which users should connect to for DMS access" +msgstr "" +"Server adresse eller IP og port til hvilke brukere som skal koble seg til " +"for DMS tilgang" + +#. module: document_ftp +#: model:ir.ui.menu,name:document_ftp.menu_document_browse +msgid "Shared Repository (FTP)" +msgstr "Wikimedia Commons (FTP)" + +#. module: document_ftp +#: view:document.ftp.browse:0 +msgid "_Cancel" +msgstr "_Avbryt" + +#. module: document_ftp +#: view:document.ftp.configuration:0 +msgid "Configure FTP Server" +msgstr "Konfigurer FTP serveren" + +#. module: document_ftp +#: view:document.ftp.configuration:0 +msgid "title" +msgstr "tittel" + +#. module: document_ftp +#: model:ir.model,name:document_ftp.model_document_ftp_browse +msgid "Document FTP Browse" +msgstr "Document FTP Bla i." + +#. module: document_ftp +#: view:document.ftp.configuration:0 +msgid "Knowledge Application Configuration" +msgstr "Kunnskap Programkonfigurasjon" + +#. module: document_ftp +#: model:ir.actions.act_window,name:document_ftp.action_ftp_browse +msgid "Document Browse" +msgstr "Dokument Bla i." + +#. module: document_ftp +#: view:document.ftp.browse:0 +msgid "Browse Document" +msgstr "Bla igjennom dokument" + +#. module: document_ftp +#: view:document.ftp.configuration:0 +msgid "res_config_contents" +msgstr "res_konfig_innhold" diff --git a/addons/document_page/document_page.py b/addons/document_page/document_page.py index 7304e2e9075..092d39f22e9 100644 --- a/addons/document_page/document_page.py +++ b/addons/document_page/document_page.py @@ -29,11 +29,13 @@ class document_page(osv.osv): _description = "Document Page" _order = 'name' - def _get_page_index(self, cr, uid, page): + def _get_page_index(self, cr, uid, page, link=True): index = [] for subpage in page.child_ids: index += ["<li>"+ self._get_page_index(cr, uid, subpage) +"</li>"] - r = '<a href="#id=%s">%s</a>'%(page.id,page.name) + r = '' + if link: + r = '<a href="#id=%s">%s</a>'%(page.id,page.name) if index: r += "<ul>" + "".join(index) + "</ul>" return r @@ -42,7 +44,7 @@ class document_page(osv.osv): res = {} for page in self.browse(cr, uid, ids, context=context): if page.type == "category": - content = self._get_page_index(cr, uid, page) + content = self._get_page_index(cr, uid, page, link=False) else: content = page.content res[page.id] = content @@ -120,12 +122,12 @@ class document_page_history(osv.osv): text2 = history_pool.read(cr, uid, [v2], ['content'])[0]['content'] line1 = line2 = '' if text1: - line1 = tools.ustr(text1.splitlines(1)) + line1 = text1.splitlines(1) if text2: - line2=tools.ustr(text2.splitlines(1)) + line2 = text2.splitlines(1) if (not line1 and not line2) or (line1 == line2): raise osv.except_osv(_('Warning!'), _('There are no changes in revisions.')) diff = difflib.HtmlDiff() - return diff.make_file(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=False) + return diff.make_table(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=True) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/document_page/document_page_view.xml b/addons/document_page/document_page_view.xml index fc3bd86fdaf..23c4b8f29c8 100644 --- a/addons/document_page/document_page_view.xml +++ b/addons/document_page/document_page_view.xml @@ -78,29 +78,29 @@ </field> </record> <!-- page action --> - <record id="action_category" model="ir.actions.act_window"> - <field name="name">Category</field> + <record id="action_page" model="ir.actions.act_window"> + <field name="name">Pages</field> <field name="res_model">document.page</field> - <field name="domain">[('type','=','category')]</field> - <field name="context">{'default_type': 'category'}</field> + <field name="domain">[('type','=','content')]</field> + <field name="context">{'default_type': 'content'}</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="view_id" ref="view_wiki_tree"/> <field name="search_view_id" ref="view_wiki_filter"/> + <field name="help">Create web pages</field> </record> - <menuitem id="menu_category" parent="menu_wiki" name="Categories" action="action_category" sequence="10"/> - <record id="action_page" model="ir.actions.act_window"> - <field name="name">Pages</field> + <menuitem id="menu_page" parent="menu_wiki" name="Pages" action="action_page" sequence="10"/> + <record id="action_category" model="ir.actions.act_window"> + <field name="name">Category</field> <field name="res_model">document.page</field> - <field name="domain">[('type','=','content')]</field> - <field name="context">{'default_type': 'content'}</field> + <field name="domain">[('type','=','category')]</field> + <field name="context">{'default_type': 'category'}</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> <field name="view_id" ref="view_wiki_tree"/> <field name="search_view_id" ref="view_wiki_filter"/> - <field name="help">Create web pages</field> </record> - <menuitem id="menu_page" parent="menu_wiki" name="Pages" action="action_page" sequence="20"/> + <menuitem id="menu_category" parent="menu_wiki" name="Categories" action="action_category" sequence="20"/> <!-- History Tree view --> <record model="ir.ui.view" id="view_wiki_history_tree"> diff --git a/addons/document_page/static/src/css/document_page.css b/addons/document_page/static/src/css/document_page.css index 723ff8d9b11..ddbbcffd45b 100644 --- a/addons/document_page/static/src/css/document_page.css +++ b/addons/document_page/static/src/css/document_page.css @@ -1,15 +1,12 @@ -.oe_form_readonly .oe_notebook { - display: none; -} - -.oe_document_page ul, .oe_document_page li { - padding: 2px 8px; - margin: 2px 8px; - list-style-type: circle; -} - .oe_form_editable .oe_document_page { display: none; } +table.diff {font-family:Courier; border:medium;} +.diff_header {background-color:#e0e0e0} +td.diff_header {text-align:right} +.diff_next {background-color:#c0c0c0} +.diff_add {background-color:#aaffaa} +.diff_chg {background-color:#ffff77} +.diff_sub {background-color:#ffaaaa} diff --git a/addons/document_page/wizard/document_page_show_diff_view.xml b/addons/document_page/wizard/document_page_show_diff_view.xml index f268db7ad2e..b1dfb536853 100644 --- a/addons/document_page/wizard/document_page_show_diff_view.xml +++ b/addons/document_page/wizard/document_page_show_diff_view.xml @@ -8,7 +8,7 @@ <field name="model">wizard.document.page.history.show_diff</field> <field name="arch" type="xml"> <form string="Difference" version="7.0"> - <field name="diff"/> + <field name="diff" widget="html" options='{"safe": true}'/> <footer> <button string="Cancel" class="oe_link" special="cancel" /> </footer> diff --git a/addons/document_webdav/i18n/nb.po b/addons/document_webdav/i18n/nb.po new file mode 100644 index 00000000000..d51ade795b3 --- /dev/null +++ b/addons/document_webdav/i18n/nb.po @@ -0,0 +1,197 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 17:44+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: document_webdav +#: field:document.webdav.dir.property,create_date:0 +#: field:document.webdav.file.property,create_date:0 +msgid "Date Created" +msgstr "Dato opprettet" + +#. module: document_webdav +#: model:ir.ui.menu,name:document_webdav.menu_document_props +msgid "Documents" +msgstr "Dokumenter" + +#. module: document_webdav +#: constraint:document.directory:0 +msgid "Error! You can not create recursive Directories." +msgstr "Feil! Du kan opprette rekursive kataloger." + +#. module: document_webdav +#: view:document.webdav.dir.property:0 +#: view:document.webdav.file.property:0 +msgid "Search Document properties" +msgstr "Søk Dokumentegenskaper." + +#. module: document_webdav +#: view:document.webdav.dir.property:0 +#: field:document.webdav.dir.property,namespace:0 +#: view:document.webdav.file.property:0 +#: field:document.webdav.file.property,namespace:0 +msgid "Namespace" +msgstr "Navnerom" + +#. module: document_webdav +#: field:document.directory,dav_prop_ids:0 +msgid "DAV properties" +msgstr "DAV egenskaper" + +#. module: document_webdav +#: model:ir.model,name:document_webdav.model_document_webdav_file_property +msgid "document.webdav.file.property" +msgstr "Dokumentet.WebDAV.fil.egenskap" + +#. module: document_webdav +#: view:document.webdav.dir.property:0 +#: view:document.webdav.file.property:0 +msgid "Group By..." +msgstr "Grupper etter ..." + +#. module: document_webdav +#: view:document.directory:0 +msgid "These properties will be added to WebDAV requests" +msgstr "Disse egenskapene vil bli lagt til WebDAV-forespørsler" + +#. module: document_webdav +#: model:ir.actions.act_window,name:document_webdav.action_file_props_form +msgid "DAV Properties for Documents" +msgstr "DAV Eiendommer for Dokumenter." + +#. module: document_webdav +#: code:addons/document_webdav/webdav.py:37 +#, python-format +msgid "PyWebDAV Import Error!" +msgstr "PyWebDAV importere Feil!" + +#. module: document_webdav +#: view:document.webdav.file.property:0 +#: field:document.webdav.file.property,file_id:0 +msgid "Document" +msgstr "Dokument" + +#. module: document_webdav +#: model:ir.ui.menu,name:document_webdav.menu_folder_props +msgid "Folders" +msgstr "Mapper" + +#. module: document_webdav +#: sql_constraint:document.directory:0 +msgid "Directory cannot be parent of itself!" +msgstr "Katalogen kan ikke være overordnede av seg selv!" + +#. module: document_webdav +#: view:document.directory:0 +msgid "Dynamic context" +msgstr "Dynamisk sammenheng" + +#. module: document_webdav +#: view:document.directory:0 +msgid "WebDAV properties" +msgstr "WebDAV egenskaper." + +#. module: document_webdav +#: sql_constraint:document.directory:0 +msgid "The directory name must be unique !" +msgstr "Katalogen Navnet må være unikt!" + +#. module: document_webdav +#: code:addons/document_webdav/webdav.py:37 +#, python-format +msgid "" +"Please install PyWebDAV from " +"http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-" +"0.9.4.tar.gz&can=2&q=/" +msgstr "" +"Vennligst installer PyWebDAV fra " +"http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-" +"0.9.4.tar.gz&can=2&q=/" + +#. module: document_webdav +#: model:ir.actions.act_window,name:document_webdav.action_dir_props_form +msgid "DAV Properties for Folders" +msgstr "DAV egenskaper for mapper." + +#. module: document_webdav +#: view:document.directory:0 +#: view:document.webdav.dir.property:0 +#: view:document.webdav.file.property:0 +msgid "Properties" +msgstr "Egenskaper" + +#. module: document_webdav +#: field:document.webdav.dir.property,name:0 +#: field:document.webdav.file.property,name:0 +msgid "Name" +msgstr "Navn" + +#. module: document_webdav +#: model:ir.model,name:document_webdav.model_document_webdav_dir_property +msgid "document.webdav.dir.property" +msgstr "Dokumentet.WebDAV.dir.eiendom" + +#. module: document_webdav +#: field:document.webdav.dir.property,value:0 +#: field:document.webdav.file.property,value:0 +msgid "Value" +msgstr "Verdi" + +#. module: document_webdav +#: field:document.webdav.dir.property,dir_id:0 +#: model:ir.model,name:document_webdav.model_document_directory +msgid "Directory" +msgstr "Katalog" + +#. module: document_webdav +#: field:document.webdav.dir.property,write_uid:0 +#: field:document.webdav.file.property,write_uid:0 +msgid "Last Modification User" +msgstr "Siste endring Bruker" + +#. module: document_webdav +#: view:document.webdav.dir.property:0 +msgid "Dir" +msgstr "Retn" + +#. module: document_webdav +#: field:document.webdav.dir.property,write_date:0 +#: field:document.webdav.file.property,write_date:0 +msgid "Date Modified" +msgstr "Dato endret" + +#. module: document_webdav +#: field:document.webdav.dir.property,create_uid:0 +#: field:document.webdav.file.property,create_uid:0 +msgid "Creator" +msgstr "Opprettet av" + +#. module: document_webdav +#: model:ir.ui.menu,name:document_webdav.menu_properties +msgid "DAV Properties" +msgstr "DAV Egenskaper." + +#. module: document_webdav +#: sql_constraint:document.directory:0 +msgid "Directory must have a parent or a storage" +msgstr "Katalog må ha en overordnede eller en lagringsplass." + +#. module: document_webdav +#: field:document.webdav.dir.property,do_subst:0 +#: field:document.webdav.file.property,do_subst:0 +msgid "Substitute" +msgstr "Erstatte" diff --git a/addons/document_webdav/nodes.py b/addons/document_webdav/nodes.py index d6ceca458cd..a8fedee4fe3 100644 --- a/addons/document_webdav/nodes.py +++ b/addons/document_webdav/nodes.py @@ -25,6 +25,8 @@ from tools.safe_eval import safe_eval as eval import time import urllib import uuid +from openerp import SUPERUSER_ID + try: from tools.dict_tools import dict_filter except ImportError: @@ -233,7 +235,7 @@ class node_acl_mixin(object): if props_to_delete: # explicitly delete, as admin, any of the ids we have identified. - propobj.unlink(cr, 1, props_to_delete) + propobj.unlink(cr, SUPERUSER_ID, props_to_delete) if lock_data.get('unlock_mode', False): return lock_found and True diff --git a/addons/document_webdav/test_davclient.py b/addons/document_webdav/test_davclient.py index f1f2e58c835..0f66363c553 100755 --- a/addons/document_webdav/test_davclient.py +++ b/addons/document_webdav/test_davclient.py @@ -42,6 +42,7 @@ from tools import config from xmlrpclib import Transport, ProtocolError import StringIO import base64 +from openerp import SUPERUSER_ID _logger = logging.getLogger(__name__) @@ -361,7 +362,7 @@ class DAVClient(object): to break if "base_crypt" is used. """ ruob = obj.pool.get('res.users') - res = ruob.read(cr, 1, [uid,], ['login', 'password']) + res = ruob.read(cr, SUPERUSER_ID, [uid,], ['login', 'password']) assert res, "uid %s not found" % uid self.user = res[0]['login'] self.passwd = res[0]['password'] diff --git a/addons/edi/i18n/nb.po b/addons/edi/i18n/nb.po new file mode 100644 index 00000000000..bc8add1e6e9 --- /dev/null +++ b/addons/edi/i18n/nb.po @@ -0,0 +1,411 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 01:37+0100\n" +"PO-Revision-Date: 2012-09-08 19:13+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:53+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: edi +#: sql_constraint:res.currency:0 +msgid "The currency code must be unique per company!" +msgstr "Valutakode må være unik pr. firma!" + +#. module: edi +#: model:ir.model,name:edi.model_res_partner_address +msgid "Partner Addresses" +msgstr "Partner adresser" + +#. module: edi +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "Firmanavn må være unikt !" + +#. module: edi +#: constraint:res.partner:0 +msgid "Error ! You cannot create recursive associated members." +msgstr "Feil! Du kan ikke opprette rekursive tilknyttede medlemmer." + +#. module: edi +#: field:edi.document,name:0 +msgid "EDI token" +msgstr "EDI tegn." + +#. module: edi +#: help:edi.document,name:0 +msgid "Unique identifier for retrieving an EDI document." +msgstr "Unik identifikator for å hente en EDI dokument." + +#. module: edi +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "Feil ! Du kan ikke lage rekursive firmaer." + +#. module: edi +#: model:ir.model,name:edi.model_res_company +msgid "Companies" +msgstr "Firmaer" + +#. module: edi +#: sql_constraint:edi.document:0 +msgid "EDI Tokens must be unique!" +msgstr "EDI tegnet må være unikt!" + +#. module: edi +#: model:ir.model,name:edi.model_res_currency +msgid "Currency" +msgstr "Valuta" + +#. module: edi +#: code:addons/edi/models/edi.py:153 +#, python-format +msgid "" +"The document you are trying to import requires the OpenERP `%s` application. " +"You can install it by connecting as the administrator and opening the " +"configuration assistant." +msgstr "" +"Dokumentet du prøver å importere krever OpenERP `% s` søknad. Du kan " +"installere det ved å koble som administrator og åpne konfigurasjonen " +"assistent." + +#. module: edi +#: help:edi.document,document:0 +msgid "EDI document content" +msgstr "EDI dokumentinnhold" + +#. module: edi +#: model:ir.model,name:edi.model_edi_document +msgid "EDI Document" +msgstr "EDI dokument." + +#. module: edi +#: code:addons/edi/models/edi.py:48 +#, python-format +msgid "'%s' is an invalid external ID" +msgstr "'% s' er en ugyldig ekstern ID." + +#. module: edi +#: model:ir.model,name:edi.model_res_partner +msgid "Partner" +msgstr "Partner" + +#. module: edi +#: code:addons/edi/models/edi.py:152 +#, python-format +msgid "Missing Application" +msgstr "Manglende Søknad." + +#. module: edi +#: field:edi.document,document:0 +msgid "Document" +msgstr "Dokument" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:23 +msgid "View/Print" +msgstr "Vis / Skriv ut" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:28 +msgid "Import this document" +msgstr "Importer dette dokumentet." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:33 +msgid "Import it into an existing OpenERP instance" +msgstr "Importere den inn i en eksisterende OpenERP eksempel." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:36 +msgid "OpenERP instance address:" +msgstr "OpenERP eksempel adresse:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:39 +msgid "Import" +msgstr "Import" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:44 +msgid "Import it into a new OpenERP Online instance" +msgstr "Importere den til en ny OpenERP Online eksempel." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:47 +msgid "Create my new OpenERP instance" +msgstr "Opprett min nye OpenERP eksempel." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:52 +msgid "Import into another application" +msgstr "Importere til et annet program." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:54 +msgid "" +"OpenERP's Electronic Data Interchange documents are based on a generic and " +"language\n" +" independent" +msgstr "" +"OpenERP's elektronisk Data utveksler dokumentene er basert på en generisk og " +"språk.\n" +" " +" uavhengig." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:56 +msgid "JSON" +msgstr "JSON" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:56 +msgid "" +"serialization of the document's attribute.\n" +" It is usually very quick and straightforward to " +"create a small plug-in for your preferred\n" +" application that will be capable of importing " +"any OpenERP EDI document.\n" +" You can find out more details about how to do " +"this and what the content of OpenERP EDI documents\n" +" is like in the" +msgstr "" +"Serialisering av dokumentets attributtet.\n" +" Det er vanligvis svært rask og grei å lage en " +"liten plug-in for din foretrukne.\n" +" program som vil være i stand til å importere " +"noen OpenERP EDI dokument.\n" +" Du kan finne ut mer informasjon om hvordan du " +"gjør dette og hva innholdet i OpenERP EDI dokumenter\n" +" er som i." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:60 +msgid "OpenERP documentation" +msgstr "OpenERP dokumentasjon." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:61 +msgid "To get started immediately," +msgstr "Å komme i gang umiddelbart," + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:62 +msgid "see is all it takes to use this EDI document in Python" +msgstr "Se er alt som trengs for å bruke denne EDI dokumentet i Python." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:70 +msgid "You can download the raw EDI document here:" +msgstr "Du kan laste ned den rå EDI dokumentet her:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:73 +msgid "Download" +msgstr "Nedlastning" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:87 +msgid "Powered by" +msgstr "Drevet av." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:87 +msgid "OpenERP" +msgstr "OpenERP" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:34 +msgid "Invoice" +msgstr "Faktura" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:37 +msgid "Description" +msgstr "Beskrivelse:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:38 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:41 +msgid "Date" +msgstr "Dato" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:39 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:40 +msgid "Your Reference" +msgstr "Din referanse." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:50 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:57 +msgid "Product Description" +msgstr "Produktbeskrivelse" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:51 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:58 +msgid "Quantity" +msgstr "Antall" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:52 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:59 +msgid "Unit Price" +msgstr "Enhetspris" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:53 +msgid "Discount" +msgstr "Rabatt" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:54 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:61 +msgid "Price" +msgstr "Pris" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:72 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:81 +msgid "Net Total:" +msgstr "Netto total:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:83 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:92 +msgid "Taxes:" +msgstr "Avgifter:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:94 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:103 +msgid "Total:" +msgstr "Totalt:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:106 +msgid "Tax" +msgstr "Skatt" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:107 +msgid "Base Amount" +msgstr "Grunnbeløp" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:108 +msgid "Amount" +msgstr "Beløp" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:121 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:113 +msgid "Notes:" +msgstr "Notater:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:129 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:121 +msgid "Pay Online" +msgstr "Betal online." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:133 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:125 +msgid "Paypal" +msgstr "Paypal" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:135 +msgid "" +"You may directly pay this invoice online via Paypal's secure payment gateway:" +msgstr "" +"Du kan betale direkte denne fakturaen online via Paypal er sikker betaling " +"gateway:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:145 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:137 +msgid "Bank Wire Transfer" +msgstr "Bank Tråd overføring." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:147 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:139 +msgid "Please transfer" +msgstr "Vennligst overfør." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:148 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:140 +msgid "to" +msgstr "til" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:149 +msgid "" +"(postal address on the invoice header)\n" +" using one of the following bank accounts. Be sure to " +"mention the invoice\n" +" reference" +msgstr "" +"(postadresse på fakturaen header)\n" +"Ved hjelp av en av følgende bankkontoer. Sørg for å nevne faktura.\n" +"Referanse." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:151 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:143 +msgid "on the transfer:" +msgstr "På overføring:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:36 +msgid "Order" +msgstr "Ordre" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:42 +msgid "Salesman" +msgstr "Selger" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:43 +msgid "Payment terms" +msgstr "Betalingsbetingelser" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:60 +msgid "Discount(%)" +msgstr "Raball(%)" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:127 +msgid "" +"You may directly pay this order online via Paypal's secure payment gateway:" +msgstr "" +"Du kan betale direkte i denne rekkefølgen online via Paypal's sikker " +"betaling gateway." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:141 +msgid "" +"(postal address on the order header)\n" +" using one of the following bank accounts. Be sure to " +"mention the document\n" +" reference" +msgstr "" +"Ostal adresse på bestillingen topp)\n" +"Ved hjelp av en av følgende bankkontoer. Sørg for å nevne dokumentet.\n" +"Referanse." diff --git a/addons/edi/i18n/ru.po b/addons/edi/i18n/ru.po new file mode 100644 index 00000000000..92fba05f843 --- /dev/null +++ b/addons/edi/i18n/ru.po @@ -0,0 +1,390 @@ +# Russian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 01:37+0100\n" +"PO-Revision-Date: 2012-09-10 15:12+0000\n" +"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n" +"Language-Team: Russian <ru@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: edi +#: sql_constraint:res.currency:0 +msgid "The currency code must be unique per company!" +msgstr "Код валюты должен быть уникальным в компании!" + +#. module: edi +#: model:ir.model,name:edi.model_res_partner_address +msgid "Partner Addresses" +msgstr "Адреса контрагентов" + +#. module: edi +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "Название компании должно быть уникальным!" + +#. module: edi +#: constraint:res.partner:0 +msgid "Error ! You cannot create recursive associated members." +msgstr "Ошибка! Вы не можете создавать рекурсивные ссылки на участников." + +#. module: edi +#: field:edi.document,name:0 +msgid "EDI token" +msgstr "" + +#. module: edi +#: help:edi.document,name:0 +msgid "Unique identifier for retrieving an EDI document." +msgstr "" + +#. module: edi +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "Ошибка ! Нельзя создать рекурсивные организации." + +#. module: edi +#: model:ir.model,name:edi.model_res_company +msgid "Companies" +msgstr "Компании" + +#. module: edi +#: sql_constraint:edi.document:0 +msgid "EDI Tokens must be unique!" +msgstr "" + +#. module: edi +#: model:ir.model,name:edi.model_res_currency +msgid "Currency" +msgstr "Валюта" + +#. module: edi +#: code:addons/edi/models/edi.py:153 +#, python-format +msgid "" +"The document you are trying to import requires the OpenERP `%s` application. " +"You can install it by connecting as the administrator and opening the " +"configuration assistant." +msgstr "" + +#. module: edi +#: help:edi.document,document:0 +msgid "EDI document content" +msgstr "" + +#. module: edi +#: model:ir.model,name:edi.model_edi_document +msgid "EDI Document" +msgstr "EDI документ" + +#. module: edi +#: code:addons/edi/models/edi.py:48 +#, python-format +msgid "'%s' is an invalid external ID" +msgstr "" + +#. module: edi +#: model:ir.model,name:edi.model_res_partner +msgid "Partner" +msgstr "Контрагент" + +#. module: edi +#: code:addons/edi/models/edi.py:152 +#, python-format +msgid "Missing Application" +msgstr "Отсутствие приложения" + +#. module: edi +#: field:edi.document,document:0 +msgid "Document" +msgstr "Документ" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:23 +msgid "View/Print" +msgstr "Просмотр/Печать" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:28 +msgid "Import this document" +msgstr "Импорт этого документа" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:33 +msgid "Import it into an existing OpenERP instance" +msgstr "Импорт в существующую OpenERP" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:36 +msgid "OpenERP instance address:" +msgstr "Адрес этой OpenERP:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:39 +msgid "Import" +msgstr "Импорт" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:44 +msgid "Import it into a new OpenERP Online instance" +msgstr "Импорт в новый онлайн экземпляр OpenERP" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:47 +msgid "Create my new OpenERP instance" +msgstr "Создать мой новый экземпляр OpenERP" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:52 +msgid "Import into another application" +msgstr "Импорт в другое приложение" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:54 +msgid "" +"OpenERP's Electronic Data Interchange documents are based on a generic and " +"language\n" +" independent" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:56 +msgid "JSON" +msgstr "JSON" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:56 +msgid "" +"serialization of the document's attribute.\n" +" It is usually very quick and straightforward to " +"create a small plug-in for your preferred\n" +" application that will be capable of importing " +"any OpenERP EDI document.\n" +" You can find out more details about how to do " +"this and what the content of OpenERP EDI documents\n" +" is like in the" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:60 +msgid "OpenERP documentation" +msgstr "OpenERP документация" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:61 +msgid "To get started immediately," +msgstr "Чтобы приступить к работе немедленно," + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:62 +msgid "see is all it takes to use this EDI document in Python" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:70 +msgid "You can download the raw EDI document here:" +msgstr "Вы можете скачать исходный документ EDI здесь:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:73 +msgid "Download" +msgstr "Скачать" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:87 +msgid "Powered by" +msgstr "На базе" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi.xml:87 +msgid "OpenERP" +msgstr "OpenERP" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:34 +msgid "Invoice" +msgstr "Счет" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:37 +msgid "Description" +msgstr "Описание" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:38 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:41 +msgid "Date" +msgstr "Дата" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:39 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:40 +msgid "Your Reference" +msgstr "Ваша ссылка" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:50 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:57 +msgid "Product Description" +msgstr "Описание товара" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:51 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:58 +msgid "Quantity" +msgstr "Количество" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:52 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:59 +msgid "Unit Price" +msgstr "Цена за ед." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:53 +msgid "Discount" +msgstr "Скидка" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:54 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:61 +msgid "Price" +msgstr "Цена" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:72 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:81 +msgid "Net Total:" +msgstr "Чистый итог:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:83 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:92 +msgid "Taxes:" +msgstr "Налоги:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:94 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:103 +msgid "Total:" +msgstr "Итого:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:106 +msgid "Tax" +msgstr "Налог" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:107 +msgid "Base Amount" +msgstr "Базовая сумма" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:108 +msgid "Amount" +msgstr "Сумма" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:121 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:113 +msgid "Notes:" +msgstr "Примечания:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:129 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:121 +msgid "Pay Online" +msgstr "Оплата онлайн" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:133 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:125 +msgid "Paypal" +msgstr "Paypal" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:135 +msgid "" +"You may directly pay this invoice online via Paypal's secure payment gateway:" +msgstr "" +"Вы можете напрямую оплатить этот счет в режиме онлайн через безопасный " +"платежный шлюз PayPal:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:145 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:137 +msgid "Bank Wire Transfer" +msgstr "Банковский перевод" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:147 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:139 +msgid "Please transfer" +msgstr "Пожалуйста, переведите" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:148 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:140 +msgid "to" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:149 +msgid "" +"(postal address on the invoice header)\n" +" using one of the following bank accounts. Be sure to " +"mention the invoice\n" +" reference" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_account.xml:151 +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:143 +msgid "on the transfer:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:36 +msgid "Order" +msgstr "Заказ" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:42 +msgid "Salesman" +msgstr "Менеджер продаж" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:43 +msgid "Payment terms" +msgstr "Условия платежа" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:60 +msgid "Discount(%)" +msgstr "Скидка ( %)" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:127 +msgid "" +"You may directly pay this order online via Paypal's secure payment gateway:" +msgstr "" +"Вы можете напрямую оплатить этот заказ он-лайн через безопасный платежный " +"шлюз PayPal:" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/edi/static/src/xml/edi_sale_purchase.xml:141 +msgid "" +"(postal address on the order header)\n" +" using one of the following bank accounts. Be sure to " +"mention the document\n" +" reference" +msgstr "" diff --git a/addons/edi/static/src/xml/edi.xml b/addons/edi/static/src/xml/edi.xml index 88e09824b9b..111cb6d9d26 100644 --- a/addons/edi/static/src/xml/edi.xml +++ b/addons/edi/static/src/xml/edi.xml @@ -6,7 +6,7 @@ <t t-call="WebClient"/> </t> <t t-name="EdiView"> - <table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" id="oe_app" class="oe-application oe_forms"> + <table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" id="oe_app" class="oe-application oe_forms oe_semantic_html_override"> <tr> <td colspan="2" valign="top" id="oe_header" class="header"> <div> <a href="/" class="company_logo_link"> diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index b43b7d19b85..4080e62f002 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -28,6 +28,8 @@ from osv import osv from osv import fields import tools from tools.translate import _ +from tools.html_sanitize import html_sanitize +from tools import append_content_to_html from urllib import quote as quote _logger = logging.getLogger(__name__) @@ -38,10 +40,8 @@ except ImportError: class email_template(osv.osv): "Templates for sending email" - _inherit = 'mail.message' _name = "email.template" _description = 'Email Templates' - _rec_name = 'name' # override mail.message's behavior def render_template(self, cr, uid, template, model, res_id, context=None): """Render the given template text, replace mako expressions ``${expr}`` @@ -99,66 +99,46 @@ class email_template(osv.osv): mod_name = False if model_id: mod_name = self.pool.get('ir.model').browse(cr, uid, model_id, context).model - return {'value':{'model': mod_name}} - - def name_get(self, cr, uid, ids, context=None): - """ Override name_get of mail.message: return directly the template - name, and not the generated name from mail.message.common.""" - return [(record.id, record.name) for record in self.browse(cr, uid, ids, context=context)] + return {'value': {'model': mod_name}} _columns = { - 'name': fields.char('Name', size=250), - 'model_id': fields.many2one('ir.model', 'Related document model'), - 'lang': fields.char('Language Selection', size=250, + 'name': fields.char('Name'), + 'model_id': fields.many2one('ir.model', 'Applies to', help="The kind of document with with this template can be used"), + 'model': fields.related('model_id', 'model', type='char', string='Related Document Model', + size=128, select=True, store=True, readonly=True), + 'lang': fields.char('Language', help="Optional translation language (ISO code) to select when sending out an email. " "If not set, the english version will be used. " "This should usually be a placeholder expression " "that provides the appropriate language code, e.g. " - "${object.partner_id.lang.code}."), + "${object.partner_id.lang.code}.", + placeholder="${object.partner_id.lang.code}"), 'user_signature': fields.boolean('Add Signature', help="If checked, the user's signature will be appended to the text version " "of the message"), - 'report_name': fields.char('Report Filename', size=200, translate=True, + 'subject': fields.char('Subject', translate=True, help="Subject (placeholders may be used here)",), + 'email_from': fields.char('From', help="Sender address (placeholders may be used here)"), + 'email_to': fields.char('To', help="Comma-separated recipient addresses (placeholders may be used here)"), + 'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"), + 'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"), + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, + help="Optional preferred server for outgoing mails. If not set, the highest " + "priority one will be used."), + 'body_html': fields.text('Body', translate=True, help="Rich-text/HTML version of the message (placeholders may be used here)"), + 'report_name': fields.char('Report Filename', translate=True, help="Name to use for the generated report file (may contain placeholders)\n" "The extension can be omitted and will then come from the report type."), - 'report_template':fields.many2one('ir.actions.report.xml', 'Optional report to print and attach'), - 'ref_ir_act_window':fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True, + 'report_template': fields.many2one('ir.actions.report.xml', 'Optional report to print and attach'), + 'ref_ir_act_window': fields.many2one('ir.actions.act_window', 'Sidebar action', readonly=True, help="Sidebar action to make this template available on records " "of the related document model"), - 'ref_ir_value':fields.many2one('ir.values', 'Sidebar Button', readonly=True, + 'ref_ir_value': fields.many2one('ir.values', 'Sidebar Button', readonly=True, help="Sidebar button to open the sidebar action"), - 'track_campaign_item': fields.boolean('Resource Tracking', - help="Enable this is you wish to include a special tracking marker " - "in outgoing emails so you can identify replies and link " - "them back to the corresponding resource record. " - "This is useful for CRM leads for example"), - - # Overridden mail.message.common fields for technical reasons: - 'model': fields.related('model_id','model', type='char', string='Related Document Model', - size=128, select=True, store=True, readonly=True), - # we need a separate m2m table to avoid ID collisions with the original mail.message entries 'attachment_ids': fields.many2many('ir.attachment', 'email_template_attachment_rel', 'email_template_id', - 'attachment_id', 'Files to attach', + 'attachment_id', 'Attachments', help="You may attach files to this template, to be added to all " "emails created from this template"), - - # Overridden mail.message.common fields to make tooltips more appropriate: - 'subject':fields.char('Subject', size=512, translate=True, help="Subject (placeholders may be used here)",), - 'email_from': fields.char('From', size=128, help="Sender address (placeholders may be used here)"), - 'email_to': fields.char('To', size=256, help="Comma-separated recipient addresses (placeholders may be used here)"), - 'email_cc': fields.char('Cc', size=256, help="Carbon copy recipients (placeholders may be used here)"), - 'email_bcc': fields.char('Bcc', size=256, help="Blind carbon copy recipients (placeholders may be used here)"), - 'reply_to': fields.char('Reply-To', size=250, help="Preferred response address (placeholders may be used here)"), - 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False, - help="Optional preferred server for outgoing mails. If not set, the highest " - "priority one will be used."), - 'body_text': fields.text('Text Contents', translate=True, help="Plaintext version of the message (placeholders may be used here)"), - 'body_html': fields.text('Rich-text Contents', translate=True, help="Rich-text/HTML version of the message (placeholders may be used here)"), - 'message_id': fields.char('Message-Id', size=256, help="Message-ID SMTP header to use in outgoing messages based on this template. " - "Please note that this overrides the 'Resource Tracking' option, " - "so if you simply need to track replies to outgoing emails, enable " - "that option instead.\n" - "Placeholders must be used here, as this value always needs to be unique!"), + 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), # Fake fields used to implement the placeholder assistant 'model_object_field': fields.many2one('ir.model.fields', string="Field", @@ -172,12 +152,12 @@ class email_template(osv.osv): help="When a relationship field is selected as first field, " "this field lets you select the target field within the " "destination document model (sub-model)."), - 'null_value': fields.char('Null value', help="Optional value to use if the target field is empty", size=128), - 'copyvalue': fields.char('Expression', size=256, help="Final placeholder expression, to be copy-pasted in the desired template field."), + 'null_value': fields.char('Default Value', help="Optional value to use if the target field is empty"), + 'copyvalue': fields.char('Placeholder Expression', help="Final placeholder expression, to be copy-pasted in the desired template field."), } _defaults = { - 'track_campaign_item': True + 'auto_delete': True, } def create_action(self, cr, uid, ids, context=None): @@ -195,7 +175,7 @@ class email_template(osv.osv): 'res_model': 'mail.compose.message', 'src_model': src_obj, 'view_type': 'form', - 'context': "{'mail.compose.message.mode':'mass_mail', 'mail.compose.template_id' : %d}" % (template.id), + 'context': "{'default_composition_mode': 'mass_mail', 'default_template_id' : %d}" % (template.id), 'view_mode':'form,tree', 'view_id': res_id, 'target': 'new', @@ -222,7 +202,7 @@ class email_template(osv.osv): if template.ref_ir_value: ir_values_obj = self.pool.get('ir.values') ir_values_obj.unlink(cr, uid, template.ref_ir_value.id, context) - except: + except Exception: raise osv.except_osv(_("Warning"), _("Deletion of the action record failed.")) return True @@ -294,65 +274,33 @@ class email_template(osv.osv): :param res_id: id of the record to use for rendering the template (model is taken from template definition) :returns: a dict containing all relevant fields for creating a new - mail.message entry, with the addition one additional - special key ``attachments`` containing a list of + mail.mail entry, with one extra key ``attachments``, in the + format expected by :py:meth:`mail_thread.message_post`. """ if context is None: context = {} - values = { - 'subject': False, - 'body_text': False, - 'body_html': False, - 'email_from': False, - 'email_to': False, - 'email_cc': False, - 'email_bcc': False, - 'reply_to': False, - 'auto_delete': False, - 'model': False, - 'res_id': False, - 'mail_server_id': False, - 'attachments': False, - 'attachment_ids': False, - 'message_id': False, - 'state': 'outgoing', - 'content_subtype': 'plain', - 'partner_ids': [], - } - if not template_id: - return values - report_xml_pool = self.pool.get('ir.actions.report.xml') template = self.get_email_template(cr, uid, template_id, res_id, context) - - for field in ['subject', 'body_text', 'body_html', 'email_from', - 'email_to', 'email_cc', 'email_bcc', 'reply_to', - 'message_id']: + values = {} + for field in ['subject', 'body_html', 'email_from', + 'email_to', 'email_cc', 'reply_to']: values[field] = self.render_template(cr, uid, getattr(template, field), template.model, res_id, context=context) \ or False - - # if email_to: find or create a partner - if values['email_to']: - partner_id = self.pool.get('mail.thread').message_partner_by_email(cr, uid, values['email_to'], context=context)['partner_id'] - if not partner_id: - partner_id = self.pool.get('res.partner').name_create(cr, uid, values['email_to'], context=context) - values['partner_ids'] = [partner_id] - - if values['body_html']: - values.update(content_subtype='html') - if template.user_signature: signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature - values['body_text'] += '\n\n' + signature + values['body_html'] = append_content_to_html(values['body_html'], signature) + + if values['body_html']: + values['body'] = html_sanitize(values['body_html']) - values.update(mail_server_id = template.mail_server_id.id or False, - auto_delete = template.auto_delete, + values.update(mail_server_id=template.mail_server_id.id or False, + auto_delete=template.auto_delete, model=template.model, res_id=res_id or False) - attachments = {} - # Add report as a Document + attachments = [] + # Add report in attachments if template.report_template: report_name = self.render_template(cr, uid, template.report_name, template.model, res_id, context=context) report_service = 'report.' + report_xml_pool.browse(cr, uid, template.report_template.id, context).report_name @@ -368,12 +316,11 @@ class email_template(osv.osv): ext = "." + format if not report_name.endswith(ext): report_name += ext - attachments[report_name] = result + attachments.append(report_name, result) - # Add document attachments + # Add template attachments for attach in template.attachment_ids: - # keep the bytes as fetched from the db, base64 encoded - attachments[attach.datas_fname] = attach.datas + attachments.append((attach.datas_fname, attach.datas)) values['attachments'] = attachments return values @@ -391,12 +338,12 @@ class email_template(osv.osv): :returns: id of the mail.message that was created """ if context is None: context = {} - mail_message = self.pool.get('mail.message') + mail_mail = self.pool.get('mail.mail') ir_attachment = self.pool.get('ir.attachment') values = self.generate_email(cr, uid, template_id, res_id, context=context) assert 'email_from' in values, 'email_from is missing or empty after template rendering, send_mail() cannot proceed' attachments = values.pop('attachments') or {} - msg_id = mail_message.create(cr, uid, values, context=context) + msg_id = mail_mail.create(cr, uid, values, context=context) # link attachments attachment_ids = [] for fname, fcontent in attachments.iteritems(): @@ -404,15 +351,15 @@ class email_template(osv.osv): 'name': fname, 'datas_fname': fname, 'datas': fcontent, - 'res_model': mail_message._name, + 'res_model': mail_mail._name, 'res_id': msg_id, } context.pop('default_type', None) attachment_ids.append(ir_attachment.create(cr, uid, attachment_data, context=context)) if attachment_ids: - mail_message.write(cr, uid, msg_id, {'attachment_ids': [(6, 0, attachment_ids)]}, context=context) + mail_mail.write(cr, uid, msg_id, {'attachment_ids': [(6, 0, attachment_ids)]}, context=context) if force_send: - mail_message.send(cr, uid, [msg_id], context=context) + mail_mail.send(cr, uid, [msg_id], context=context) return msg_id # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/email_template_view.xml b/addons/email_template/email_template_view.xml index a04790d1c1c..448655a3121 100644 --- a/addons/email_template/email_template_view.xml +++ b/addons/email_template/email_template_view.xml @@ -1,99 +1,67 @@ <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> - <record model="ir.ui.view" id="email_template_form"> <field name="name">email.template.form</field> <field name="model">email.template</field> <field name="arch" type="xml"> <form string="Templates" version="7.0"> - <sheet> - <group col="4"> - <field name="name" required="1"/> - <field name="model_id" required="1" on_change="onchange_model_id(model_id)"/> - <field name="model" invisible="1"/> - </group> - <notebook> - <page string="Email Details"> - <group> - <field name="email_from" required="1"/> - <field name="email_to" required="1"/> - <field name="email_cc"/> - <field name="email_bcc"/> - <field name="reply_to"/> - <field name="subject" required="1"/> - <notebook colspan="4"> - <page string="Body (Text)"> - <field name="body_text" colspan="4" width="250" height="250" nolabel="1"/> - </page> - <page string="Body (Rich/HTML)"> - <field name="body_html" colspan="4" width="250" height="250" nolabel="1"/> - <label string="Note: This is Raw HTML." colspan="4"/> - </page> - </notebook> - <field name="user_signature"/> - <notebook colspan="4"> - <page string="Dynamic Values Builder"> - <group> - <field name="model_object_field" - domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]" - on_change="onchange_sub_model_object_value_field(model_object_field)"/> - <field name="sub_object" readonly="1"/> - <field name="sub_model_object_field" - domain="[('model_id','=',sub_object),('ttype','!=','one2many'),('ttype','!=','many2many')]" - attrs="{'readonly':[('sub_object','=',False)],'required':[('sub_object','!=',False)]}" - on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field)"/> - <field name="null_value" on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field,null_value)"/> - <field name="copyvalue"/> + <sheet> + <div class="oe_title"> + <label for="name" class="oe_edit_only"/><h1><field name="name" required="1"/></h1> + <h3><label for="model_id"/><field name="model_id" required="1" on_change="onchange_model_id(model_id)" class="oe_inline"/></h3> + <field name="model" invisible="1"/> + </div> + <div class="oe_right oe_button_box" name="buttons"> + <field name="ref_ir_act_window" invisible="1"/> + <button name="create_action" string="Add context action" type="object" + attrs="{'invisible':[('ref_ir_act_window','!=',False)]}" + help="Display an option on related documents to open a composition wizard with this template"/> + <button name="unlink_action" string="Remove context action" type="object" + attrs="{'invisible':[('ref_ir_act_window','=',False)]}" + help="Remove the contextual action to use this template on related documents"/> + <button name="%(wizard_email_template_preview)d" string="Preview" + type="action" target="new" + context="{'template_id':active_id}"/> + </div> + <notebook> + <page string="Email Details"> + <group> + <group string="Addressing"> + <field name="email_from" required="1"/> + <field name="email_to" required="1"/> + <field name="email_cc"/> + <field name="reply_to"/> + <field name="user_signature"/> + </group> + <group string="Dynamic Value Builder" class="oe_edit_only"> + <field name="model_object_field" domain="[('model_id','=',model_id),('ttype','!=','one2many'),('ttype','!=','many2many')]" on_change="onchange_sub_model_object_value_field(model_object_field)"/> + <field name="sub_object" readonly="1"/> + <field name="sub_model_object_field" domain="[('model_id','=',sub_object),('ttype','!=','one2many'),('ttype','!=','many2many')]" attrs="{'readonly':[('sub_object','=',False)],'required':[('sub_object','!=',False)]}" on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field)"/> + <field name="null_value" on_change="onchange_sub_model_object_value_field(model_object_field,sub_model_object_field,null_value)"/> + <field name="copyvalue"/> + </group> + <group string="Contents" colspan="2"> + <field name="subject" required="1"/> + <field name="body_html" width="250" height="450" nolabel="1" colspan="2" placeholder="Email contents (in raw HTML format)"/> </group> - </page> - </notebook> - <button name="%(wizard_email_template_preview)d" string="Preview Template" - type="action" colspan="4" target="new" icon="gtk-zoom-fit" context="{'template_id':active_id}"/> - </group> - </page> - <page string="Advanced"> - <group colspan="2" col="2"> - <group colspan="2" col="2"> - <separator string="Sidebar Button" colspan="2"/> - <button name="create_action" string="Add sidebar button" type="object" icon="gtk-execute" - colspan="2" attrs="{'invisible':[('ref_ir_act_window','!=',False)]}" - help="Display a button in the sidebar of related documents to open a composition wizard with this template" - /> - <field name="ref_ir_act_window" attrs="{'invisible':[('ref_ir_act_window','=',False)]}"/> - <field name="ref_ir_value" attrs="{'invisible':[('ref_ir_act_window','=',False)]}"/> - <button name="unlink_action" string="Remove sidebar button" type="object" icon="gtk-delete" - colspan="2" attrs="{'invisible':[('ref_ir_act_window','=',False)]}" - help="Remove the sidebar button currently displayed on related documents" - /> </group> - <group colspan="2" col="2"> - <separator string="Advanced Options" colspan="2"/> + </page> + <page string="Advanced"> + <group> + <field name="lang"/> <field name="mail_server_id"/> - <field name="track_campaign_item"/> - <field name="message_id"/> <field name="auto_delete"/> - <field name="lang"/> + <field name="report_template" domain="[('model','=',model)]"/> + <field name="report_name" class="oe_inline" + attrs="{'invisible':[('report_template','=',False)]}"/> + <field name="attachment_ids"> + <tree><field name="name"/></tree> + </field> </group> - </group> - <group colspan="2" col="2"> - <separator string="Attachments" colspan="2"/> - <notebook colspan="2"> - <page string="Attach Report"> - <group> - <field name="report_template" colspan="4" - domain="[('model','=',model)]"/> - <field name="report_name" colspan="4" /> - </group> - </page> - <page string="Attach existing files"> - <field name="attachment_ids" colspan="4" nolabel="1" height="350"/> - </page> - </notebook> - </group> - </page> - </notebook> - </sheet> + </page> + </notebook> + </sheet> </form> </field> </record> @@ -103,9 +71,9 @@ <field name="model">email.template</field> <field name="arch" type="xml"> <tree string="Templates"> - <field name="model_id"/> <field name="mail_server_id" invisible="1"/> <field name="name"/> + <field name="model_id"/> <field name="subject"/> <field name="email_from"/> <field name="email_to"/> diff --git a/addons/email_template/i18n/ar.po b/addons/email_template/i18n/ar.po index 8a89940d55a..777d520ab7b 100644 --- a/addons/email_template/i18n/ar.po +++ b/addons/email_template/i18n/ar.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:34+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -316,8 +316,8 @@ msgid "Add Signature" msgstr "إضافة توقيع" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "نص عادي نسخة من رسالة (ويمكن استخدام العناصر البديلة هنا)" @@ -460,8 +460,8 @@ msgid "Cc" msgstr "نسخة" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/bg.po b/addons/email_template/i18n/bg.po index e75f20173fd..511acb384a5 100644 --- a/addons/email_template/i18n/bg.po +++ b/addons/email_template/i18n/bg.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:34+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/ca.po b/addons/email_template/i18n/ca.po index 49804a15b25..e53aadf814c 100644 --- a/addons/email_template/i18n/ca.po +++ b/addons/email_template/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -307,8 +307,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -449,8 +449,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/da.po b/addons/email_template/i18n/da.po index 08e8ef5795b..2a1f19128c4 100644 --- a/addons/email_template/i18n/da.po +++ b/addons/email_template/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/de.po b/addons/email_template/i18n/de.po index 299c243c0ed..838c035af4a 100644 --- a/addons/email_template/i18n/de.po +++ b/addons/email_template/i18n/de.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -317,8 +317,8 @@ msgid "Add Signature" msgstr "Signatur hinzufügen" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Text-Version der Nachricht (Platzhalter können verwendet werden)" @@ -465,8 +465,8 @@ msgid "Cc" msgstr "CC" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "Text Inhalt" diff --git a/addons/email_template/i18n/email_template.pot b/addons/email_template/i18n/email_template.pot index 0990a940397..519755ac8f0 100644 --- a/addons/email_template/i18n/email_template.pot +++ b/addons/email_template/i18n/email_template.pot @@ -289,8 +289,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -425,8 +425,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/es.po b/addons/email_template/i18n/es.po index 082735c52f7..368f6ca8476 100644 --- a/addons/email_template/i18n/es.po +++ b/addons/email_template/i18n/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -307,8 +307,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -449,8 +449,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/es_CL.po b/addons/email_template/i18n/es_CL.po index 28f7b0d864b..94fbfad1d9f 100644 --- a/addons/email_template/i18n/es_CL.po +++ b/addons/email_template/i18n/es_CL.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -305,8 +305,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -447,8 +447,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/es_CR.po b/addons/email_template/i18n/es_CR.po index 444ba7ac5df..5e2d107161d 100644 --- a/addons/email_template/i18n/es_CR.po +++ b/addons/email_template/i18n/es_CR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" "Language: es\n" #. module: email_template @@ -325,8 +325,8 @@ msgid "Add Signature" msgstr "Agregar Fima" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" "Versión en formato texto del mensaje (marcadores de posición se pueden " @@ -475,8 +475,8 @@ msgid "Cc" msgstr "Cc" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/es_EC.po b/addons/email_template/i18n/es_EC.po index 6614bc83fc1..74f8e228c91 100644 --- a/addons/email_template/i18n/es_EC.po +++ b/addons/email_template/i18n/es_EC.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/et.po b/addons/email_template/i18n/et.po index 20888f21509..1407f14811b 100644 --- a/addons/email_template/i18n/et.po +++ b/addons/email_template/i18n/et.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/fi.po b/addons/email_template/i18n/fi.po index ea713d160e1..fe4c40f5a95 100644 --- a/addons/email_template/i18n/fi.po +++ b/addons/email_template/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -308,8 +308,8 @@ msgid "Add Signature" msgstr "Lisää allekirjoitus" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Tekstiversio viestistä (voit käyttä täytettä)" @@ -450,8 +450,8 @@ msgid "Cc" msgstr "Kopio viestistä" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/fr.po b/addons/email_template/i18n/fr.po index e608e65df31..204167b1448 100644 --- a/addons/email_template/i18n/fr.po +++ b/addons/email_template/i18n/fr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -322,8 +322,8 @@ msgid "Add Signature" msgstr "Ajouter une signature" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Version texte du message (des variables peuvent être utilisées ici)" @@ -469,8 +469,8 @@ msgid "Cc" msgstr "Copie à (CC)" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/hr.po b/addons/email_template/i18n/hr.po index b0a9017c923..b0d8757db77 100644 --- a/addons/email_template/i18n/hr.po +++ b/addons/email_template/i18n/hr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/hu.po b/addons/email_template/i18n/hu.po index dd639b18660..8e37f5cc98d 100644 --- a/addons/email_template/i18n/hu.po +++ b/addons/email_template/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/it.po b/addons/email_template/i18n/it.po index ac5b384d22d..e81017e9e8d 100644 --- a/addons/email_template/i18n/it.po +++ b/addons/email_template/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -307,8 +307,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -449,8 +449,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/ja.po b/addons/email_template/i18n/ja.po index 19137ca482a..58c19dad2e5 100644 --- a/addons/email_template/i18n/ja.po +++ b/addons/email_template/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -310,8 +310,8 @@ msgid "Add Signature" msgstr "署名を追加する" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "このメッセージのテキスト" @@ -452,8 +452,8 @@ msgid "Cc" msgstr "写し(CC)" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "テキストの内容" diff --git a/addons/email_template/i18n/mn.po b/addons/email_template/i18n/mn.po index 272ac0a445b..33c32862a0e 100644 --- a/addons/email_template/i18n/mn.po +++ b/addons/email_template/i18n/mn.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -316,8 +316,8 @@ msgid "Add Signature" msgstr "Гарын үсэг нэмэх" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Зурвасын цэвэр текст хувилбар (энд хувьсагч хэрэглэж болно)" @@ -463,8 +463,8 @@ msgid "Cc" msgstr "Cc" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/nb.po b/addons/email_template/i18n/nb.po index 4d313a0f35e..7ebba9bd384 100644 --- a/addons/email_template/i18n/nb.po +++ b/addons/email_template/i18n/nb.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/nl.po b/addons/email_template/i18n/nl.po index be40e7d5683..b77f4ecd6e5 100644 --- a/addons/email_template/i18n/nl.po +++ b/addons/email_template/i18n/nl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -323,8 +323,8 @@ msgid "Add Signature" msgstr "Handtekening toevoegen" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" "Platte tekst versie van het bericht (tijdelijke aanduidingen kan hier worden " @@ -474,8 +474,8 @@ msgid "Cc" msgstr "Cc" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "Tekst inhoud" diff --git a/addons/email_template/i18n/pl.po b/addons/email_template/i18n/pl.po index c1c85f1cce5..327ab735302 100644 --- a/addons/email_template/i18n/pl.po +++ b/addons/email_template/i18n/pl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -306,8 +306,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -448,8 +448,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/pt.po b/addons/email_template/i18n/pt.po index f6bd3da718f..20a12007c16 100644 --- a/addons/email_template/i18n/pt.po +++ b/addons/email_template/i18n/pt.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -322,8 +322,8 @@ msgid "Add Signature" msgstr "Adicionar Assinatura" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" "Texto Simples sem formatação da mensagem (espaços reservados podem ser " @@ -472,8 +472,8 @@ msgid "Cc" msgstr "Cc" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/pt_BR.po b/addons/email_template/i18n/pt_BR.po index d1f2dddc371..ae61051acd4 100644 --- a/addons/email_template/i18n/pt_BR.po +++ b/addons/email_template/i18n/pt_BR.po @@ -15,8 +15,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -319,8 +319,8 @@ msgid "Add Signature" msgstr "Adicionar Assinatura" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Versão em Texto da mensagem (placeholders podem ser usados aqui)" @@ -467,8 +467,8 @@ msgid "Cc" msgstr "Cópia para" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "Conteúdo de Texto" diff --git a/addons/email_template/i18n/ro.po b/addons/email_template/i18n/ro.po index 0eda9a2f996..8331071afb7 100644 --- a/addons/email_template/i18n/ro.po +++ b/addons/email_template/i18n/ro.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -320,8 +320,8 @@ msgid "Add Signature" msgstr "Adauga Semnatura" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Versiunea simpla a mesajului (aici pot fi folositi inlocuitori)" @@ -467,8 +467,8 @@ msgid "Cc" msgstr "Cc (copie carbon)" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/ru.po b/addons/email_template/i18n/ru.po index 7d90042bfe4..6b61705ff48 100644 --- a/addons/email_template/i18n/ru.po +++ b/addons/email_template/i18n/ru.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -303,8 +303,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -445,8 +445,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/sr.po b/addons/email_template/i18n/sr.po index 3c670f40bb1..703a71980d1 100644 --- a/addons/email_template/i18n/sr.po +++ b/addons/email_template/i18n/sr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -306,8 +306,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -448,8 +448,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/sr@latin.po b/addons/email_template/i18n/sr@latin.po index 808c900f19c..3ab2d2cf368 100644 --- a/addons/email_template/i18n/sr@latin.po +++ b/addons/email_template/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -306,8 +306,8 @@ msgid "Add Signature" msgstr "" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "" @@ -448,8 +448,8 @@ msgid "Cc" msgstr "" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/sv.po b/addons/email_template/i18n/sv.po index b6b6fe45466..bec8aeb784b 100644 --- a/addons/email_template/i18n/sv.po +++ b/addons/email_template/i18n/sv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -319,8 +319,8 @@ msgid "Add Signature" msgstr "Lägg till signatur" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Ren textversion av meddelandet (platshållare kan användas här)" @@ -465,8 +465,8 @@ msgid "Cc" msgstr "Kopia" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "" diff --git a/addons/email_template/i18n/tr.po b/addons/email_template/i18n/tr.po index 0746bd02381..3770b758b47 100644 --- a/addons/email_template/i18n/tr.po +++ b/addons/email_template/i18n/tr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -318,8 +318,8 @@ msgid "Add Signature" msgstr "İmza Ekle" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Mesajın düz metin biçimi (burada yer tutucular kullanılabilir)" @@ -463,8 +463,8 @@ msgid "Cc" msgstr "Cc" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "Metin içeriği" @@ -524,6 +524,7 @@ msgid "" "Sidebar action to make this template available on records of the related " "document model" msgstr "" +"Kenar çubuğu bu şablonun ilgili belge modellerinde kullanılmasını sağlar" #. module: email_template #: field:email.template,model_object_field:0 @@ -553,7 +554,7 @@ msgstr "" #: help:email.template,email_bcc:0 #: help:email_template.preview,email_bcc:0 msgid "Blind carbon copy recipients (placeholders may be used here)" -msgstr "" +msgstr "Alıcıların görğnmez karbon kopyası (burada yer tutucular kullanılr)" #. module: email_template #: help:email.template,lang:0 @@ -564,6 +565,10 @@ msgid "" "a placeholder expression that provides the appropriate language code, e.g. " "${object.partner_id.lang.code}." msgstr "" +"Bir eposta gönderilirken seçilecek seçime bağlı çeviri dili (ISO kodu). Eğer " +"ayarlanmamışsa, İngilizce sürümü kullanılacaktır. Bu genellikle uygun dil " +"kodunu belirten bir yertutucu ifadesi olabilir, örneğin; " +"${object.partner_id.lang.code}." #. module: email_template #: field:email_template.preview,res_id:0 @@ -574,7 +579,7 @@ msgstr "Örnek Döküman" #: help:email.template,email_to:0 #: help:email_template.preview,email_to:0 msgid "Comma-separated recipient addresses (placeholders may be used here)" -msgstr "" +msgstr "Virgülle ayrılmış alıcı adresleri (burada yertutucular kullanılır)" #. module: email_template #: field:email.template,name:0 @@ -608,6 +613,10 @@ msgid "" "instead.\n" "Placeholders must be used here, as this value always needs to be unique!" msgstr "" +"Giden mesajlarda Mesaj-SMTP Kimliği bu şablonu temel alır. Lütfen bunun " +"'Kaynak İzleme' seçeneğini gçersiz kılacağını not edin, bunun yerine giden " +"epostaların yanıtlarını izlemek için o seçeneği etkinleştirin.\n" +"Bu değer her zaman benzersiz olacağından burada yertutucular kullanılmalıdır!" #. module: email_template #: field:email.template,headers:0 @@ -625,7 +634,7 @@ msgstr "Bcc" #: help:email.template,reply_to:0 #: help:email_template.preview,reply_to:0 msgid "Preferred response address (placeholders may be used here)" -msgstr "" +msgstr "Yeğlenen yanıtlama adresi (burada yertutucular kullanılır)" #. module: email_template #: view:email.template:0 @@ -636,7 +645,7 @@ msgstr "Kenar Çubuğu butonunu kaldır" #: help:email.template,null_value:0 #: help:email_template.preview,null_value:0 msgid "Optional value to use if the target field is empty" -msgstr "" +msgstr "Hedef alanı boşsa kullanılacak seçimli değer" #. module: email_template #: view:email.template:0 @@ -653,7 +662,7 @@ msgstr "Mesaj referansları, tanımlayıcılar ya da önceki mesajlar" #: help:email.template,ref_ir_value:0 #: help:email_template.preview,ref_ir_value:0 msgid "Sidebar button to open the sidebar action" -msgstr "" +msgstr "Kenar çubuğu işlevini açacak kenar çubuğu düğmesi" #. module: email_template #: constraint:res.partner:0 @@ -673,6 +682,8 @@ msgid "" "Optional preferred server for outgoing mails. If not set, the highest " "priority one will be used." msgstr "" +"Giden postalar için yeğlenen seçimli sunucu. Eğer ayarlanmaışsa, en yüksek " +"öncelikli olan kullanılacaktır." #. module: email_template #: selection:email.template,state:0 @@ -687,6 +698,8 @@ msgid "" "When a relationship field is selected as first field, this field lets you " "select the target field within the destination document model (sub-model)." msgstr "" +"İlk alan olarak ilişki alanı seçilmişse, bu alan, varış belge modeli içinden " +"hedef alanını seçmenizi sağlar." #. module: email_template #: view:email.template:0 @@ -704,6 +717,7 @@ msgstr "Yazdırıp eklemek için opsiyonel rapor" #: help:email_template.preview,body_html:0 msgid "Rich-text/HTML version of the message (placeholders may be used here)" msgstr "" +"Mesajın Zengin-metin/HTML sürümü (burada yertutucular kullanılmalıdır)" #, python-format #~ msgid "" diff --git a/addons/email_template/i18n/zh_CN.po b/addons/email_template/i18n/zh_CN.po index 587b60a046d..66374b3d14b 100644 --- a/addons/email_template/i18n/zh_CN.po +++ b/addons/email_template/i18n/zh_CN.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:35+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -307,8 +307,8 @@ msgid "Add Signature" msgstr "添加签名" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "内容的纯文本版本(可以在这里用占位符)" @@ -449,8 +449,8 @@ msgid "Cc" msgstr "抄送" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "文本内容" diff --git a/addons/email_template/i18n/zh_TW.po b/addons/email_template/i18n/zh_TW.po index 9094107915f..c7526fbce77 100644 --- a/addons/email_template/i18n/zh_TW.po +++ b/addons/email_template/i18n/zh_TW.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-31 04:56+0000\n" -"X-Generator: Launchpad (build 15887)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:59+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: email_template #: field:email.template,subtype:0 @@ -307,8 +307,8 @@ msgid "Add Signature" msgstr "加入簽名" #. module: email_template -#: help:email.template,body_text:0 -#: help:email_template.preview,body_text:0 +#: help:email.template,body:0 +#: help:email_template.preview,body:0 msgid "Plaintext version of the message (placeholders may be used here)" msgstr "Plaintext 消息版本(佔位符可以用在這)" @@ -450,8 +450,8 @@ msgid "Cc" msgstr "副本" #. module: email_template -#: field:email.template,body_text:0 -#: field:email_template.preview,body_text:0 +#: field:email.template,body:0 +#: field:email_template.preview,body:0 msgid "Text Contents" msgstr "文字內容" diff --git a/addons/email_template/security/ir.model.access.csv b/addons/email_template/security/ir.model.access.csv index d2d6adf3bf3..adce0420793 100644 --- a/addons/email_template/security/ir.model.access.csv +++ b/addons/email_template/security/ir.model.access.csv @@ -1,5 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_email_template,email.template,model_email_template,,1,0,0,0
+access_email_template,email.template,model_email_template,,1,1,1,0
access_email_template_system,email.template system,model_email_template,base.group_system,1,1,1,1
-access_email_template_manager,email.template,model_email_template,,1,1,1,1
-access_email_template_preview_system,email.template.preview system,model_email_template_preview,base.group_system,1,1,1,1
diff --git a/addons/hr_evaluation/wizard/__init__.py b/addons/email_template/tests/__init__.py index 64406ac6b1e..d63c5634cc3 100644 --- a/addons/hr_evaluation/wizard/__init__.py +++ b/addons/email_template/tests/__init__.py @@ -1,8 +1,8 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -18,8 +18,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## +from . import test_mail -#import hr_evaluation_mail -import mail_compose_message +checks = [ + test_mail, +] -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file diff --git a/addons/email_template/tests/test_mail.py b/addons/email_template/tests/test_mail.py new file mode 100644 index 00000000000..85ab3afc902 --- /dev/null +++ b/addons/email_template/tests/test_mail.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import base64 +from openerp.tests import common + +class test_message_compose(common.TransactionCase): + + def _mock_smtp_gateway(self, *args, **kwargs): + return True + + def _mock_build_email(self, *args, **kwargs): + self._build_email_args = args + self._build_email_kwargs = kwargs + return self.build_email_real(*args, **kwargs) + + def setUp(self): + super(test_message_compose, self).setUp() + self.mail_group = self.registry('mail.group') + self.mail_mail = self.registry('mail.mail') + self.mail_message = self.registry('mail.message') + self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') + + # Install mock SMTP gateway + self.build_email_real = self.registry('ir.mail_server').build_email + self.registry('ir.mail_server').build_email = self._mock_build_email + self.registry('ir.mail_server').send_email = self._mock_smtp_gateway + + # create a 'pigs' and 'bird' groups that will be used through the various tests + self.group_pigs_id = self.mail_group.create(self.cr, self.uid, + {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) + self.group_bird_id = self.mail_group.create(self.cr, self.uid, + {'name': 'Bird', 'description': 'I am angry !'}) + + def test_00_message_compose_wizard(self): + """ Tests designed for the mail.compose.message wizard updated by email_template. """ + cr, uid = self.cr, self.uid + mail_compose = self.registry('mail.compose.message') + self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a.a'}) + user_admin = self.res_users.browse(cr, uid, uid) + p_a_id = user_admin.partner_id.id + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + group_bird = self.mail_group.browse(cr, uid, self.group_bird_id) + + # Mail data + _subject1 = 'Pigs' + _subject2 = 'Bird' + _body_html1 = 'Fans of Pigs, unite !\n<pre>Admin</pre>\n' + _body_html2 = 'I am angry !\n<pre>Admin</pre>\n' + _attachments = [ + {'name': 'First', 'datas_fname': 'first.txt', 'datas': base64.b64encode('My first attachment')}, + {'name': 'Second', 'datas_fname': 'second.txt', 'datas': base64.b64encode('My second attachment')} + ] + _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')] + + # Create template on mail.group, with attachments + group_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'mail.group')])[0] + email_template = self.registry('email.template') + email_template_id = email_template.create(cr, uid, {'model_id': group_model_id, + 'name': 'Pigs Template', 'subject': '${object.name}', + 'body_html': '${object.description}', 'user_signature': True, + 'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])], + 'email_to': 'b@b.b c@c.c', 'email_cc': 'd@d.d'}) + + # ---------------------------------------- + # CASE1: comment and save as template + # ---------------------------------------- + + # 1. Comment on pigs + compose_id = mail_compose.create(cr, uid, + {'subject': 'Forget me subject', 'body': '<p>Dummy body</p>'}, + {'default_composition_mode': 'comment', 'default_model': 'mail.group', + 'default_res_id': self.group_pigs_id, + 'default_template_id': email_template_id, + 'active_ids': [self.group_pigs_id, self.group_bird_id]}) + compose = mail_compose.browse(cr, uid, compose_id) + + # 2. Save current composition form as a template + mail_compose.save_as_template(cr, uid, [compose_id], context={'default_model': 'mail.group'}) + # Test: email_template subject, body_html, model + last_template_id = email_template.search(cr, uid, [('model', '=', 'mail.group'), ('subject', '=', 'Forget me subject')], limit=1)[0] + self.assertTrue(last_template_id, 'email_template not found for model mail.group, subject Forget me subject') + last_template = email_template.browse(cr, uid, last_template_id) + self.assertEqual(last_template.body_html, '<p>Dummy body</p>', 'email_template incorrect body_html') + + # ---------------------------------------- + # CASE2: comment with template, save as template + # ---------------------------------------- + + # 1. Comment on pigs + compose_id = mail_compose.create(cr, uid, + {'subject': 'Forget me subject', 'body': 'Dummy body'}, + {'default_composition_mode': 'comment', 'default_model': 'mail.group', + 'default_res_id': self.group_pigs_id, + 'default_template_id': email_template_id, + 'active_ids': [self.group_pigs_id, self.group_bird_id]}) + compose = mail_compose.browse(cr, uid, compose_id) + + # 2. Perform 'toggle_template', to set use_template and use template_id + mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'}) + compose.refresh() + message_pids = [partner.id for partner in compose.partner_ids] + partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])]) + # Test: mail.compose.message: subject, body, content_subtype, partner_ids + self.assertEqual(compose.subject, _subject1, 'mail.compose.message subject incorrect') + self.assertEqual(compose.body, _body_html1, 'mail.compose.message body incorrect') + self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect') + self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect') + # Test: mail.compose.message: attachments + # Test: mail.message: attachments + for attach in compose.attachment_ids: + self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') + self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect') + self.assertIn((attach.name, base64.b64decode(attach.datas)), _attachments_test, + 'mail.message attachment name / data incorrect') + + # 3. Perform 'toggle_template': template is not set anymore + mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'}) + compose.refresh() + # Test: subject, body, partner_ids + self.assertEqual(compose.subject, False, 'mail.compose.message subject incorrect') + self.assertEqual(compose.body, '', 'mail.compose.message body incorrect') + + # ---------------------------------------- + # CASE3: mass_mail with template + # ---------------------------------------- + + # 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added + compose_id = mail_compose.create(cr, uid, + {'subject': 'Forget me subject', 'body': 'Dummy body'}, + {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', + 'default_res_id': self.group_pigs_id, + 'default_template_id': email_template_id, + 'default_partner_ids': [p_a_id], + 'active_ids': [self.group_pigs_id, self.group_bird_id]}) + compose = mail_compose.browse(cr, uid, compose_id) + + # 2. Perform 'toggle_template', to set use_template and use template_id + mail_compose.toggle_template(cr, uid, [compose_id], {'default_composition_mode': 'comment', 'default_model': 'mail.group'}) + compose.refresh() + message_pids = [partner.id for partner in compose.partner_ids] + partner_ids = [p_a_id] + # Test: mail.compose.message: subject, body, content_subtype, partner_ids + self.assertEqual(compose.subject, '${object.name}', 'mail.compose.message subject incorrect') + self.assertEqual(compose.body, '${object.description}', 'mail.compose.message body incorrect') + self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect') + self.assertEqual(set(message_pids), set(partner_ids), 'mail.compose.message partner_ids incorrect') + + # 3. Post the comment, get created message + mail_compose.send_mail(cr, uid, [compose_id], {'default_res_id': -1, 'active_ids': [self.group_pigs_id, self.group_bird_id]}) + group_pigs.refresh() + group_bird.refresh() + message_pigs = group_pigs.message_ids[0] + message_bird = group_bird.message_ids[0] + # Test: subject, body + self.assertEqual(message_pigs.subject, _subject1, 'mail.message subject on Pigs incorrect') + self.assertEqual(message_bird.subject, _subject2, 'mail.message subject on Bird incorrect') + self.assertEqual(message_pigs.body, _body_html1, 'mail.message body on Pigs incorrect') + self.assertEqual(message_bird.body, _body_html2, 'mail.message body on Bird incorrect') + # Test: partner_ids: p_a_id (default) + 3 newly created partners + message_pigs_pids = [partner.id for partner in message_pigs.partner_ids] + message_bird_pids = [partner.id for partner in message_bird.partner_ids] + partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])]) + [p_a_id] + self.assertEqual(len(message_pigs_pids), len(partner_ids), 'mail.message on pigs incorrect number of partner_ids') + self.assertEqual(len(message_bird_pids), len(partner_ids), 'mail.message on bird partner_ids incorrect') + self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of partner_ids') + self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird partner_ids incorrect') diff --git a/addons/email_template/wizard/email_template_preview.py b/addons/email_template/wizard/email_template_preview.py index 4ef0e9fa156..862c34cc426 100644 --- a/addons/email_template/wizard/email_template_preview.py +++ b/addons/email_template/wizard/email_template_preview.py @@ -26,7 +26,6 @@ class email_template_preview(osv.osv_memory): _inherit = "email.template" _name = "email_template.preview" _description = "Email Template Preview" - _rec_name = "subject" def _get_records(self, cr, uid, context=None): """ @@ -66,30 +65,19 @@ class email_template_preview(osv.osv_memory): return result _columns = { - 'res_id':fields.selection(_get_records, 'Sample Document'), + 'res_id': fields.selection(_get_records, 'Sample Document'), } def on_change_res_id(self, cr, uid, ids, res_id, context=None): - if not res_id: - return {} + if not res_id: return {} vals = {} email_template = self.pool.get('email.template') template_id = context and context.get('template_id') - template = email_template.get_email_template(cr, uid, template_id=template_id, record_id=res_id, context=context) - model = template.model - vals['email_to'] = self.render_template(cr, uid, template.email_to, model, res_id, context) - vals['email_cc'] = self.render_template(cr, uid, template.email_cc, model, res_id, context) - vals['email_bcc'] = self.render_template(cr, uid, template.email_bcc, model, res_id, context) - vals['reply_to'] = self.render_template(cr, uid, template.reply_to, model, res_id, context) - vals['subject'] = self.render_template(cr, uid, template.subject, model, res_id, context) - description = self.render_template(cr, uid, template.body_text, model, res_id, context) or '' - if template.user_signature: - signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature - description += '\n' + signature - vals['body_text'] = description - if template.body_html: - vals['body_html'] = self.render_template(cr, uid, template.body_html, model, res_id, context) or '' - vals['report_name'] = self.render_template(cr, uid, template.report_name, model, res_id, context) + template = email_template.browse(cr, uid, template_id, context=context) + vals['name'] = template.name + mail_values = email_template.generate_email(cr, uid, template_id, res_id, context=context) + for k in ('email_from','email_to','email_cc','reply_to','subject','body_html'): + vals[k] = mail_values[k] return {'value': vals} # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/email_template/wizard/email_template_preview_view.xml b/addons/email_template/wizard/email_template_preview_view.xml index 39fb413fcde..5e8eaa3a836 100644 --- a/addons/email_template/wizard/email_template_preview_view.xml +++ b/addons/email_template/wizard/email_template_preview_view.xml @@ -6,27 +6,22 @@ <field name="name">email_template.preview.form</field> <field name="model">email_template.preview</field> <field name="arch" type="xml"> - <form string="Email Preview"> - <field name="model_id" invisible="1"/> - <field name="res_id" on_change="on_change_res_id(res_id, context)"/> - <group col="2" colspan="4"> - <field name="email_to" readonly="1"/> - <field name="email_cc" readonly="1" attrs="{'invisible': [('email_cc','=',False)]}"/> - <field name="email_bcc" readonly="1" attrs="{'invisible': [('email_bcc','=',False)]}"/> - <field name="reply_to" readonly="1" attrs="{'invisible': [('reply_to','=',False)]}"/> - <field name="subject" readonly="1"/> + <form string="Email Preview" version="7.0"> + <div> + <h2>Preview of <field name="name" readonly="1" class="oe_inline"/></h2> + <field name="model_id" invisible="1"/> + <h3>Using sample document + <field name="res_id" on_change="on_change_res_id(res_id, context)" class="oe_inline"/> + </h3> + </div> + <group> + <field name="email_from" readonly="1"/> + <field name="email_to" readonly="1"/> + <field name="email_cc" readonly="1" attrs="{'invisible':[('email_cc','=',False)]}"/> + <field name="reply_to" readonly="1" attrs="{'invisible':[('reply_to','=',False)]}"/> + <field name="subject" readonly="1"/> + <field name="body_html" widget="html" readonly="1" options='{"safe": true}'/> </group> - <group col="4" colspan="4"> - <notebook> - <page string="Body (Text)"> - <field name="body_text" nolabel="1" colspan="4" height="350" width="350" readonly="1"/> - </page> - <page string="Body (Rich/HTML)" attrs="{'invisible': [('body_html','=', False)]}"> - <field name="body_html" nolabel="1" colspan="4" height="350" width="350" readonly="1"/> - </page> - </notebook> - </group> - <field name="report_name" colspan="4" readonly="1"/> </form> </field> </record> diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index 1b007a990c0..c0cb7fc92e5 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -19,138 +19,157 @@ # ############################################################################## -import base64 +import tools from osv import osv from osv import fields -from tools.translate import _ -import tools - class mail_compose_message(osv.osv_memory): _inherit = 'mail.compose.message' def _get_templates(self, cr, uid, context=None): - """ - Return Email Template of particular Model. - """ if context is None: context = {} - record_ids = [] - email_template= self.pool.get('email.template') model = False - if context.get('message_id'): - mail_message = self.pool.get('mail.message') - message_data = mail_message.browse(cr, uid, int(context.get('message_id')), context) - model = message_data.model - elif context.get('mail.compose.target.model') or context.get('active_model'): - model = context.get('mail.compose.target.model', context.get('active_model')) + email_template_obj = self.pool.get('email.template') + message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id'))) + + if context.get('default_composition_mode') == 'reply' and message_id: + message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context) + if message_data: + model = message_data.model + else: + model = context.get('default_model', context.get('active_model')) + if model: - record_ids = email_template.search(cr, uid, [('model', '=', model)]) - return email_template.name_get(cr, uid, record_ids, context) + [(False,'')] + record_ids = email_template_obj.search(cr, uid, [('model', '=', model)], context=context) + return email_template_obj.name_get(cr, uid, record_ids, context) + [(False, '')] return [] + def default_get(self, cr, uid, fields, context=None): + if context is None: + context = {} + result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + result['template_id'] = context.get('default_template_id', context.get('mail.compose.template_id', False)) + # pre-render the template if any + if result.get('use_template'): + onchange_res = self.onchange_use_template(cr, uid, [], result.get('use_template'), result.get('template_id'), + result.get('composition_mode'), result.get('model'), result.get('res_id'), context=context) + result.update(onchange_res['value']) + return result + _columns = { 'use_template': fields.boolean('Use Template'), - 'template_id': fields.selection(_get_templates, 'Template', - size=-1 # means we want an int db column - ), - } - - _defaults = { - 'template_id' : lambda self, cr, uid, context={} : context.get('mail.compose.template_id', False) + # incredible hack of the day: size=-1 means we want an int db column instead of an str one + 'template_id': fields.selection(_get_templates, 'Template', size=-1), } - def on_change_template(self, cr, uid, ids, use_template, template_id, email_from=None, email_to=None, context=None): - if context is None: - context = {} - values = {} - if template_id: - res_id = context.get('active_id', False) - if context.get('mail.compose.message.mode') == 'mass_mail': - # use the original template values - to be rendered when actually sent - # by super.send_mail() - values = self.pool.get('email.template').read(cr, uid, template_id, self.fields_get_keys(cr, uid), context) - else: - # render the mail as one-shot - values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) - # get partner_ids back - values['dest_partner_ids'] = values['partner_ids'] - # retrofit generated attachments in the expected field format - if values['attachments']: - attachment = values.pop('attachments') - attachment_obj = self.pool.get('ir.attachment') - att_ids = [] - for fname, fcontent in attachment.iteritems(): - data_attach = { - 'name': fname, - 'datas': fcontent, - 'datas_fname': fname, - 'description': fname, - 'res_model' : self._name, - 'res_id' : ids[0] if ids else False - } - att_ids.append(attachment_obj.create(cr, uid, data_attach)) - values['attachment_ids'] = att_ids + def onchange_template_id(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None): + """ - use_template not set: return default_get + - use_template set in mass_mailing: we cannot render, so return the template values + - use_template set: return rendered values """ + if use_template and template_id and composition_mode == 'mass_mail': + values = self.pool.get('email.template').read(cr, uid, template_id, ['subject', 'body_html'], context) + values.pop('id') + elif use_template and template_id: + values = self.generate_email_for_composer(cr, uid, template_id, res_id, context=context) + # transform attachments into attachment_ids + values['attachment_ids'] = [] + ir_attach_obj = self.pool.get('ir.attachment') + for attach_fname, attach_datas in values.pop('attachments', []): + data_attach = { + 'name': attach_fname, + 'datas': attach_datas, + 'datas_fname': attach_fname, + 'res_model': model, + 'res_id': res_id, + } + values['attachment_ids'].append(ir_attach_obj.create(cr, uid, data_attach, context=context)) else: - # restore defaults - values = self.default_get(cr, uid, self.fields_get_keys(cr, uid), context) - values.update(use_template=use_template, template_id=template_id) + values = self.default_get(cr, uid, ['body', 'body_html', 'subject', 'partner_ids', 'attachment_ids'], context=context) + if values.get('body_html'): + values['body'] = values.pop('body_html') + values.update(use_template=use_template, template_id=template_id) return {'value': values} - def template_toggle(self, cr, uid, ids, context=None): + def toggle_template(self, cr, uid, ids, context=None): + """ hit toggle template mode button: calls onchange_use_template to + emulate an on_change, then writes the values to update the form. """ for record in self.browse(cr, uid, ids, context=context): - values = {} - use_template = record.use_template - # simulate an on_change on use_template - values.update(self.onchange_use_template(cr, uid, ids, not use_template, context=context)['value']) - record.write(values) - return False - - def onchange_use_template(self, cr, uid, ids, use_template, context=None): - values = {'use_template': use_template} - for record in self.browse(cr, uid, ids, context=context): - if not use_template: - # equivalent to choosing an empty template - onchange_template_values = self.on_change_template(cr, uid, record.id, use_template, - False, email_from=record.email_from, email_to=record.email_to, context=context) - values.update(onchange_template_values['value']) - return {'value': values} + onchange_res = self.onchange_use_template(cr, uid, ids, not record.use_template, + record.template_id, record.composition_mode, record.model, record.res_id, context=context).get('value', {}) + # update partner_ids and attachment_ids + onchange_res['partner_ids'] = [(4, partner_id) for partner_id in onchange_res.pop('partner_ids', [])] + onchange_res['attachment_ids'] = [(4, attachment_id) for attachment_id in onchange_res.pop('attachment_ids', [])] + record.write(onchange_res) + return True + + def onchange_use_template(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None): + """ onchange_use_template (values: True or False). If use_template is + False, we do as an onchange with template_id False for values """ + values = self.onchange_template_id(cr, uid, ids, use_template, + template_id, composition_mode, model, res_id, context=context) + # force html when using templates + if use_template: + values['value']['content_subtype'] = 'html' + return values def save_as_template(self, cr, uid, ids, context=None): - if context is None: - context = {} + """ hit save as template button: current form value will be a new + template attached to the current document. """ email_template = self.pool.get('email.template') - model_pool = self.pool.get('ir.model') + ir_model_pool = self.pool.get('ir.model') for record in self.browse(cr, uid, ids, context=context): - model = record.model or context.get('active_model') - model_ids = model_pool.search(cr, uid, [('model', '=', model)]) + model_ids = ir_model_pool.search(cr, uid, [('model', '=', record.model)], context=context) model_id = model_ids and model_ids[0] or False model_name = '' if model_id: - model_name = model_pool.browse(cr, uid, model_id, context=context).name + model_name = ir_model_pool.browse(cr, uid, model_id, context=context).name template_name = "%s: %s" % (model_name, tools.ustr(record.subject)) values = { 'name': template_name, - 'email_from': record.email_from or False, 'subject': record.subject or False, - 'body_text': record.body_text or False, - 'email_to': record.email_to or False, - 'email_cc': record.email_cc or False, - 'email_bcc': record.email_bcc or False, - 'reply_to': record.reply_to or False, + 'body_html': record.body or False, 'model_id': model_id or False, 'attachment_ids': [(6, 0, [att.id for att in record.attachment_ids])] } template_id = email_template.create(cr, uid, values, context=context) - record.write({'template_id': template_id, - 'use_template': True}) + record.write({'template_id': template_id, 'use_template': True}) + return True + + #------------------------------------------------------ + # Wizard validation and send + #------------------------------------------------------ - # _reopen same wizard screen with new template preselected - return False + def generate_email_for_composer(self, cr, uid, template_id, res_id, context=None): + """ Call email_template.generate_email(), get fields relevant for + mail.compose.message, transform email_cc and email_to into partner_ids """ + template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context) + # filter template values + fields = ['body', 'body_html', 'subject', 'email_to', 'email_cc', 'attachments'] + values = dict((field, template_values[field]) for field in fields if template_values.get(field)) + values['body'] = values.pop('body_html', '') + # transform email_to, email_cc into partner_ids + values['partner_ids'] = [] + mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', '')) + for mail in mails: + partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context) + values['partner_ids'].append(partner_id) + return values + + def render_message(self, cr, uid, wizard, res_id, context=None): + """ Generate an email from the template for given (model, res_id) pair. + This method is meant to be inherited by email_template that will + produce a more complete dictionary, with email_to, ... + """ + # generate the composer email + values = self.generate_email_for_composer(cr, uid, wizard.template_id, res_id, context=context) + # get values to return + email_dict = super(mail_compose_message, self).render_message(cr, uid, wizard, res_id, context) + email_dict.update(values) + return email_dict - # override the basic implementation def render_template(self, cr, uid, template, model, res_id, context=None): return self.pool.get('email.template').render_template(cr, uid, template, model, res_id, context=context) diff --git a/addons/email_template/wizard/mail_compose_message_view.xml b/addons/email_template/wizard/mail_compose_message_view.xml index 0d4362d350e..8d5bc26b213 100644 --- a/addons/email_template/wizard/mail_compose_message_view.xml +++ b/addons/email_template/wizard/mail_compose_message_view.xml @@ -10,15 +10,16 @@ <data> <xpath expr="//form/footer/button" position="before"> <field name="use_template" invisible="1"/> - <button icon="gtk-paste" type="object" name="template_toggle" - string="" help="Use a message template" /> + <button icon="gtk-paste" type="object" name="toggle_template" + string="" help="Use a message template" invisible="1"/> <button icon="gtk-save" type="object" name="save_as_template" - string="" help="Save as a new template"/> + string="" help="Save as a new template" invisible="1"/> </xpath> <xpath expr="//form/notebook" position="after"> <group attrs="{'invisible':[('use_template','=',False)]}" colspan="4" col="4"> - <field name="template_id" colspan="3" - on_change="on_change_template(use_template, template_id, email_from, email_to, context)"/> + <field name="use_template" invisible="1"/> + <field name="template_id" colspan="3" invisible="1" + on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/> </group> </xpath> </data> @@ -31,20 +32,22 @@ <field name="inherit_id" ref="mail.email_compose_message_wizard_form_chatter"/> <field name="arch" type="xml"> <data> - <xpath expr="//field[@name='dest_partner_ids']" position="after"> + <xpath expr="//field[@name='partner_ids']" position="after"> <field name="use_template" colspan="2" nolabel="1" invisible="1" - on_change="onchange_use_template(use_template, context)"/> + on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/> <field name="template_id" colspan="2" nolabel="1" attrs="{'invisible':[('use_template','=',False)]}" - on_change="on_change_template(use_template, template_id, False, False, context)"/> + on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/> </xpath> - <xpath expr="//a[@class='oe_mail_compose_message_checklist']" position="before"> + <xpath expr="//button[@class='oe_mail_compose_message_attachment']" position="before"> <button icon="/email_template/static/src/img/email_template.png" - type="object" name="template_toggle" string="" - help="Use a message template"/> + type="object" name="toggle_template" string="" + help="Use a message template" + attrs="{'invisible':[('content_subtype','!=','html')]}"/> <button icon="/email_template/static/src/img/email_template_save.png" type="object" name="save_as_template" string="" - help="Save as a new template"/> + help="Save as a new template" + attrs="{'invisible':[('content_subtype','!=','html')]}"/> </xpath> </data> </field> diff --git a/addons/event/board_association_view.xml b/addons/event/board_association_view.xml index b38a78afac9..4f4fbd05109 100644 --- a/addons/event/board_association_view.xml +++ b/addons/event/board_association_view.xml @@ -50,7 +50,6 @@ <board style="2-1"> <column> <action string="Next Events" name="%(act_event_view)d"/> - <action string="New Registrations" name="%(act_event_view_registration)d"/> </column> <column> <action string="Events Filling By Status" name="%(act_event_reg)d"/> diff --git a/addons/event/email_template.xml b/addons/event/email_template.xml index 2666ece9921..95ffbf46d8d 100644 --- a/addons/event/email_template.xml +++ b/addons/event/email_template.xml @@ -7,15 +7,12 @@ <field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field> <field name="email_to" >${object.email}</field> <field name="subject">Your registration at ${object.event_id.name}</field> - <field name="body_text"> - hello ${object.name}, - - The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin} to ${object.event_id.date_end}. For any further information please contact our event department. - - we thank you for your participation - - best regards - </field> + <field name="body_html"><![CDATA[ + <p>Hello ${object.name},</p> + <p>The event ${object.event_id.name} that you registered for is confirmed and will be held from ${object.event_id.date_begin} to ${object.event_id.date_end}. + For any further information please contact our event department.</p> + <p>Thank you for your participation!</p> + <p>Best regards</p>]]></field> </record> </data> @@ -27,15 +24,12 @@ <field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field> <field name="email_to" >${object.email}</field> <field name="subject">Your registration at ${object.event_id.name}</field> - <field name="body_text"> - hello ${object.name}, - - We confirm you that your registration to the event ${object.event_id.name} has been recorded. You will automatically receive an email providing you more practical information (such as the schedule, the plan...) as soon as the event will be confirmed to be held. - - we thank you for your participation - - best regards - </field> + <field name="body_html"><![CDATA[ + <p>Hello ${object.name},</p> + <p>We confirm that your registration to the event ${object.event_id.name} has been recorded. + You will automatically receive an email providing you more practical information (such as the schedule, the agenda...) as soon as the event is confirmed.</p> + <p>Thank you for your participation!</p> + <p>Best regards</p>]]></field> </record> <!-- Default Values --> diff --git a/addons/event/event.py b/addons/event/event.py index 7f0c77661e4..747981f854b 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -19,10 +19,8 @@ # ############################################################################## -import time from osv import fields, osv from tools.translate import _ -import decimal_precision as dp from openerp import SUPERUSER_ID class event_type(osv.osv): @@ -39,8 +37,8 @@ class event_type(osv.osv): } _defaults = { 'default_registration_min': 0, - 'default_registration_max':0, - } + 'default_registration_max': 0, + } event_type() @@ -49,11 +47,15 @@ class event_event(osv.osv): _name = 'event.event' _description = __doc__ _order = 'date_begin' - _inherit = ['ir.needaction_mixin','mail.thread'] + _inherit = ['mail.thread','ir.needaction_mixin'] def name_get(self, cr, uid, ids, context=None): if not ids: - return [] + return [] + + if isinstance(ids, (long, int)): + ids = [ids] + res = [] for record in self.browse(cr, uid, ids, context=context): date = record.date_begin.split(" ")[0] @@ -99,7 +101,6 @@ class event_event(osv.osv): return self.write(cr, uid, ids, {'state': 'done'}, context=context) def check_registration_limits(self, cr, uid, ids, context=None): - register_pool = self.pool.get('event.registration') for self.event in self.browse(cr, uid, ids, context=context): total_confirmed = self.event.register_current if total_confirmed < self.event.register_min or total_confirmed > self.event.register_max and self.event.register_max!=0: @@ -109,7 +110,7 @@ class event_event(osv.osv): for event in self.browse(cr, uid, ids, context=context): available_seats = event.register_avail if available_seats and no_of_registration > available_seats: - raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats)) + raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats)) elif available_seats == 0: raise osv.except_osv(_('Warning!'),_("No Tickets Available!")) @@ -139,7 +140,6 @@ class event_event(osv.osv): @param context: A standard dictionary for contextual values @return: Dictionary of function fields value. """ - register_pool = self.pool.get('event.registration') res = {} for event in self.browse(cr, uid, ids, context=context): res[event.id] = {} @@ -262,33 +262,52 @@ class event_event(osv.osv): } return {'value': dic} + def on_change_address_id(self, cr, uid, ids, address_id, context=None): + values = { + 'street' : False, + 'city' : False, + 'zip' : False, + } + if isinstance(address_id, (long, int)): + address = self.pool.get('res.partner').browse(cr, uid, address_id, context=context) + + values.update({ + 'street' : address.street, + + 'city' : address.city, + 'zip' : address.zip, + }) + + return {'value' : values} + + # ---------------------------------------- # OpenChatter methods and notifications # ---------------------------------------- def create_send_note(self, cr, uid, ids, context=None): message = _("Event has been <b>created</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def button_cancel_send_note(self, cr, uid, ids, context=None): message = _("Event has been <b>cancelled</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def button_draft_send_note(self, cr, uid, ids, context=None): message = _("Event has been set to <b>draft</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def button_done_send_note(self, cr, uid, ids, context=None): message = _("Event has been <b>done</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def button_confirm_send_note(self, cr, uid, ids, context=None): message = _("Event has been <b>confirmed</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True event_event() @@ -334,7 +353,7 @@ class event_registration(osv.osv): return self.write(cr, uid, ids, {'state': 'draft'}, context=context) def confirm_registration(self, cr, uid, ids, context=None): - self.message_append(cr, uid, ids,_('State set to open'),body_text= _('Open')) + self.message_post(cr, uid, ids, body=_('State set to open'), context=context) return self.write(cr, uid, ids, {'state': 'open'}, context=context) def create(self, cr, uid, vals, context=None): @@ -364,13 +383,13 @@ class event_registration(osv.osv): if today >= registration.event_id.date_begin: values = {'state': 'done', 'date_closed': today} self.write(cr, uid, ids, values) - self.message_append(cr, uid, ids, _('State set to Done'), body_text=_('Done')) + self.message_post(cr, uid, ids, body=_('State set to Done'), context=context) else: raise osv.except_osv(_('Error!'),_("You must wait for the starting day of the event to do this action.") ) return True def button_reg_cancel(self, cr, uid, ids, context=None, *args): - self.message_append(cr, uid, ids,_('State set to Cancel'),body_text= _('Cancel')) + self.message_post(cr, uid, ids, body=_('State set to Cancel'), context=context) return self.write(cr, uid, ids, {'state': 'cancel'}) def mail_user(self, cr, uid, ids, context=None): @@ -440,12 +459,12 @@ class event_registration(osv.osv): def create_send_note(self, cr, uid, ids, context=None): message = _("Registration has been <b>created</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def do_draft_send_note(self, cr, uid, ids, context=None): message = _("Registration has been set as <b>draft</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True event_registration() diff --git a/addons/event/event_data.xml b/addons/event/event_data.xml index d8d75950bcc..2c19fdc574f 100644 --- a/addons/event/event_data.xml +++ b/addons/event/event_data.xml @@ -14,11 +14,13 @@ </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Events Organisation has been installed</value> - <value>From the top menu Events, you can organize events, manage registrations, automate communication around your event and sell events through your quotations.</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Events Organisation application installed!</field> + <field name="body">From the top Events menu, you can organize events, manage registrations, automate communication around your event and sell events through your quotations.</field> + </record> + </data> </openerp> diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index dce96e4c3cf..e2ff2e67e87 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -124,7 +124,7 @@ <group> <label for="address_id" string="Location Address"/> <div> - <field name="address_id" widget="many2one_address_google_map" widget_option="{'placeholder':'.oe_google_map'}"/> + <field name="address_id" widget="many2one_address_google_map" widget_option="{'placeholder':'.oe_google_map'}" on_change="on_change_address_id(address_id)" /> <field name="street" placeholder="Street..."/> <div> <field name="zip" class="oe_inline" placeholder="Zip"/> @@ -143,7 +143,7 @@ </group> </group> </div> - <!-- <div class="oe_right" style="height: 200px"></div> --> + <div class="oe_google_map" style="height: 200px"></div> <notebook> <page string="Event Description"> <field name="note" colspan="4" nolabel="1"/> @@ -208,7 +208,7 @@ <field name="name">event.event.tree</field> <field name="model">event.event</field> <field name="arch" type="xml"> - <tree string="Events" fonts="bold:needaction_pending==True" colors="red:(register_min and register_min>register_current) or (register_max and register_max<register_current);grey:state=='cancel'"> + <tree string="Events" fonts="bold:message_unread==True" colors="red:(register_min and register_min>register_current) or (register_max and register_max<register_current);grey:state=='cancel'"> <field name="name" string="Name"/> <field name="type"/> <field name="date_begin"/> @@ -219,7 +219,7 @@ <field name="main_speaker_id" groups="base.extended"/> <field name="user_id"/> <field name="state"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> </tree> </field> </record> @@ -326,7 +326,7 @@ <field name="arch" type="xml"> <search string="Events"> <field name="name" string="Events"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-check" string="Unconfirmed" name="draft" domain="[('state','=','draft')]" help="Events in New state"/> <filter icon="terp-camera_test" string="Confirmed" domain="[('state','=','confirm')]" help="Confirmed events"/> @@ -421,7 +421,7 @@ <field name="name">event.registration.tree</field> <field name="model">event.registration</field> <field name="arch" type="xml"> - <tree string="Registration" fonts="bold:needaction_pending==True"> + <tree string="Registration" fonts="bold:message_unread==True"> <field name="create_date"/> <field name="partner_id"/> <field name="name"/> @@ -431,7 +431,7 @@ <field name="user_id"/> <field name="origin"/> <field name="state"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> </tree> </field> </record> @@ -516,7 +516,7 @@ <field name="arch" type="xml"> <search string="Event Registration"> <field name="name" string="Participant" filter_domain="['|','|','|',('name','ilike',self),('partner_id','ilike',self),('email','ilike',self),('origin','ilike',self)]"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-check" string="New" name="draft" domain="[('state','=','draft')]" help="Registrations in unconfirmed state"/> <filter icon="terp-camera_test" string="Confirmed" domain="[('state','=','open')]" help="Confirmed registrations"/> diff --git a/addons/event_project/i18n/nb.po b/addons/event_project/i18n/nb.po new file mode 100644 index 00000000000..ea1485578a7 --- /dev/null +++ b/addons/event_project/i18n/nb.po @@ -0,0 +1,114 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 17:58+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: event_project +#: model:ir.model,name:event_project.model_event_project +msgid "Event Project" +msgstr "Arrangement Prosjekt" + +#. module: event_project +#: field:event.project,date:0 +msgid "Date End" +msgstr "Sluttdato" + +#. module: event_project +#: view:event.project:0 +msgid "Ok" +msgstr "Ok" + +#. module: event_project +#: help:event.project,project_id:0 +msgid "" +"This is Template Project. Project of event is a duplicate of this Template. " +"After click on 'Create Retro-planning', New Project will be duplicated from " +"this template project." +msgstr "" +"Dette er ett mal Prosjekt. Prosjektet av arrangementet er en dublett av " +"denne malen. Etter klikk på \"Create Retro-planlegging\", vil New Project " +"dupliseres fra denne malen prosjektet." + +#. module: event_project +#: view:event.project:0 +#: model:ir.actions.act_window,name:event_project.action_event_project +msgid "Retro-Planning" +msgstr "Retro-Planlegging" + +#. module: event_project +#: constraint:event.event:0 +msgid "Error ! Closing Date cannot be set before Beginning Date." +msgstr "Feil ! Sluttdato kan ikke settes før startdato." + +#. module: event_project +#: field:event.event,project_id:0 +msgid "Project" +msgstr "Prosjekt" + +#. module: event_project +#: field:event.project,project_id:0 +msgid "Template of Project" +msgstr "Mal av prosjekt" + +#. module: event_project +#: view:event.event:0 +msgid "All tasks" +msgstr "Alle oppgaver" + +#. module: event_project +#: view:event.event:0 +#: model:ir.actions.act_window,name:event_project.act_event_task +msgid "Tasks" +msgstr "Oppgaver" + +#. module: event_project +#: constraint:event.event:0 +msgid "Error ! You cannot create recursive event." +msgstr "Feil! Du kan ikke opprette rekursive hendelse." + +#. module: event_project +#: field:event.event,task_ids:0 +msgid "Project tasks" +msgstr "Prosjektoppgaver" + +#. module: event_project +#: view:event.project:0 +msgid "Close" +msgstr "Lukke" + +#. module: event_project +#: field:event.project,date_start:0 +msgid "Date Start" +msgstr "Startdato" + +#. module: event_project +#: view:event.event:0 +msgid "Create Retro-Planning" +msgstr "Opprett Retro - planlegging." + +#. module: event_project +#: model:ir.model,name:event_project.model_event_event +msgid "Event" +msgstr "Arrangement" + +#. module: event_project +#: view:event.event:0 +msgid "Tasks Management" +msgstr "" + +#~ msgid "Tasks management" +#~ msgstr "Oppgaver ledelse" diff --git a/addons/event_sale/event_sale.py b/addons/event_sale/event_sale.py index 681116d064e..1d7a30ee2ee 100644 --- a/addons/event_sale/event_sale.py +++ b/addons/event_sale/event_sale.py @@ -88,5 +88,5 @@ class sale_order_line(osv.osv): } registration_id = registration_obj.create(cr, uid, dic, context=context) message = _("The registration %s has been created from the Sale Order %s.") % (registration_id, order_line.order_id.name) - registration_obj.message_append_note(cr, uid, [registration_id], body=message, context=context) + registration_obj.message_post(cr, uid, [registration_id], body=message, context=context) return super(sale_order_line, self).button_confirm(cr, uid, ids, context=context) diff --git a/addons/event_sale/event_sale_view.xml b/addons/event_sale/event_sale_view.xml index 53e8a4cc18f..c39cff04bd9 100644 --- a/addons/event_sale/event_sale_view.xml +++ b/addons/event_sale/event_sale_view.xml @@ -8,8 +8,9 @@ <field name="arch" type="xml"> <field name="purchase_ok" position="after"> <field name="event_ok" on_change="onchange_event_ok(event_ok, context)"/> + <label for="event_ok"/> </field> - <field name='procure_method' position='before'> + <field name='default_code' position='before'> <field name="event_type_id" attrs="{'readonly': [('event_ok', '=', False)]}"/> </field> </field> diff --git a/addons/fetchmail/fetchmail.py b/addons/fetchmail/fetchmail.py index 544a4f81b96..70a05c6bd0e 100644 --- a/addons/fetchmail/fetchmail.py +++ b/addons/fetchmail/fetchmail.py @@ -77,7 +77,7 @@ class fetchmail_server(osv.osv): "emails to the existing conversations (documents)."), 'priority': fields.integer('Server Priority', readonly=True, states={'draft':[('readonly', False)]}, help="Defines the order of processing, " "lower values mean higher priority"), - 'message_ids': fields.one2many('mail.message', 'fetchmail_server_id', 'Messages', readonly=True), + 'message_ids': fields.one2many('mail.mail', 'fetchmail_server_id', 'Messages', readonly=True), 'configuration' : fields.text('Configuration'), 'script' : fields.char('Script', readonly=True, size=64), } @@ -232,8 +232,8 @@ openerp_mailgate.py -u %(uid)d -p PASSWORD -o %(model)s -d %(dbname)s --host=HOS server.write({'date': time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)}) return True -class mail_message(osv.osv): - _inherit = "mail.message" +class mail_mail(osv.osv): + _inherit = "mail.mail" _columns = { 'fetchmail_server_id': fields.many2one('fetchmail.server', "Inbound Mail Server", readonly=True, @@ -247,7 +247,7 @@ class mail_message(osv.osv): fetchmail_server_id = context.get('fetchmail_server_id') if fetchmail_server_id: values['fetchmail_server_id'] = fetchmail_server_id - res = super(mail_message,self).create(cr, uid, values, context=context) + res = super(mail_mail,self).create(cr, uid, values, context=context) return res def write(self, cr, uid, ids, values, context=None): @@ -256,7 +256,7 @@ class mail_message(osv.osv): fetchmail_server_id = context.get('fetchmail_server_id') if fetchmail_server_id: values['fetchmail_server_id'] = server_id - res = super(mail_message,self).write(cr, uid, ids, values, context=context) + res = super(mail_mail,self).write(cr, uid, ids, values, context=context) return res diff --git a/addons/fetchmail/fetchmail_view.xml b/addons/fetchmail/fetchmail_view.xml index cf651b5fd3d..3cdc886ba2b 100644 --- a/addons/fetchmail/fetchmail_view.xml +++ b/addons/fetchmail/fetchmail_view.xml @@ -104,27 +104,16 @@ /> <record model="ir.ui.view" id="email_message_tree_view"> - <field name="name">mail.message.list.fetchmail</field> - <field name="model">mail.message</field> - <field name="inherit_id" ref="mail.view_email_message_tree"/> + <field name="name">mail.mail.form.fetchmail</field> + <field name="model">mail.mail</field> + <field name="inherit_id" ref="mail.view_mail_form"/> <field name="arch" type="xml"> - <field name="user_id" position="after"> + <field name="references" position="after"> <field name="fetchmail_server_id"/> </field> </field> </record> - <record model="ir.ui.view" id="email_message_search_view"> - <field name="name">mail.message.inherit.search</field> - <field name="model">mail.message</field> - <field name="inherit_id" ref="mail.view_email_message_search"/> - <field name="arch" type="xml"> - <xpath expr="/search/group/filter[@string='Thread']" position="before"> - <filter string="Mail Server" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'fetchmail_server_id'}"/> - </xpath> - </field> - </record> - <act_window context="{'search_default_server_id': active_id, 'default_fetchmail_server_id': active_id}" id="act_server_history" name="Messages" domain="[('email_from', '!=', False), ('fetchmail_server_id', '=', active_id)]" diff --git a/addons/fetchmail/i18n/nb.po b/addons/fetchmail/i18n/nb.po new file mode 100644 index 00000000000..b5a6c409b79 --- /dev/null +++ b/addons/fetchmail/i18n/nb.po @@ -0,0 +1,335 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-08 18:59+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:52+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: fetchmail +#: selection:fetchmail.server,state:0 +msgid "Confirmed" +msgstr "Bekreftet" + +#. module: fetchmail +#: field:fetchmail.server,server:0 +msgid "Server Name" +msgstr "Tjener navn" + +#. module: fetchmail +#: field:fetchmail.server,script:0 +msgid "Script" +msgstr "Manus" + +#. module: fetchmail +#: help:fetchmail.server,priority:0 +msgid "Defines the order of processing, lower values mean higher priority" +msgstr "" +"Definerer rekkefølgen av behandlingen, lavere verdier betyr høyere prioritet." + +#. module: fetchmail +#: help:fetchmail.server,is_ssl:0 +msgid "" +"Connections are encrypted with SSL/TLS through a dedicated port (default: " +"IMAPS=993, POP3S=995)" +msgstr "" +"Tilkoblinger er kryptert med SSL / TLS gjennom en egen port (standard: IMAPS " +"= 993, POP3S = 995)" + +#. module: fetchmail +#: field:fetchmail.server,attach:0 +msgid "Keep Attachments" +msgstr "Hold Vedlegg" + +#. module: fetchmail +#: help:fetchmail.server,original:0 +msgid "" +"Whether a full original copy of each email should be kept for referenceand " +"attached to each processed message. This will usually double the size of " +"your message database." +msgstr "" +"Om en full original kopi av hver e-post bør holdes for referenceand festet " +"til hver bearbeidet melding. Dette vil vanligvis doble størrelsen på " +"meldingen database." + +#. module: fetchmail +#: field:fetchmail.server,priority:0 +msgid "Server Priority" +msgstr "Tjener prioritet." + +#. module: fetchmail +#: field:fetchmail.server,state:0 +msgid "State" +msgstr "Stat" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "POP" +msgstr "POP" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Fetch Now" +msgstr "Hente nå." + +#. module: fetchmail +#: model:ir.actions.act_window,name:fetchmail.action_email_server_tree +#: model:ir.ui.menu,name:fetchmail.menu_action_fetchmail_server_tree +msgid "Incoming Mail Servers" +msgstr "Innkommende Mail Tjenere." + +#. module: fetchmail +#: field:fetchmail.server,port:0 +msgid "Port" +msgstr "Port" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "POP/IMAP Servers" +msgstr "POP/IMAP tjenere." + +#. module: fetchmail +#: selection:fetchmail.server,type:0 +msgid "Local Server" +msgstr "Lokal tjener." + +#. module: fetchmail +#: field:fetchmail.server,user:0 +msgid "Username" +msgstr "Brukernavn:" + +#. module: fetchmail +#: model:ir.model,name:fetchmail.model_fetchmail_server +msgid "POP/IMAP Server" +msgstr "POP/IMAP Tjener." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Reset Confirmation" +msgstr "Tilbakestille Bekreftelse." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "SSL" +msgstr "SSL" + +#. module: fetchmail +#: model:ir.model,name:fetchmail.model_mail_message +msgid "Email Message" +msgstr "E-post Melding." + +#. module: fetchmail +#: field:fetchmail.server,date:0 +msgid "Last Fetch Date" +msgstr "Siste Hente Dato." + +#. module: fetchmail +#: help:fetchmail.server,action_id:0 +msgid "" +"Optional custom server action to trigger for each incoming mail, on the " +"record that was created or updated by this mail" +msgstr "" +"Ekstra tilpasset server tiltak for å utløse for hver innkommende e-post, på " +"plata som ble opprettet eller oppdatert i denne posten." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "# of emails" +msgstr "# Av e-post." + +#. module: fetchmail +#: field:fetchmail.server,original:0 +msgid "Keep Original" +msgstr "Hold Original" + +#. module: fetchmail +#: code:addons/fetchmail/fetchmail.py:155 +#, python-format +msgid "" +"Here is what we got instead:\n" +" %s" +msgstr "" +"Her er hva vi fikk i stedet:\n" +" % s" + +#. module: fetchmail +#: view:fetchmail.server:0 +#: field:fetchmail.server,configuration:0 +msgid "Configuration" +msgstr "Konfigurasjon" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Incoming Mail Server" +msgstr "Innkommende E-post tjener." + +#. module: fetchmail +#: code:addons/fetchmail/fetchmail.py:155 +#, python-format +msgid "Connection test failed!" +msgstr "Tilkoblingen var mislykket!" + +#. module: fetchmail +#: help:fetchmail.server,server:0 +msgid "Hostname or IP of the mail server" +msgstr "Vertsnavn eller IP for e-postserveren." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Server type IMAP." +msgstr "Tjener type IMAP." + +#. module: fetchmail +#: field:fetchmail.server,name:0 +msgid "Name" +msgstr "Navn" + +#. module: fetchmail +#: field:fetchmail.server,is_ssl:0 +msgid "SSL/TLS" +msgstr "SSL/TLS" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Test & Confirm" +msgstr "Test & Bekreft" + +#. module: fetchmail +#: field:fetchmail.server,action_id:0 +msgid "Server Action" +msgstr "Tjenerhandling" + +#. module: fetchmail +#: field:mail.message,fetchmail_server_id:0 +msgid "Inbound Mail Server" +msgstr "Inngående E-post tjener." + +#. module: fetchmail +#: field:fetchmail.server,message_ids:0 +#: model:ir.actions.act_window,name:fetchmail.act_server_history +msgid "Messages" +msgstr "Meldinger" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Search Incoming Mail Servers" +msgstr "Søk innkommende post tjenere." + +#. module: fetchmail +#: field:fetchmail.server,active:0 +msgid "Active" +msgstr "Aktiv" + +#. module: fetchmail +#: help:fetchmail.server,attach:0 +msgid "" +"Whether attachments should be downloaded. If not enabled, incoming emails " +"will be stripped of any attachments before being processed" +msgstr "" +"Om vedlegg skal lastes ned. Hvis ikke er aktivert, vil innkommende e-post " +"blir fratatt eventuelle vedlegg før de behandles." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Advanced Options" +msgstr "" + +#. module: fetchmail +#: selection:fetchmail.server,type:0 +msgid "IMAP Server" +msgstr "IMAP-tjener" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "IMAP" +msgstr "IMAP" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Server type POP." +msgstr "Tjener type POP." + +#. module: fetchmail +#: field:fetchmail.server,password:0 +msgid "Password" +msgstr "Passord:" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Actions to Perform on Incoming Mails" +msgstr "Tiltak for å utføre på innkommende post." + +#. module: fetchmail +#: field:fetchmail.server,type:0 +msgid "Server Type" +msgstr "Tjener Type." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Login Information" +msgstr "Inn loggings informasjon." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Server Information" +msgstr "Tjenerinformasjon" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "If SSL required." +msgstr "Hvis SSL nødvendig." + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Advanced" +msgstr "Avansert" + +#. module: fetchmail +#: view:fetchmail.server:0 +msgid "Server & Login" +msgstr "Tjener og Logg inn." + +#. module: fetchmail +#: help:fetchmail.server,object_id:0 +msgid "" +"Process each incoming mail as part of a conversation corresponding to this " +"document type. This will create new documents for new conversations, or " +"attach follow-up emails to the existing conversations (documents)." +msgstr "" +"Behandle hver innkommende post som del av en samtale tilsvarende denne " +"dokumenttypen. Dette vil skape nye dokumenter for nye samtaler, eller legge " +"oppfølging e-post til de eksisterende samtaler (dokumenter)." + +#. module: fetchmail +#: field:fetchmail.server,object_id:0 +msgid "Create a New Record" +msgstr "Oppretter en ny oppføring." + +#. module: fetchmail +#: selection:fetchmail.server,state:0 +msgid "Not Confirmed" +msgstr "Ikke bekreftet." + +#. module: fetchmail +#: selection:fetchmail.server,type:0 +msgid "POP Server" +msgstr "POP tjener." + +#. module: fetchmail +#: view:mail.message:0 +msgid "Mail Server" +msgstr "E-posttjener" + +#~ msgid "Advanced options" +#~ msgstr "Avanserte alternativer" diff --git a/addons/google_base_account/i18n/nb.po b/addons/google_base_account/i18n/nb.po new file mode 100644 index 00000000000..7b14c1d29b4 --- /dev/null +++ b/addons/google_base_account/i18n/nb.po @@ -0,0 +1,122 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 19:23+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: google_base_account +#: field:res.users,gmail_user:0 +msgid "Username" +msgstr "Brukernavn:" + +#. module: google_base_account +#: model:ir.actions.act_window,name:google_base_account.act_google_login_form +msgid "Google Login" +msgstr "Google Logg inn" + +#. module: google_base_account +#: code:addons/google_base_account/wizard/google_login.py:29 +#, python-format +msgid "Google Contacts Import Error!" +msgstr "Google kontakter Import Feil!" + +#. module: google_base_account +#: view:res.users:0 +msgid " Synchronization " +msgstr " Synkronisering " + +#. module: google_base_account +#: sql_constraint:res.users:0 +msgid "You can not have two users with the same login !" +msgstr "Du kan ikke ha to brukere med samme login!" + +#. module: google_base_account +#: code:addons/google_base_account/wizard/google_login.py:75 +#, python-format +msgid "Error" +msgstr "Feil" + +#. module: google_base_account +#: view:google.login:0 +msgid "Google login" +msgstr "Google Logg Inn." + +#. module: google_base_account +#: model:ir.model,name:google_base_account.model_res_users +msgid "res.users" +msgstr "res.Brukere." + +#. module: google_base_account +#: field:google.login,password:0 +msgid "Google Password" +msgstr "Google passord." + +#. module: google_base_account +#: view:google.login:0 +msgid "_Cancel" +msgstr "_Avbryt" + +#. module: google_base_account +#: view:res.users:0 +msgid "Google Account" +msgstr "Google-konto" + +#. module: google_base_account +#: field:google.login,user:0 +msgid "Google Username" +msgstr "Google Brukernavn." + +#. module: google_base_account +#: code:addons/google_base_account/wizard/google_login.py:29 +#, python-format +msgid "" +"Please install gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" +msgstr "" +"Vennligst Installer gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" + +#. module: google_base_account +#: model:ir.model,name:google_base_account.model_google_login +msgid "Google Contact" +msgstr "Google kontakt" + +#. module: google_base_account +#: view:google.login:0 +msgid "_Login" +msgstr "_Logg inn" + +#. module: google_base_account +#: constraint:res.users:0 +msgid "The chosen company is not in the allowed companies for this user" +msgstr "" +"Det valgte firmaet er ikke i listen over tillatte firmaer for denne brukeren" + +#. module: google_base_account +#: field:res.users,gmail_password:0 +msgid "Password" +msgstr "Passord:" + +#. module: google_base_account +#: code:addons/google_base_account/wizard/google_login.py:75 +#, python-format +msgid "Authentication fail check the user and password !" +msgstr "Autentisering mislykkes sjekk brukernavn og passord!" + +#. module: google_base_account +#: view:google.login:0 +msgid "ex: user@gmail.com" +msgstr "Ex:Bruker@gmail.com" diff --git a/addons/hr/hr.py b/addons/hr/hr.py index 966c29cab07..90f967fb4b4 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -180,17 +180,16 @@ class hr_employee(osv.osv): 'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True), 'coach_id': fields.many2one('hr.employee', 'Coach'), 'job_id': fields.many2one('hr.job', 'Job'), + # image: all image fields are base64 encoded and PIL-supported 'image': fields.binary("Photo", - help="This field holds the image used as a photo for the "\ - "employee. The image is base64 encoded, and PIL-supported. "\ - "It is limited to a 1024x1024 px image."), + help="This field holds the image used as photo for the employee, limited to 1024x1024px."), 'image_medium': fields.function(_get_image, fnct_inv=_set_image, string="Medium-sized photo", type="binary", multi="_get_image", store = { 'hr.employee': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Medium-sized photo of the employee. It is automatically "\ - "resized as a 180x180 px image, with aspect ratio preserved. "\ + "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="Smal-sized photo", type="binary", multi="_get_image", @@ -198,7 +197,7 @@ class hr_employee(osv.osv): 'hr.employee': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Small-sized photo of the employee. It is automatically "\ - "resized as a 50x50 px image, with aspect ratio preserved. "\ + "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), 'passport_id':fields.char('Passport No', size=64), 'color': fields.integer('Color Index'), @@ -212,7 +211,7 @@ class hr_employee(osv.osv): try: (model, mail_group_id) = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees') employee = self.browse(cr, uid, employee_id, context=context) - self.pool.get('mail.group').message_append_note(cr, uid, [mail_group_id], body='Welcome to %s! Please help him make its first steps in OpenERP!' % (employee.name), context=context) + self.pool.get('mail.group').message_post(cr, uid, [mail_group_id], body='Welcome to %s! Please help them take the first steps with OpenERP!' % (employee.name), context=context) except: pass # group deleted: do not push a message return employee_id @@ -290,8 +289,12 @@ class hr_department(osv.osv): 'member_ids': fields.one2many('hr.employee', 'department_id', 'Members', readonly=True), } -hr_department() - + def copy(self, cr, uid, ids, default=None, context=None): + if default is None: + default = {} + default = default.copy() + default['member_ids'] = [] + return super(hr_department, self).copy(cr, uid, ids, default, context=context) class res_users(osv.osv): _name = 'res.users' diff --git a/addons/hr/hr_data.xml b/addons/hr/hr_data.xml index 18c50836f07..514cf1f80f6 100644 --- a/addons/hr/hr_data.xml +++ b/addons/hr/hr_data.xml @@ -2,14 +2,16 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Employee Directory has been installed</value> - <value>Manage your human resources in OpenERP: employees and hierarchy, HR departments and jobs. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Employee Directory application installed!</field> + <field name="body">Manage your human resources with OpenERP: employees and their hierarchy, HR departments and job positions. -More HR features are available from the following modules: Recruitment Process (manage job positions and recruitment), Timesheets Validation (record timesheets and attendance), Leaves Management (keep track of employee leaves), Expenses Management (manage employee expenses), Employee Appraisals (organize employee surveys, where employees evaluate their subordinates or their manager.)</value> - </function> +More HR features are available via extra applications: Recruitment Process (manage job positions and recruitment), Timesheet Validation (record timesheets and attendance), +Leave Management (keep track of employee leaves), Expense Management (manage employee expenses), Employee Appraisals (organize employee surveys, where employees evaluate their subordinates or their manager).</field> + </record> <record id="employee" model="hr.employee"> <field name="name">Administrator</field> diff --git a/addons/hr/hr_demo.xml b/addons/hr/hr_demo.xml index 20d3e093505..8cb5ade52fe 100644 --- a/addons/hr/hr_demo.xml +++ b/addons/hr/hr_demo.xml @@ -4,107 +4,6 @@ <!--Users--> - <record id="base.user_root" model="res.users"> - <field name="image">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</field> - </record> - - - <record id="base.user_fp" model="res.users"> - <field name="name">Fabien</field> - <field name="login">fp</field> - <field name="password">fp</field> - <field name="image">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</field> - </record> - - <record id="base.user_al" model="res.users"> - <field name="name">Antony</field> - <field name="login">al</field> - <field name="password">al</field> - <field name="image">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</field> - </record> - - <record id="base.user_mit" model="res.users"> - <field name="name">Minh</field> - <field name="login">mit</field> - <field name="password">mit</field> - <field name="image">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</field> - </record> - - <record id="base.user_niv" model="res.users"> - <field name="name">Nicolas</field> - <field name="login">niv</field> - <field name="password">niv</field> - <field name="image">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</field> - </record> - - <record id="base.user_stw" model="res.users"> - <field name="name">Stéphane</field> - <field name="login">stw</field> - <field name="password">stw</field> - <field name="image">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</field> - </record> - - <record id="base.user_chs" model="res.users"> - <field name="name">Christophe</field> - <field name="login">chs</field> - <field name="password">chs</field> - <field name="image">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</field> - </record> - - <record id="base.user_qdp" model="res.users"> - <field name="name">Quentin</field> - <field name="login">qdp</field> - <field name="password">qdp</field> - <field name="image">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</field> - </record> - - <record id="base.user_fme" model="res.users"> - <field name="name">Fabien M.</field> - <field name="login">fme</field> - <field name="password">fme</field> - </record> - - <record id="base.user_fpi" model="res.users"> - <field name="name">Francois</field> - <field name="login">fpi</field> - <field name="password">fpi</field> - <field name="image">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</field> - </record> - - <record id="base.user_jth" model="res.users"> - <field name="name">Julien</field> - <field name="login">jth</field> - <field name="password">jth</field> - <field name="image">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</field> - </record> - - <record id="base.user_ngh" model="res.users"> - <field name="name">Nicoleta</field> - <field name="login">ngh</field> - <field name="password">ngh</field> - <field name="image">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</field> - </record> - - <record id="base.user_vad" model="res.users"> - <field name="name">Valérie</field> - <field name="login">vad</field> - <field name="password">vad</field> - <field name="image">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</field> - </record> - - <record id="base.user_apr" model="res.users"> - <field name="name">Aline</field> - <field name="login">apr</field> - <field name="password">apr</field> - <field name="image">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</field> - </record> - - <record id="base.user_phu" model="res.users"> - <field name="name">Phuong</field> - <field name="login">phu</field> - <field name="password">phu</field> - </record> - <!--Department--> <record id="dep_management" model="hr.department"> @@ -193,7 +92,7 @@ <record id="employee_fp" model="hr.employee"> <field name="name">Fabien Pinckaers</field> - <field name="user_id" ref="base.user_fp"/> + <field name="user_id" ref="base.user_root"/> <field name="department_id" ref="dep_management"/> <field name="job_id" ref="hr.job_ceo"/> <field name="category_ids" eval="[(6, 0, [ref('employee_category_2')])]"/> @@ -205,7 +104,6 @@ <record id="employee_al" model="hr.employee"> <field name="name">Antony Lesuisse</field> - <field name="user_id" ref="base.user_al"/> <field name="department_id" ref="dep_rd"/> <field name="job_id" ref="hr.job_cto"/> <field name="category_ids" eval="[(6, 0, [ref('employee_category_3')])]"/> @@ -217,7 +115,6 @@ <record id="employee_mit" model="hr.employee"> <field name="name">Minh Tran</field> - <field name="user_id" ref="base.user_mit"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -231,7 +128,6 @@ <record id="employee_niv" model="hr.employee"> <field name="name">Nicolas Vanhoren</field> - <field name="user_id" ref="base.user_niv"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -244,7 +140,6 @@ <record id="employee_stw" model="hr.employee"> <field name="name">Stéphane Wirtel</field> - <field name="user_id" ref="base.user_stw"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -257,7 +152,6 @@ <record id="employee_chs" model="hr.employee"> <field name="name">Christophe Simonis</field> - <field name="user_id" ref="base.user_chs"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -270,7 +164,7 @@ <record id="employee_qdp" model="hr.employee"> <field name="name">Quentin De Paoli</field> - <field name="user_id" ref="base.user_qdp"/> + <field name="user_id" ref="base.user_demo"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -283,7 +177,6 @@ <record id="employee_fme" model="hr.employee"> <field name="name">Fabien Meghazi</field> - <field name="user_id" ref="base.user_fme"/> <field name="department_id" ref="dep_rd"/> <field name="parent_id" ref="employee_al"/> <field name="job_id" ref="hr.job_developer"/> @@ -295,7 +188,6 @@ <record id="employee_fpi" model="hr.employee"> <field name="name">Francois Pietquin</field> - <field name="user_id" ref="base.user_fpi"/> <field name="department_id" ref="dep_ps"/> <field name="parent_id" ref="employee_fp"/> <field name="job_id" ref="hr.job_consultant"/> @@ -308,7 +200,6 @@ <record id="employee_jth" model="hr.employee"> <field name="name">Julien Thewys</field> - <field name="user_id" ref="base.user_jth"/> <field name="department_id" ref="dep_ps"/> <field name="parent_id" ref="employee_fp"/> <field name="job_id" ref="hr.job_consultant"/> @@ -321,7 +212,6 @@ <record id="employee_ngh" model="hr.employee"> <field name="name">Nicoleta Gherlea</field> - <field name="user_id" ref="base.user_ngh"/> <field name="department_id" ref="dep_sales"/> <field name="parent_id" ref="employee_fp"/> <field name="job_id" ref="hr.job_marketing"/> @@ -334,7 +224,6 @@ <record id="employee_vad" model="hr.employee"> <field name="name">Valérie Descamps</field> - <field name="user_id" ref="base.user_vad"/> <field name="department_id" ref="dep_administration"/> <field name="parent_id" ref="employee_fp"/> <field name="job_id" ref="hr.job_hrm"/> diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index 99d7af027e9..00ed2afbef2 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -20,16 +20,12 @@ <field name="arch" type="xml"> <form string="Employee" version="7.0"> <sheet> - <field name="image_small" widget='image' class="oe_right oe_image_small"/> + <field name="image_medium" widget='image' class="oe_left oe_avatar"/> <div class="oe_title"> <label for="name" class="oe_edit_only"/> <h1> <field name="name"/> </h1> - <label for="job_id" class="oe_edit_only"/> - <h2> - <field name="job_id" options='{"no_open": true}' domain="[('state','!=','old')]" context="{'form_view_ref': 'hr.view_hr_job_employee_form'}"/> - </h2> <label for="category_ids" class="oe_edit_only"/> <field name="category_ids" widget="many2many_tags" placeholder="e.g. Part Time"/> </div> @@ -37,44 +33,55 @@ <!-- Put here related buttons --> </div> <notebook> - <page string="Information"> + <page string="Public Information"> <group> - <group> + <group string="Contact Information"> + <field name="work_email" widget="email"/> + <field name="work_phone"/> + <field name="mobile_phone"/> + </group> + <group string="Position"> <field name="department_id" on_change="onchange_department_id(department_id)"/> - <field name="company_id" groups="base.group_multi_company" on_change="onchange_company(company_id)"/> - <field name="user_id" on_change="onchange_user(user_id)"/> + <field name="job_id" options='{"no_open": true}' domain="[('state','!=','old')]" context="{'form_view_ref': 'hr.view_hr_job_employee_form'}"/> <field name="parent_id"/> <field name="coach_id"/> </group> <group> - <field name="work_email" widget="email"/> - <field name="work_phone"/> - <field name="address_id" on_change="onchange_address_id(address_id)"/> - <field name="mobile_phone"/> - <field name="identification_id" groups="base.group_hr_user"/> - <field name="passport_id" groups="base.group_hr_user"/> - <field name="otherid" groups="base.group_hr_user"/> - </group> - <group name="active_group"> - <field name="active"/> + <field name="company_id" groups="base.group_multi_company" on_change="onchange_company(company_id)"/> + <field name="user_id" on_change="onchange_user(user_id)" string="Related User"/> + <field name="address_id" on_change="onchange_address_id(address_id)" context="{'show_address': 1}" options='{"always_reload": true, "highlight_first_line": true}'/> </group> </group> <field name="notes" placeholder="Other Information ..." colspan="4"/> </page> <page string="Personal Information" groups="base.group_hr_user"> - <group col="4"> - <group> + <group> + <group string="Citizenship & Other Info"> <field name="country_id" options='{"no_open": true}'/> + <field name="identification_id" groups="base.group_hr_user"/> + <field name="passport_id" groups="base.group_hr_user"/> <field name="bank_account_id"/> - <field name="address_home_id"/> + <field name="otherid" groups="base.group_hr_user"/> </group> - <group> + <group string="Contact Information"> + <field name="address_home_id" context="{'show_address': 1}" options='{"always_reload": true, "highlight_first_line": true}'/> + </group> + <group string="Status"> <field name="gender"/> <field name="marital"/> + </group> + <group string="Birth"> <field name="birthday"/> </group> </group> </page> + <page string="HR Settings" groups="base.group_hr_user"> + <group> + <group string="Active" name="active_group"> + <field name="active"/> + </group> + </group> + </page> </notebook> </sheet> </form> diff --git a/addons/hr/i18n/nl.po b/addons/hr/i18n/nl.po index 9a16f1fc1fb..e63c2c1f1cb 100644 --- a/addons/hr/i18n/nl.po +++ b/addons/hr/i18n/nl.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-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-08-26 14:02+0000\n" +"PO-Revision-Date: 2012-09-03 17:10+0000\n" "Last-Translator: Erwin <Unknown>\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: 2012-08-28 06:24+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: hr #: model:process.node,name:hr.process_node_openerpuser0 @@ -195,7 +195,7 @@ msgstr "Vrouw" #: help:hr.job,expected_employees:0 msgid "" "Expected number of employees for this job position after new recruitment." -msgstr "" +msgstr "Verwachte aantal werknemers voor deze functie na de nieuwe werving." #. module: hr #: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_new_config @@ -710,12 +710,12 @@ msgstr "Ondergeschikten" #. module: hr #: field:hr.job,no_of_employee:0 msgid "Number of employees currently occupying this job position." -msgstr "" +msgstr "Huidige aantal werknemers welke deze functie bekleden ." #. module: hr #: field:hr.job,no_of_recruitment:0 msgid "Number of new employees you expect to recruit." -msgstr "" +msgstr "Verwachte aantal te werven nieuwe werknemers." #~ msgid "Sunday" #~ msgstr "Zondag" diff --git a/addons/hr/res_config.py b/addons/hr/res_config.py index 4973417ec88..411040f45b8 100644 --- a/addons/hr/res_config.py +++ b/addons/hr/res_config.py @@ -32,15 +32,15 @@ class hr_config_settings(osv.osv_memory): help ="""This installs the module hr_attendance."""), 'module_hr_timesheet': fields.boolean('manage timesheets', help ="""This installs the module hr_timesheet."""), - 'module_hr_holidays': fields.boolean('manage leaves and allocation requests', + 'module_hr_holidays': fields.boolean('manage holidays, leaves and allocation requests', help ="""This installs the module hr_holidays."""), 'module_hr_expense': fields.boolean('manage employees expenses', help ="""This installs the module hr_expense."""), - 'module_hr_recruitment': fields.boolean('manage the recruitment pipe', + 'module_hr_recruitment': fields.boolean('manage the recruitment process', help ="""This installs the module hr_recruitment."""), - 'module_hr_contract': fields.boolean('manage contract per employee', + 'module_hr_contract': fields.boolean('record contracts per employee', help ="""This installs the module hr_contract."""), - 'module_hr_evaluation': fields.boolean('manage employees periodic evaluation', + 'module_hr_evaluation': fields.boolean('organize employees periodic evaluation', help ="""This installs the module hr_evaluation."""), 'module_hr_payroll': fields.boolean('manage payroll', help ="""This installs the module hr_payroll."""), diff --git a/addons/hr/res_config_view.xml b/addons/hr/res_config_view.xml index ad36818e525..32f96fb2abb 100644 --- a/addons/hr/res_config_view.xml +++ b/addons/hr/res_config_view.xml @@ -33,6 +33,19 @@ <button string="Cancel" type="object" name="cancel" class="oe_link"/> </header> <separator string="Human Resources Management"/> + <group name="recruitment_grp"> + <label for="id" string="Talent Management"/> + <div name="recruitment"> + <div> + <field name="module_hr_recruitment" class="oe_inline"/> + <label for="module_hr_recruitment"/> + </div> + <div> + <field name="module_hr_evaluation" class="oe_inline"/> + <label for="module_hr_evaluation"/> + </div> + </div> + </group> <group> <label for="id" string="Additional Features"/> <div> @@ -44,21 +57,6 @@ <field name="module_hr_expense" class="oe_inline"/> <label for="module_hr_expense"/> </div> - <div> - <field name="module_hr_evaluation" class="oe_inline"/> - <label for="module_hr_evaluation"/> - </div> - <div> - <field name="module_hr_contract" class="oe_inline"/> - <label for="module_hr_contract"/> - </div> - <div name="hr_payroll"> - <field name="module_hr_payroll" class="oe_inline"/> - <label for="module_hr_payroll"/> - <button name="%(open_payroll_modules)d" type="action" - string="Install your country's payroll" icon="gtk-go-forward" - attrs="{'invisible': [('module_hr_payroll','=',False)]}" class="oe_link"/> - </div> </div> </group> <group name="timesheet_grp"> @@ -78,12 +76,19 @@ </div> </div> </group> - <group name="recruitment_grp"> - <label for="id" string="Recruitment"/> - <div name="recruitment"> + <group name="contract_grp"> + <label for="id" string="Contracts"/> + <div> <div> - <field name="module_hr_recruitment" class="oe_inline"/> - <label for="module_hr_recruitment"/> + <field name="module_hr_contract" class="oe_inline"/> + <label for="module_hr_contract"/> + </div> + <div name="hr_payroll"> + <field name="module_hr_payroll" class="oe_inline"/> + <label for="module_hr_payroll"/> + <button name="%(open_payroll_modules)d" type="action" + string="Install your country's payroll" icon="gtk-go-forward" + attrs="{'invisible': [('module_hr_payroll','=',False)]}" class="oe_link"/> </div> </div> </group> diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml index 5e4c3052858..d46a0f0ba19 100644 --- a/addons/hr/security/hr_security.xml +++ b/addons/hr/security/hr_security.xml @@ -2,13 +2,19 @@ <openerp> <data noupdate="1"> + <record model="res.groups" id="base.group_user"> + <field name="comment">the user will be able to manage his own human resources stuff (leave request, timesheets, ...), if he is linked to an employee in the system.</field> + </record> + <record id="base.group_hr_user" model="res.groups"> <field name="name">Officer</field> <field name="category_id" ref="base.module_category_human_resources"/> <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> + <field name="comment">the user will be able to approve document created by employees.</field> </record> <record id="base.group_hr_manager" model="res.groups"> <field name="name">Manager</field> + <field name="comment">the user will have an access to the human resources configuration as well as statistic reports.</field> <field name="category_id" ref="base.module_category_human_resources"/> <field name="implied_ids" eval="[(4, ref('base.group_hr_user'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/> diff --git a/addons/hr/static/src/css/hr.css b/addons/hr/static/src/css/hr.css index 11e72fad31e..eb0728c6f1c 100644 --- a/addons/hr/static/src/css/hr.css +++ b/addons/hr/static/src/css/hr.css @@ -65,4 +65,5 @@ .oe_employee_details li { margin: 2px 0; + padding: 0; } diff --git a/addons/hr_attendance/__openerp__.py b/addons/hr_attendance/__openerp__.py index 985c42e5a04..cb2cd261773 100644 --- a/addons/hr_attendance/__openerp__.py +++ b/addons/hr_attendance/__openerp__.py @@ -43,7 +43,6 @@ actions(Sign in/Sign out) performed by them. 'wizard/hr_attendance_bymonth_view.xml', 'wizard/hr_attendance_byweek_view.xml', 'wizard/hr_attendance_error_view.xml', - 'wizard/hr_attendance_sign_in_out_view.xml', ], 'demo': ['hr_attendance_demo.xml'], 'test': [ @@ -53,5 +52,10 @@ actions(Sign in/Sign out) performed by them. 'installable': True, 'auto_install': False, 'certificate': '0063495605613', + + #web + "js": ["static/src/js/attendance.js"], + 'qweb' : ["static/src/xml/attendance.xml"], + 'css' : ["static/src/css/slider.css"], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_attendance/hr_attendance.py b/addons/hr_attendance/hr_attendance.py index 7a0cc7a8e51..d3bc79bda1a 100644 --- a/addons/hr_attendance/hr_attendance.py +++ b/addons/hr_attendance/hr_attendance.py @@ -112,9 +112,23 @@ class hr_employee(osv.osv): for res in cr.fetchall(): result[res[1]] = res[0] == 'sign_in' and 'present' or 'absent' return result + + def _last_sign(self, cr, uid, ids, name, args, context=None): + result = {} + if not ids: + return result + for id in ids: + result[id] = False + cr.execute("""select max(name) as name + from hr_attendance + where action in ('sign_in', 'sign_out') and employee_id = %s""",(id,)) + for res in cr.fetchall(): + result[id] = res[0] + return result _columns = { 'state': fields.function(_state, type='selection', selection=[('absent', 'Absent'), ('present', 'Present')], string='Attendance'), + 'last_sign': fields.function(_last_sign, type='datetime', string='Last Sign'), } def _action_check(self, cr, uid, emp_id, dt=False, context=None): @@ -122,30 +136,25 @@ class hr_employee(osv.osv): res = cr.fetchone() return not (res and (res[0]>=(dt or time.strftime('%Y-%m-%d %H:%M:%S')))) - def attendance_action_change(self, cr, uid, ids, type='action', context=None, dt=False, *args): - obj_attendance = self.pool.get('hr.attendance') - id = False - warning_sign = 'sign' - res = {} - - #Special case when button calls this method: type=context - if isinstance(type, dict): - type = type.get('type','action') - if type == 'sign_in': - warning_sign = "Sign In" - elif type == 'sign_out': - warning_sign = "Sign Out" - for emp in self.read(cr, uid, ids, ['id'], context=context): - if not self._action_check(cr, uid, emp['id'], dt, context): - raise osv.except_osv(_('Warning!'), _('You tried to %s with a date anterior to another event !\nTry to contact the administrator to correct attendances.')%(warning_sign,)) - - res = {'action': type, 'employee_id': emp['id']} - if dt: - res['name'] = dt - id = obj_attendance.create(cr, uid, res, context=context) - - if type != 'action': - return id + def attendance_action_change(self, cr, uid, ids, context=None): + if context is None: + context = {} + action_date = context.get('action_date', False) + action = context.get('action', False) + hr_attendance = self.pool.get('hr.attendance') + warning_sign = {'sign_in': _('Sign In'), 'sign_out': _('Sign Out')} + for employee in self.browse(cr, uid, ids, context=context): + if not action: + if employee.state == 'present': action = 'sign_out' + if employee.state == 'absent': action = 'sign_in' + + if not self._action_check(cr, uid, employee.id, action_date, context): + raise osv.except_osv(_('Warning'), _('You tried to %s with a date anterior to another event !\nTry to contact the HR Manager to correct attendances.')%(warning_sign[action],)) + + vals = {'action': action, 'employee_id': employee.id} + if action_date: + vals['name'] = action_date + hr_attendance.create(cr, uid, vals, context=context) return True hr_employee() diff --git a/addons/hr_attendance/hr_attendance_view.xml b/addons/hr_attendance/hr_attendance_view.xml index bda89fd9d63..c686b4c1ff0 100644 --- a/addons/hr_attendance/hr_attendance_view.xml +++ b/addons/hr_attendance/hr_attendance_view.xml @@ -126,9 +126,9 @@ <field name="active" position="before"> <label for="state"/> <div> - <field name="state" class="oe_inline"/> - <button class="oe_inline oe_right" name="attendance_action_change" states="present" string="Sign Out" type="object" icon="gtk-go-forward" context="{'type':'sign_out'}" groups="base.group_hr_user"/> - <button class="oe_inline oe_right" name="attendance_action_change" states="absent" string="Sign In" type="object" icon="gtk-go-back" context="{'type':'sign_in'}" groups="base.group_hr_user"/> + <field name="state" class="oe_inline"/> - + <button class="oe_link" name="attendance_action_change" states="present" string="Sign Out" type="object" context="{'type':'sign_out'}" groups="base.group_hr_user"/> + <button class="oe_link" name="attendance_action_change" states="absent" string="Sign In" type="object" context="{'type':'sign_in'}" groups="base.group_hr_user"/> </div> </field> </field> @@ -136,3 +136,5 @@ </data> </openerp> + + diff --git a/addons/hr_attendance/i18n/nb.po b/addons/hr_attendance/i18n/nb.po new file mode 100644 index 00000000000..97c2618a419 --- /dev/null +++ b/addons/hr_attendance/i18n/nb.po @@ -0,0 +1,576 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:06+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: hr_attendance +#: model:ir.ui.menu,name:hr_attendance.menu_hr_time_tracking +msgid "Time Tracking" +msgstr "Tid Sporing" + +#. module: hr_attendance +#: view:hr.attendance:0 +msgid "Group By..." +msgstr "Grupper etter ..." + +#. module: hr_attendance +#: view:hr.attendance:0 +msgid "Today" +msgstr "I dag" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "March" +msgstr "Mars" + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +msgid "" +"You did not sign out the last time. Please enter the date and time you " +"signed out." +msgstr "" +"Du logget ikke ut siste gang. Vennligst skriv inn datoen og klokkeslettet du " +"logget ut." + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Total period:" +msgstr "Total periode:" + +#. module: hr_attendance +#: field:hr.action.reason,name:0 +msgid "Reason" +msgstr "Grunn" + +#. module: hr_attendance +#: view:hr.attendance.error:0 +msgid "Print Attendance Report Error" +msgstr "Skriv ut Oppmøte Rapporter Feil." + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:161 +#, python-format +msgid "The sign-out date must be in the past" +msgstr "Logg - ut må være i fortiden." + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Date Signed" +msgstr "Signeringsdato" + +#. module: hr_attendance +#: model:ir.actions.act_window,help:hr_attendance.open_view_attendance +msgid "" +"The Time Tracking functionality aims to manage employee attendances from " +"Sign in/Sign out actions. You can also link this feature to an attendance " +"device using OpenERP's web service features." +msgstr "" +"Tid Sporing funksjonalitet mål er å forvalte de ansattes tilskuertall fra " +"Logg inn / Logg ut handlinger. Du kan også koble denne funksjonen til en " +"frammøte enheten ved hjelp OpenERP web-service funksjoner." + +#. module: hr_attendance +#: view:hr.action.reason:0 +msgid "Attendance reasons" +msgstr "Oppmøte grunner" + +#. module: hr_attendance +#: view:hr.attendance:0 +#: field:hr.attendance,day:0 +msgid "Day" +msgstr "Dag" + +#. module: hr_attendance +#: selection:hr.employee,state:0 +msgid "Present" +msgstr "Presentere" + +#. module: hr_attendance +#: model:ir.model,name:hr_attendance.model_hr_sign_in_out_ask +msgid "Ask for Sign In Out" +msgstr "Spør for pålogging ut." + +#. module: hr_attendance +#: field:hr.attendance,action_desc:0 +#: model:ir.model,name:hr_attendance.model_hr_action_reason +msgid "Action Reason" +msgstr "Handling grunn." + +#. module: hr_attendance +#: view:hr.sign.in.out:0 +msgid "Ok" +msgstr "Ok" + +#. module: hr_attendance +#: view:hr.action.reason:0 +msgid "Define attendance reason" +msgstr "Definer frammøte grunn." + +#. module: hr_attendance +#: code:addons/hr_attendance/report/attendance_by_month.py:184 +#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_month +#, python-format +msgid "Attendances By Month" +msgstr "Tilskuertall Pr.mnd" + +#. module: hr_attendance +#: field:hr.sign.in.out,name:0 +#: field:hr.sign.in.out.ask,name:0 +msgid "Employees name" +msgstr "Ansattnavn" + +#. module: hr_attendance +#: model:ir.actions.act_window,name:hr_attendance.open_view_attendance_reason +#: model:ir.ui.menu,name:hr_attendance.menu_open_view_attendance_reason +msgid "Attendance Reasons" +msgstr "Oppmøte grunner" + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:161 +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:167 +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:174 +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:179 +#, python-format +msgid "UserError" +msgstr "Brukerfeil" + +#. module: hr_attendance +#: field:hr.attendance.error,end_date:0 +#: field:hr.attendance.week,end_date:0 +msgid "Ending Date" +msgstr "Sluttdato" + +#. module: hr_attendance +#: code:addons/hr_attendance/hr_attendance.py:140 +#, python-format +msgid "Warning" +msgstr "Advarsel" + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:174 +#, python-format +msgid "The Sign-in date must be in the past" +msgstr "Logg inn dato må være i fortiden." + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:167 +#, python-format +msgid "A sign-in must be right after a sign-out !" +msgstr "En pålogging må være rett etter en logg ut!" + +#. module: hr_attendance +#: field:hr.employee,state:0 +#: model:ir.model,name:hr_attendance.model_hr_attendance +msgid "Attendance" +msgstr "Oppmøte" + +#. module: hr_attendance +#: field:hr.attendance.error,max_delay:0 +msgid "Max. Delay (Min)" +msgstr "Maks. Forsinkelse (Min)" + +#. module: hr_attendance +#: view:hr.attendance.error:0 +#: view:hr.attendance.month:0 +#: view:hr.attendance.week:0 +msgid "Print" +msgstr "Skriv ut" + +#. module: hr_attendance +#: view:hr.attendance:0 +msgid "Hr Attendance Search" +msgstr "Hr Oppmøte Søk." + +#. module: hr_attendance +#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_week +msgid "Attendances By Week" +msgstr "Tilskuertall etter uke." + +#. module: hr_attendance +#: constraint:hr.attendance:0 +msgid "Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)" +msgstr "Feil: Logg på (hhv. Logg ut) må følge Logg ut (hhv. Logg inn)" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "July" +msgstr "juli" + +#. module: hr_attendance +#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_error +#: model:ir.actions.report.xml,name:hr_attendance.attendance_error_report +msgid "Attendance Error Report" +msgstr "Oppmøte Feilrapport" + +#. module: hr_attendance +#: field:hr.attendance.error,init_date:0 +#: field:hr.attendance.week,init_date:0 +msgid "Starting Date" +msgstr "Startdato" + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Min Delay" +msgstr "Minimum forsinkelse." + +#. module: hr_attendance +#: selection:hr.attendance,action:0 +#: view:hr.employee:0 +msgid "Sign In" +msgstr "Logg Inn" + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Operation" +msgstr "Handling" + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_error.py:49 +#, python-format +msgid "No Data Available" +msgstr "Ingen informasjon tilgjengelig" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "September" +msgstr "September" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "December" +msgstr "Desember" + +#. module: hr_attendance +#: field:hr.attendance.month,month:0 +msgid "Month" +msgstr "Måned" + +#. module: hr_attendance +#: field:hr.action.reason,action_type:0 +msgid "Action Type" +msgstr "Handlingstype" + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "" +"(*) A negative delay means that the employee worked more than encoded." +msgstr "" +"(*) En negativ forsinkelse innebærer at arbeidstakeren jobbet mer enn kodet." + +#. module: hr_attendance +#: view:hr.attendance:0 +msgid "My Attendance" +msgstr "Min Oppmøte" + +#. module: hr_attendance +#: help:hr.attendance,action_desc:0 +msgid "" +"Specifies the reason for Signing In/Signing Out in case of extra hours." +msgstr "Angir årsaken til pålogging / logge ut I tilfelle ekstra timer." + +#. module: hr_attendance +#: model:ir.model,name:hr_attendance.model_hr_attendance_month +msgid "Print Monthly Attendance Report" +msgstr "Skriv ut månedlig Oppmøte Rapporter." + +#. module: hr_attendance +#: model:ir.model,name:hr_attendance.model_hr_sign_in_out +msgid "Sign In Sign Out" +msgstr "Logg inn - Logg ut" + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:105 +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:129 +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:146 +#: view:hr.sign.in.out:0 +#: model:ir.actions.act_window,name:hr_attendance.action_hr_attendance_sigh_in_out +#: model:ir.ui.menu,name:hr_attendance.menu_hr_attendance_sigh_in_out +#, python-format +msgid "Sign in / Sign out" +msgstr "Logg inn / Logg ut" + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +msgid "hr.sign.out.ask" +msgstr "hr.signere.ut.spør." + +#. module: hr_attendance +#: view:hr.attendance.week:0 +msgid "Print Attendance Report Weekly" +msgstr "Skriv ut Oppmøte Rapporter Ukentlig." + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "August" +msgstr "August" + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_sign_in_out.py:179 +#, python-format +msgid "A sign-out must be right after a sign-in !" +msgstr "Et logg-ut må være rett etter en logg - inn!" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "June" +msgstr "Juni" + +#. module: hr_attendance +#: model:ir.model,name:hr_attendance.model_hr_attendance_error +msgid "Print Error Attendance Report" +msgstr "Print Feil Oppmøte Rapporter." + +#. module: hr_attendance +#: field:hr.attendance,name:0 +msgid "Date" +msgstr "Dato" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "November" +msgstr "November" + +#. module: hr_attendance +#: constraint:hr.employee:0 +msgid "Error ! You cannot create recursive Hierarchy of Employees." +msgstr "Feil! Du kan ikke opprette rekursiv Hierarki av ansatte." + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "October" +msgstr "Oktober" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "January" +msgstr "Januar" + +#. module: hr_attendance +#: selection:hr.action.reason,action_type:0 +#: view:hr.sign.in.out:0 +#: view:hr.sign.in.out.ask:0 +msgid "Sign in" +msgstr "Logg inn" + +#. module: hr_attendance +#: view:hr.attendance.error:0 +msgid "Analysis Information" +msgstr "Analyse Informasjon." + +#. module: hr_attendance +#: view:hr.sign.in.out:0 +msgid "Sign-out Entry Must Follow Sign-in." +msgstr "" + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Attendance Errors" +msgstr "Oppmøte feil" + +#. module: hr_attendance +#: field:hr.attendance,action:0 +#: selection:hr.attendance,action:0 +msgid "Action" +msgstr "Handling" + +#. module: hr_attendance +#: view:hr.sign.in.out:0 +msgid "" +"If you need your staff to sign in when they arrive at work and sign out " +"again at the end of the day, OpenERP allows you to manage this with this " +"tool. If each employee has been linked to a system user, then they can " +"encode their time with this action button." +msgstr "" +"Hvis du trenger dine ansatte til å logge på når de kommer på jobb og logge " +"ut igjen på slutten av dagen, kan OpenERP tillate deg å administrere dette " +"med dette verktøyet. Hvis hver ansatt har vært knyttet til et system bruker, " +"så de kan kode sin tid med denne handlingen knappen." + +#. module: hr_attendance +#: model:ir.model,name:hr_attendance.model_hr_attendance_week +msgid "Print Week Attendance Report" +msgstr "Skriv Ut Uke Oppmøte Rapporter." + +#. module: hr_attendance +#: field:hr.sign.in.out,emp_id:0 +#: field:hr.sign.in.out.ask,emp_id:0 +msgid "Empoyee ID" +msgstr "Ansatt ID" + +#. module: hr_attendance +#: view:hr.attendance.error:0 +#: view:hr.attendance.month:0 +#: view:hr.attendance.week:0 +#: view:hr.sign.in.out:0 +#: view:hr.sign.in.out.ask:0 +msgid "Cancel" +msgstr "Avbryt" + +#. module: hr_attendance +#: help:hr.action.reason,name:0 +msgid "Specifies the reason for Signing In/Signing Out." +msgstr "Angir årsaken til pålogging / logger av." + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "" +"(*) A positive delay means that the employee worked less than recorded." +msgstr "" +"(*) En positiv forsinkelse innebærer at arbeidstakeren jobbet mindre enn " +"registrert." + +#. module: hr_attendance +#: view:hr.attendance.month:0 +msgid "Print Attendance Report Monthly" +msgstr "Skriv ut Oppmøte Rapporter Månedlig." + +#. module: hr_attendance +#: selection:hr.action.reason,action_type:0 +#: view:hr.sign.in.out:0 +#: view:hr.sign.in.out.ask:0 +msgid "Sign out" +msgstr "Logg av" + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Delay" +msgstr "Forsinkelse" + +#. module: hr_attendance +#: view:hr.attendance:0 +#: model:ir.model,name:hr_attendance.model_hr_employee +msgid "Employee" +msgstr "Ansatt" + +#. module: hr_attendance +#: code:addons/hr_attendance/hr_attendance.py:140 +#, python-format +msgid "" +"You tried to %s with a date anterior to another event !\n" +"Try to contact the administrator to correct attendances." +msgstr "" +"Du prøvde å% s med en dato fremre til en annen hendelse!\n" +"Prøv å kontakte administrator for å rette tilskuertall." + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +#: field:hr.sign.in.out.ask,last_time:0 +msgid "Your last sign out" +msgstr "Din siste Logg ut." + +#. module: hr_attendance +#: report:report.hr.timesheet.attendance.error:0 +msgid "Date Recorded" +msgstr "Dato Innspilt" + +#. module: hr_attendance +#: model:ir.actions.act_window,name:hr_attendance.open_view_attendance +#: model:ir.ui.menu,name:hr_attendance.menu_hr_attendance +#: model:ir.ui.menu,name:hr_attendance.menu_open_view_attendance +msgid "Attendances" +msgstr "Tilskuertall" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "May" +msgstr "Mai" + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +msgid "Your last sign in" +msgstr "Din siste logg inn." + +#. module: hr_attendance +#: selection:hr.attendance,action:0 +#: view:hr.employee:0 +msgid "Sign Out" +msgstr "Logg Ut" + +#. module: hr_attendance +#: model:ir.actions.act_window,help:hr_attendance.action_hr_attendance_sigh_in_out +msgid "" +"Sign in / Sign out. In some companies, staff have to sign in when they " +"arrive at work and sign out again at the end of the day. If each employee " +"has been linked to a system user, then they can encode their time with this " +"action button." +msgstr "" +"Logg inn / Logg ut. I noen bedrifter, ansatte må logge inn når de kommer på " +"jobb og Logg ut igjen på slutten av dagen. Hvis hver ansatt har vært knyttet " +"til et system bruker, så de kan kode sin tid med denne handlingen knappen." + +#. module: hr_attendance +#: field:hr.attendance,employee_id:0 +msgid "Employee's Name" +msgstr "Ansatte's navn." + +#. module: hr_attendance +#: selection:hr.employee,state:0 +msgid "Absent" +msgstr "Fraværende" + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "February" +msgstr "Februar" + +#. module: hr_attendance +#: view:hr.attendance:0 +msgid "Employee attendances" +msgstr "Ansattes tilskuertall" + +#. module: hr_attendance +#: field:hr.sign.in.out,state:0 +msgid "Current state" +msgstr "Nåværende tilstand." + +#. module: hr_attendance +#: selection:hr.attendance.month,month:0 +msgid "April" +msgstr "April" + +#. module: hr_attendance +#: view:hr.attendance.error:0 +msgid "Bellow this delay, the error is considered to be voluntary" +msgstr "Under denne forsinkelsen, er feilen anses å være frivillig." + +#. module: hr_attendance +#: code:addons/hr_attendance/wizard/hr_attendance_error.py:49 +#, python-format +msgid "No records found for your selection!" +msgstr "Ingen rader funnet med angitt utvalg!" + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +msgid "" +"You did not sign in the last time. Please enter the date and time you signed " +"in." +msgstr "" +"Du hadde ikke logget ut siste gang. Vennligst skriv inn datoen og " +"klokkeslettet du logget." + +#. module: hr_attendance +#: field:hr.attendance.month,year:0 +msgid "Year" +msgstr "År" + +#. module: hr_attendance +#: view:hr.sign.in.out.ask:0 +msgid "hr.sign.in.out.ask" +msgstr "hr.Logg.i.ut.spør" + +#~ msgid "Sign-Out Entry must follow Sign-In." +#~ msgstr "Logg ut Entry må følge pålogging." diff --git a/addons/hr_attendance/static/src/css/slider.css b/addons/hr_attendance/static/src/css/slider.css new file mode 100644 index 00000000000..664d4f96e41 --- /dev/null +++ b/addons/hr_attendance/static/src/css/slider.css @@ -0,0 +1,33 @@ + +.openerp .oe_attendance_status { + height: 32px; + width: 32px; + display: inline-block; +} +.openerp .oe_attendance_signin { + float:left; + height: 32px; + width: 32px; + background: url(/hr_attendance/static/src/img/emp-out32.png); + cursor: pointer; +} +.openerp .oe_attendance_signin:hover { + background: url(/hr_attendance/static/src/img/emp-out-disable32.png); +} +.openerp .oe_attendance_status.oe_attendance_signed .oe_attendance_signin { + display: none; +} + +.openerp .oe_attendance_signout { + float:right; + height: 32px; + width: 32px; + background: url(/hr_attendance/static/src/img/emp-in32.png); + cursor: pointer; +} +.openerp .oe_attendance_signout:hover { + background: url(/hr_attendance/static/src/img/emp-in-disable32.png); +} +.openerp .oe_attendance_status.oe_attendance_nosigned .oe_attendance_signout { + display: none; +} diff --git a/addons/hr_attendance/static/src/img/emp-in-disable32.png b/addons/hr_attendance/static/src/img/emp-in-disable32.png Binary files differnew file mode 100644 index 00000000000..1968e0de464 --- /dev/null +++ b/addons/hr_attendance/static/src/img/emp-in-disable32.png diff --git a/addons/hr_attendance/static/src/img/emp-in32.png b/addons/hr_attendance/static/src/img/emp-in32.png Binary files differnew file mode 100644 index 00000000000..26e3c01556b --- /dev/null +++ b/addons/hr_attendance/static/src/img/emp-in32.png diff --git a/addons/hr_attendance/static/src/img/emp-out-disable32.png b/addons/hr_attendance/static/src/img/emp-out-disable32.png Binary files differnew file mode 100644 index 00000000000..b7099e44529 --- /dev/null +++ b/addons/hr_attendance/static/src/img/emp-out-disable32.png diff --git a/addons/hr_attendance/static/src/img/emp-out32.png b/addons/hr_attendance/static/src/img/emp-out32.png Binary files differnew file mode 100644 index 00000000000..d2a6cbb5013 --- /dev/null +++ b/addons/hr_attendance/static/src/img/emp-out32.png diff --git a/addons/hr_attendance/static/src/js/attendance.js b/addons/hr_attendance/static/src/js/attendance.js new file mode 100644 index 00000000000..1b5e271b4ba --- /dev/null +++ b/addons/hr_attendance/static/src/js/attendance.js @@ -0,0 +1,84 @@ + +openerp.hr_attendance = function (instance) { + + var QWeb = instance.web.qweb; + var _t = instance.web._t; + var _lt = instance.web._lt; + + instance.hr_attendance.AttendanceSlider = instance.web.Widget.extend({ + template: 'AttendanceSlider', + init: function (parent) { + this._super(parent); + this.set({"signed_in": false}); + }, + start: function() { + var self = this; + var tmp = function() { + this.$el.toggleClass("oe_attendance_nosigned", ! this.get("signed_in")); + this.$el.toggleClass("oe_attendance_signed", this.get("signed_in")); + }; + this.on("change:signed_in", this, tmp); + _.bind(tmp, this)(); + this.$(".oe_attendance_signin").click(function() { + self.do_update_attendance(); + }); + this.$(".oe_attendance_signout").click(function() { + self.do_update_attendance(); + }); + this.$el.tipsy({ + title: function() { + var last_text = instance.web.format_value(self.last_sign, {type: "datetime"}); + var current_text = instance.web.format_value(new Date(), {type: "datetime"}); + var duration = self.last_sign ? $.timeago(self.last_sign) : "none"; + if (self.get("signed_in")) { + return _.str.sprintf(_t("Last sign in: %s,<br />%s.<br />Click to sign out."), last_text, duration); + } else { + return _.str.sprintf(_t("Click to Sign In at %s."), current_text); + } + }, + html: true, + }); + return this.check_attendance(); + }, + do_update_attendance: function () { + var self = this; + var hr_employee = new instance.web.DataSet(self, 'hr.employee'); + hr_employee.call('attendance_action_change', [ + [self.employee.id] + ]).done(function (result) { + self.last_sign = new Date(); + self.set({"signed_in": ! self.get("signed_in")}); + }); + }, + check_attendance: function () { + var self = this; + self.employee = false; + this.$el.hide(); + var employee = new instance.web.DataSetSearch(self, 'hr.employee', self.session.user_context, [ + ['user_id', '=', self.session.uid] + ]); + return employee.read_slice(['id', 'name', 'state', 'last_sign']).pipe(function (res) { + if (_.isEmpty(res)) + return; + self.$el.show(); + self.employee = res[0]; + self.last_sign = instance.web.str_to_datetime(self.employee.last_sign); + self.set({"signed_in": self.employee.state !== "absent"}); + }); + }, + }); + + instance.web.UserMenu.include({ + do_update: function () { + this._super(); + var self = this; + this.update_promise = this.update_promise.then(function () { + if (self.attendanceslider) + return; + self.attendanceslider = new instance.hr_attendance.AttendanceSlider(self); + + self.attendanceslider.prependTo(instance.webclient.$('.oe_systray')); + }); + }, + }); +} diff --git a/addons/hr_attendance/static/src/xml/attendance.xml b/addons/hr_attendance/static/src/xml/attendance.xml new file mode 100644 index 00000000000..3fd9bcf2798 --- /dev/null +++ b/addons/hr_attendance/static/src/xml/attendance.xml @@ -0,0 +1,13 @@ +<template> + <t t-name="AttendanceSlider"> + <div class="oe_attendance_status oe_attendance_nosigned" data-tipsy="true"> + <div class="oe_attendance_signin"></div> + <div class="oe_attendance_signout"></div> + </div> + </t> +</template> + + + + + diff --git a/addons/hr_attendance/test/attendance_process.yml b/addons/hr_attendance/test/attendance_process.yml index 4ad765d65df..7902bf11d9d 100644 --- a/addons/hr_attendance/test/attendance_process.yml +++ b/addons/hr_attendance/test/attendance_process.yml @@ -1,16 +1,8 @@ - In order to test attendance process in OpenERP, I entry of SignIn of employee. - - !python {model: hr.sign.in.out}: | - context.update({'emp_id': [ref('hr.employee_al')]}) #TOFIX: emp_ids instead of 'emp_id' -- - !record {model: hr.sign.in.out, id: employee_sign_in}: - {} -- - Employee Signs In. -- - !python {model: hr.sign.in.out}: | - self.si_check(cr, uid, [ref("employee_sign_in")], context=context) + !python {model: hr.employee}: | + self.attendance_action_change(cr, uid, [ref("hr.employee_al")], context=context) - I check that Employee is "Present". - @@ -19,10 +11,10 @@ - After few seconds, employee sign's out. - - !python {model: hr.sign.in.out}: | + !python {model: hr.employee}: | import time time.sleep(2) - self.so_check(cr, uid, [ref("employee_sign_in")], context=context) + self.attendance_action_change(cr, uid, [ref("hr.employee_al")], context=context) - I check that Employee is "Absent". - diff --git a/addons/hr_attendance/wizard/__init__.py b/addons/hr_attendance/wizard/__init__.py index a414c9a8798..6269c507d8c 100644 --- a/addons/hr_attendance/wizard/__init__.py +++ b/addons/hr_attendance/wizard/__init__.py @@ -19,9 +19,8 @@ # ############################################################################## -import hr_attendance_sign_in_out import hr_attendance_error import hr_attendance_byweek import hr_attendance_bymonth -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_attendance/wizard/hr_attendance_sign_in_out.py b/addons/hr_attendance/wizard/hr_attendance_sign_in_out.py deleted file mode 100644 index 0ad4323ad78..00000000000 --- a/addons/hr_attendance/wizard/hr_attendance_sign_in_out.py +++ /dev/null @@ -1,184 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## -import time - -from osv import osv, fields -from tools.translate import _ - -class hr_si_so_ask(osv.osv_memory): - _name = 'hr.sign.in.out.ask' - _description = 'Ask for Sign In Out' - _columns = { - 'name': fields.char('Employees name', size=32, required=True, readonly=True), - 'last_time': fields.datetime('Your last sign out', required=True), - 'emp_id': fields.many2one('hr.employee', 'Empoyee ID', readonly=True), - } - - def _get_empname(self, cr, uid, context=None): - emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)) - if emp_id: - employee = self.pool.get('hr.employee').browse(cr, uid, emp_id, context=context)[0].name - return employee - return '' - - def _get_empid(self, cr, uid, context=None): - emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)) - if emp_id: - return emp_id[0] - return False - - _defaults = { - 'name': _get_empname, - 'emp_id': _get_empid, - } - - def sign_in(self, cr, uid, ids, context=None): - data = self.read(cr, uid, ids, [], context=context)[0] - data['emp_id'] = data['emp_id'] and data['emp_id'][0] - return self.pool.get('hr.sign.in.out').sign_in(cr, uid, data, context) - - def sign_out(self, cr, uid, ids, context=None): - data = self.read(cr, uid, ids, [], context=context)[0] - data['emp_id'] = data['emp_id'] and data['emp_id'][0] - return self.pool.get('hr.sign.in.out').sign_out(cr, uid, data, context) - -hr_si_so_ask() - -class hr_sign_in_out(osv.osv_memory): - _name = 'hr.sign.in.out' - _description = 'Sign In Sign Out' - - _columns = { - 'name': fields.char('Employees name', size=32, required=True, readonly=True), - 'state': fields.char('Current state', size=32, required=True, readonly=True), - 'emp_id': fields.many2one('hr.employee', 'Empoyee ID', readonly=True), - } - - def _get_empid(self, cr, uid, context=None): - emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context)) - if emp_id: - employee = self.pool.get('hr.employee').browse(cr, uid, emp_id, context=context)[0] - return {'name': employee.name, 'state': employee.state, 'emp_id': emp_id[0]} - return {} - - def default_get(self, cr, uid, fields_list, context=None): - res = super(hr_sign_in_out, self).default_get(cr, uid, fields_list, context=context) - res_emp = self._get_empid(cr, uid, context=context) - res.update(res_emp) - return res - - def si_check(self, cr, uid, ids, context=None): - obj_model = self.pool.get('ir.model.data') - att_obj = self.pool.get('hr.attendance') - data = self.read(cr, uid, ids, [], context=context)[0] - data['emp_id'] = data['emp_id'] and data['emp_id'][0] - emp_id = data['emp_id'] - att_id = att_obj.search(cr, uid, [('employee_id', '=', emp_id)], limit=1, order='name desc') - last_att = att_obj.browse(cr, uid, att_id, context=context) - if last_att: - last_att = last_att[0] - cond = not last_att or last_att.action == 'sign_out' - if cond: - return self.sign_in(cr, uid, data, context) - else: - model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','view_hr_attendance_so_ask')], context=context) - resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] - return { - 'name': _('Sign in / Sign out'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr.sign.in.out.ask', - 'views': [(resource_id,'form')], - 'type': 'ir.actions.act_window', - 'context': context, - 'target': 'new', - } - - def so_check(self, cr, uid, ids, context=None): - obj_model = self.pool.get('ir.model.data') - att_obj = self.pool.get('hr.attendance') - data = self.read(cr, uid, ids, [], context=context)[0] - data['emp_id'] = data['emp_id'] and data['emp_id'][0] - emp_id = data['emp_id'] - att_id = att_obj.search(cr, uid, [('employee_id', '=', emp_id),('action', '!=', 'action')], limit=1, order='name desc') - last_att = att_obj.browse(cr, uid, att_id, context=context) - if last_att: - last_att = last_att[0] - if not att_id and not last_att: - model_data_ids = obj_model.search(cr, uid, [('model','=','ir.ui.view'),('name','=','view_hr_attendance_message')], context=context) - resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] - return { - 'name': _('Sign in / Sign out'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr.sign.in.out', - 'views': [(resource_id,'form')], - 'type': 'ir.actions.act_window', - 'context': context, - 'target': 'new', - } - - cond = last_att and last_att['action'] == 'sign_in' - if cond: - return self.sign_out(cr, uid, data, context) - else: - model_data_ids = obj_model.search(cr, uid, [('model','=','ir.ui.view'),('name','=','view_hr_attendance_si_ask')], context=context) - resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] - return { - 'name': _('Sign in / Sign out'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'hr.sign.in.out.ask', - 'views': [(resource_id,'form')], - 'type': 'ir.actions.act_window', - 'target': 'new', - } - - def sign_in(self, cr, uid, data, context=None): - if context is None: - context = {} - emp_id = data['emp_id'] - if 'last_time' in data: - if data['last_time'] > time.strftime('%Y-%m-%d %H:%M:%S'): - raise osv.except_osv(_('User Error!'), _('The sign-out date must be in the past.')) - self.pool.get('hr.attendance').create(cr, uid, {'name': data['last_time'], 'action': 'sign_out', - 'employee_id': emp_id}, context=context) - try: - self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_in') - except: - raise osv.except_osv(_('User Error!'), _('A sign-in must be right after a sign-out!')) - return {'type': 'ir.actions.act_window_close'} # To do: Return Success message - - def sign_out(self, cr, uid, data, context=None): - emp_id = data['emp_id'] - if 'last_time' in data: - if data['last_time'] > time.strftime('%Y-%m-%d %H:%M:%S'): - raise osv.except_osv(_('User Error!'), _('The sign-in date must be in the past.')) - self.pool.get('hr.attendance').create(cr, uid, {'name':data['last_time'], 'action':'sign_in', 'employee_id':emp_id}, context=context) - try: - self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_out') - except: - raise osv.except_osv(_('User Error!'), _('A sign-out must be right after a sign-in.')) - return {'type': 'ir.actions.act_window_close'} # To do: Return Success message - -hr_sign_in_out() - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_attendance/wizard/hr_attendance_sign_in_out_view.xml b/addons/hr_attendance/wizard/hr_attendance_sign_in_out_view.xml deleted file mode 100644 index 5c84be5107f..00000000000 --- a/addons/hr_attendance/wizard/hr_attendance_sign_in_out_view.xml +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - <record id="view_hr_attendance_sigh_in_out" model="ir.ui.view"> - <field name="name">hr.sign.in.out.form</field> - <field name="model">hr.sign.in.out</field> - <field name="arch" type="xml"> - <form string="Sign in / Sign out" version="7.0"> - <group> - <separator colspan="4" string="Sign in / Sign out"/> - <label colspan="4" nolabel="1" string="If you need your staff to sign in when they arrive at work and sign out again at the end of the day, OpenERP allows you to manage this with this tool. If each employee has been linked to a system user, then they can encode their time with this action button."/> - <newline/> - </group> - <group colspan="4" > - <field name="name" /> - <field name="state" /> - </group> - <footer> - <button string="Sign in" name="si_check" type="object" class="oe_highlight"/> - <button string="Sign out" name="so_check" type="object" class="oe_highlight"/> - or - <button string="Cancel" class="oe_link" special="cancel" /> - </footer> - </form> - </field> - </record> - - <record id="view_hr_attendance_message" model="ir.ui.view"> - <field name="name">hr.sign.in.out.form</field> - <field name="model">hr.sign.in.out</field> - <field name="arch" type="xml"> - <form string="Sign in / Sign out" version="7.0"> - <separator string="Sign-out Entry Must Follow Sign-in." colspan="4" /> - </form> - </field> - </record> - - <record id="action_hr_attendance_sigh_in_out" model="ir.actions.act_window"> - <field name="name">Sign in / Sign out</field> - <field name="res_model">hr.sign.in.out</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="view_id" ref="view_hr_attendance_sigh_in_out"/> - <field name="target">new</field> - <field name="help">Sign in / Sign out. In some companies, staff have to sign in when they arrive at work and sign out again at the end of the day. If each employee has been linked to a system user, then they can encode their time with this action button.</field> - </record> - - <menuitem action="action_hr_attendance_sigh_in_out" id="menu_hr_attendance_sigh_in_out" - parent="menu_hr_attendance" sequence="4"/> - - <record id="view_hr_attendance_so_ask" model="ir.ui.view"> - <field name="name">hr.sign.in.out.ask.form</field> - <field name="model">hr.sign.in.out.ask</field> - <field name="arch" type="xml"> - <form string="hr.sign.out.ask" version="7.0"> - <header> - <button string="Sign in" name="sign_in" type="object" class="oe_highlight"/> - or - <button string="Cancel" class="oe_link" special="cancel" /> - </header> - <group colspan="4" > - <separator string="You did not sign out the last time. Please enter the date and time you signed out." colspan="4" /> - <field name="name" /> - <field name="last_time" string="Your last sign out" /> - </group> - </form> - </field> - </record> - - <record id="view_hr_attendance_si_ask" model="ir.ui.view"> - <field name="name">hr.sign.in.out.ask.form</field> - <field name="model">hr.sign.in.out.ask</field> - <field name="arch" type="xml"> - <form string="hr.sign.in.out.ask" version="7.0"> - <header> - <button string="Sign out" name="sign_out" type="object" class="oe_highlight"/> - or - <button string="Cancel" class="oe_link" special="cancel" /> - </header> - <group colspan="4" > - <separator string="You did not sign in the last time. Please enter the date and time you signed in." colspan="4" /> - <field name="name" /> - <field name="last_time" string="Your last sign in" /> - </group> - </form> - </field> - </record> - - </data> -</openerp> diff --git a/addons/hr_contract/hr_contract_view.xml b/addons/hr_contract/hr_contract_view.xml index 10620cb9a4b..92ae2524921 100644 --- a/addons/hr_contract/hr_contract_view.xml +++ b/addons/hr_contract/hr_contract_view.xml @@ -1,7 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data> + + +<!-- Contracts Button on Employee Form --> + <act_window + context="{'search_default_employee_id': [active_id], 'default_employee_id': active_id}" + id="act_hr_employee_2_hr_contract" + name="Contracts" + res_model="hr.contract" + src_model="hr.employee" + groups="base.group_hr_manager"/> + <menuitem id="next_id_56" name="Contract" parent="hr.menu_hr_configuration" sequence="30" groups="base.group_no_one"/> <record id="hr_hr_employee_view_form2" model="ir.ui.view"> <field name="name">hr.hr.employee.view.form2</field> @@ -9,19 +20,20 @@ <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> <data> - <xpath expr="//field[@name='parent_id']" position="before"> + <xpath expr="//div[@name='button_box']" position="inside"> + <button name="%(act_hr_employee_2_hr_contract)d" string="Contracts" type="action"/> + </xpath> + <xpath expr="//field[@name='coach_id']" position="after"> <field name="manager"/> </xpath> <xpath expr="//group[@name='active_group']" position="before"> - <group> + <group string="Contract"> + <field name="medic_exam" string="Medical Exam"/> <field name="vehicle"/> <field name="vehicle_distance"/> </group> </xpath> - <field name="active" position="before"> - <field name="medic_exam" string="Medical Exam"/> - </field> - <field name="birthday" position="before"> + <field name="marital" position="after"> <field name="children"/> </field> <field name="birthday" position="after"> @@ -177,15 +189,5 @@ <menuitem action="action_hr_contract_type" id="hr_menu_contract_type" parent="next_id_56" sequence="6" groups="base.group_no_one"/> <menuitem action="action_hr_contract" id="hr_menu_contract" parent="hr.menu_hr_main" name="Contracts" sequence="4" groups="base.group_hr_manager"/> - <!-- Contracts Button on Employee Form --> - - <act_window - context="{'search_default_employee_id': [active_id], 'default_employee_id': active_id}" - id="act_hr_employee_2_hr_contract" - name="Contracts" - res_model="hr.contract" - src_model="hr.employee" - groups="base.group_hr_manager"/> - </data> </openerp> diff --git a/addons/hr_contract/i18n/nb.po b/addons/hr_contract/i18n/nb.po new file mode 100644 index 00000000000..9d1886e3ed4 --- /dev/null +++ b/addons/hr_contract/i18n/nb.po @@ -0,0 +1,270 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-08 18:49+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:52+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: hr_contract +#: field:hr.contract,wage:0 +msgid "Wage" +msgstr "Lønn" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Information" +msgstr "Informasjon" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Trial Period" +msgstr "Prøveperiode" + +#. module: hr_contract +#: field:hr.contract,trial_date_start:0 +msgid "Trial Start Date" +msgstr "Prøve Startdato" + +#. module: hr_contract +#: view:hr.employee:0 +msgid "Medical Examination" +msgstr "Legeundersøkelse" + +#. module: hr_contract +#: field:hr.employee,vehicle:0 +msgid "Company Vehicle" +msgstr "Selskapets Kjøretøy." + +#. module: hr_contract +#: view:hr.employee:0 +msgid "Miscellaneous" +msgstr "Diverse" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Current" +msgstr "Nåværende" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Group By..." +msgstr "Grupper etter ..." + +#. module: hr_contract +#: field:hr.contract,department_id:0 +msgid "Department" +msgstr "Avdeling" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Overpassed" +msgstr "Overpassert" + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.contract,employee_id:0 +#: model:ir.model,name:hr_contract.model_hr_employee +msgid "Employee" +msgstr "Ansatt" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Search Contract" +msgstr "Søk Kontrakt" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Contracts in progress" +msgstr "Kontrakter i fremgang" + +#. module: hr_contract +#: field:hr.employee,vehicle_distance:0 +msgid "Home-Work Distance" +msgstr "Hjem-arbeid Avstand." + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.employee,contract_ids:0 +#: model:ir.actions.act_window,name:hr_contract.act_hr_employee_2_hr_contract +#: model:ir.actions.act_window,name:hr_contract.action_hr_contract +#: model:ir.ui.menu,name:hr_contract.hr_menu_contract +msgid "Contracts" +msgstr "Kontrakter" + +#. module: hr_contract +#: view:hr.employee:0 +msgid "Personal Info" +msgstr "Personlig Info" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Contracts whose end date already passed" +msgstr "Kontrakter som sluttdato har allerede passert." + +#. module: hr_contract +#: help:hr.employee,contract_id:0 +msgid "Latest contract of the employee" +msgstr "Siste kontrakten av den ansatte" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Job" +msgstr "Jobb" + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.contract,advantages:0 +msgid "Advantages" +msgstr "Fordeler" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Valid for" +msgstr "Gyldig for." + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Work Permit" +msgstr "Arbeidstillatelse" + +#. module: hr_contract +#: field:hr.employee,children:0 +msgid "Number of Children" +msgstr "Antall barn" + +#. module: hr_contract +#: model:ir.actions.act_window,name:hr_contract.action_hr_contract_type +#: model:ir.ui.menu,name:hr_contract.hr_menu_contract_type +msgid "Contract Types" +msgstr "Kontrakt typer." + +#. module: hr_contract +#: constraint:hr.employee:0 +msgid "Error ! You cannot create recursive Hierarchy of Employees." +msgstr "Feil! Du kan ikke opprette rekursiv Hierarki av ansatte." + +#. module: hr_contract +#: field:hr.contract,date_end:0 +msgid "End Date" +msgstr "Sluttdato" + +#. module: hr_contract +#: help:hr.contract,wage:0 +msgid "Basic Salary of the employee" +msgstr "Grunnlønn for den ansatte." + +#. module: hr_contract +#: field:hr.contract,name:0 +msgid "Contract Reference" +msgstr "Kontrakt referanse." + +#. module: hr_contract +#: help:hr.employee,vehicle_distance:0 +msgid "In kilometers" +msgstr "I kilometer." + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.contract,notes:0 +msgid "Notes" +msgstr "Notater" + +#. module: hr_contract +#: field:hr.contract,permit_no:0 +msgid "Work Permit No" +msgstr "Arbeidstillatelse Ingen." + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.employee,contract_id:0 +#: model:ir.model,name:hr_contract.model_hr_contract +#: model:ir.ui.menu,name:hr_contract.next_id_56 +msgid "Contract" +msgstr "Kontrakt" + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.contract,type_id:0 +#: view:hr.contract.type:0 +#: field:hr.contract.type,name:0 +#: model:ir.model,name:hr_contract.model_hr_contract_type +msgid "Contract Type" +msgstr "Kontrakt type." + +#. module: hr_contract +#: view:hr.contract:0 +#: field:hr.contract,working_hours:0 +msgid "Working Schedule" +msgstr "Arbeidsplan" + +#. module: hr_contract +#: view:hr.employee:0 +msgid "Job Info" +msgstr "Jobb informasjon." + +#. module: hr_contract +#: field:hr.contract,visa_expire:0 +msgid "Visa Expire Date" +msgstr "Visa utløpsdato." + +#. module: hr_contract +#: field:hr.contract,job_id:0 +msgid "Job Title" +msgstr "Jobb Tittel" + +#. module: hr_contract +#: field:hr.employee,manager:0 +msgid "Is a Manager" +msgstr "Er en leder." + +#. module: hr_contract +#: field:hr.contract,date_start:0 +msgid "Start Date" +msgstr "Startdato" + +#. module: hr_contract +#: constraint:hr.contract:0 +msgid "Error! contract start-date must be lower then contract end-date." +msgstr "Feil! Kontrakten startdato må være lavere enn kontrakt sluttdato." + +#. module: hr_contract +#: field:hr.contract,visa_no:0 +msgid "Visa No" +msgstr "Visa Nei." + +#. module: hr_contract +#: field:hr.employee,place_of_birth:0 +msgid "Place of Birth" +msgstr "Fødested" + +#. module: hr_contract +#: view:hr.contract:0 +msgid "Duration" +msgstr "Varighet" + +#. module: hr_contract +#: field:hr.employee,medic_exam:0 +msgid "Medical Examination Date" +msgstr "Legeundersøkelse Dato" + +#. module: hr_contract +#: field:hr.contract,trial_date_end:0 +msgid "Trial End Date" +msgstr "Prøve Sluttdato ." + +#. module: hr_contract +#: view:hr.contract.type:0 +msgid "Search Contract Type" +msgstr "Søk Kontraktstype" diff --git a/addons/hr_evaluation/__openerp__.py b/addons/hr_evaluation/__openerp__.py index 2662b637334..4be0f94947c 100644 --- a/addons/hr_evaluation/__openerp__.py +++ b/addons/hr_evaluation/__openerp__.py @@ -38,11 +38,10 @@ created and it can be defined which level of employee hierarchy fills what and final review and evaluation is done by the manager. Every evaluation filled by the employees can be viewed in the form of pdf file. """, - 'demo': ['hr_evaluation_demo.xml'], - 'data': [ + "demo": ["hr_evaluation_demo.xml"], + "data": [ 'security/ir.model.access.csv', 'security/hr_evaluation_security.xml', -# 'wizard/hr_evaluation_mail_view.xml', 'hr_evaluation_view.xml', 'report/hr_evaluation_report_view.xml', 'board_hr_evaluation_view.xml', diff --git a/addons/hr_evaluation/hr_evaluation.py b/addons/hr_evaluation/hr_evaluation.py index def21f2b169..3508dc36d49 100644 --- a/addons/hr_evaluation/hr_evaluation.py +++ b/addons/hr_evaluation/hr_evaluation.py @@ -228,9 +228,13 @@ class hr_evaluation(osv.osv): body = phase.mail_body % {'employee_name': child.name, 'user_signature': child.user_id.signature, 'eval_name': phase.survey_id.title, 'date': time.strftime('%Y-%m-%d'), 'time': time } sub = phase.email_subject - dest = [child.work_email] - if dest: - mail_message.schedule_with_attach(cr, uid, evaluation.employee_id.work_email, dest, sub, body, context=context) + if child.work_email: + vals = {'state': 'outgoing', + 'subject': sub, + 'body_html': '<pre>%s</pre>' % body, + 'email_to': child.work_email, + 'email_from': evaluation.employee_id.work_email} + self.pool.get('mail.mail').create(cr, uid, vals, context=context) self.write(cr, uid, ids, {'state':'wait'}, context=context) return True diff --git a/addons/hr_evaluation/hr_evaluation_data.xml b/addons/hr_evaluation/hr_evaluation_data.xml index cf632a795cc..2cf656a72bb 100644 --- a/addons/hr_evaluation/hr_evaluation_data.xml +++ b/addons/hr_evaluation/hr_evaluation_data.xml @@ -14,12 +14,14 @@ <field name="sequence">100</field> </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Employee Appraisals has been installed</value> - <value>Create evaluations for your subordinates or manager. You can define a plan with several surveys, where you organize evaluation surveys related to the hierarchy levels. Evaluations filled by employees can exported as pdf files.</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Employee Appraisals application installed!</field> + <field name="body">Manage employee reviews: you can define an appraisal campaign with several steps, with specific evaluation surveys according to hierarchy levels. +Evaluations filled by employees may be exported as pdf files.</field> + </record> <record id="survey_2" model="survey"> <field name="title">Self Appraisal</field> diff --git a/addons/hr_evaluation/hr_evaluation_view.xml b/addons/hr_evaluation/hr_evaluation_view.xml index 12c5dc0af6d..8744567c597 100644 --- a/addons/hr_evaluation/hr_evaluation_view.xml +++ b/addons/hr_evaluation/hr_evaluation_view.xml @@ -128,14 +128,12 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> - <page string="Personal Information" position="after"> - <page string="Appraisal" groups="base.group_hr_user"> - <group col="4"> - <field name="evaluation_plan_id" on_change="onchange_evaluation_plan_id(evaluation_plan_id, evaluation_date)"/> - <field name="evaluation_date"/> - </group> - </page> - </page> + <xpath expr="//group[@name='active_group']" position="before"> + <group string="Appraisals"> + <field name="evaluation_plan_id" on_change="onchange_evaluation_plan_id(evaluation_plan_id, evaluation_date)"/> + <field name="evaluation_date"/> + </group> + </xpath> </field> </record> @@ -267,16 +265,27 @@ <field name="arch" type="xml"> <form string="Interview Appraisal" version="7.0"> <header> - <button string="Cancel" name="survey_req_cancel" states="draft,waiting_answer" type="object" icon="gtk-cancel"/> - <button name="action_print_survey" string="Print Survey" type="object" states="draft" icon="gtk-print" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0}" attrs="{'readonly':[('survey_id','=',False)]}" class="oe_highlight"/> - <button string="Send Request" name="survey_req_waiting_answer" states="draft" type="object" icon="gtk-yes" class="oe_highlight"/> - <button name="%(survey.action_view_survey_question_message)d" string="Answer Survey" type="action" states="waiting_answer" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0, 'active' : response,'request' : True, 'object' : 'hr.evaluation.interview', 'cur_id' : active_id}" attrs="{'readonly':[('survey_id','=',False)]}"/> - <button string="Done" name="survey_req_done" states="waiting_answer" type="object" icon="gtk-jump-to"/> + <button string="Cancel" name="survey_req_cancel" type="object" + states="draft,waiting_answer"/> + <button string="Print Survey" name="action_print_survey" type="object" + states="draft" context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0}" + attrs="{'readonly':[('survey_id','=',False)]}" class="oe_highlight"/> + <button string="Send Request" name="survey_req_waiting_answer" type="object" + states="draft" class="oe_highlight"/> + <button string="Answer Survey" name="%(survey.action_view_survey_question_message)d" type="action" + states="waiting_answer" + context="{'survey_id': survey_id, 'response_id': [response], 'response_no':0, 'active' : response,'request' : True, 'object' : 'hr.evaluation.interview', 'cur_id' : active_id}" + attrs="{'readonly':[('survey_id','=',False)]}"/> + <button string="Done" name="survey_req_done" type="object" + states="waiting_answer"/> <field name="state" widget="statusbar" statusbar_visible="waiting_answer,done"/> </header> <sheet> <div class="oe_right oe_button_box" name="button_box"> - <button string="Send Reminder Email" name="%(mail.action_email_compose_message_wizard)d" icon="terp-mail-message-new" type="action" states="waiting_answer"/> + <button string="Send Reminder Email" name="%(mail.action_email_compose_message_wizard)d" type="action" + states="waiting_answer" + context="{'default_body_text': 'Hello,\n\nKindly post your response for the survey interview.\n\nThanks', + 'default_subject': 'Reminder to fill up Survey' }"/> </div> <group> <group col="3" colspan="1"> @@ -389,21 +398,23 @@ <!-- Email Compose message Action--> <act_window - id="evaluation_reminders" name="Appraisal Reminders" - res_model="mail.compose.message" - src_model="hr.evaluation.interview" - view_type="form" view_mode="form" - target="new" multi="True" - key2="client_action_multi" - context="{'mail.compose.message.mode':'mass_mail'}"/> + id="evaluation_reminders" name="Appraisal Reminders" + res_model="mail.compose.message" + src_model="hr.evaluation.interview" + view_type="form" view_mode="form" + target="new" multi="True" + key2="client_action_multi" + context="{'default_composition_mode': 'mass_mail', + 'default_body_text': 'Hello,\n\nKindly post your response for the survey interview.\n\nThanks', + 'default_subject': 'Reminder to fill up Survey'}"/> <!-- Appraisal Interviews Button on Employee Form --> <act_window - context="{'search_default_user_to_review_id': [active_id], 'default_user_to_review_id': active_id}" - id="act_hr_employee_2_hr__evaluation_interview" - name="Appraisal Interviews" - res_model="hr.evaluation.interview" - src_model="hr.employee"/> + context="{'search_default_user_to_review_id': [active_id], 'default_user_to_review_id': active_id}" + id="act_hr_employee_2_hr__evaluation_interview" + name="Appraisal Interviews" + res_model="hr.evaluation.interview" + src_model="hr.employee"/> </data> </openerp> diff --git a/addons/hr_evaluation/wizard/hr_evaluation_mail.py b/addons/hr_evaluation/wizard/hr_evaluation_mail.py deleted file mode 100644 index 35c54a4cca3..00000000000 --- a/addons/hr_evaluation/wizard/hr_evaluation_mail.py +++ /dev/null @@ -1,42 +0,0 @@ -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## - -from osv import fields, osv -import tools - -class hr_evaluation_reminder(osv.osv_memory): - _name = "hr.evaluation.reminder" - _description = "Sends Reminders to employees to fill the evaluations" - _columns = { - 'evaluation_id': fields.many2one('hr.evaluation.interview', 'Interview', required=True) - } - - def send_mail(self, cr, uid, ids, context=None): - mail_message = self.pool.get('mail.message') - hr_evaluation_interview_obj = self.pool.get('hr.evaluation.interview') - evaluation_data = self.read(cr, uid, ids, context=context)[0] - current_interview = hr_evaluation_interview_obj.browse(cr, uid, evaluation_data.get('evaluation_id')) - if current_interview.state == "waiting_answer" and current_interview.user_to_review_id.work_email : - msg = " Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks," %(current_interview.user_to_review_id.name, current_interview.survey_id.title) - mail_message.schedule_with_attach(cr, uid, tools.config['email_from'], [current_interview.user_to_review_id.work_email],\ - 'Reminder to fill up Survey', msg, context=context) - return {'type': 'ir.actions.act_window_close'} - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_evaluation/wizard/hr_evaluation_mail_view.xml b/addons/hr_evaluation/wizard/hr_evaluation_mail_view.xml deleted file mode 100644 index 89ae99bb6c3..00000000000 --- a/addons/hr_evaluation/wizard/hr_evaluation_mail_view.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<openerp> - <data> - - <record id="view_hr_evaluation_send_mail" model="ir.ui.view"> - <field name="name">hr.evaluation.send.mail</field> - <field name="model">hr.evaluation.reminder</field> - <field name="arch" type="xml"> - <form string="Evaluation Reminders" version="7.0"> - <group string="Send Evaluation Reminder"> - <field name="evaluation_id"/> - </group> - <footer> - <button name="send_mail" string="Send Mail" type="object" class="oe_highlight"/> - or - <button string="Cancel" class="oe_link" special="cancel" /> - </footer> - </form> - </field> - </record> - - <record id="action_hr_evaluation_send_mail" model="ir.actions.act_window"> - <field name="name">Evaluation Send Mail</field> - <field name="res_model">hr.evaluation.reminder</field> - <field name="type">ir.actions.act_window</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="view_id" ref="view_hr_evaluation_send_mail"/> - <field name="context">{'record_id':active_id}</field> - <field name="target">new</field> - </record> - - </data> -</openerp> diff --git a/addons/hr_evaluation/wizard/mail_compose_message.py b/addons/hr_evaluation/wizard/mail_compose_message.py deleted file mode 100644 index 7e4ba326947..00000000000 --- a/addons/hr_evaluation/wizard/mail_compose_message.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-Today OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv -from osv import fields -import tools -from tools.translate import _ - -class mail_compose_message(osv.osv_memory): - _inherit = 'mail.compose.message' - - def get_value(self, cr, uid, model, resource_id, context=None): - ''' - To get values of the resource_id for the model - @param model: Object - @param resource_id: id of a record for which values to be read - - @return: Returns a dictionary - ''' - if context is None: - context = {} - result = super(mail_compose_message, self).get_value(cr, uid, model, resource_id, context=context) - if model == 'hr.evaluation.interview' and resource_id: - model_pool = self.pool.get(model) - record_data = model_pool.browse(cr, uid, resource_id, context) - if record_data.state == "waiting_answer": - msg = _("Hello %s, \n\n Kindly post your response for '%s' survey interview. \n\n Thanks,") %(record_data.user_to_review_id.name, record_data.survey_id.title) - result.update({ - 'email_to': record_data.user_to_review_id.work_email or False, - 'subject': _("Reminder to fill up Survey"), - 'body_text': msg, - }) - return result - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_expense/__openerp__.py b/addons/hr_expense/__openerp__.py index 9c148ad9019..63fca88b148 100644 --- a/addons/hr_expense/__openerp__.py +++ b/addons/hr_expense/__openerp__.py @@ -21,14 +21,14 @@ { - 'name': 'Expenses Management', + 'name': 'Expense Management', 'version': '1.0', 'category': 'Human Resources', 'sequence': 30, 'summary': 'Expenses Validation, Invoicing', 'description': """ -This module aims to manage employee's expenses. -=============================================== +This module aims to manage employee expenses. +============================================= The whole workflow is implemented: ---------------------------------- diff --git a/addons/hr_expense/board_hr_expense_view.xml b/addons/hr_expense/board_hr_expense_view.xml index fe89a4afc7a..3f896a76fae 100644 --- a/addons/hr_expense/board_hr_expense_view.xml +++ b/addons/hr_expense/board_hr_expense_view.xml @@ -17,7 +17,7 @@ <field name="inherit_id" ref="hr.board_hr_form"/> <field name="arch" type="xml"> <xpath expr="/form/board/column[1]" position="inside"> - <action name="%(action_my_expense)d" string="My Expenses" creatable="true"/> + <action name="%(action_my_expense)d" string="My Expenses"/> </xpath> </field> </record> diff --git a/addons/hr_expense/hr_expense.py b/addons/hr_expense/hr_expense.py index 594b869c71e..83f986e59cc 100644 --- a/addons/hr_expense/hr_expense.py +++ b/addons/hr_expense/hr_expense.py @@ -256,6 +256,10 @@ class hr_expense_line(osv.osv): res = dict(cr.fetchall()) return res + def _get_uom_id(self, cr, uid, context=None): + result = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'product', 'product_uom_unit') + return result and result[1] or False + _columns = { 'name': fields.char('Expense Note', size=128, required=True), 'date_value': fields.date('Date', required=True), @@ -264,7 +268,7 @@ class hr_expense_line(osv.osv): 'unit_amount': fields.float('Unit Price', digits_compute=dp.get_precision('Product Price')), 'unit_quantity': fields.float('Quantities', digits_compute= dp.get_precision('Product Unit of Measure')), 'product_id': fields.many2one('product.product', 'Product', domain=[('hr_expense_ok','=',True)]), - 'uom_id': fields.many2one('product.uom', 'Unit of Measure'), + 'uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True), 'description': fields.text('Description'), 'analytic_account': fields.many2one('account.analytic.account','Analytic account'), 'ref': fields.char('Reference', size=32), @@ -273,20 +277,31 @@ class hr_expense_line(osv.osv): _defaults = { 'unit_quantity': 1, 'date_value': lambda *a: time.strftime('%Y-%m-%d'), + 'uom_id': _get_uom_id, } _order = "sequence, date_value desc" - def onchange_product_id(self, cr, uid, ids, product_id, uom_id, employee_id, context=None): + def onchange_product_id(self, cr, uid, ids, product_id, context=None): res = {} if product_id: product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) res['name'] = product.name amount_unit = product.price_get('standard_price')[product.id] res['unit_amount'] = amount_unit - if not uom_id: - res['uom_id'] = product.uom_id.id + res['uom_id'] = product.uom_id.id return {'value': res} + def onchange_uom(self, cr, uid, ids, product_id, uom_id, context=None): + res = {'value':{}} + if not uom_id or not product_id: + return res + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom = self.pool.get('product.uom').browse(cr, uid, uom_id, context=context) + if uom.category_id.id != product.uom_id.category_id.id: + res['warning'] = {'title': _('Warning'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure')} + res['value'].update({'uom_id': product.uom_id.id}) + return res + hr_expense_line() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_expense/hr_expense_data.xml b/addons/hr_expense/hr_expense_data.xml index 6e41f3d4e05..2a9f3ae0a56 100644 --- a/addons/hr_expense/hr_expense_data.xml +++ b/addons/hr_expense/hr_expense_data.xml @@ -2,20 +2,20 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Expenses Management has been installed</value> - <value>Manage your employee's expenses, with validations by employee manager and accountant, creation and payment of invoices. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Expense Management application installed!</field> + <field name="body">Manage your employees' expenses, after due validation by their manager and the accountant, then generate and pay the corresponding invoices. -This module also uses the analytic accounting and is compatible with the invoice on timesheet module so that you will be able to automatically re-invoice your customer's expenses if your work by project.</value> - </function> +This feature is also linked to analytic accounting and compatible with timesheet invoices, so you will be able to automatically re-invoice project-related expenses to your customers.</field> + </record> <!-- Resource: product.uom.categ --> <record id="cat_expense" model="product.category"> <field name="parent_id" ref="product.product_category_all"/> <field name="name">Expenses</field> </record> - </data> </openerp> diff --git a/addons/hr_expense/hr_expense_demo.xml b/addons/hr_expense/hr_expense_demo.xml index e1d6b0a00b0..eddaf03ad38 100644 --- a/addons/hr_expense/hr_expense_demo.xml +++ b/addons/hr_expense/hr_expense_demo.xml @@ -11,6 +11,8 @@ <field name="type">service</field> <field name="name">Car Travel Expenses</field> <field name="default_code">CarTRA</field> + <field name="uom_id" ref="product.product_uom_km"/> + <field name="uom_po_id" ref="product.product_uom_km"/> <field name="categ_id" ref="hr_expense.cat_expense"/> <field name="hr_expense_ok" eval="True" /> <field name="image">/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCACAAIADASIAAhEBAxEB/8QAHQABAAMBAQEBAQEAAAAAAAAAAAYHCAUEAgMBCf/EAD0QAAEDAwMCBAQDBgILAAAAAAECAwQABREGEiEHMRMiQVEUYXGBCDJCFVKRobHBJHIjJSYzU2JjkqLR8f/EABsBAQADAAMBAAAAAAAAAAAAAAABAgMEBQYH/8QAIREAAgICAgIDAQAAAAAAAAAAAAECEQMxBCESQQUTUWH/2gAMAwEAAhEDEQA/ANl0pSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUPHegFKqHUP4gtE2TXh07LcJgoy29dUry028P04A5SOxXnAPpgE1JI3UITZkQw7HcDbHnkNOTXE4QkrVtTgjKSMlOeQRkcHnFZSUdkWTqlKVYkUpSgFKUoBSlKAUpXE1xMulv0vMm2cxhLZSFgyM+GlORuUrHIATk/b1qG6Vg7ZIHc4qNaj11piwrLM25oXIGR8OwkuuZAzgpTnB5HfFQeTZdYX8PN3i7lCOApiNuc86TlaVHyNp4IAOF9s5znPI6j6ah6S6eXS5QGC1P8ACKWpD7xeU3htawNoASMLQPygE5I9TnD7rdIo5P0d299Urk7GcVYLO2yEpUS9O3KSkjuFBHlGMHPn4xzWf+tnVPUEh1Nni6tdnvutlD7cTLDCSrgM5GN54OSQQPRRAOIrctd3U6MtGjUY8KFtakORStXibfKlWVAFKNoSoggH37VCY6ZbL0gKt6UPqVtbfU4MqSfTk5Hb78e1bKPthJvtn2i2NiIVXNxK+fEVtPc+xz+ntx645x2q4/w89U2YUiboXVCXYtuuEdMeBdnFK2Q9qdjbSgo4DQz5VJxtOArjGyrm2y3aplxcX4zkRtDyuMgZdQjA/wC/Ga5Oqp4MVQTyokhORkE+386SUZ2i9H+lOnppuNliTFpCHHGgXUfuL/Un6g5H2r31lD8LPW+NAeh9ONWuhkKV4dsmqVkBZyrwnD8zkpUfmPQVq+pSpUBSlKkClKUArk3m+x4ClMto+IkpHKEqwEf5len05PyqLXnqA0qZc7ZEbVEXBeLDrzxCVgj9SUH0PO1R4I55qttQa8tMRCm/j20jJJ2qK1E+pOM8/WrKP6VcixpespgdKX32WEezIz/M819sXq1XJCmH5aJSHElDranMkpIwoY+hNZyvvVG3N7gw0+8r5kJH9/6VCrlr+5z3P8FFaZVnyqJKiPvxVvG+inmkaR6vWm6XnpmyI0xKH23izcAuYhlLrjYU3u3OnbgLSVbPXII5AqsurXUlFi6YR+nfhplagdUpElBUsphpLhUhlCjys7SEjBKQngFQwa7GutcX3S+grVKbiPLub7CPEuyHCExJPhJbdQoAYKlBsLAUcEqKiCUis7Wtplx5+7uOmXIySSs7ikkZOCe6vf6/XPEx4v30WUVdnmZdnW1pEaTIQ4XleYk8NH90+/19Tx7Z6d1c0/OsMdpMyQzNZWsy5ToIZCR2Awnyqzj1OMnPoK/F1SGoy33mytx9OFIUnkJI/JgjP1FerR9jTPv8Ry5SEORY2Hww5kkkKATvJJCtqlA49QOScVbO1GHm21XfRtjVvxS2Xj0W6VQbppC4S7/MhBmehCWhHdWVuNIdbd3KKkpKFZ8pwD3BPOQbP0/orRRjqdTpi2pZQNjS0FTgW2M4P1PJ9TznknnixrlEYuirN8SFtIS0ypWQUJI8MEj67Nx5PJ+RzNL3c0xVfDoTtbCMg+mc9v8A73z64OPG8jm5c7t9HY/QoKl7IDrXpR03vYU27p2PbFKjrdhzYp/0jqinJBbIyoJBSR6Z7j3sjor+1bfppWnL9fReJ1uecbjvqaKVripVtRuUSfFUkgpK+DwMjPKonp+beJ18iMSYMl21yWkre3tEtI3NhxCgVDGQrbyPXNdaM87a5chKch21J+JbISUpUjcpS0pGBnxEL291eYcklFb8H5HJgyJTdxK5cCa62WlXy4422MuLSn2ye9RzWGqWLNNh2popVPmJW4AeQ20j8yz9SQAPqedpFeG33tmUwVpWC8OHNx8wPzr2KVnWN0Sd2elP+7bUr5qO0f8Av+Vc6ZcXlJKfHDYP/DHP8T/bFcaVcfdVciZdAkHzVdRKuRR/XnpZrGfcF3vSVyRcvOVuQpjpS73Jw252wCcgHbgjOTk1nfVN41hZ2HWNQ2qZapiAUp+IjKShw+4VjaftxW35N1JJwquRcbhEU2USi0pK+CleDu+WPWrqBRyRgiFqyciSFzW0SW8+Ydj9vSrl6UR2dQartwaT/g20/GOkjshIyM/faPvVwXLRGjr86r/Yy0urPd1UVLSj9wN1fpaek1hscG4v22Iq3qkMbXfAeWAUpO4ABROBkDOO9Sk1tkOn2kVjrzTolWU3+VqJS1SyqZ8A40Qhkr525Ku4TgE49KgMaBPhRkKctc2JBffKg8Y60NLTgEjcocn2we38tb6f0XZLWlEhMZoyCkbn3AFOH5bjzj5V+2qLDpq+W/4G7wxJZGdqQ4pGCRjPBHPPHtUSSeiYWtmSEyDKkPyjhLcfhBUO6+c/w/vUs6KFiVqb4aU+USSUuJU3jKACf3sDuUd8f3rgdQNLS9DXUxVuqftYG6HJcwTIUT+VWOygAB7flxxXO0fqlNm1XCmr8kcqKLglxHKUHGSfXAUEHjuBxXXc/FLJx5wjujl8eajkjJ6NGavkSUaptbCg1CakR222kqUA22dxSSAkEpSVZ75JIUe2APXe71Mt8VyLMl+NsT5VDkAdsA4B9D3yf6V+V1tMC6WaN4KmfEfUBCkB3ISrnc1+YknKUY743n6VH9WW25sSVIvihHS3ILCnEhSmXlAE5BAynjBAI5BOOxFeGjO1o9B4J0Szpzd79b9YIgy5a3bdIaCg0V7ko3I3pUnP5T2BHYg+vBFlXV1hy5xfihva8Vt0jKU7Qg7iSc5UBtBx37/pJxWumbe/EQu6PEttMN+L4jx2hW4KwST8kng4+5xUvsEVV5vq2GtoRPyXE5CiljCUrcKh2Ckp2pHuoHnHDDDJlko122YZ1GLcl6R99f8ARNy1Xb5z2m7oLVqYRVxYshTqkBTZIJRweCRuAVg7d5I5ArHcbXPWHpC+mzamtklyMydjQuCFHA/6T6TyPllQHtW2uvGlnr7CgzoEow7hBcK47wUUgKIwQSOQDx5h2IFQS0ajuakpsmqWGlOq8gRLQkeL9P0ufVOa+jJnn/4Qbpp1jd1daYzsiO5BnSZSozEZRDod2hGVhWEYTlYHPr6nBxZ93tV9iqQiVJitleM4bPH/AJVz9R6Z0u6mOxL0/ZpIaR4jTbkVKvCCieyTwnJB7d8VFbhFsiY6X4dptewZCHERkEcexxitV5PTMnS2SCTAd+JQybiJfiAEFlwBI+RIA9vevjUlw0toiz/tW+T4kRCuEuPDcXFeyEDKln7Go7YrwtqRIkTJJ8Fsc+gSkDKjx8qg8bTEjqXqJOodRoubsOXIDEKPEJRllLqEOYcKVBKUJWpWwAFzw3DkYzUzk49CCUuz1x+vWm7jfGosCdJjulwJZcfihttRzwMjtn54q2pOoPjrGp9HAdjk49iU8isfdcOn8bSa7fcrYzPisyh4ciJLWFuRpCUpK0pWEp8RvJUlK9oyW19wATpK1ufCabjR5jqWlljCysgYURk/1qMb8tia8X0S9dzdU0nzY8orrQrLHmJbW7qizN+IAQ2mQFOD5FJxzVUy7zECdrkyRKOPyNDw0fx7/wAzXPN6fBKYENmPnuoJ3KP39amUor2EpP0XLqTpPbtQRBHmXNclkAkbYYVtVwUrSd/BBGQaqbSH4ZpMXWFwumqbquXDYdxbURI53PoyTveSrhJHHlBVznngZ8bMG93VwA/EO5OQOcD7VLrB071LL2nxJDST/wA5FYuTbNVGkTmLo1+2x3E2mTcQpWCpp9tXhukdtxCs/fn6HAxH9W6a1TcoyIqdLvObVBxXguJ8EqxjKUZTtPzxzx6ZBlen+lD6QlU2fIVj08U1OrToe0wkjLfiEeqjmuo5PxHH5E3k7Te69nMw8zJiVLtf0q3T2mtbzGmWZNvh2dKUhLj8p/x3iUgBJQE8oAx23f0AEz0fo692ic7KfujcpTy9ziwVFaz7qJFT+NAiRkgNMoTj2FeoADsK34vx+HjO4Lv9ZTLyJ5dnjulvZuEcsvDIIqA6g6cuvMuIgzlpbUMFlwBxsj22qzx9MVZVK5ybWjjtJ7M63LSeqrE24iHbLctkncRGa8Hce2cDIzUEuX7WhtLjrtT8ZtSysthRUgKPchPYfathuNoWMLSCPmK5Vx05apwIeitkn5VosrRm8SZhjXd7etdmSlTbjSpbyGB5VDgqG4k/5c1ZfS7Ulu05p6JeEwlyQLeqK4I7Sd6XGy45sVglSiUhaxwkDcANxXxd2pOkumb3BchzYTbrDndJ4wfcEcg/MVAJf4aLWp3/AFdqO7QG1DatKFgkj23DB/jmqzm5Oy0IKKozp1h+M1tq9y1W5LLU1UpyZMcS0geCQNjYUtCQpZxkjf5khSQexqeWPSmoLiwz4zTrz+xIccOTuVjk8/OtCaE6KaQ0lEDEGIXFE5cddIUtw+5P9gAKsCFaLfESEsx0JA+VULmdtP8ASC5yilUkbEn3qxLB0itcQJVJAWoVaaUpSMJAFf2gOHa9LWi3pAZit5Hriuw0y02MIQlI+Qr9KUApSlAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAf/2Q==</field> diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 9978490b29a..5389f88b885 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -89,7 +89,7 @@ <form string="Expense Lines" version="7.0"> <group> <group> - <field name="product_id" on_change="onchange_product_id(product_id, uom_id, parent.employee_id, context)" context="{'default_hr_expense_ok':1}"/> + <field name="product_id" on_change="onchange_product_id(product_id, context)" context="{'default_hr_expense_ok':1}"/> <field name="name"/> <field name="ref"/> <field domain="[('type','=','normal')]" name="analytic_account" groups="analytic.group_analytic_accounting"/> @@ -99,12 +99,24 @@ <label for="unit_quantity"/> <div> <field name="unit_quantity" class="oe_inline"/> - <field name="uom_id" on_change="onchange_product_id(product_id, uom_id, parent.employee_id, context)" class="oe_inline"/> + <field name="uom_id" on_change="onchange_uom(product_id, uom_id, context)" class="oe_inline"/> </div> <field name="date_value" /> </group> </group> </form> + <tree string="Expense Lines" editable="bottom"> + <field name="sequence" invisible="1"/> + <field name="product_id" on_change="onchange_product_id(product_id, context)" context="{'default_hr_expense_ok':1}"/> + <field name="date_value" string="Expense Date"/> + <field name="name"/> + <field name="ref"/> + <field domain="[('type','in',['normal','contract']), ('parent_id','!=',False)]" name="analytic_account" groups="analytic.group_analytic_accounting"/> + <field name="uom_id" on_change="onchange_uom(product_id, uom_id, context)"/> + <field name="unit_amount"/> + <field name="unit_quantity"/> + <field name="total_amount" sum="Total"/> + </tree> </field> <group> <div> diff --git a/addons/hr_holidays/__openerp__.py b/addons/hr_holidays/__openerp__.py index d5a2728c763..070ae0344b0 100644 --- a/addons/hr_holidays/__openerp__.py +++ b/addons/hr_holidays/__openerp__.py @@ -21,7 +21,7 @@ { - 'name': 'Leaves Management', + 'name': 'Leave Management', 'version': '1.5', 'author': 'OpenERP SA', 'category': 'Human Resources', @@ -29,8 +29,8 @@ 'summary': 'Holidays, Allocation and Leave Requests', 'website': 'http://www.openerp.com', 'description': """ -This module allows you to manage leaves and leave's requests. -============================================================= +This module allows you to manage leaves and leave requests. +=========================================================== Implements a dashboard for human resource management that includes: ------------------------------------------------------------------- diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 5840217d7ee..759170224bd 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -25,6 +25,7 @@ import datetime, time from itertools import groupby from operator import itemgetter +import math import netsvc from osv import fields, osv from tools.translate import _ @@ -94,7 +95,7 @@ class hr_holidays(osv.osv): _name = "hr.holidays" _description = "Leave" _order = "type desc, date_from asc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = [ 'mail.thread','ir.needaction_mixin'] def _employee_get(self, cr, uid, context=None): ids = self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) @@ -112,7 +113,7 @@ class hr_holidays(osv.osv): return result _columns = { - 'name': fields.char('Description', required=True, size=64), + 'name': fields.char('Description', size=64), 'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')], 'State', readonly=True, help='The state is set to \'To Submit\', when a holiday request is created.\ \nThe state is \'To Approve\', when holiday request is confirmed by user.\ @@ -123,8 +124,6 @@ class hr_holidays(osv.osv): 'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), 'holiday_status_id': fields.many2one("hr.holidays.status", "Leave Type", required=True,readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), 'employee_id': fields.many2one('hr.employee', "Employee", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='Leave Manager can let this field empty if this leave request/allocation is for every employee'), - #'manager_id': fields.many2one('hr.employee', 'Leave Manager', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'), - #'notes': fields.text('Notes',readonly=True, states={'draft':[('readonly',False)]}), 'manager_id': fields.many2one('hr.employee', 'First Approval', invisible=False, readonly=True, help='This area is automatically filled by the user who validate the leave'), 'notes': fields.text('Reasons',readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), 'number_of_days_temp': fields.float('Number of Days', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), @@ -134,8 +133,8 @@ class hr_holidays(osv.osv): 'parent_id': fields.many2one('hr.holidays', 'Parent'), 'linked_request_ids': fields.one2many('hr.holidays', 'parent_id', 'Linked Requests',), 'department_id':fields.related('employee_id', 'department_id', string='Department', type='many2one', relation='hr.department', readonly=True, store=True), - 'category_id': fields.many2one('hr.employee.category', "Category", help='Category of Employee'), - 'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Category')], 'Allocation Type', help='By Employee: Allocation/Request for individual Employee, By Employee Category: Allocation/Request for group of employees in category', required=True), + 'category_id': fields.many2one('hr.employee.category', "Category", help='Category of Employee', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}), + 'holiday_type': fields.selection([('employee','By Employee'),('category','By Employee Category')], 'Allocation Mode', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, help='By Employee: Allocation/Request for individual Employee, By Employee Category: Allocation/Request for group of employees in category', required=True), 'manager_id2': fields.many2one('hr.employee', 'Second Approval', readonly=True, help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)'), 'double_validation': fields.related('holiday_status_id', 'double_validation', type='boolean', relation='hr.holidays.status', string='Apply Double Validation'), } @@ -147,16 +146,11 @@ class hr_holidays(osv.osv): 'holiday_type': 'employee' } _sql_constraints = [ - ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "You have to select an employee or a category."), + ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."), ('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be before the end date !"), ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0 !"), ] - def create(self, cr, uid, vals, context=None): - obj_id = super(hr_holidays, self).create(cr, uid, vals, context=context) - self.create_notificate(cr, uid, [obj_id], context=context) - return obj_id - def _create_resource_leave(self, cr, uid, leaves, context=None): '''This method will create entry in resource calendar leave object at the time of holidays validated ''' obj_res_leave = self.pool.get('resource.calendar.leaves') @@ -201,8 +195,8 @@ class hr_holidays(osv.osv): def unlink(self, cr, uid, ids, context=None): for rec in self.browse(cr, uid, ids, context=context): - if rec.state<>'draft': - raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is not in draft state !')) + if rec.state not in ['draft', 'cancel', 'confirm']: + raise osv.except_osv(_('Warning!'),_('You cannot delete a leave which is in %s state!')%(rec.state)) return super(hr_holidays, self).unlink(cr, uid, ids, context) def onchange_date_from(self, cr, uid, ids, date_to, date_from): @@ -210,7 +204,7 @@ class hr_holidays(osv.osv): if date_to and date_from: diff_day = self._get_number_of_days(date_from, date_to) result['value'] = { - 'number_of_days_temp': round(diff_day)+1 + 'number_of_days_temp': round(math.floor(diff_day))+1 } return result result['value'] = { @@ -244,29 +238,30 @@ class hr_holidays(osv.osv): self.unlink(cr, uid, to_unlink, context=context) return True - def holidays_validate(self, cr, uid, ids, context=None): + def holidays_first_validate(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False - self.holidays_validate_notificate(cr, uid, ids, context=context) + self.holidays_first_validate_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager}) - def holidays_validate2(self, cr, uid, ids, context=None): + def holidays_validate(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False self.write(cr, uid, ids, {'state':'validate'}) data_holiday = self.browse(cr, uid, ids) - holiday_ids = [] for record in data_holiday: - if record.holiday_status_id.double_validation: - holiday_ids.append(record.id) + if record.double_validation: + self.write(cr, uid, [record.id], {'manager_id2': manager}) + else: + self.write(cr, uid, [record.id], {'manager_id': manager}) if record.holiday_type == 'employee' and record.type == 'remove': meeting_obj = self.pool.get('crm.meeting') meeting_vals = { - 'name': record.name, + 'name': record.name or _('Leave Request'), 'categ_ids': record.holiday_status_id.categ_id and [(6,0,[record.holiday_status_id.categ_id.id])] or [], 'duration': record.number_of_days_temp * 8, 'description': record.notes, @@ -301,25 +296,27 @@ class hr_holidays(osv.osv): wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'confirm', cr) wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'validate', cr) wf_service.trg_validate(uid, 'hr.holidays', leave_id, 'second_validate', cr) - if holiday_ids: - self.holidays_valid2_notificate(cr, uid, holiday_ids, context=context) - self.write(cr, uid, holiday_ids, {'manager_id2': manager}) + self.holidays_validate_notificate(cr, uid, ids, context=context) return True def holidays_confirm(self, cr, uid, ids, context=None): self.check_holidays(cr, uid, ids, context=context) + for record in self.browse(cr, uid, ids, context=context): + if record.employee_id and record.employee_id.parent_id and record.employee_id.parent_id.user_id: + self.message_subscribe(cr, uid, [record.id], user_ids=[record.employee_id.parent_id.user_id.id], context=context) self.holidays_confirm_notificate(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state':'confirm'}) - def holidays_refuse(self, cr, uid, ids, approval, context=None): + def holidays_refuse(self, cr, uid, ids, context=None): obj_emp = self.pool.get('hr.employee') ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)]) manager = ids2 and ids2[0] or False - if approval == 'first_approval': - self.write(cr, uid, ids, {'state': 'refuse', 'manager_id': manager}) - else: - self.write(cr, uid, ids, {'state': 'refuse', 'manager_id2': manager}) - self.holidays_refuse_notificate(cr, uid, ids, approval, context=context) + for holiday in self.browse(cr, uid, ids, context=context): + if holiday.state == 'validate1': + self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id': manager}) + else: + self.write(cr, uid, [holiday.id], {'state': 'refuse', 'manager_id2': manager}) + self.holidays_refuse_notificate(cr, uid, ids, context=context) self.holidays_cancel(cr, uid, ids, context=context) return True @@ -333,7 +330,7 @@ class hr_holidays(osv.osv): # If a category that created several holidays, cancel all related wf_service = netsvc.LocalService("workflow") for request in record.linked_request_ids or []: - wf_service.trg_validate(uid, 'hr.holidays', request.id, 'cancel', cr) + wf_service.trg_validate(uid, 'hr.holidays', request.id, 'refuse', cr) self._remove_resource_leave(cr, uid, ids, context=context) return True @@ -347,68 +344,53 @@ class hr_holidays(osv.osv): if leaves_rest < record.number_of_days_temp: raise osv.except_osv(_('Warning!'), _('There are not enough %s allocated for employee %s; please create an allocation request for this leave type.') % (record.holiday_status_id.name, record.employee_id.name)) return True - + # ----------------------------- # OpenChatter and notifications # ----------------------------- - - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(hr_holidays, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if obj.state == 'confirm' and obj.employee_id.parent_id: - result[obj.id] = [obj.employee_id.parent_id.user_id.id] - elif obj.state == 'validate1': - # get group_hr_manager: everyone will be warned of second validation - res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_hr_manager') or False - obj_id = res and res[1] or False - if obj_id: - hr_manager_group = self.pool.get('res.groups').read(cr, uid, [obj_id], ['users'], context=context)[0] - result[obj.id] = hr_manager_group['users'] - return result - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' and 'manager' to the monitored fields """ - res = super(hr_holidays, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - # TODO: add manager - return res + ['user_id'] - + def needaction_domain_get(self, cr, uid, ids, context=None): + # to be tested, otherwise convert into employee_id in ... + emp_obj = self.pool.get('hr.employee') + empids = emp_obj.search(cr, uid, [('parent_id.user_id','=',uid)], context=context) + dom = [ + '&', ('state','=','confirm'),('employee_id', 'in', empids) + ] + # if this user is a hr.manager, he should do second validations + if self.pool.get('res.users').has_group(cr, uid, 'base.group_hr_manager'): + dom = ['|'] + dom + [ ('state','=','validate1') ] + return dom + def create_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, ids, _('System notification'), - _("The %s request has been <b>created</b> and is waiting confirmation.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context) + self.message_post(cr, uid, ids, + _("The request has been <b>created</b> and is waiting confirmation."), context=context) return True def holidays_confirm_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request has been <b>confirmed</b> and is waiting for validation by the manager.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification') + self.message_post(cr, uid, [obj.id], + _("The request has been <b>submitted</b> and is waiting for validation by the manager."), context=context) + def holidays_first_validate_notificate(self, cr, uid, ids, context=None): + for obj in self.browse(cr, uid, ids, context=context): + self.message_post(cr, uid, [obj.id], + _("The request has been <b>approved</b>. A second validation is necessary and is now pending."), context=context) + def holidays_validate_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - if obj.holiday_status_id.double_validation: - self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request has been <b>approved</b>. A second validation is necessary and is now pending.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context) + if obj.double_validation: + self.message_post(cr, uid, [obj.id], + _("The request has been <b>double validated</b>. The validation process is now over."), context=context) else: - self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request has been <b>approved</b>. The validation process is now over.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context) - - def holidays_valid2_notificate(self, cr, uid, ids, context=None): - for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request has been <b>double validated</b>. The validation process is now over.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context) + self.message_post(cr, uid, [obj.id], + _("The request has been <b>approved</b>. The validation process is now over."), context=context) - def holidays_refuse_notificate(self, cr, uid, ids, approval, context=None): + def holidays_refuse_notificate(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids): - self.message_append_note(cr, uid, [obj.id], _('System notification'), - _("The %s request has been <b>refused</b>. The validation process is now over.") - % ('leave' if obj.type == 'remove' else 'allocation',), type='notification', context=context) - -hr_holidays() + self.message_post(cr, uid, [obj.id], + _("The request has been <b>refused</b>. The validation process is now over."), context=context) + class resource_calendar_leaves(osv.osv): _inherit = "resource.calendar.leaves" diff --git a/addons/hr_holidays/hr_holidays_data.xml b/addons/hr_holidays/hr_holidays_data.xml index 9db0f2e987a..1e534093eb3 100644 --- a/addons/hr_holidays/hr_holidays_data.xml +++ b/addons/hr_holidays/hr_holidays_data.xml @@ -13,12 +13,15 @@ <field name="sequence">100</field> </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Leaves Management has been installed</value> - <value>Manage employee leaves from the top menu "Human Resources". Employees can create leave requests that are validated by their manager and/or HR people. Once validated, they are visible in the employee's calendar. HR people can define leave types and allocate off-days for employees.</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Leave Management application installed!</field> + <field name="body">Manage employee leaves from the top menu "Human Resources". Employees can create leave requests that are validated by their manager and/or HR officers. +Once validated, they are visible in the employee's calendar. HR officers can define leave types and allocate leaves to employees and employee categories.</field> + </record> + <!-- Casual leave --> <record model="hr.holidays.status" id="holiday_status_cl"> diff --git a/addons/hr_holidays/hr_holidays_demo.xml b/addons/hr_holidays/hr_holidays_demo.xml index 6ad5a442a38..4c9d18683c7 100644 --- a/addons/hr_holidays/hr_holidays_demo.xml +++ b/addons/hr_holidays/hr_holidays_demo.xml @@ -12,7 +12,7 @@ <record model="hr.holidays" id="hr_holidays_employee1_cl"> <field name="name">Trip with Family</field> - <field name="holiday_status_id" ref="holiday_status_cl"/> + <field name="holiday_status_id" ref="holiday_status_comp"/> <field eval="time.strftime('%Y-%m-01')" name="date_from"/> <field eval="time.strftime('%Y-%m-03')" name="date_to"/> <field name="type">remove</field> @@ -52,13 +52,10 @@ <field name="employee_id" ref="hr.employee_fp"/> </record> - <workflow action="confirm" model="hr.holidays" ref="hr_holidays_employee1_allocation_cl"/> <workflow action="validate" model="hr.holidays" ref="hr_holidays_employee1_allocation_cl"/> - <workflow action="confirm" model="hr.holidays" ref="hr_holidays_employee1_sl"/> <workflow action="validate" model="hr.holidays" ref="hr_holidays_employee1_sl"/> - <workflow action="confirm" model="hr.holidays" ref="hr_holidays_employee1_int_tour"/> <workflow action="validate" model="hr.holidays" ref="hr_holidays_employee1_int_tour"/> </data> </openerp> diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index 711d07bbfe8..5a2e0603b88 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -44,7 +44,6 @@ <field name="arch" type="xml"> <search string="Search Leave"> <field name="date_from"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> <separator/> <filter icon="terp-check" domain="[('state','=','draft')]" string="To Confirm"/> <filter icon="terp-camera_test" domain="[('state','=','confirm')]" string="To Approve" name="approve"/> @@ -84,56 +83,42 @@ </field> </record> - <record model="ir.ui.view" id="edit_holiday_new"> + <record model="ir.ui.view" id="edit_holiday_new"> <field name="name">Leave Request</field> <field name="model">hr.holidays</field> <field name="priority">1</field> <field name="arch" type="xml"> <form string="Leave Request" version="7.0"> <header> - <button string="Submit to Manager" name="confirm" states="draft" type="workflow" class="oe_highlight"/> <button string="Approve" name="validate" states="confirm" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> <button string="Validate" name="second_validate" states="validate1" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> - <button string="Refuse" name="refuse" states="confirm,validate1" type="workflow" groups="base.group_hr_user,base.group_hr_manager" class="oe_highlight"/> - <button string="Reset to New" name="set_to_draft" states="refuse,validate" type="object" groups="base.group_hr_user" /> + <button string="Refuse" name="refuse" states="confirm,validate1,validate" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> + <button string="Reset to New" name="set_to_draft" states="refuse" type="object" groups="base.group_hr_user" /> <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate" statusbar_colors='{"confirm":"blue","validate1":"blue","refuse":"red"}'/> </header> <sheet string="Leave Request"> <group> <group> <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/> - <field name="holiday_type" on_change="onchange_type(holiday_type)" attrs="{'readonly':[('state','!=','draft')]}" width="130" string="Mode" groups="base.group_hr_user"/> - <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/> - <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'invisible':[('holiday_type','=','employee')], 'readonly':[('state','!=','draft'), ('state','!=','confirm')]}"/> - <field name="department_id" attrs="{'readonly':[('holiday_type','=','category')]}" groups="base.group_hr_user"/> - </group> - <group> <field name="holiday_status_id" on_change="onchange_status_id(holiday_status_id)" context="{'employee_id':employee_id}"/> <label for="number_of_days_temp" string="Duration"/> <div> - <field name="date_from" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/> - - <field name="date_to" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/> + <group col="3"> + <field name="date_from" nolabel="1" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/><label string="-" class="oe_inline" /> + <field name="date_to" nolabel="1" on_change="onchange_date_from(date_to, date_from)" required="1" class="oe_inline"/> + </group> <div> <field name="number_of_days_temp" class="oe_inline"/> days </div> </div> + <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'invisible':[('holiday_type','=','employee')], 'readonly':[('state','!=','draft'), ('state','!=','confirm')]}"/> + </group> + <group> + <field name="holiday_type" on_change="onchange_type(holiday_type)" attrs="{'readonly':[('state','!=','draft')]}" width="130" string="Mode" groups="base.group_hr_user"/> + <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/> + <field name="department_id" attrs="{'readonly':[('holiday_type','=','category')]}" groups="base.group_hr_user"/> </group> </group> - <notebook colspan="4"> - <page string="General"> - <group> - <group> - <field name="manager_id" groups="base.group_no_one"/> - <field name="double_validation" invisible="1"/> - </group> - <group> - <field name="manager_id2" attrs="{'invisible':[('double_validation', '!=', True)]}" groups="base.group_no_one"/> - </group> - </group> - <newline/> - <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/> - </page> - </notebook> </sheet> <div class="oe_chatter"> <field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/> @@ -150,37 +135,27 @@ <field name="arch" type="xml"> <form string="Allocation Request" version="7.0"> <header> - <button string="Submit to Manager" name="confirm" states="draft" type="workflow" class="oe_highlight"/> <button string="Approve" name="validate" states="confirm" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> <button string="Validate" name="second_validate" states="validate1" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> - <button string="Refuse" name="refuse" states="confirm,validate1" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> - <button string="Reset to New" name="set_to_draft" states="cancel,validate,refuse" type="object" groups="base.group_hr_user" /> + <button string="Refuse" name="refuse" states="confirm,validate,validate1" type="workflow" groups="base.group_hr_user" class="oe_highlight"/> + <button string="Reset to New" name="set_to_draft" states="cancel,refuse" type="object" groups="base.group_hr_user" /> <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate" statusbar_colors='{"confirm":"blue","validate1":"blue","refuse":"red"}'/> </header> <sheet> <group> <group> - <field name="name"/> - <field name="holiday_type" on_change="onchange_type(holiday_type)" attrs="{'readonly':[('state','!=','draft')]}" string="Allocation Mode" groups="base.group_hr_manager"/> - <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')], 'invisible':[('holiday_type','=','category')]}"/> - <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly':[('state','!=','draft')], 'invisible':[('holiday_type','=','employee')]}"/> - <field name="department_id" attrs="{'readonly':['|', ('holiday_type','=','category'), ('state','not in' ,('draft', 'confirm'))]}"/> - </group> - <group> + <field name="name" required="1" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/> <field name="holiday_status_id" on_change="onchange_status_id(holiday_status_id)" context="{'employee_id':employee_id}"/> <field name="number_of_days_temp"/> </group> + <group> + <field name="holiday_type" on_change="onchange_type(holiday_type)"/> + <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')], 'invisible':[('holiday_type','=','category')]}"/> + <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'invisible':[('holiday_type','=','employee')]}"/> + <field name="department_id" attrs="{'invisible':[('holiday_type','=','category')]}"/> + </group> </group> - <notebook> - <page string="General"> - <group col="4"> - <field name="double_validation" invisible="1"/> - <field name="manager_id"/> - <field name="manager_id2" attrs="{'invisible':[('double_validation', '!=', True)]}"/> - </group> - <field name="notes" placeholder="Add a reason..."/> - </page> - </notebook> + <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/> </sheet> <div class="oe_chatter"> <field name="message_ids" colspan="4" widget="mail_thread" nolabel="1"/> @@ -194,8 +169,7 @@ <field name="name">hr.holidays.allocation.tree</field> <field name="model">hr.holidays</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests"> - <field name="needaction_pending" invisible="1"/> + <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests"> <field name="employee_id"/> <field name="holiday_type"/> <field name="category_id"/> @@ -225,7 +199,6 @@ <field name="state"/> </header> <group col="4"> - <field name="name"/> <field name="holiday_status_id"/> <field name="type"/> <field name="date_from" on_change="onchange_date_from(date_to, date_from)" attrs="{'readonly':[('type','=','add')], 'required':[('type','=','remove')]}"/> @@ -233,7 +206,7 @@ <field name="number_of_days_temp"/> <field name="manager_id"/> </group> - <field name="notes"/> + <field name="name" placeholder="Add a reason..."/> </form> </field> </record> @@ -243,8 +216,7 @@ <field name="model">hr.holidays</field> <field name="priority">20</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leaves Summary"> - <field name="needaction_pending" invisible="1"/> + <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leaves Summary"> <field name="employee_id"/> <field name="category_id" invisible="1"/> <field name="department_id" invisible="1"/> @@ -266,8 +238,7 @@ <field name="name">hr.holidays.tree</field> <field name="model">hr.holidays</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests"> - <field name="needaction_pending" invisible="1"/> + <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests"> <field name="employee_id"/> <field name="holiday_type" string="Mode" groups="base.group_hr_user"/> <field name="name"/> @@ -288,12 +259,12 @@ <menuitem name="Leaves" parent="hr.menu_hr_root" id="menu_open_ask_holidays" sequence="20"/> <record model="ir.actions.act_window" id="open_ask_holidays"> - <field name="name">Leave Requests</field> + <field name="name">My Leave Requests</field> <field name="res_model">hr.holidays</field> <field name="view_type">form</field> <field name="view_id" ref="edit_holiday_new"/> <field name="context">{}</field> - <field name="domain">[('type','=','remove')]</field> + <field name="domain">[('type','=','remove'),('employee_id.user_id','=', uid)]</field> <field name="search_view_id" ref="view_hr_holidays_filter"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> @@ -327,7 +298,7 @@ <field name="view_id" ref="view_holiday_new_calendar"/> <field name="act_window_id" ref="open_ask_holidays"/> </record> - + <menuitem name="My Leave Requests" parent="menu_open_ask_holidays" id="menu_open_ask_holidays_new" action="open_ask_holidays"/> <record model="ir.actions.act_window" id="request_approve_holidays"> @@ -357,11 +328,11 @@ <menuitem name="Leave Requests to Approve" parent="menu_open_ask_holidays" id="menu_request_approve_holidays" action="request_approve_holidays" groups="base.group_hr_user"/> <record model="ir.actions.act_window" id="open_allocation_holidays"> - <field name="name">Allocation Requests</field> + <field name="name">My Allocation Requests</field> <field name="res_model">hr.holidays</field> <field name="view_type">form</field> <field name="context">{'default_type':'add'}</field> - <field name="domain">[('type','=','add')]</field> + <field name="domain">[('type','=','add'),('employee_id.user_id','=', uid)]</field> <field name="view_id" ref="allocation_leave_new"/> <field name="search_view_id" ref="view_hr_holidays_filter"/> </record> @@ -380,7 +351,7 @@ <field name="act_window_id" ref="open_allocation_holidays"/> </record> - <menuitem name="Allocation Requests" parent="menu_open_ask_holidays" id="menu_open_allocation_holidays" action="open_allocation_holidays"/> + <menuitem name="My Allocation Requests" parent="menu_open_ask_holidays" id="menu_open_allocation_holidays" action="open_allocation_holidays"/> <record model="ir.actions.act_window" id="open_company_allocation"> <field name="name">Leaves Summary</field> @@ -542,13 +513,17 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> - <field name="active" position="before"> - <label for="remaining_leaves"/> - <div> - <field name="remaining_leaves" class="oe_inline"/> - <button name="%(act_hr_employee_holiday_request)d" string="Leaves" type="action" class="oe_inline oe_right"/> - </div> - </field> + <xpath expr="//group[@name='active_group']" position="before"> + <group string="Leaves"> + <label for="remaining_leaves"/> + <div> + <field name="remaining_leaves" class="oe_inline"/> + </div> + </group> + </xpath> + <xpath expr="//div[@name='button_box']" position="inside"> + <button name="%(act_hr_employee_holiday_request)d" string="Leaves" type="action"/> + </xpath> </field> </record> diff --git a/addons/hr_holidays/hr_holidays_workflow.xml b/addons/hr_holidays/hr_holidays_workflow.xml index 7013d62a5ed..a2e1d54f96c 100644 --- a/addons/hr_holidays/hr_holidays_workflow.xml +++ b/addons/hr_holidays/hr_holidays_workflow.xml @@ -2,7 +2,16 @@ <openerp> <data> - <!-- Workflow definition --> + <!-- Workflow definition + 1. draft->submitted (no signal) + 2. submitted->accepted (validate signal) if not double_validation + 2. submitted -> first_accepted (validate signal) if double_validation + 2. submitted->refused (refuse signal) + 3. accepted->refused (refuse signal) + 4. first_accepted -> accepted (second_validate signal) + 4. first_accepted -> refused (refuse signal) + + --> <record model="workflow" id="wkf_holidays"> <field name="name">hr.wkf.holidays</field> @@ -10,13 +19,13 @@ <field name="on_create">True</field> </record> - <record model="workflow.activity" id="act_draft"> + <record model="workflow.activity" id="act_draft"> <!-- draft --> <field name="wkf_id" ref="wkf_holidays" /> <field name="flow_start">True</field> <field name="name">draft</field> </record> - <record model="workflow.activity" id="act_confirm"> + <record model="workflow.activity" id="act_confirm"> <!-- submitted --> <field name="wkf_id" ref="wkf_holidays" /> <field name="name">confirm</field> <field name="kind">function</field> @@ -24,74 +33,94 @@ <field name="split_mode">OR</field> </record> - <record model="workflow.activity" id="act_validate"> + <record model="workflow.activity" id="act_validate"> <!-- accepted --> <field name="wkf_id" ref="wkf_holidays" /> <field name="name">validate</field> <field name="kind">function</field> <field name="action">holidays_validate()</field> </record> - <record model="workflow.activity" id="act_validate1"> + <record model="workflow.activity" id="act_validate1"> <!-- first_accepted --> <field name="wkf_id" ref="wkf_holidays" /> - <field name="name">second_validate</field> + <field name="name">first_validate</field> <field name="kind">function</field> - <field name="action">holidays_validate2()</field> + <field name="action">holidays_first_validate()</field> + <field name="split_mode">OR</field> </record> - <record model="workflow.activity" id="act_refuse"> + <record model="workflow.activity" id="act_refuse"> <!-- refused --> <field name="wkf_id" ref="wkf_holidays" /> <field name="name">refuse</field> <field name="flow_stop">True</field> - <field name="kind">stopall</field> + <field name="kind">function</field> + <field name="action">holidays_refuse()</field> </record> <!-- workflow transition --> - <record model="workflow.transition" id="t1"> + <record model="workflow.transition" id="holiday_draft2confirm"> <!-- 1. draft->submitted (no signal) --> <field name="act_from" ref="act_draft" /> <field name="act_to" ref="act_confirm" /> - <field name="signal">confirm</field> </record> - <record model="workflow.transition" id="t2"> + <record model="workflow.transition" id="holiday_confirm2validate"> <!-- 2. submitted->accepted (validate signal) if not double_validation--> <field name="act_from" ref="act_confirm" /> <field name="act_to" ref="act_validate" /> <field name="signal">validate</field> - <field name="condition">True</field> + <field name="condition">not double_validation</field> <field name="group_id" ref="base.group_hr_user"/> </record> - <record model="workflow.transition" id="t3"> + <record model="workflow.transition" id="holiday_confirm2validate1"> <!-- 2. submitted -> first_accepted (validate signal) if double_validation--> + <field name="act_from" ref="act_confirm" /> + <field name="act_to" ref="act_validate1" /> + <field name="condition">double_validation</field> + <field name="group_id" ref="base.group_hr_user"/> + </record> + + + <record model="workflow.transition" id="holiday_confirm2refuse"> <!-- 2. submitted->refused (refuse signal) --> <field name="act_from" ref="act_confirm" /> <field name="act_to" ref="act_refuse" /> <field name="signal">refuse</field> - <field name="condition">holidays_refuse('first_approval')</field> + <field name="condition">True</field> <field name="group_id" ref="base.group_hr_user"/> </record> - <record model="workflow.transition" id="t4"> + <record model="workflow.transition" id="holiday_validate2refuse"> <!-- 3. accepted->refused (refuse signal) --> <field name="act_from" ref="act_validate" /> <field name="act_to" ref="act_refuse" /> <field name="signal">refuse</field> - <field name="condition">holidays_refuse('second_approval')</field> + <field name="condition">True</field> <field name="group_id" ref="base.group_hr_user"/> </record> - <record model="workflow.transition" id="t8"> + <record model="workflow.transition" id="holiday_validate2refuse"> <!-- 3. accepted->refused (refuse signal) --> <field name="act_from" ref="act_validate" /> - <field name="act_to" ref="act_validate1" /> + <field name="act_to" ref="act_refuse" /> + <field name="signal">refuse</field> + <field name="condition">True</field> + <field name="group_id" ref="base.group_hr_user"/> + </record> + + + <record model="workflow.transition" id="holiday_validate1_validate"> <!-- 4. first_accepted -> accepted (second_validate signal) --> + <field name="act_from" ref="act_validate1" /> + <field name="act_to" ref="act_validate" /> + <field name="condition">True</field> <field name="signal">second_validate</field> - <field name="group_id" ref="base.group_hr_manager" /> + <field name="group_id" ref="base.group_hr_user"/> </record> - <record model="workflow.transition" id="t11"> - <field name="act_from" ref="act_validate" /> - <field name="act_to" ref="act_validate1" /> - <field name="condition">not holiday_status_id.double_validation</field> - <field name="signal" eval="False"/> + <record model="workflow.transition" id="holiday_validate1_refuse"> <!-- 4. first_accepted->refused (refuse signal) --> + <field name="act_from" ref="act_validate1" /> + <field name="act_to" ref="act_refuse" /> + <field name="signal">refuse</field> + <field name="condition">True</field> + <field name="group_id" ref="base.group_hr_user"/> </record> </data> diff --git a/addons/hr_holidays/i18n/nl.po b/addons/hr_holidays/i18n/nl.po index ef84f17a58b..4c44ef380bb 100644 --- a/addons/hr_holidays/i18n/nl.po +++ b/addons/hr_holidays/i18n/nl.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-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-02-19 13:05+0000\n" +"PO-Revision-Date: 2012-09-03 17:12+0000\n" "Last-Translator: Erwin <Unknown>\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: 2012-08-28 06:23+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: hr_holidays #: selection:hr.holidays.status,color_name:0 @@ -416,6 +416,8 @@ msgid "" "When selected, the Allocation/Leave Requests for this type require a second " "validation to be approved." msgstr "" +"Wanneer aangevinkt heeft deze toewijzing of aanvraag een tweede controle " +"nodig om te worden goedgekeurd." #. module: hr_holidays #: selection:hr.employee,current_leave_state:0 selection:hr.holidays,state:0 @@ -787,6 +789,8 @@ msgid "" "If you select this checkbox, the system allows the employees to take more " "leaves than the available ones for this type." msgstr "" +"Indien u deze optie aanvinkt, dan staat u toe dat werknemers meer verlof " +"aanvragen dan dat ze nog beschikbaar hebben." #. module: hr_holidays #: help:hr.holidays.status,leaves_taken:0 diff --git a/addons/hr_holidays/security/ir.model.access.csv b/addons/hr_holidays/security/ir.model.access.csv index b21b6ca64e4..0375a17ac53 100644 --- a/addons/hr_holidays/security/ir.model.access.csv +++ b/addons/hr_holidays/security/ir.model.access.csv @@ -4,5 +4,5 @@ access_hr_holidays_user,hr.holidays.user,model_hr_holidays,base.group_hr_user,1, access_hr_holidays_employee,hr.holidays.employee,model_hr_holidays,base.group_user,1,1,1,1
access_hr_holydays_status_employee,hr.holidays.status employee,model_hr_holidays_status,base.group_user,1,0,0,0
access_hr_holidays_remain_user,hr.holidays.ramain.user,model_hr_holidays_remaining_leaves_user,base.group_hr_user,1,1,1,1
-access_resource_calendar_leaves_manager,resource_calendar_leaves_manager,resource.model_resource_calendar_leaves,base.group_hr_manager,1,1,1,1
+access_resource_calendar_leaves_user,resource_calendar_leaves_user,resource.model_resource_calendar_leaves,base.group_hr_user,1,1,1,1
access_crm_meeting_type_manager,crm.meeting.type.manager,base_calendar.model_crm_meeting_type,base.group_hr_manager,1,1,1,1
diff --git a/addons/hr_holidays/security/ir_rule.xml b/addons/hr_holidays/security/ir_rule.xml index 8e8c97bf7ea..ff0270026a4 100644 --- a/addons/hr_holidays/security/ir_rule.xml +++ b/addons/hr_holidays/security/ir_rule.xml @@ -1,6 +1,19 @@ <?xml version="1.0"?> <openerp> <data> - + <record id="property_rule_holidays_employee" model="ir.rule"> + <field name="name">Employee Holidays</field> + <field model="ir.model" name="model_id" ref="model_hr_holidays"/> + <field name="domain_force">[('employee_id.user_id','=',user.id)]</field> + <field name="groups" eval="[(4,ref('base.group_user'))]"/> + </record> + + <record id="property_rule_holidays_officer" model="ir.rule"> + <field name="name">Holidays Officer</field> + <field model="ir.model" name="model_id" ref="model_hr_holidays"/> + <field name="domain_force">[(1,'=',1)]</field> + <field name="groups" eval="[(4,ref('base.group_hr_user'))]"/> + </record> + </data> </openerp> diff --git a/addons/hr_holidays/test/test_hr_holiday.yml b/addons/hr_holidays/test/test_hr_holiday.yml index 9228a7a381e..f0e85d552cb 100644 --- a/addons/hr_holidays/test/test_hr_holiday.yml +++ b/addons/hr_holidays/test/test_hr_holiday.yml @@ -11,10 +11,6 @@ employee_id: hr.employee_fp type: remove - - I confirmed the holiday request by clicking on "Confirm" button. -- - !workflow {model: hr.holidays, action: confirm, ref: hr_holidays_employee1_cl} -- I find a small mistake on my leave request to I click on "Refuse" button to correct a mistake. - !workflow {model: hr.holidays, action: refuse, ref: hr_holidays_employee1_cl} diff --git a/addons/hr_payroll/i18n/nl.po b/addons/hr_payroll/i18n/nl.po index 8e9c0b35b6d..2ad4fc5aa0b 100644 --- a/addons/hr_payroll/i18n/nl.po +++ b/addons/hr_payroll/i18n/nl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-01-22 19:09+0000\n" +"PO-Revision-Date: 2012-09-03 17:17+0000\n" "Last-Translator: Erwin <Unknown>\n" "Language-Team: Dutch <nl@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:33+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: hr_payroll #: field:hr.payslip.line,condition_select:0 @@ -357,7 +357,7 @@ msgstr "Half-jaarlijks" #. module: hr_payroll #: view:hr.salary.rule:0 msgid "Children Definition" -msgstr "" +msgstr "Definitie onderliggende" #. module: hr_payroll #: report:paylip.details:0 report:payslip:0 @@ -465,7 +465,7 @@ msgstr "" #. module: hr_payroll #: view:hr.payslip:0 msgid "Worked Days & Inputs" -msgstr "" +msgstr "Gewerkte dagen & ingaves" #. module: hr_payroll #: field:hr.payslip,details_by_salary_rule_category:0 @@ -522,7 +522,7 @@ msgstr "Bereik" #: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payroll_structure_tree #: model:ir.ui.menu,name:hr_payroll.menu_hr_payroll_structure_tree msgid "Salary Structures Hierarchy" -msgstr "" +msgstr "Salaris structuur hiërarchie" #. module: hr_payroll #: view:hr.payslip:0 @@ -769,7 +769,7 @@ msgstr "Contributie" #: code:addons/hr_payroll/hr_payroll.py:347 #, python-format msgid "Refund Payslip" -msgstr "" +msgstr "Credit salarisstrook" #. module: hr_payroll #: field:hr.rule.input,input_id:0 @@ -852,7 +852,7 @@ msgstr "Salaris Structuur" #. module: hr_payroll #: field:hr.contribution.register,register_line_ids:0 msgid "Register Line" -msgstr "" +msgstr "Register regel" #. module: hr_payroll #: view:hr.payslip:0 view:hr.payslip.employees:0 @@ -1083,7 +1083,7 @@ msgstr "Totaal" #: help:hr.payslip.line,appears_on_payslip:0 #: help:hr.salary.rule,appears_on_payslip:0 msgid "Used for the display of rule on payslip" -msgstr "" +msgstr "Wordt gebruikt om de regel van de salaristrook weer te geven" #. module: hr_payroll #: view:hr.payslip.line:0 @@ -1093,7 +1093,7 @@ msgstr "Zoek loonafschrift regels" #. module: hr_payroll #: view:hr.payslip:0 msgid "Details By Salary Rule Category" -msgstr "" +msgstr "details per salarisregel categorie" #. module: hr_payroll #: help:hr.payslip.input,code:0 help:hr.payslip.worked_days,code:0 diff --git a/addons/hr_payroll_account/hr_payroll_account.py b/addons/hr_payroll_account/hr_payroll_account.py index 7156735636b..184f9a06867 100644 --- a/addons/hr_payroll_account/hr_payroll_account.py +++ b/addons/hr_payroll_account/hr_payroll_account.py @@ -218,6 +218,17 @@ class hr_payslip_run(osv.osv): 'journal_id': fields.many2one('account.journal', 'Expense Journal', states={'draft': [('readonly', False)]}, readonly=True, required=True), } + def _get_default_journal(self, cr, uid, context=None): + model_data = self.pool.get('ir.model.data') + res = model_data.search(cr, uid, [('name', '=', 'expenses_journal')]) + if res: + return model_data.browse(cr, uid, res[0]).res_id + return False + + _defaults = { + 'journal_id': _get_default_journal, + } + hr_payslip_run() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_payroll_account/i18n/nb.po b/addons/hr_payroll_account/i18n/nb.po new file mode 100644 index 00000000000..d1c0bf47a00 --- /dev/null +++ b/addons/hr_payroll_account/i18n/nb.po @@ -0,0 +1,141 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 19:55+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: hr_payroll_account +#: field:hr.payslip,move_id:0 +msgid "Accounting Entry" +msgstr "Regnskapsmessig oppføring" + +#. module: hr_payroll_account +#: field:hr.salary.rule,account_tax_id:0 +msgid "Tax Code" +msgstr "Skattekode" + +#. module: hr_payroll_account +#: field:hr.payslip,journal_id:0 +#: field:hr.payslip.run,journal_id:0 +msgid "Expense Journal" +msgstr "Bekostning Journal" + +#. module: hr_payroll_account +#: code:addons/hr_payroll_account/hr_payroll_account.py:157 +#: code:addons/hr_payroll_account/hr_payroll_account.py:173 +#, python-format +msgid "Adjustment Entry" +msgstr "Justering oppføring" + +#. module: hr_payroll_account +#: field:hr.contract,analytic_account_id:0 +#: field:hr.salary.rule,analytic_account_id:0 +msgid "Analytic Account" +msgstr "Analytisk konto" + +#. module: hr_payroll_account +#: model:ir.model,name:hr_payroll_account.model_hr_salary_rule +msgid "hr.salary.rule" +msgstr "hr.salary.rule" + +#. module: hr_payroll_account +#: model:ir.model,name:hr_payroll_account.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "Lønnsslipp Batcher." + +#. module: hr_payroll_account +#: field:hr.contract,journal_id:0 +msgid "Salary Journal" +msgstr "lønn Journal" + +#. module: hr_payroll_account +#: model:ir.model,name:hr_payroll_account.model_hr_payslip +msgid "Pay Slip" +msgstr "Lønnslipp" + +#. module: hr_payroll_account +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "Lønnslipp \" Dato fra \" må være før \" Dato Til \"" + +#. module: hr_payroll_account +#: help:hr.payslip,period_id:0 +msgid "Keep empty to use the period of the validation(Payslip) date." +msgstr "Hold tom for å bruke tid på validering (lønnsslipp) dato." + +#. module: hr_payroll_account +#: code:addons/hr_payroll_account/hr_payroll_account.py:171 +#, python-format +msgid "" +"The Expense Journal \"%s\" has not properly configured the Debit Account!" +msgstr "Bekostning Journal \"% s\" har ikke riktig konfigurert Debet konto!" + +#. module: hr_payroll_account +#: code:addons/hr_payroll_account/hr_payroll_account.py:155 +#, python-format +msgid "" +"The Expense Journal \"%s\" has not properly configured the Credit Account!" +msgstr "" +"Bekostning Journal \"% s\" har ikke riktig konfigurert kreditt-konto!" + +#. module: hr_payroll_account +#: field:hr.salary.rule,account_debit:0 +msgid "Debit Account" +msgstr "Debetkonto" + +#. module: hr_payroll_account +#: code:addons/hr_payroll_account/hr_payroll_account.py:102 +#, python-format +msgid "Payslip of %s" +msgstr "Lønnslipp av %s." + +#. module: hr_payroll_account +#: model:ir.model,name:hr_payroll_account.model_hr_contract +msgid "Contract" +msgstr "Kontrakt" + +#. module: hr_payroll_account +#: constraint:hr.contract:0 +msgid "Error! contract start-date must be lower then contract end-date." +msgstr "Feil! Kontrakten startdato må være lavere enn kontrakt sluttdato." + +#. module: hr_payroll_account +#: field:hr.payslip,period_id:0 +msgid "Force Period" +msgstr "Kraft Periode." + +#. module: hr_payroll_account +#: field:hr.salary.rule,account_credit:0 +msgid "Credit Account" +msgstr "Kredittkonto" + +#. module: hr_payroll_account +#: model:ir.model,name:hr_payroll_account.model_hr_payslip_employees +msgid "Generate payslips for all selected employees" +msgstr "Generer lønnsslipper for alle utvalgte ansatte." + +#. module: hr_payroll_account +#: code:addons/hr_payroll_account/hr_payroll_account.py:155 +#: code:addons/hr_payroll_account/hr_payroll_account.py:171 +#, python-format +msgid "Configuration Error!" +msgstr "Konfigurasjonsfeil!" + +#. module: hr_payroll_account +#: view:hr.contract:0 +#: view:hr.salary.rule:0 +msgid "Accounting" +msgstr "Regnskap" diff --git a/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py index a00ff97ef6b..7e3f4bbbe2b 100644 --- a/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py +++ b/addons/hr_payroll_account/wizard/hr_payroll_payslips_by_employees.py @@ -31,8 +31,9 @@ class hr_payslip_employees(osv.osv_memory): context = {} if context and context.get('active_id', False): run_data = run_pool.read(cr, uid, context['active_id'], ['journal_id']) - journal_id = run_data.get('journal_id', False)[0] - context.update({'journal_id': journal_id}) + journal_id = run_data.get('journal_id', False) + journal_id = journal_id and journal_id[0] or False + if journal_id: context.update({'journal_id': journal_id}) return super(hr_payslip_employees, self).compute_sheet(cr, uid, ids, context=context) hr_payslip_employees() diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 6b4839b7b2b..2748834ae9a 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -90,8 +90,7 @@ class hr_applicant(base_stage, osv.Model): _name = "hr.applicant" _description = "Applicant" _order = "id desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] - _mail_compose_message = True + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_department_id(self, cr, uid, context=None): """ Gives default department by checking if present in the context """ @@ -132,13 +131,17 @@ class hr_applicant(base_stage, osv.Model): department_id = self._resolve_department_id_from_context(cr, uid, context=context) search_domain = [] if department_id: - search_domain += ['|', '&', ('department_id', '=', department_id), ('fold', '=', False)] - search_domain += ['|', ('id', 'in', ids), '&', ('department_id', '=', False), ('fold', '=', False)] + search_domain += ['|', ('department_id', '=', department_id)] + search_domain += ['|', ('id', 'in', ids), ('department_id', '=', False)] stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) - return result + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + return result, fold def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @@ -161,13 +164,11 @@ class hr_applicant(base_stage, osv.Model): date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create - date_until = issue.date_open elif field in ['day_close']: if issue.date_closed: date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S") - date_until = issue.date_closed ans = date_close - date_create if ans: duration = float(ans.days) @@ -185,7 +186,7 @@ class hr_applicant(base_stage, osv.Model): 'create_date': fields.datetime('Creation Date', readonly=True, select=True), 'write_date': fields.datetime('Update Date', readonly=True), 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', - domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"), + domain="['&', ('fold', '=', False), '|', ('department_id', '=', department_id), ('department_id', '=', False)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=AVAILABLE_STATES, string="State", readonly=True, help='The state is set to \'Draft\', when a case is created.\ @@ -328,14 +329,13 @@ class hr_applicant(base_stage, osv.Model): if custom_values is None: custom_values = {} custom_values.update({ 'name': msg.get('subject') or _("No Subject"), - 'description': msg.get('body_text'), + 'description': msg.get('body'), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): @@ -360,7 +360,7 @@ class hr_applicant(base_stage, osv.Model): 'revenue': 'planned_revenue', 'probability': 'probability', } - for line in msg.get('body_text', '').split('\n'): + for line in msg.get('body', '').split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower(), False): @@ -457,23 +457,18 @@ class hr_applicant(base_stage, osv.Model): # OpenChatter methods and notifications # ------------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(hr_applicant, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ if not stage_id: return True stage_name = self.pool.get('hr.recruitment.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) + return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) def case_get_note_msg_prefix(self, cr, uid, id, context=None): return 'Applicant' def case_open_send_note(self, cr, uid, ids, context=None): message = _("Applicant has been set <b>in progress</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def case_close_send_note(self, cr, uid, ids, context=None): if context is None: @@ -481,23 +476,23 @@ class hr_applicant(base_stage, osv.Model): for applicant in self.browse(cr, uid, ids, context=context): if applicant.emp_id: message = _("Applicant has been <b>hired</b> and created as an employee.") - self.message_append_note(cr, uid, [applicant.id], body=message, context=context) + self.message_post(cr, uid, [applicant.id], body=message, context=context) else: message = _("Applicant has been <b>hired</b>.") - self.message_append_note(cr, uid, [applicant.id], body=message, context=context) + self.message_post(cr, uid, [applicant.id], body=message, context=context) return True def case_cancel_send_note(self, cr, uid, ids, context=None): msg = 'Applicant <b>refused</b>.' - return self.message_append_note(cr, uid, ids, body=msg, context=context) + return self.message_post(cr, uid, ids, body=msg, context=context) def case_reset_send_note(self, cr, uid, ids, context=None): message =_("Applicant has been set as <b>new</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def create_send_note(self, cr, uid, ids, context=None): message = _("Applicant has been <b>created</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) class hr_job(osv.osv): diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index d90f5ab596a..84289da7bae 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -2,14 +2,15 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Recruitment Process has been installed</value> - <value>Manage job positions and the recruitment process in your company. This module is integrated with the module Survey to help you to define interviews for different jobs. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Recruitment Process application installed!</field> + <field name="body">Manage job positions and your company's recruitment process. This application is integrated with the Survey application to help you define interviews for different jobs. -You can automatically create application records from an email gateway, that you can configure in the Human Resources Settings.</value> - </function> +You can automatically receive job application though an email gateway, see the Human Resources settings.</field> + </record> <!-- Meeting Types (for interview meetings) --> <record model="crm.meeting.type" id="categ_meet_interview"> diff --git a/addons/hr_recruitment/hr_recruitment_demo.yml b/addons/hr_recruitment/hr_recruitment_demo.yml index 59508b83c28..3f00c7e643b 100644 --- a/addons/hr_recruitment/hr_recruitment_demo.yml +++ b/addons/hr_recruitment/hr_recruitment_demo.yml @@ -52,6 +52,7 @@ partner_name: 'John Bruno' state: 'done' job_id: hr.job_developer + color: 5 stage_id: stage_job5 name: 'Marketing Job' - @@ -100,6 +101,9 @@ partner_name: 'David Armstrong' job_id: hr.job_developer stage_id: stage_job2 + color: 4 name: 'Advertisement' salary_expected: 11000.0 - +- + !record {model: hr.job, id: hr.job_developer}: + survey_id: hr_recruitment.survey_job_0 diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index d1866f124af..acb8e06cbdb 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -76,8 +76,8 @@ <field name="name">Applicants</field> <field name="model">hr.applicant</field> <field name="arch" type="xml"> - <tree string="Applicants" fonts="bold:needaction_pending==True" colors="grey:state in ('cancel','done');blue:state=='pending'"> - <field name="needaction_pending" invisible="1"/> + <tree string="Applicants" fonts="bold:message_unread==True" colors="grey:state in ('cancel','done');blue:state=='pending'"> + <field name="message_unread" invisible="1"/> <field name="create_date" groups="base.group_no_one"/> <field name="name" string="Subject"/> <field name="partner_name"/> @@ -95,7 +95,7 @@ <field name="availability" invisible="1"/> <field name="department_id" invisible="context.get('invisible_department', True)"/> <field name="user_id"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> </tree> </field> </record> @@ -107,7 +107,7 @@ <form string="Jobs - Recruitment Form" version="7.0"> <header> <button name="case_close_with_emp" string="Hire" type="object" - states="open,pending" class="oe_highlight"/> + states="draft,open,pending" class="oe_highlight"/> <button name="case_cancel" string="Refuse" type="object" states="draft,open,pending" class="oe_highlight"/> <field name="stage_id" widget="statusbar" clickable="True"/> @@ -115,13 +115,13 @@ <sheet> <div class="oe_right oe_button_box"> <button name="action_makeMeeting" type="object" - string="Schedule Interview" icon="gtk-index" help="Schedule interview with this applicant"/> + string="Schedule Meeting" help="Schedule interview with this applicant"/> <button name="%(survey.action_view_survey_question_message)d" type="action" - string="Start Interview" icon="gtk-execute" help="Answer related job question" + string="Start Interview" help="Answer related job question" context="{'survey_id': survey, 'response_id': [response], 'response_no':0 ,'active' : response, 'object' : 'hr.applicant'}" attrs="{'invisible':[('survey','=',False)]}"/> <button name="action_print_survey" type="object" - string="Print Interview" icon="gtk-print" help="Print interview report" + string="Print Interview" help="Print interview report" attrs="{'invisible':[('survey','=',False)]}"/> </div> <div class="oe_title"> @@ -208,29 +208,28 @@ <field name="arch" type="xml"> <search string="Search Jobs"> <field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> - <filter icon="terp-document-new" string="New" domain="[('state','=','draft')]" help="All Initial Jobs"/> - <filter icon="terp-camera_test" string="In Progress" domain="[('state','=','open')]" help="Open Jobs"/> - <filter icon="terp-gtk-media-pause" string="Pending" domain="[('state','=','pending')]" help="Pending Jobs"/> + <filter string="New" domain="[('state','=','draft')]" help="All Initial Jobs"/> + <filter string="In Progress" domain="[('state','=','open')]" help="Open Jobs"/> + <filter string="Pending" domain="[('state','=','pending')]" help="Pending Jobs"/> <separator/> - <filter string="Unassigned Recruitments" domain="[('user_id','=',False)]" help="Unassigned Recruitments" icon="terp-personal-"/> + <filter string="Unassigned Recruitments" domain="[('user_id','=',False)]" help="Unassigned Recruitments"/> <separator/> - <filter icon="terp-gtk-jump-to-ltr" string="Next Actions" context="{'invisible_next_action':False, 'invisible_next_date':False}" + <filter string="Next Actions" context="{'invisible_next_action':False, 'invisible_next_date':False}" domain="[('date_action','<>',False)]" help="Filter and view on next actions and date"/> <field name="job_id"/> <field name="user_id"/> <group expand="0" string="Group By..."> - <filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> - <filter string="Department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id'}"/> - <filter string="Job" icon="terp-gtk-select-all" domain="[]" context="{'group_by':'job_id'}"/> - <filter string="Degree" icon="terp-gtk-select-all" domain="[]" context="{'group_by':'type_id'}"/> - <filter string="Availability" icon="terp-camera_test" domain="[]" context="{'group_by':'availability'}"/> - <filter string="Appreciation" icon="terp-face-plain" domain="[]" context="{'group_by':'priority'}"/> - <filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> - <filter string="Source" icon="terp-face-plain" domain="[]" context="{'group_by':'source_id'}"/> - <filter string="Creation Date" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/> + <filter string="Responsible" domain="[]" context="{'group_by':'user_id'}"/> + <filter string="Department" domain="[]" context="{'group_by':'department_id'}"/> + <filter string="Job" domain="[]" context="{'group_by':'job_id'}"/> + <filter string="Degree" domain="[]" context="{'group_by':'type_id'}"/> + <filter string="Availability" domain="[]" context="{'group_by':'availability'}"/> + <filter string="Appreciation" domain="[]" context="{'group_by':'priority'}"/> + <filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/> + <filter string="Source" domain="[]" context="{'group_by':'source_id'}"/> + <filter string="Creation Date" domain="[]" context="{'group_by':'create_date'}" groups="base.group_no_one"/> </group> </search> </field> @@ -278,12 +277,12 @@ </ul> </t> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> + <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_semantic_html_override"> <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">i</span> <ul class="oe_dropdown_menu"> - <li><a type="edit">Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li><a name="action_makeMeeting" type="object">Schedule Interview</a></li> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> @@ -340,7 +339,7 @@ <div groups="base.group_user"> <field name="survey_id" class="oe_inline"/> <button class="oe_inline" - string="Interview" icon="gtk-print" + string="Interview" name="action_print_survey" type="object" attrs="{'invisible':[('survey_id','=',False)]}"/> </div> @@ -376,10 +375,15 @@ <field name="arch" type="xml"> <form string="Stage" version="7.0"> <group string="Stage Definition"> - <field name="name"/> - <field name="department_id"/> - <field name="sequence"/> - <field name="state"/> + <group> + <field name="name"/> + <field name="department_id"/> + </group> + <group> + <field name="state"/> + <field name="sequence"/> + <field name="fold"/> + </group> </group> <separator string="Requirements"/> <field name="requirements"/> diff --git a/addons/hr_recruitment/report/hr_recruitment_report_view.xml b/addons/hr_recruitment/report/hr_recruitment_report_view.xml index 30a20a25eb8..1b0b8bfa7c5 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report_view.xml +++ b/addons/hr_recruitment/report/hr_recruitment_report_view.xml @@ -71,7 +71,6 @@ <filter string="Department" name="department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id'}"/> <filter string="Degree" name="degree" icon="terp-gtk-select-all" domain="[]" context="{'group_by':'type_id'}"/> <filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}" /> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Day" name = "day" icon="terp-go-today" domain="[]" context="{'group_by':'day'}" help="Creation Date"/> <filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'month'}" help="Creation Date"/> <filter string="Year" icon="terp-go-year" domain="[]" context="{'group_by':'year'}" help="Creation Date"/> diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 6ece274fea1..43b677d7978 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -77,7 +77,17 @@ </search> </field> </record> - + + <record model="ir.actions.act_window" id="act_analytic_cost_revenue"> + <field name="context">{'search_default_account_id': [active_id], 'default_account_id': active_id, 'search_default_group_date': 1, 'search_default_group_journal': 1}</field> + <field name="name">Costs & Revenues</field> + <field name="res_model">account.analytic.line</field> + <field name="src_model">account.analytic.account</field> + <field name="view_mode">tree,form</field> + <field name="view_type">form</field> + </record> + + <record id="account_analytic_account_timesheet_form" model="ir.ui.view"> <field name="name">account.analytic.account.invoice.form</field> <field name="model">account.analytic.account</field> @@ -87,6 +97,12 @@ <field name="use_timesheets"/> <label for="use_timesheets"/> </xpath> + <xpath expr='//div[@name="buttons"]' position='inside'> + <button string="Costs and Revenues" + name="%(act_analytic_cost_revenue)d" + type="action"/> + + </xpath> </field> </record> @@ -115,16 +131,12 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> - <page string="Personal Information" position="after"> - <page string="Timesheets" groups="base.group_hr_user"> - <group> - <group col="2"> - <field name="product_id" domain="[('type','=','service')]"/> - <field name="journal_id"/> - </group> - </group> - </page> - </page> + <xpath expr="//group[@name='active_group']" position="before"> + <group string="Timesheets"> + <field name="product_id" domain="[('type','=','service')]"/> + <field name="journal_id"/> + </group> + </xpath> </field> </record> diff --git a/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py b/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py index 34cfee40262..e92e88bdcbd 100644 --- a/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py +++ b/addons/hr_timesheet/wizard/hr_timesheet_sign_in_out.py @@ -91,7 +91,7 @@ class hr_so_project(osv.osv_memory): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id - emp_obj.attendance_action_change(cr, uid, [emp_id], type='sign_out', dt=data.date) + emp_obj.attendance_action_change(cr, uid, [emp_id], {'action':'sign_out', 'action_date':data.date}) self._write(cr, uid, data, emp_id, context=context) return {'type': 'ir.actions.act_window_close'} @@ -99,7 +99,7 @@ class hr_so_project(osv.osv_memory): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id - emp_obj.attendance_action_change(cr, uid, [emp_id], type='action', dt=data.date) + emp_obj.attendance_action_change(cr, uid, [emp_id], {'action':'action', 'action_date':data.date}) self._write(cr, uid, data, emp_id, context=context) return {'type': 'ir.actions.act_window_close'} @@ -156,7 +156,7 @@ class hr_si_project(osv.osv_memory): emp_obj = self.pool.get('hr.employee') for data in self.browse(cr, uid, ids, context=context): emp_id = data.emp_id.id - emp_obj.attendance_action_change(cr, uid, [emp_id], type = 'sign_in' ,dt=data.date or False) + emp_obj.attendance_action_change(cr, uid, [emp_id], {'action':'sign_in', 'action_date':data.date}) return {'type': 'ir.actions.act_window_close'} def default_get(self, cr, uid, fields_list, context=None): diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index 4cba4a18680..a1562d53a61 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -95,25 +95,25 @@ class account_analytic_account(osv.osv): def set_close(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'close'}, context=context) message = _("Contract has been <b>closed</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_cancel(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'cancelled'}, context=context) message = _("Contract has been <b>cancelled</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_open(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'open'}, context=context) message = _("Contract has been <b>opened</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_pending(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state':'pending'}, context=context) message = _("Contract has been set as <b>pending</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True account_analytic_account() diff --git a/addons/hr_timesheet_invoice/report/report_analytic_view.xml b/addons/hr_timesheet_invoice/report/report_analytic_view.xml index 9e0b8dd59ee..a2566688799 100644 --- a/addons/hr_timesheet_invoice/report/report_analytic_view.xml +++ b/addons/hr_timesheet_invoice/report/report_analytic_view.xml @@ -122,13 +122,13 @@ <field name="search_view_id" ref="account_analytic_line_to_invoice_view_filter"/> </record> - <act_window - domain="[('invoice_id','=',False),('to_invoice','<>',False), ('journal_id.type', '=', 'general')]" - context="{'search_default_account_id': [active_id], 'default_account_id': active_id,'search_default_to_invoice': 1}" - id="act_acc_analytic_acc_2_report_acc_analytic_line_to_invoice" - name="Timesheet Lines to Invoice" - res_model="account.analytic.line" - src_model="account.analytic.account"/> + <record id="act_acc_analytic_acc_2_report_acc_analytic_line_to_invoice" model="ir.actions.act_window"> + <field name="domain">[]</field> + <field name="context">{'search_default_account_id': [active_id], 'default_account_id': active_id,'search_default_to_invoice': 1}</field> + <field name="name">To Invoice</field> + <field name="res_model">account.analytic.line</field> + <field name="src_model">account.analytic.account</field> + </record> </data> </openerp> diff --git a/addons/hr_timesheet_sheet/__openerp__.py b/addons/hr_timesheet_sheet/__openerp__.py index 34fbb602c9a..b1f03e7d6e0 100644 --- a/addons/hr_timesheet_sheet/__openerp__.py +++ b/addons/hr_timesheet_sheet/__openerp__.py @@ -27,8 +27,8 @@ 'sequence': 16, 'summary': 'Timesheets, Attendances, Activities', 'description': """ -This module helps you to easily encode and validate timesheet and attendances within the same view. -=================================================================================================== +This module helps you to easily record and validate timesheets and attendances within the same view. +==================================================================================================== * It will maintain attendances and track (sign in/sign out) events. * Track the timesheet lines. diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index ca28b5bdb28..fcdbfda0667 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -312,31 +312,12 @@ class hr_timesheet_sheet(osv.osv): self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) return True - def check_sign(self, cr, uid, ids, typ, context=None): - sheet = self.browse(cr, uid, ids, context=context)[0] - if not sheet.date_current == time.strftime('%Y-%m-%d'): - raise osv.except_osv(_('Error!'), _('You cannot sign in/sign out from other date than today.')) - return True - - def sign(self, cr, uid, ids, typ, context=None): - self.check_sign(cr, uid, ids, typ, context=context) - sign_obj = self.pool.get('hr.sign.in.out') - sheet = self.browse(cr, uid, ids, context=context)[0] - context['emp_id'] = [sheet.employee_id.id] - sign_id = sign_obj.create(cr, uid, {}, context=context) - methods = {'sign_in': sign_obj.si_check, - 'sign_out': sign_obj.so_check} - wizard_result = methods[typ](cr, uid, [sign_id], context=context) - if wizard_result.get('type', False) == 'ir.actions.act_window_close': - return True # ensure we do not close the main window ! - wizard_result['nodestroy'] = True # do not destroy the main window ! - return wizard_result - - def sign_in(self, cr, uid, ids, context=None): - return self.sign(cr, uid, ids, 'sign_in', context=context) - - def sign_out(self, cr, uid, ids, context=None): - return self.sign(cr, uid, ids, 'sign_out', context=context) + def attendance_action_change(self, cr, uid, ids, context=None): + hr_employee = self.pool.get('hr.employee') + employee_ids = [] + for sheet in self.browse(cr, uid, ids, context=context): + if sheet.employee_id.id not in employee_ids: employee_ids.append(sheet.employee_id.id) + return hr_employee.attendance_action_change(cr, uid, employee_ids, context=context) _columns = { 'name': fields.char('Note', size=64, select=1, @@ -442,8 +423,10 @@ class hr_timesheet_sheet(osv.osv): return True def name_get(self, cr, uid, ids, context=None): - if not len(ids): + if not ids: return [] + if isinstance(ids, (long, int)): + ids = [ids] return [(r['id'], r['date_from'] + ' - ' + r['date_to']) \ for r in self.read(cr, uid, ids, ['date_from', 'date_to'], context=context, load='_classic_write')] diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_data.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_data.xml index 5458f397e36..87edf949972 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_data.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_data.xml @@ -1,24 +1,23 @@ <?xml version="1.0" ?> <openerp> <data noupdate="1"> - - <!-- Notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Timesheets Validation has been installed</value> - <value>From the top menu "Human Resources", encode and validate timesheets and attendances.</value> - </function> + <!-- notify all employees of module installation --> + <record model="mail.message" id="hr_timesheet_module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Timesheet Validation application installed!</field> + <field name="body">From the top menu "Human Resources", enter and validate timesheets and attendances.</field> + </record> <record id="ir_actions_server_timsheet_sheet" model="ir.actions.server"> - <field eval="5" name="sequence"/> - <field eval=""""code"""" name="state"/> - <field eval=""""ir.actions.server"""" name="type"/> + <field name="sequence" eval="5"/> + <field name="state">code</field> + <field name="type">ir.actions.server</field> <field name="model_id" ref="model_hr_timesheet_current_open"/> - <field eval="[(6,0,[])]" name="child_ids"/> - <field eval=""""action = pool.get('hr.timesheet.current.open').open_timesheet(cr, uid, None, context)"""" name="code"/> - <field eval=""""True"""" name="condition"/> - <field eval=""""My Timesheet"""" name="name"/> + <field name="code">action = pool.get('hr.timesheet.current.open').open_timesheet(cr, uid, None, context)</field> + <field name="condition">True</field> + <field name="name">My Timesheet</field> </record> <menuitem name="My Current Timesheet" id="menu_act_hr_timesheet_sheet_form_my_current" parent="hr_attendance.menu_hr_time_tracking" action="ir_actions_server_timsheet_sheet" sequence="1"/> diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml index a73bbb3050e..42497eb537e 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml @@ -2,12 +2,12 @@ <openerp> <data noupdate="1"> - <record id="sheet1" model="hr_timesheet_sheet.sheet"> +<!-- <record id="sheet1" model="hr_timesheet_sheet.sheet"> <field name="name">Sheet 1</field> <field name="user_id" ref="base.user_root"/> <field name="employee_id" ref="hr.employee_fp" /> <field eval="time.strftime('%Y-%m-%d')" name="date_current"/> </record> - +--> </data> </openerp> diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 6c4ee63cbe6..5dd71eadd1b 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -92,9 +92,9 @@ </tree> </field> <group> - <div align="right"> - <button name="sign_in" string="Sign In" type="object" icon="terp-gtk-jump-to-ltr"/> - <button name="sign_out" string="Sign Out" type="object" icon="terp-gtk-jump-to-rtl"/> + <div align="right" groups="base.group_hr_manager"> + <button name="attendance_action_change" attrs="{'invisible': [('state_attendance', '=', 'present')]}" type="object" string="Sign In"/> + <button name="attendance_action_change" attrs="{'invisible': ['|', ('state_attendance','=',False), ('state_attendance', '=', 'absent')]}" type="object" string="Sign Out"/> </div> </group> </group> diff --git a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml index f4f9326890f..849c2fa7caa 100644 --- a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml +++ b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml @@ -26,7 +26,7 @@ Now, at the time of login, I create Attendances and perform "Sign In" action. - !python {model: hr_timesheet_sheet.sheet}: | - self.sign_in(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], {}) + self.attendance_action_change(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], {}) task_work = self.search(cr, uid, [("name","=","Quentin Paolinon")],context)[0] task_ids = self.browse(cr, uid, task_work, context) assert task_ids.state == "draft", "I State In Timesheet" @@ -69,7 +69,7 @@ - account_id: account.analytic_agrolait date: !eval time.strftime('%Y-%m-%d') name: 'Develop yaml for hr module' - user_id: base.user_qdp + user_id: base.user_demo unit_amount: 6.00 amount: -90.00 product_id: product.product_product_consultant @@ -97,7 +97,7 @@ date: !eval time.strftime('%Y-%m-%d') name: 'Develop yaml for hr module' unit_amount: 2.00 - user_id: base.user_qdp + user_id: base.user_demo amount: -90.00 product_id: product.product_product_consultant general_account_id: account.a_expense diff --git a/addons/idea/idea.py b/addons/idea/idea.py index 3be4dd2e28d..e2e5ec50ddf 100644 --- a/addons/idea/idea.py +++ b/addons/idea/idea.py @@ -67,21 +67,21 @@ class idea_idea(osv.osv): def idea_cancel(self, cr, uid, ids, context={}): self.write(cr, uid, ids, { 'state': 'cancel' }) - self.message_append_note(cr, uid, ids, body=_('Idea canceled.'), context=context) + self.message_post(cr, uid, ids, body=_('Idea canceled.'), context=context) return True def idea_open(self, cr, uid, ids, context={}): self.write(cr, uid, ids, { 'state': 'open'}) - self.message_append_note(cr, uid, ids, body=_('Idea accepted.'), context=context) + self.message_post(cr, uid, ids, body=_('Idea accepted.'), context=context) return True def idea_close(self, cr, uid, ids, context={}): - self.message_append_note(cr, uid, ids, body=_('Idea done.'), context=context) + self.message_post(cr, uid, ids, body=_('Idea done.'), context=context) self.write(cr, uid, ids, { 'state': 'close' }) return True def idea_draft(self, cr, uid, ids, context={}): - self.message_append_note(cr, uid, ids, body=_('Idea reset to draft.'), context=context) + self.message_post(cr, uid, ids, body=_('Idea reset to draft.'), context=context) self.write(cr, uid, ids, { 'state': 'draft' }) return True idea_idea() diff --git a/addons/import_base/i18n/nb.po b/addons/import_base/i18n/nb.po new file mode 100644 index 00000000000..62b30d04da6 --- /dev/null +++ b/addons/import_base/i18n/nb.po @@ -0,0 +1,105 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-08 19:39+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:52+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: import_base +#: code:addons/import_base/import_framework.py:434 +#, python-format +msgid "Import failed due to an unexpected error" +msgstr "Import mislyktes på grunn av en uventet feil." + +#. module: import_base +#: code:addons/import_base/import_framework.py:461 +#, python-format +msgid "started at %s and finished at %s \n" +msgstr "Startet på% s og ferdig på% s \n" + +#. module: import_base +#: code:addons/import_base/import_framework.py:448 +#, python-format +msgid "Import of your data finished at %s" +msgstr "Import av dine data ferdig på% s" + +#. module: import_base +#: code:addons/import_base/import_framework.py:463 +#, python-format +msgid "" +"but failed, in consequence no data were imported to keep database " +"consistency \n" +" error : \n" +msgstr "" +"Men mislyktes, i følge ingen data ble importert for å holde databasen " +"konsistens\n" +" feil: \n" + +#. module: import_base +#: code:addons/import_base/import_framework.py:477 +#, python-format +msgid "" +"The import of data \n" +" instance name : %s \n" +msgstr "" +"Import av data\n" +" Eksempel navn:% s \n" + +#. module: import_base +#: code:addons/import_base/import_framework.py:470 +#, python-format +msgid "%s has been successfully imported from %s %s, %s \n" +msgstr "% s har blitt importert fra% s% s,% s \n" + +#. module: import_base +#: code:addons/import_base/import_framework.py:447 +#, python-format +msgid "Data Import failed at %s due to an unexpected error" +msgstr "Dataimport mislyktes ved% s på grunn av en uventet feil." + +#. module: import_base +#: code:addons/import_base/import_framework.py:436 +#, python-format +msgid "Import finished, notification email sended" +msgstr "Import ferdig, e-postmelding sendt." + +#. module: import_base +#: code:addons/import_base/import_framework.py:190 +#, python-format +msgid "%s is not a valid model name" +msgstr "% s er ikke en gyldig modellnavn." + +#. module: import_base +#: model:ir.ui.menu,name:import_base.menu_import_crm +msgid "Import" +msgstr "Import" + +#. module: import_base +#: code:addons/import_base/import_framework.py:467 +#, python-format +msgid "with no warning" +msgstr "Uten advarsel." + +#. module: import_base +#: code:addons/import_base/import_framework.py:469 +#, python-format +msgid "with warning : %s" +msgstr "Med advarsel :%s" + +#. module: import_base +#: code:addons/import_base/import_framework.py:191 +#, python-format +msgid " fields imported : " +msgstr " Felt importert: " diff --git a/addons/import_base/import_framework.py b/addons/import_base/import_framework.py index 74d2185b57e..7e0a055d95b 100644 --- a/addons/import_base/import_framework.py +++ b/addons/import_base/import_framework.py @@ -425,7 +425,7 @@ class import_framework(Thread): email_id = email_obj.create(self.cr, self.uid, { 'email_from' : 'import@module.openerp', 'email_to' : self.email, - 'body_text' : self.get_email_body(result, error), + 'body' : self.get_email_body(result, error), 'subject' : self.get_email_subject(result, error), 'auto_delete' : True}) email_obj.send(self.cr, self.uid, [email_id]) diff --git a/addons/import_google/i18n/mn.po b/addons/import_google/i18n/mn.po new file mode 100644 index 00000000000..9b0376c0d98 --- /dev/null +++ b/addons/import_google/i18n/mn.po @@ -0,0 +1,217 @@ +# Mongolian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-08-31 07:21+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Mongolian <mn@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-01 04:55+0000\n" +"X-Generator: Launchpad (build 15890)\n" + +#. module: import_google +#: help:synchronize.google.import,group_name:0 +msgid "Choose which group to import, By default it takes all." +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Google Calendar Events" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Events" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "" +"No Google Username or password Defined for user.\n" +"Please define in user view" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#, python-format +msgid "" +"Invalid login detail !\n" +" Specify Username/Password." +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,supplier:0 +msgid "Supplier" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Options" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,group_name:0 +msgid "Group Name" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_crm_case_categ +msgid "Category of Case" +msgstr "" + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_contact_form +#: model:ir.ui.menu,name:import_google.menu_sync_contact +msgid "Import Google Contacts" +msgstr "" + +#. module: import_google +#: view:google.import.message:0 +msgid "Import Google Data" +msgstr "" + +#. module: import_google +#: view:crm.meeting:0 +msgid "Meeting Type" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "" +"Please install gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_login +msgid "Google Contact" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import contacts from a google account" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Please specify correct user and password !" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,customer:0 +msgid "Customer" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Cancel" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_synchronize_google_import +msgid "synchronize.google.import" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Contacts" +msgstr "" + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_form +#: model:ir.ui.menu,name:import_google.menu_sync_calendar +msgid "Import Google Calendar" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:50 +#, python-format +msgid "Import google" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Error" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "Warning !" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,create_partner:0 +msgid "Options" +msgstr "Тохируулга" + +#. module: import_google +#: view:google.import.message:0 +msgid "_Ok" +msgstr "" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "Google Contacts Import Error!" +msgstr "" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_import_message +msgid "Import Message" +msgstr "" + +#. module: import_google +#: field:synchronize.google.import,calendar_name:0 +msgid "Calendar Name" +msgstr "" + +#. module: import_google +#: help:synchronize.google.import,supplier:0 +msgid "Check this box to set newly created partner as Supplier." +msgstr "" + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Import only address" +msgstr "Зөвхөн хаяг оруулах" + +#. module: import_google +#: field:crm.case.categ,user_id:0 +msgid "User" +msgstr "" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Partner Status for this Group:" +msgstr "" + +#. module: import_google +#: field:google.import.message,name:0 +msgid "Message" +msgstr "" + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Create partner for each contact" +msgstr "" + +#. module: import_google +#: help:synchronize.google.import,customer:0 +msgid "Check this box to set newly created partner as Customer." +msgstr "" diff --git a/addons/import_google/i18n/nb.po b/addons/import_google/i18n/nb.po new file mode 100644 index 00000000000..a6414341f8f --- /dev/null +++ b/addons/import_google/i18n/nb.po @@ -0,0 +1,226 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-09 16:36+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-10 04:41+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: import_google +#: help:synchronize.google.import,group_name:0 +msgid "Choose which group to import, By default it takes all." +msgstr "Velg hvilken gruppe du vil importere, standard tar alt." + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Google Calendar Events" +msgstr "Importer Google Kalender Hendelser." + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Events" +msgstr "_Importer hendelser." + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "" +"No Google Username or password Defined for user.\n" +"Please define in user view" +msgstr "" +"Ingen Google-brukernavn eller passord definert for brukeren.\n" +"Vennligst angi i brukerens visning." + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#, python-format +msgid "" +"Invalid login detail !\n" +" Specify Username/Password." +msgstr "" +"Ugyldig pålogging detalj!\n" +" Angi brukernavn / passord." + +#. module: import_google +#: field:synchronize.google.import,supplier:0 +msgid "Supplier" +msgstr "Leverandør" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import Options" +msgstr "Alternativer for import" + +#. module: import_google +#: field:synchronize.google.import,group_name:0 +msgid "Group Name" +msgstr "Gruppe Navn" + +#. module: import_google +#: model:ir.model,name:import_google.model_crm_case_categ +msgid "Category of Case" +msgstr "Kategori for hendelse" + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_contact_form +#: model:ir.ui.menu,name:import_google.menu_sync_contact +msgid "Import Google Contacts" +msgstr "Importer Google kontakter." + +#. module: import_google +#: view:google.import.message:0 +msgid "Import Google Data" +msgstr "Importer Google data." + +#. module: import_google +#: view:crm.meeting:0 +msgid "Meeting Type" +msgstr "Møtetype" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "" +"Please install gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" +msgstr "" +"Vennligst Installer gdata-python-client from http://code.google.com/p/gdata-" +"python-client/downloads/list" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_login +msgid "Google Contact" +msgstr "Google kontakt" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Import contacts from a google account" +msgstr "Importer kontakter fra en Google-konto" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Please specify correct user and password !" +msgstr "Vennligst oppgi riktig bruker og passord!" + +#. module: import_google +#: field:synchronize.google.import,customer:0 +msgid "Customer" +msgstr "Kunde" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Cancel" +msgstr "_Avbryt" + +#. module: import_google +#: model:ir.model,name:import_google.model_synchronize_google_import +msgid "synchronize.google.import" +msgstr "Synkronisere.google.import" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "_Import Contacts" +msgstr "_Importer kontakter." + +#. module: import_google +#: model:ir.actions.act_window,name:import_google.act_google_login_form +#: model:ir.ui.menu,name:import_google.menu_sync_calendar +msgid "Import Google Calendar" +msgstr "Importer Google kalender." + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:50 +#, python-format +msgid "Import google" +msgstr "Importer Google." + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:127 +#: code:addons/import_google/wizard/import_google_data.py:133 +#, python-format +msgid "Error" +msgstr "Feil" + +#. module: import_google +#: code:addons/import_google/wizard/import_google_data.py:71 +#, python-format +msgid "Warning !" +msgstr "Advarsel !" + +#. module: import_google +#: field:synchronize.google.import,create_partner:0 +msgid "Options" +msgstr "Valg" + +#. module: import_google +#: view:google.import.message:0 +msgid "_Ok" +msgstr "_Ok" + +#. module: import_google +#: code:addons/import_google/wizard/import_google.py:38 +#: code:addons/import_google/wizard/import_google_data.py:28 +#, python-format +msgid "Google Contacts Import Error!" +msgstr "Google kontakter Import Feil!" + +#. module: import_google +#: model:ir.model,name:import_google.model_google_import_message +msgid "Import Message" +msgstr "Importer melding." + +#. module: import_google +#: field:synchronize.google.import,calendar_name:0 +msgid "Calendar Name" +msgstr "Kalendernavn" + +#. module: import_google +#: help:synchronize.google.import,supplier:0 +msgid "Check this box to set newly created partner as Supplier." +msgstr "Kryss av for å sette nyopprettede partner som leverandør." + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Import only address" +msgstr "Importer bare adresse." + +#. module: import_google +#: field:crm.case.categ,user_id:0 +msgid "User" +msgstr "Bruker" + +#. module: import_google +#: view:synchronize.google.import:0 +msgid "Partner Status for this Group:" +msgstr "" + +#. module: import_google +#: field:google.import.message,name:0 +msgid "Message" +msgstr "Melding" + +#. module: import_google +#: selection:synchronize.google.import,create_partner:0 +msgid "Create partner for each contact" +msgstr "Opprett partner for hver kontakt." + +#. module: import_google +#: help:synchronize.google.import,customer:0 +msgid "Check this box to set newly created partner as Customer." +msgstr "Kryss av for å sette nyopprettede partner som Kunden." + +#~ msgid "Partner status for this group:" +#~ msgstr "Partner status for denne gruppen." diff --git a/addons/import_sugarcrm/i18n/fr.po b/addons/import_sugarcrm/i18n/fr.po index e9872a1f655..64673de4bc6 100644 --- a/addons/import_sugarcrm/i18n/fr.po +++ b/addons/import_sugarcrm/i18n/fr.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 00:36+0000\n" -"PO-Revision-Date: 2012-05-24 17:37+0000\n" -"Last-Translator: Numérigraphe <Unknown>\n" +"PO-Revision-Date: 2012-09-03 22:20+0000\n" +"Last-Translator: Nicolas JEUDY <njeudy@simplee.fr>\n" "Language-Team: French <fr@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:42+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: import_sugarcrm #: code:addons/import_sugarcrm/import_sugarcrm.py:1105 @@ -38,7 +38,7 @@ msgstr "(Prochainement)" #. module: import_sugarcrm #: field:import.sugarcrm,document:0 msgid "Documents" -msgstr "" +msgstr "Documents" #. module: import_sugarcrm #: view:import.sugarcrm:0 @@ -58,7 +58,7 @@ msgstr "" #. module: import_sugarcrm #: field:import.sugarcrm,contact:0 msgid "Contacts" -msgstr "" +msgstr "Contacts" #. module: import_sugarcrm #: view:import.sugarcrm:0 @@ -79,7 +79,7 @@ msgstr "Nom de l'instance" #. module: import_sugarcrm #: field:import.sugarcrm,project_task:0 msgid "Project Tasks" -msgstr "" +msgstr "Tâches du projet" #. module: import_sugarcrm #: field:import.sugarcrm,email_from:0 @@ -134,27 +134,27 @@ msgstr "" #. module: import_sugarcrm #: field:import.sugarcrm,employee:0 msgid "Employee" -msgstr "" +msgstr "Employé" #. module: import_sugarcrm #: view:import.sugarcrm:0 msgid "Document" -msgstr "" +msgstr "Document" #. module: import_sugarcrm #: help:import.sugarcrm,document:0 msgid "Check this box to import sugarCRM Documents into OpenERP documents" -msgstr "" +msgstr "Cochez cette case pour importer les documents SugarCRM dans OpenERP" #. module: import_sugarcrm #: view:import.sugarcrm:0 msgid "Import Data From SugarCRM" -msgstr "" +msgstr "Importee les données de SugarCRM" #. module: import_sugarcrm #: view:import.sugarcrm:0 msgid "CRM" -msgstr "" +msgstr "CRM" #. module: import_sugarcrm #: view:import.message:0 @@ -166,7 +166,7 @@ msgstr "" #. module: import_sugarcrm #: field:import.sugarcrm,call:0 msgid "Calls" -msgstr "" +msgstr "Appels" #. module: import_sugarcrm #: view:import.sugarcrm:0 @@ -176,7 +176,7 @@ msgstr "" #. module: import_sugarcrm #: view:import.message:0 msgid "_Ok" -msgstr "" +msgstr "_Valider" #. module: import_sugarcrm #: help:import.sugarcrm,opportunity:0 @@ -184,11 +184,13 @@ msgid "" "Check this box to import sugarCRM Leads and Opportunities into OpenERP Leads " "and Opportunities" msgstr "" +"Cochez cette case pour importer les Pistes et les Opportunités SugarCRM dans " +"OpenERP." #. module: import_sugarcrm #: field:import.sugarcrm,email_history:0 msgid "Email and Note" -msgstr "" +msgstr "Email et note" #. module: import_sugarcrm #: help:import.sugarcrm,url:0 @@ -208,12 +210,12 @@ msgstr "" #. module: import_sugarcrm #: view:import.sugarcrm:0 msgid "_Import" -msgstr "" +msgstr "_Importer" #. module: import_sugarcrm #: field:import.sugarcrm,user:0 msgid "User" -msgstr "" +msgstr "Utilisateur" #. module: import_sugarcrm #: code:addons/import_sugarcrm/import_sugarcrm.py:1105 diff --git a/addons/import_sugarcrm/import_sugarcrm.py b/addons/import_sugarcrm/import_sugarcrm.py index 440a7b2b6f6..1646fb7ff2a 100644 --- a/addons/import_sugarcrm/import_sugarcrm.py +++ b/addons/import_sugarcrm/import_sugarcrm.py @@ -226,7 +226,7 @@ class sugar_import(import_framework): 'model': 'model', 'partner_id/.id': 'partner_id/.id', 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'), - 'body_text': 'description', + 'body': 'description', 'body_html' : 'description_html', } diff --git a/addons/l10n_be_hr_payroll/l10n_be_hr_payroll_view.xml b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll_view.xml index 893a977e62c..b1951a2a032 100644 --- a/addons/l10n_be_hr_payroll/l10n_be_hr_payroll_view.xml +++ b/addons/l10n_be_hr_payroll/l10n_be_hr_payroll_view.xml @@ -38,11 +38,11 @@ <record id="hr_employee_form__l10n_be_view_for" model="ir.ui.view"> <field name="name">hr.employee.inherit.form</field> <field name="model">hr.employee</field> - <field name="inherit_id" ref="hr.view_employee_form"/> + <field name="inherit_id" ref="hr_contract.hr_hr_employee_view_form2"/> <field name="priority">30</field> <field name="arch" type="xml"> <data> - <xpath expr="//field[@name='gender']" position="before"> + <xpath expr="//field[@name='vehicle_distance']" position="after"> <field name="resident_bool" eval="False"/> </xpath> <xpath expr="//field[@name='marital']" position="after"> diff --git a/addons/l10n_br/account.py b/addons/l10n_br/account.py index 6e38aaa690f..64d3c2cda79 100644 --- a/addons/l10n_br/account.py +++ b/addons/l10n_br/account.py @@ -29,6 +29,7 @@ import decimal_precision as dp from tools.misc import currency from tools.translate import _ from tools import config +from openerp import SUPERUSER_ID class account_tax_code_template(osv.osv): @@ -53,7 +54,7 @@ class account_tax_template(osv.osv): def get_precision_tax(): def change_digit_tax(cr): - res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, 'Account') + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, SUPERUSER_ID, 'Account') return (16, res+2) return change_digit_tax @@ -91,7 +92,7 @@ class account_tax(osv.osv): def get_precision_tax(): def change_digit_tax(cr): - res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, 'Account') + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, SUPERUSER_ID, 'Account') return (16, res+2) return change_digit_tax diff --git a/addons/l10n_ca/account_chart_en.xml b/addons/l10n_ca/account_chart_en.xml index 0d67e081d08..acb4fa4802b 100644 --- a/addons/l10n_ca/account_chart_en.xml +++ b/addons/l10n_ca/account_chart_en.xml @@ -6,7 +6,6 @@ <record id="chart0_en" model="account.account.template"> <field name="code">0</field> <field name="name">Account Chart CA EN</field> - <field eval="0" name="parent_id"/> <field name="type">view</field> <field name="user_type" ref="account.data_account_type_view"/> </record> diff --git a/addons/l10n_ca/i18n/nb.po b/addons/l10n_ca/i18n/nb.po new file mode 100644 index 00000000000..47276cdded2 --- /dev/null +++ b/addons/l10n_ca/i18n/nb.po @@ -0,0 +1,128 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2010-08-02 21:08+0000\n" +"PO-Revision-Date: 2012-09-09 16:43+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-10 04:41+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_receivable +msgid "Receivable" +msgstr "Fordring" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.acct_type_asset_view +msgid "Asset View" +msgstr "Eiendel Vis" + +#. module: l10n_ca +#: model:ir.module.module,description:l10n_ca.module_meta_information +msgid "" +"This is the module to manage the canadian accounting chart in OpenERP." +msgstr "" +"Dette er modulen for å administrere kanadiske regnskap diagrammet i OpenERP." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.acct_type_expense_view +msgid "Expense View" +msgstr "Kostnad Vis" + +#. module: l10n_ca +#: constraint:account.account.template:0 +msgid "Error ! You can not create recursive account templates." +msgstr "Feil ! Du kan ikke opprette rekursive konto maler." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.acct_type_income_view +msgid "Income View" +msgstr "Inntekt Vis" + +#. module: l10n_ca +#: model:ir.module.module,shortdesc:l10n_ca.module_meta_information +msgid "Canada - Chart of Accounts" +msgstr "Canada - Kontoplan" + +#. module: l10n_ca +#: constraint:account.account.type:0 +msgid "Error ! You can not create recursive types." +msgstr "Feil ! Du kan ikke opprette rekursive typer." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_tax +msgid "Tax" +msgstr "Skatt" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_cash +msgid "Cash" +msgstr "Kontant" + +#. module: l10n_ca +#: model:ir.actions.todo,note:l10n_ca.config_call_account_template_ca +msgid "" +"Generate Chart of Accounts from a Chart Template. You will be asked to pass " +"the name of the company, the chart template to follow, the no. of digits to " +"generate the code for your accounts and Bank account, currency to create " +"Journals. Thus,the pure copy of chart Template is generated.\n" +"\tThis is the same wizard that runs from Financial " +"Management/Configuration/Financial Accounting/Financial Accounts/Generate " +"Chart of Accounts from a Chart Template." +msgstr "" +"Generere Kontoplan fra en diagrammal. Du vil bli bedt om å passere navnet på " +"selskapet, diagrammal til følge, nei. sifre som skal generere koden for " +"kontoene og bankkonto, valuta å skape Journals. Dermed er den rene kopi av " +"diagrammal generert.\n" +"\t Dette er den samme veiviser som går fra økonomistyring / konfigurasjon / " +"finansiell Regnskap / finansregnskapet / Generer Kontoplan fra en diagrammal." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_payable +msgid "Payable" +msgstr "Utgift" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_asset +msgid "Asset" +msgstr "Eiendel" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_equity +msgid "Equity" +msgstr "Egenkapital" + +#. module: l10n_ca +#: constraint:account.tax.code.template:0 +msgid "Error ! You can not create recursive Tax Codes." +msgstr "Feil ! Du kan ikke opprette rekursive skatte koder." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.acct_type_liability_view +msgid "Liability View" +msgstr "Ansvar Vis." + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_expense +msgid "Expense" +msgstr "Utgift" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_income +msgid "Income" +msgstr "Inntekt" + +#. module: l10n_ca +#: model:account.account.type,name:l10n_ca.account_type_view +msgid "View" +msgstr "Vis" diff --git a/addons/l10n_cn/i18n/nb.po b/addons/l10n_cn/i18n/nb.po new file mode 100644 index 00000000000..cafcf30d8e8 --- /dev/null +++ b/addons/l10n_cn/i18n/nb.po @@ -0,0 +1,66 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-12-23 09:56+0000\n" +"PO-Revision-Date: 2012-09-08 19:45+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:52+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_profit_and_loss +msgid "损益类" +msgstr "Gevinst eller tap kategori." + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_all +msgid "所有科目" +msgstr "Alle fag." + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_equity +msgid "所有者权益类" +msgstr "Eiernes egenkapital." + +#. module: l10n_cn +#: model:ir.actions.todo,note:l10n_cn.config_call_account_template_cn_chart +msgid "" +"Generate Chart of Accounts from a Chart Template. You will be asked to pass " +"the name of the company, the chart template to follow, the no. of digits to " +"generate the code for your accounts and Bank account, currency to create " +"Journals. Thus,the pure copy of chart Template is generated.\n" +"\tThis is the same wizard that runs from Financial " +"Management/Configuration/Financial Accounting/Financial Accounts/Generate " +"Chart of Accounts from a Chart Template." +msgstr "" +"Generere Kontoplan fra en diagrammal. Du vil bli bedt om å passere navnet på " +"selskapet, diagrammal til følge, nei. sifre som skal generere koden for " +"kontoene og bankkonto, valuta å skape Journals. Dermed er den rene kopi av " +"diagrammal generert.\n" +"\t Dette er den samme veiviser som går fra økonomistyring / konfigurasjon / " +"finansiell Regnskap / finansregnskapet / Generer Kontoplan fra en diagrammal." + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_debt +msgid "负债类" +msgstr "Gjeld klasse." + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_cost +msgid "成本类" +msgstr "Kostnaden for klassen" + +#. module: l10n_cn +#: model:account.account.type,name:l10n_cn.user_type_capital +msgid "资产类" +msgstr "Aktivaklasser" diff --git a/addons/l10n_de/i18n/nb.po b/addons/l10n_de/i18n/nb.po new file mode 100644 index 00000000000..b8ba51a6810 --- /dev/null +++ b/addons/l10n_de/i18n/nb.po @@ -0,0 +1,66 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2010-12-15 15:05+0000\n" +"PO-Revision-Date: 2012-09-08 19:47+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-09 04:52+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_non_eu_sale_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_non_eu_sale_skr04 +msgid "Kunde Ausland" +msgstr "Kunde Ausland" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_no_id_sale_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_no_id_sale_skr04 +msgid "Kunde EU (ohne USt-ID)" +msgstr "Kunde EU (ohne USt-ID)" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_non_eu_purchase_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_non_eu_purchase_skr04 +msgid "Lieferant Ausland" +msgstr "" + +#. module: l10n_de +#: model:ir.module.module,shortdesc:l10n_de.module_meta_information +msgid "Deutschland - SKR03 and SKR04" +msgstr "" + +#. module: l10n_de +#: model:ir.module.module,description:l10n_de.module_meta_information +msgid "" +"Dieses Modul beinhaltet einen deutschen Kontenrahmen basierend auf dem " +"SKR03." +msgstr "" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_vat_id_purchase_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_vat_id_purchase_skr04 +msgid "Lieferant EU Unternehmen (mit USt-ID)" +msgstr "" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_no_id_purchase_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_no_id_purchase_skr04 +msgid "Lieferant EU (ohne Ust-ID)" +msgstr "" + +#. module: l10n_de +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_vat_id_sale_skr03 +#: model:account.fiscal.position.template,name:l10n_de.fiscal_position_eu_vat_id_sale_skr04 +msgid "Kunde EU Unternehmen (mit USt-ID)" +msgstr "" diff --git a/addons/l10n_gr/account_chart.xml b/addons/l10n_gr/account_chart.xml index 0ad5579d867..950d9e7b09c 100644 --- a/addons/l10n_gr/account_chart.xml +++ b/addons/l10n_gr/account_chart.xml @@ -7,7 +7,6 @@ <record id="chartgr_0" model="account.account.template"> <field name="code">0</field> <field name="name">Γενικό Λογιστικό Σχέδιο</field> - <!--<field eval="0" name="parent_id"/> --> <field name="type">view</field> <field name="user_type" ref="account_type_view"/> </record> diff --git a/addons/l10n_gt/account_chart.xml b/addons/l10n_gt/account_chart.xml index ffb4177d743..0f9aecfb423 100644 --- a/addons/l10n_gt/account_chart.xml +++ b/addons/l10n_gt/account_chart.xml @@ -7,7 +7,6 @@ <record id="plan_raiz" model="account.account.template"> <field name="code">-</field> <field name="name">Plan contable de Guatemala (sencillo)</field> - <field eval="0" name="parent_id"/> <field name="type">view</field> <field name="user_type" ref="cuenta_vista"/> </record> diff --git a/addons/l10n_hn/account_chart.xml b/addons/l10n_hn/account_chart.xml index 7bd17b0e82d..228c60ef4dc 100644 --- a/addons/l10n_hn/account_chart.xml +++ b/addons/l10n_hn/account_chart.xml @@ -7,7 +7,6 @@ <record id="plan_raiz" model="account.account.template"> <field name="code">-</field> <field name="name">Plan contable de Honduras (sencillo)</field> - <field eval="0" name="parent_id"/> <field name="type">view</field> <field name="user_type" ref="cuenta_vista"/> </record> diff --git a/addons/l10n_in/l10n_in_private_chart.xml b/addons/l10n_in/l10n_in_private_chart.xml index db11d0638c8..f38f95750cf 100644 --- a/addons/l10n_in/l10n_in_private_chart.xml +++ b/addons/l10n_in/l10n_in_private_chart.xml @@ -8,7 +8,6 @@ <field name="name">Partnership/Private Firm Chart of Account</field> <field name="code">0</field> <field name="type">view</field> - <field eval="0" name="parent_id"/> <field name="user_type" ref="account.data_account_type_view"/> <field name="reconcile" eval="False"/> </record> @@ -520,4 +519,4 @@ </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/l10n_in/l10n_in_public_chart.xml b/addons/l10n_in/l10n_in_public_chart.xml index 2c2dda0dd9d..4a4098d56c9 100644 --- a/addons/l10n_in/l10n_in_public_chart.xml +++ b/addons/l10n_in/l10n_in_public_chart.xml @@ -9,7 +9,6 @@ <field name="name">Public Firm Chart of Account</field> <field name="code">0</field> <field name="type">view</field> - <field eval="0" name="parent_id"/> <field name="user_type" ref="account.data_account_type_view"/> <field name="reconcile" eval="False"/> </record> @@ -673,4 +672,4 @@ </record> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/l10n_in_hr_payroll/__init__.py b/addons/l10n_in_hr_payroll/__init__.py new file mode 100644 index 00000000000..9631ccf0317 --- /dev/null +++ b/addons/l10n_in_hr_payroll/__init__.py @@ -0,0 +1,26 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import l10n_in_hr_payroll +import report +import wizard + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/__openerp__.py b/addons/l10n_in_hr_payroll/__openerp__.py new file mode 100644 index 00000000000..6d891c7a08e --- /dev/null +++ b/addons/l10n_in_hr_payroll/__openerp__.py @@ -0,0 +1,68 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +{ + 'name': 'Indian Payroll', + 'category': 'Localization', + 'init_xml': [], + 'author': 'OpenERP SA', + 'website':'http://www.openerp.com', + 'depends': ['hr_payroll'], + 'version': '1.0', + 'description': """ +Indian Payroll Salary Rules. +============================ + + -Configuration of hr_payroll for India localization + -All main contributions rules for India payslip. + * New payslip report + * Employee Contracts + * Allow to configure Basic / Gross / Net Salary + * Employee PaySlip + * Allowance / Deduction + * Integrated with Holiday Management + * Medical Allowance, Travel Allowance, Child Allowance, ... + - Payroll Advice and Report + - Yearly Salary by Head and Yearly Salary by Employee Report + """, + 'active': False, + 'update_xml': [ + 'l10n_in_hr_payroll_view.xml', + 'data/l10n_in_hr_payroll_data.xml', + 'data/hr.salary.rule.csv', + 'security/ir.model.access.csv', + 'l10n_in_hr_payroll_report.xml', + 'l10n_in_hr_payroll_sequence.xml', + 'wizard/hr_salary_employee_bymonth_view.xml', + 'wizard/hr_yearly_salary_detail_view.xml', + 'report/payment_advice_report_view.xml', + 'report/payslip_report_view.xml', + + ], + 'test': [ + 'test/payment_advice.yml', + 'test/payment_advice_batch.yml' + ], + + 'demo_xml': ['l10n_in_hr_payroll_demo.xml'], + 'installable': True +} + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv new file mode 100644 index 00000000000..2fe1ba739f4 --- /dev/null +++ b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv @@ -0,0 +1,5 @@ +"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" +1,"fix",1,1,,100,"Education Allowance For One Child","Allowance",23,"CHEAONE","hr_payroll_rule_child1","range","employee.children", +2,"fix",2,10,,200,"Education Allowance For Two Child","Allowance",24,"CHEATWO","hr_payroll_rule_child1","range","employee.children", +3,"fix",1,1,,300,"Child Hostel Allowance For One Child","Allowance",26,"CHOONE","hr_payroll_rule_child2","range","employee.children", +4,"fix",2,10,,600,"Child Hostel Allowance For Two Child","Allowance",27,"CHOTWO","hr_payroll_rule_child2","range","employee.children", diff --git a/addons/l10n_in_hr_payroll/data/l10n_in_hr_payroll_data.xml b/addons/l10n_in_hr_payroll/data/l10n_in_hr_payroll_data.xml new file mode 100644 index 00000000000..8c6449e6336 --- /dev/null +++ b/addons/l10n_in_hr_payroll/data/l10n_in_hr_payroll_data.xml @@ -0,0 +1,584 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <!-- Contribution Register --> + + <record id="hr_houserent_allowance_register" model="hr.contribution.register"> + <field name="name">Register for House Rent Allowance</field> + </record> + + <record id="hr_register_provident_fund" model="hr.contribution.register"> + <field name="name">Register for Provident Fund</field> + </record> + + <record id="hr_professional_tax_deduction_register" model="hr.contribution.register"> + <field name="name">Register for Professional Tax</field> + </record> + + <record id="hr_food_coupen_register" model="hr.contribution.register"> + <field name="name">Register for Food Coupen</field> + </record> + + <record id="hr_tds_register" model="hr.contribution.register"> + <field name="name">Register for TDS</field> + </record> + + <record id="hr_nps_contribution_register" model="hr.contribution.register"> + <field name="name">Register for NPS Contribution</field> + </record> + + <record id="hr_vpf_contribution_register" model="hr.contribution.register"> + <field name="name">Register for Voluntarily Provident Fund</field> + </record> + + <record id="hr_company_transport_register" model="hr.contribution.register"> + <field name="name">Register for Company Provided Transport Deduction</field> + </record> + + <record id="hr_labour_Welfare_fund_register" model="hr.contribution.register"> + <field name="name">Register for State Labour Welfare Fund Deduction</field> + </record> + + <record id="hr_group_term_insurance_register" model="hr.contribution.register"> + <field name="name">Register for Company Provided Group Term Insurance Deduction</field> + </record> + + <record id="hr_leave_availed_register" model="hr.contribution.register"> + <field name="name">Register for Leave Availed Deduction</field> + </record> + + <record id="hr_medical_insurance_register" model="hr.contribution.register"> + <field name="name">Register for Company Provided Medical Insurance Deduction</field> + </record> + + <record id="hr_other_deduction_register" model="hr.contribution.register"> + <field name="name">Register for Other Deduction from Employer</field> + </record> + <!-- Hr Salary Rules for allowance--> + + <record id="hr_salary_rule_da" model="hr.salary.rule"> + <field name="code">DA</field> + <field name="name">Dearness Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">percentage</field> + <field name="amount_percentage_base">contract.wage</field> + <field name="amount_percentage" eval="50"/> + <field name="sequence" eval="13"/> + <field name="note">Dearness allowance (D.A.) is part of a person's salary. It is calculated as a percent of the basic salary. This amount is then added to the basic salary along with house rent allowance to get the total salary. Or you can say that The Dearness Allowance is a part of the total compensation a person receives for having performed his or her job.</field> + </record> + + <record id="hr_salary_rule_houserentallowancemetro_nonmetro" model="hr.salary.rule"> + <field name="code">HRAMN</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and +((contract.wage + DA) * contract.house_rent_allowance_metro_nonmetro / 100) +or (contract.wage * contract.house_rent_allowance_metro_nonmetro / 100))</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="name">House Rent Allowance</field> + <field name="register_id" ref="hr_houserent_allowance_register"/> + <field name="sequence" eval="51"/> + <field name="note">HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses.</field> + </record> + + <record id="hr_salary_trans_allownce" model="hr.salary.rule"> + <field name="code">TCA</field> + <field name="name">Transport/Conveyance Allownace</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">fix</field> + <field eval="800.0" name="amount_fix"/> + <field name="sequence" eval="14"/> + <field name="note">A conveyance allowance refers to an amount of money reimbursed to someone for the operation of a vehicle or the riding of a vehicle. The allowance is typically a designated amount or percentage of total transportation expenses that is referenced in a country's tax laws or code. Organizations and private or public businesses may also offer a conveyance allowance in addition to reimbursing employees or members for transportation expenses. In this instance, the conveyance allowance may identify an unusual transport occurrence that may not be covered by a designated travel expense report such as travel to a specific job site that requires a daily bus or taxi ride.</field> + </record> + + <record id="hr_salary_rule_special" model="hr.salary.rule"> + <field name="code">SA</field> + <field name="name">Grade/Special/Management/Supplementary Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.supplementary_allowance)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = contract.supplementary_allowance</field> + <field name="sequence" eval="20"/> + <field name="note">This allowance is normally given as an additional benefit to employees and is fully taxable.</field> + </record> + + <record id="hr_payroll_rule_child1" model="hr.salary.rule"> + <field name="code">CHEA</field> + <field name="name">Child Education Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(employee.children)</field> + <field name="amount_select">fix</field> + <field name="sequence" eval="18"/> + <field name="note">Per school going child 1200 per annum is non-taxable.Maximum for 2 children, so max 2400 per annum becomes non-taxable.</field> + </record> + + <record id="hr_payroll_rule_child2" model="hr.salary.rule"> + <field name="code">CHEAH</field> + <field name="name">Child Hostel Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(employee.children)</field> + <field name="amount_select">fix</field> + <field name="note">In case the children are in hostel, the exemption available for child.</field> + <field name="sequence" eval="19"/> + </record> + + <record id="hr_payroll_rule_city1" model="hr.salary.rule"> + <field name="code">CBDA</field> + <field name="name">City Compensatory Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">none</field> + <field name="sequence" eval="21"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and ((contract.wage + DA) * 0.10) +or (contract.wage * 0.10))</field> + <field name="note">This allowance is paid to Employees who are posted in big cities. The purpose is to compensate the high cost of living in cities like Mumbai, Delhi, etc. However it is Fully Taxable.</field> + </record> + + <record id="hr_payroll_rule_metrocity" model="hr.salary.rule"> + <field name="code">CMETRO</field> + <field name="name">City Allowance for Metro city</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">none</field> + <field name="sequence" eval="22"/> + <field name="amount_select">fix</field> + <field name="amount_fix">850.0</field> + </record> + + <record id="hr_payroll_rule_nonmetrocity" model="hr.salary.rule"> + <field name="code">CNMETRO</field> + <field name="name">City Allowance for Non Metro city</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">none</field> + <field name="sequence" eval="25"/> + <field name="amount_select">fix</field> + <field name="amount_fix">800.0</field> + </record> + + <record id="hr_salary_rule_arrears" model="hr.salary.rule"> + <field name="code">ARRE</field> + <field name="name">Arrears</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.ARS.amount</field> + <field eval="0.0" name="amount_fix"/> + <field name="sequence" eval="28"/> + <field name="note">Generally arrears are fully taxable, but employee may claim exemption u/s 89(1). +One would need to compute income tax on the arrears if it would have been received in actual year. +Now difference of income tax between payment year and actual year would be allowed for deduction.</field> + </record> + + <record id="hr_salary_rule_lta" model="hr.salary.rule"> + <field name="code">LTA</field> + <field name="name">Leave Travel Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">percentage</field> + <field name="amount_percentage_base">contract.wage</field> + <field name="amount_percentage" eval="8.33"/> + <field name="sequence" eval="29"/> + <field name="note">As per Income tax rules of India, if transport bills for LTA are not provided,the amount will be taxed. E.g. If an employee has LTA allowance as Rs 50,000 in his CTC(cost to company),and he provides proofs of Rs 40,000 (boarding pass,air tickets, taxi vouchers) then income tax will be deducted for rest of the Rs 10,000. Does not matter whats the amount of LTA in an employee’s package, income tax laws only permits domestic air tickets only for LTA claim.</field> + </record> + + <record id="hr_salary_rule_le" model="hr.salary.rule"> + <field name="code">LE</field> + <field name="name">Leave Encashment</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">none</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.LE.amount</field> + <field name="sequence" eval="30"/> + <field name="note">Payment by way of leave encashment received by Central and State Govt.employees at the time of retirement in respect of the period of earned leave at credit is fully exempt. In case of other employees, the exemption is to be limited to minimum of all below: + 1.The actual amount received + 2.The cash equivalent of leave balance (max 30 days per year of service) + 3.Maximum of 10 months of leave encashment, based on last 10 months average salary + 4.Rs. 3 Lakh</field> + </record> + + <record id="hr_salary_rule_performance" model="hr.salary.rule"> + <field name="code">PI</field> + <field name="name">Performance Incentive</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.PERF.amount</field> + <field name="sequence" eval="31"/> + <field name="note">This would be fully taxable based on incentive.</field> + </record> + + <record id="hr_salary_rule_bonus" model="hr.salary.rule"> + <field name="code">BONUS</field> + <field name="name">Bonus</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.BNS.amount</field> + <field name="sequence" eval="41"/> + <field name="note">This would be fully taxable based on Bonus.</field> + </record> + + <record id="hr_salary_rule_medical_allow" model="hr.salary.rule"> + <field name="code">MEDA</field> + <field name="name">Medical Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">fix</field> + <field eval="1250.0" name="amount_fix"/> + <field name="sequence" eval="43"/> + <field name="note">This component is on-taxable up to 15000 per year (or Rs 1250 per month) on producing medical bills.</field> + </record> + + <record id="hr_salary_rule_medical" model="hr.salary.rule"> + <field name="code">MEDA</field> + <field name="name">Medical Reimbursement</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.MR.amount</field> + <field name="sequence" eval="32"/> + <field name="note">This component is on-taxable up to 15000 per year (or Rs 1250 per month) on producing medical bills.</field> + </record> + + <record id="hr_salary_rule_food_coupon" model="hr.salary.rule"> + <field name="amount_select">fix</field> + <field eval="50" name="amount_fix"/> + <field name="quantity">worked_days.WORK100.number_of_days</field> + <field name="code">FC</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="name">Food Allowance</field> + <field name="register_id" ref="hr_food_coupen_register"/> + <field name="sequence" eval="33"/> + </record> + + <record id="hr_salary_rule_journals" model="hr.salary.rule"> + <field name="code">PERJ</field> + <field name="name">Periodical Journals</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.PJ.amount</field> + <field name="sequence" eval="34"/> + <field name="note">Some employers may provide component for buying magazines, journals and books as a part of knowledge enhancement for business growth.This part would become non taxable on providing original bills.</field> + </record> + + <record id="hr_salary_rule_uniform_senior" model="hr.salary.rule"> + <field name="code">UNIFS</field> + <field name="name">Uniform/Dress Allowance for Senior Executive</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">fix</field> + <field eval="1000" name="amount_fix"/> + <field name="sequence" eval="35"/> + <field name="note">Some sections of employees mat get allowance for purchase of office dress/uniform.In such case, the component would become non-taxable.</field> + </record> + + <record id="hr_salary_rule_uniform_junior" model="hr.salary.rule"> + <field name="code">UNIFJ</field> + <field name="name">Uniform/Dress Allowance for Junior Executive</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">fix</field> + <field eval="600" name="amount_fix"/> + <field name="sequence" eval="42"/> + <field name="note">Some sections of employees mat get allowance for purchase of office dress/uniform.In such case, the component would become non-taxable.</field> + </record> + + <record id="hr_salary_rule_telephone" model="hr.salary.rule"> + <field name="code">TELR</field> + <field name="name">Telephone Reimbursement</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.TR.amount</field> + <field name="sequence" eval="36"/> + <field name="note">In some of the cases, companies may provide a component for telephone bills.Employees may provide actual phone usage bills to reimburse this component and make it non-taxable. + </field> + </record> + + <record id="hr_salary_rule_prof_develope" model="hr.salary.rule"> + <field name="code">PDA</field> + <field name="name">Professional Development Allowance</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">fix</field> + <field eval="0.0" name="amount_fix"/> + <field name="sequence" eval="37"/> + </record> + + <record id="hr_payroll_rule_car" model="hr.salary.rule"> + <field name="code">CAR</field> + <field name="name">Car Expenses Reimbursement</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.CEI.amount</field> + <field name="sequence" eval="38"/> + <field name="note">In case company provides component for this and employee use self owned car for official and personal purposes, Rs 1800 per month would be non-taxable on showing bills for fuel or can maintenance. This amount would be Rs 2400 in case car is more capacity than 1600cc.</field> + </record> + + <record id="hr_salary_rule_internet" model="hr.salary.rule"> + <field name="code">INT</field> + <field name="name">Internet Expense</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = inputs.IE.amount</field> + <field name="sequence" eval="39"/> + <field name="note">Employer may also provide reimbursement of internet expenses and thus this would become non taxable.</field> + </record> + + <record id="hr_salary_rule_driver" model="hr.salary.rule"> + <field name="code">DRI</field> + <field name="name">Driver Salary</field> + <field name="category_id" ref="hr_payroll.ALW"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.driver_salay)</field> + <field name="amount_select">fix</field> + <field eval="900.0" name="amount_fix"/> + <field name="sequence" eval="40"/> + <field name="note">Rs. 900 per month (non taxable)</field> + </record> + + + <!--hr salary rules for Deductions --> + + <record id="hr_payslip_rule_tds" model="hr.salary.rule"> + <field name="code">TDS</field> + <field name="name">Tax Deducted at Source</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.tds)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = -(contract.tds)</field> + <field name="register_id" ref="hr_tds_register"/> + <field name="sequence" eval="140"/> + <field name="note">As per income tax rules, all payment which are taxable in nature should be done after deduction of taxes at the source itself. Hence employer compute income tax on salary payment and deduct it every month. This TDS is based on employee’s saving/investment declaration at the start of year. If investments for tax saving is not done, large amount may be deducted in last few months.</field> + </record> + + <record id="hr_payslip_line_professional_tax" model="hr.salary.rule"> + <field name="code">PTD</field> + <field name="name">Professional Tax</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">python</field> + <field name="condition_python">result = contract.wage >= 3000 </field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (-200 if contract.wage >= 12000 +else -150 if ((contract.wage >= 9000) and (contract.wage <= 11999)) +else -80 if ((contract.wage >= 6000) and (contract.wage <= 8999)) +else -20 if ((contract.wage >= 3000) and (contract.wage <= 5999)) +else -0)</field> + <field name="register_id" ref="hr_professional_tax_deduction_register"/> + <field eval="145" name="sequence"/> + <field name="note">Workers living in states that impose the professional tax must submit a payment each half-year for the right to practice a profession or trade. It applies equally to employees who work for the national or state government, and those employed by private corporations. The professional tax uses a six-month accounting system, which divides the year into two periods, beginning on April 1 and October 1.</field> + </record> + + <record id="hr_payslip_rule_epf" model="hr.salary.rule"> + <field name="code">EPMF</field> + <field name="name">Employee's PF Contribution</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and - ((contract.wage + DA) * 0.12) +or - (contract.wage * 0.12))</field> + <field name= "note">Employer contribution does not become part of employee’s income and hence income tax is not payable on this part.</field> + <field name="register_id" ref="hr_register_provident_fund"/> + <field name="sequence" eval="150"/> + </record> + + <record id="hr_payslip_rule_enps" model="hr.salary.rule"> + <field name="code">ENPFC</field> + <field name="name">Employee's NPS Contribution</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and - ((contract.wage + DA) * 0.10) +or - (contract.wage * 0.10))</field> + <field name="sequence" eval="155"/> + <field name="register_id" ref="hr_nps_contribution_register"/> + <field name="note">Employee can claim deduction even of employer's contribution to NPS.</field> + </record> + + <record id="hr_payslip_rule_vpf" model="hr.salary.rule"> + <field name="code">VPF</field> + <field name="name">Voluntarily Provident Fund Contribution</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.voluntary_provident_fund)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and +- ((contract.wage + DA) * contract.voluntary_provident_fund / 100) +or - (contract.wage * contract.voluntary_provident_fund / 100))</field> + <field name="sequence" eval="160"/> + <field name="register_id" ref="hr_vpf_contribution_register"/> + <field name="note">VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government.This additional amount enjoys all the benefits of PF except that the employer is not liable to contribute any extra amount apart from 12%.An added advantage is that the interest rate is equal to the interest rate of PF and he withdrawal is tax free. Please note that the maximum contribution towards VPF is 100% of your Basic.The highest rate of interest (close to 9%) makes it a very attractive saving scheme. Because of these advantages many employees chose not to close their PF account even after getting employment else where other than India.Employees also get a major tax break on their entire contribution to the fund up to a ceiling of Rs. 70,000/-</field> + </record> + + <record id="hr_payslip_rule_cpt" model="hr.salary.rule"> + <field name="code">CPT</field> + <field name="name">Deduction for Company Provided Transport</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">none</field> + <field name="amount_select">fix</field> + <field eval="-1500.0" name="amount_fix"/> + <field name="register_id" ref="hr_company_transport_register"/> + <field name="sequence" eval="165"/> + <field name="appears_on_payslip" eval="False"/> + <field name="note">Company provided transport amount is based on company.</field> + </record> + + <record id="hr_salary_rule_food_coupon_ded" model="hr.salary.rule"> + <field name="amount_select">fix</field> + <field eval="-50" name="amount_fix"/> + <field name="quantity">worked_days.WORK100.number_of_days</field> + <field name="code">FD</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="name">Deduction Towards Food Coupons</field> + <field name="register_id" ref="hr_food_coupen_register"/> + <field name="sequence" eval="166"/> + </record> + + <record id="hr_payslip_rule_lwf_employee" model="hr.salary.rule"> + <field name="code">LWFE</field> + <field name="name">Employee's Deduction Towards State Labour Welfare Fund</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">fix</field> + <field eval="-3.0" name="amount_fix"/> + <field name="sequence" eval="170"/> + <field name="register_id" ref="hr_labour_Welfare_fund_register"/> + <field name="note">The LWF is applicable to all the members of the organisation except the Management staff (Staffs having authority to sign on the cheque/official documents on behalf of the organisation). for e.x. Employee Contribution is Rs. 3.00 and Employer contribution Rs. 6.00 Total Rs 9.00 and deposited to the LWF office.It is half yearly contribution (June and December).</field> + </record> + + <record id="hr_payslip_rule_lwf_employer" model="hr.salary.rule"> + <field name="code">LWF</field> + <field name="name">Employer's Deduction Towards State Labour Welfare Fund </field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">fix</field> + <field eval="-6.0" name="amount_fix"/> + <field name="sequence" eval="171"/> + <field name="register_id" ref="hr_labour_Welfare_fund_register"/> + <field name="appears_on_payslip" eval="False"/> + <field name="note">The LWF is applicable to all the members of the organisation except the Management staff (Staffs having authority to sign on the cheque/official documents on behalf of the organisation). for e.x. Employee Contribution is Rs. 3.00 and Employer contribution Rs. 6.00 Total Rs 9.00 and deposited to the LWF office.It is half yearly contribution (June and December).</field> + </record> + + <record id="hr_payslip_rule_cgti" model="hr.salary.rule"> + <field name="code">CGTI</field> + <field name="name">Deduction Towards Company Provided Group Term Insurance</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">fix</field> + <field eval="-1000.0" name="amount_fix"/> + <field name="register_id" ref="hr_group_term_insurance_register"/> + <field name="sequence" eval="175"/> + <field name="note">Group term insurance provides a solid foundation to a comprehensive employee benifit program,backed up by government asistance in the form of valuable tax incentives to both employees and employers.</field> + </record> + + <record id="hr_payslip_rule_dla" model="hr.salary.rule"> + <field name="code">DLA</field> + <field name="name">Deduction Towards Leave Availed</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">none</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = - (inputs.LAI.amount)</field> + <field name="register_id" ref="hr_leave_availed_register"/> + <field name="sequence" eval="180"/> + </record> + + <record id="hr_payslip_rule_cmt" model="hr.salary.rule"> + <field name="code">CMT</field> + <field name="name">Deduction Towards Company Provided Medical Insurance</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="condition_select">python</field> + <field name="condition_python">result = bool(contract.medical_insurance)</field> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = - (contract.medical_insurance)</field> + <field eval="-50.0" name="amount_fix"/> + <field name="appears_on_payslip" eval="False"/> + <field name="register_id" ref="hr_medical_insurance_register"/> + <field name="sequence" eval="185"/> + </record> + + <record id="hr_payslip_rule_ode" model="hr.salary.rule"> + <field name="code">ODE</field> + <field name="name">Other Deduction from Employer</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">fix</field> + <field eval="-200.0" name="amount_fix"/> + <field name="appears_on_payslip" eval="False"/> + <field name="register_id" ref="hr_other_deduction_register"/> + <field name="sequence" eval="187"/> + </record> + + <record id="hr_payslip_rule_ernps" model="hr.salary.rule"> + <field name="code">ENPC</field> + <field name="name">Employer's NPS Contribution</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and - ((contract.wage + DA) * 0.10) +or - (contract.wage * 0.10))</field> + <field name="sequence" eval="190"/> + <field name="register_id" ref="hr_nps_contribution_register"/> + <field name="appears_on_payslip" eval="False"/> + <field name= "note">Any amount contributed by your employer to your NPS account is treated as part of your salary and is included in your income but you can claim deduction under Section 80C for this too.thus, effectively making it exempt from tax within the limit of 10% of your basic salary. This is very useful and tax efficient for you particularly if you fall in the maximum tax.</field> + </record> + + <record id="hr_payslip_rule_erpf" model="hr.salary.rule"> + <field name="code">EPF</field> + <field name="name">Employer's PF Contribution</field> + <field name="category_id" ref="hr_payroll.DED"/> + <field name="amount_select">code</field> + <field name="amount_python_compute">result = (payslip.company_id.dearness_allowance and - ((contract.wage + DA) * 0.12) +or - (contract.wage * 0.12))</field> + <field name="sequence" eval="195"/> + <field name="register_id" ref="hr_register_provident_fund"/> + <field name="appears_on_payslip" eval="False"/> + <field name="note">Both the employees and employer contribute to the fund at the rate of 12% of the basic wages, dearness allowance and retaining allowance, if any, payable to employees per month.</field> + </record> + + <!-- Rule Inputs --> + + <record id="hr_rule_input_performance" model="hr.rule.input"> + <field name="code">PERF</field> + <field name="name">Performance of Employee</field> + <field name="input_id" ref="hr_salary_rule_performance"/> + </record> + + <record id="hr_rule_input_bonus" model="hr.rule.input"> + <field name="code">BNS</field> + <field name="name">Bonus of Employee</field> + <field name="input_id" ref="hr_salary_rule_bonus"/> + </record> + + <record id="hr_rule_input_arrears" model="hr.rule.input"> + <field name="code">ARS</field> + <field name="name">Arrears</field> + <field name="input_id" ref="hr_salary_rule_arrears"/> + </record> + + <record id="hr_rule_input_le" model="hr.rule.input"> + <field name="code">LE</field> + <field name="name">Leave Encashment</field> + <field name="input_id" ref="hr_salary_rule_le"/> + </record> + + <record id="hr_rule_input_journals" model="hr.rule.input"> + <field name="code">PJ</field> + <field name="name">Periodical Journals</field> + <field name="input_id" ref="hr_salary_rule_journals"/> + </record> + + <record id="hr_rule_input_telephone" model="hr.rule.input"> + <field name="code">TR</field> + <field name="name">Telephone Reimbursement</field> + <field name="input_id" ref="hr_salary_rule_telephone"/> + </record> + + <record id="hr_rule_input_car" model="hr.rule.input"> + <field name="code">CEI</field> + <field name="name">Car Expenses</field> + <field name="input_id" ref="hr_payroll_rule_car"/> + </record> + + <record id="hr_rule_input_internet" model="hr.rule.input"> + <field name="code">IE</field> + <field name="name">Internet Expence</field> + <field name="input_id" ref="hr_salary_rule_internet"/> + </record> + + <record id="hr_rule_input_leave_avail" model="hr.rule.input"> + <field name="code">LAI</field> + <field name="name">Leave Availed</field> + <field name="input_id" ref="hr_payslip_rule_dla"/> + </record> + <record id="hr_rule_input_medical" model="hr.rule.input"> + <field name="code">MR</field> + <field name="name">Medical Reimbursement</field> + <field name="input_id" ref="hr_salary_rule_medical"/> + </record> + </data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/i18n/bn.po b/addons/l10n_in_hr_payroll/i18n/bn.po new file mode 100644 index 00000000000..ea9fcfd7bb3 --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/bn.po @@ -0,0 +1,933 @@ +# Bengali Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: Bengali <bn@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/gu.po b/addons/l10n_in_hr_payroll/i18n/gu.po new file mode 100644 index 00000000000..2c321b13191 --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/gu.po @@ -0,0 +1,933 @@ +# Gujarati Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: Gujarati <gu@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/hi.po b/addons/l10n_in_hr_payroll/i18n/hi.po new file mode 100644 index 00000000000..d7cc9dc1519 --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/hi.po @@ -0,0 +1,933 @@ +# Hindi Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: Hindi <hi@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot b/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot new file mode 100644 index 00000000000..552f85b5997 --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot @@ -0,0 +1,933 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/ta.po b/addons/l10n_in_hr_payroll/i18n/ta.po new file mode 100644 index 00000000000..98d00c76a33 --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/ta.po @@ -0,0 +1,933 @@ +# Tamil Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: Tamil <ta@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/te.po b/addons/l10n_in_hr_payroll/i18n/te.po new file mode 100644 index 00000000000..0c6f6127c4d --- /dev/null +++ b/addons/l10n_in_hr_payroll/i18n/te.po @@ -0,0 +1,933 @@ +# Telugu Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_in_hr_payroll +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0alpha\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-08-17 06:46+0000\n" +"PO-Revision-Date: 2012-08-17 06:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: Telugu <te@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "E-mail Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,employee_bank_no:0 +msgid "Employee Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in draft state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Title" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Payment Advice from" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail +msgid "Hr Salary Employee By Category Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employees Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Allowances with Basic:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Department" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Deductions:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "A/C no." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,driver_salay:0 +msgid "Driver Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail +msgid "Yearly Salary by Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list +msgid "Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "March" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "(" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,company_id:0 +#: field:hr.payroll.advice.line,company_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,company_id:0 +msgid "Company" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "The Manager" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Letter Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "," +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Set to Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,number_of_year:0 +msgid "Total years of work experience" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "to" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Total :" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payslip.run,available_advice:0 +msgid "Made Payment Advice?" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Advices which are paid using NEFT transfer" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,tds:0 +msgid "Amount for Tax Deduction at Source" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip +msgid "Pay Slip" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,day:0 +msgid "Day" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Month of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,batch_id:0 +msgid "Batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Other Information" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Cancelled" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "For" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,voluntary_provident_fund:0 +msgid "VPF computed as percentage(%)" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,number:0 +#: report:paylip.details.in:0 +msgid "Reference" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Group By..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,medical_insurance:0 +msgid "Medical Insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Identification No" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.payroll.advice,state:0 +#: selection:payment.advice.report,state:0 +msgid "Confirmed" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,bysal:0 +#: field:payment.advice.report,bysal:0 +#: report:payroll.advice:0 +msgid "By Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Confirm" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,chaque_nos:0 +#: field:payment.advice.report,cheque_nos:0 +msgid "Cheque Numbers" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month +msgid "Yearly Salary by Head" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#, python-format +msgid "You can not confirm Payment advice without advice lines." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,state:0 +#: field:payment.advice.report,state:0 +msgid "State" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Yours Sincerely" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,medical_insurance:0 +msgid "Deduction towards company provided medical insurance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line +msgid "Bank Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Email" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payslip.run,available_advice:0 +msgid "If this box is checked which means that Payment Advice exists for current batch" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Error !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Print" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,debit_credit:0 +#: report:payroll.advice:0 +msgid "C/D" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.employee.bymonth:0 +msgid "Yearly Salary Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice +msgid "Print Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,line_ids:0 +msgid "Employee Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "July" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Configuration" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree +#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice +msgid "Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all +#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice +#: view:payment.advice.report:0 +msgid "Advices Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc:0 +msgid "IFSC" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,tds:0 +msgid "TDS" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,join_date:0 +msgid "Join Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.contract:0 +msgid "Error! Contract start-date must be less than contract end-date." +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:res.company,dearness_allowance:0 +msgid "Dearness Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "August" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Deduction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Search Payment advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "SI. No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Payment Advices which are in confirm state" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "December" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Confirm Sheet" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,month:0 +msgid "Month" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Non Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "or" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month +msgid "Hr Salary Employee By Month Report" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,category_id:0 +msgid "Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 +#, python-format +msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "To Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.employee,number_of_year:0 +msgid "No. of Years of Service" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Note" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: selection:hr.payroll.advice,state:0 +#: view:payment.advice.report:0 +#: selection:payment.advice.report,state:0 +msgid "Draft" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Date From" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,voluntary_provident_fund:0 +msgid "Voluntary Provident Fund" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Employee Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report +msgid "Payment Advice Analysis" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:payment.advice.report:0 +msgid "Status" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.contract,city_type:0 +msgid "Type of City" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:res.company,dearness_allowance:0 +msgid "Check this box if your company provide Dearness Allowance to employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,ifsc_code:0 +#: field:payment.advice.report,ifsc_code:0 +#: report:payroll.advice:0 +msgid "IFSC Code" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "June" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,nbr:0 +msgid "# Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Payment Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,date:0 +#: field:payment.advice.report,date:0 +msgid "Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "November" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Extended Filters..." +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all +msgid "This report performs analysis on Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "October" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Designation" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "January" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "Pay Head Employee Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:payroll.advice:0 +msgid "Authorized Signature" +msgstr "" + +#. module: l10n_in_hr_payroll +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract +msgid "Contract" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice.line:0 +msgid "Advice Lines" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "To," +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.contract,driver_salay:0 +msgid "Check this box if you provide allowance for driver" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice.line,advice_id:0 +#: field:hr.payslip,advice_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice +msgid "Bank Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Other No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Draft Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,neft:0 +msgid "Check this box if your company use online transfer for salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "To" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:payment.advice.report,number:0 +msgid "Number" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "September" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: view:hr.salary.employee.month:0 +#: view:yearly.salary.detail:0 +msgid "Cancel" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Day of Payment Advices" +msgstr "" + +#. module: l10n_in_hr_payroll +#: constraint:hr.employee:0 +msgid "Error! You cannot create recursive hierarchy of Employee(s)." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:yearly.salary.detail:0 +msgid "This wizard will print report which display a pay head employee breakup for a specified dates." +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Payslip Batches ready to be Adviced" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Total Salary" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,employee_id:0 +#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee +#: view:payment.advice.report:0 +#: field:payment.advice.report,employee_id:0 +msgid "Employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +msgid "Compute Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.employee,join_date:0 +msgid "Joining date of employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Dear Sir/Madam," +msgstr "Dear Sir/Madam," + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,note:0 +msgid "Description" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid ")" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:res.company:0 +msgid "Payroll" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "NEFT" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +msgid "Address" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,bank_id:0 +#: view:payment.advice.report:0 +#: field:payment.advice.report,bank_id:0 +#: report:payroll.advice:0 +#: report:salary.detail.byyear:0 +msgid "Bank" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,end_date:0 +#: field:yearly.salary.detail,date_to:0 +msgid "End Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "February" +msgstr "" + +#. module: l10n_in_hr_payroll +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payroll.advice:0 +#: field:hr.payroll.advice,name:0 +#: report:paylip.details.in:0 +#: field:payment.advice.report,name:0 +#: report:salary.employee.bymonth:0 +msgid "Name" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:hr.contract,city_type:0 +msgid "Metro" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +#: field:hr.salary.employee.month,employee_ids:0 +#: view:yearly.salary.detail:0 +#: field:yearly.salary.detail,employee_ids:0 +msgid "Employees" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Bank Account" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "April" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "Name of the Employe" +msgstr "" + +#. module: l10n_in_hr_payroll +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 +#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 +#, python-format +msgid "Please define bank account for the %s employee" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.salary.employee.month,start_date:0 +#: field:yearly.salary.detail,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.contract:0 +msgid "Allowance" +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,bank_id:0 +msgid "Select the Bank from which the salary is going to be paid" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.salary.employee.month:0 +msgid "Employee Pay Head Breakup" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:salary.detail.byyear:0 +msgid "Phone No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +msgid "Credit" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice.line,name:0 +#: report:payroll.advice:0 +msgid "Bank Account No." +msgstr "" + +#. module: l10n_in_hr_payroll +#: help:hr.payroll.advice,date:0 +msgid "Advice Date is used to search Payslips" +msgstr "" + +#. module: l10n_in_hr_payroll +#: selection:payment.advice.report,month:0 +msgid "May" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:hr.payslip.run:0 +msgid "Create Advice" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +#: field:payment.advice.report,year:0 +msgid "Year" +msgstr "" + +#. module: l10n_in_hr_payroll +#: field:hr.payroll.advice,neft:0 +#: field:payment.advice.report,neft:0 +msgid "NEFT Transaction" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:paylip.details.in:0 +#: report:salary.detail.byyear:0 +#: report:salary.employee.bymonth:0 +msgid "Total" +msgstr "" + +#. module: l10n_in_hr_payroll +#: report:payroll.advice:0 +msgid "form period" +msgstr "" + +#. module: l10n_in_hr_payroll +#: view:payment.advice.report:0 +msgid "Year of Payment Advices" +msgstr ""
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py new file mode 100644 index 00000000000..085a2679db2 --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py @@ -0,0 +1,283 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time +from datetime import datetime +from dateutil.relativedelta import relativedelta +from calendar import isleap + +from tools.translate import _ +from osv import fields, osv +import netsvc +import decimal_precision as dp + +DATETIME_FORMAT = "%Y-%m-%d" + +class hr_contract(osv.osv): + """ + Employee contract allows to add different values in fields. + Fields are used in salary rule computation. + """ + + _inherit = 'hr.contract' + _description = 'HR Contract' + + _columns = { + 'tds': fields.float('TDS', digits_compute=dp.get_precision('Payroll'), help="Amount for Tax Deduction at Source"), + 'driver_salay': fields.boolean('Driver Salary', help="Check this box if you provide allowance for driver"), + 'medical_insurance': fields.float('Medical Insurance', digits_compute=dp.get_precision('Payroll'), help="Deduction towards company provided medical insurance"), + 'voluntary_provident_fund': fields.float('Voluntary Provident Fund (%)', digits_compute=dp.get_precision('Payroll'), help="VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government and VPF computed as percentage(%)"), + 'house_rent_allowance_metro_nonmetro': fields.float('House Rent Allowance (%)', digits_compute=dp.get_precision('Payroll'), help="HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses for metro city it is 50 % and for non metro 40%.HRA computed as percentage(%)"), + 'supplementary_allowance': fields.float('Supplementary Allowance', digits_compute=dp.get_precision('Payroll')), + } + +hr_contract() + +class payroll_advice(osv.osv): + ''' + Bank Advice + ''' + _name = 'hr.payroll.advice' + _description = 'Bank Advice' + _columns = { + 'name':fields.char('Name', size=32, readonly=True, required=True, states={'draft': [('readonly', False)]},), + 'note': fields.text('Description'), + 'date': fields.date('Date', readonly=True, required=True, states={'draft': [('readonly', False)]}, help="Advice Date is used to search Payslips"), + 'state':fields.selection([ + ('draft', 'Draft'), + ('confirm', 'Confirmed'), + ('cancel', 'Cancelled'), + ], 'State', select=True, readonly=True), + 'number':fields.char('Reference', size=16, readonly=True), + 'line_ids':fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', states={'draft': [('readonly', False)]}, readonly=True), + 'chaque_nos':fields.char('Cheque Numbers', size=256), + 'neft': fields.boolean('NEFT Transaction', help="Check this box if your company use online transfer for salary"), + 'company_id':fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft': [('readonly', False)]}), + 'bank_id':fields.many2one('res.bank', 'Bank', readonly=True, states={'draft': [('readonly', False)]}, help="Select the Bank from which the salary is going to be paid"), + 'batch_id': fields.many2one('hr.payslip.run', 'Batch', readonly=True) + } + + _defaults = { + 'date': lambda * a: time.strftime('%Y-%m-%d'), + 'state': lambda * a: 'draft', + 'company_id': lambda self, cr, uid, context: \ + self.pool.get('res.users').browse(cr, uid, uid, + context=context).company_id.id, + 'note': "Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries:" + } + + def compute_advice(self, cr, uid, ids, context=None): + """ + Advice - Create Advice lines in Payment Advice and + compute Advice lines. + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of Advice’s IDs + @return: Advice lines + @param context: A standard dictionary for contextual values + """ + payslip_pool = self.pool.get('hr.payslip') + advice_line_pool = self.pool.get('hr.payroll.advice.line') + payslip_line_pool = self.pool.get('hr.payslip.line') + + for advice in self.browse(cr, uid, ids, context=context): + old_line_ids = advice_line_pool.search(cr, uid, [('advice_id', '=', advice.id)], context=context) + if old_line_ids: + advice_line_pool.unlink(cr, uid, old_line_ids, context=context) + slip_ids = payslip_pool.search(cr, uid, [('date_from', '<=', advice.date), ('date_to', '>=', advice.date), ('state', '=', 'done')], context=context) + for slip in payslip_pool.browse(cr, uid, slip_ids, context=context): + if not slip.employee_id.bank_account_id and not slip.employee_id.bank_account_id.acc_number: + raise osv.except_osv(_('Error !'), _('Please define bank account for the %s employee') % (slip.employee_id.name)) + line_ids = payslip_line_pool.search(cr, uid, [ ('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context) + if line_ids: + line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0] + advice_line = { + 'advice_id': advice.id, + 'name': slip.employee_id.bank_account_id.acc_number, + 'employee_id': slip.employee_id.id, + 'bysal': line.total + } + advice_line_pool.create(cr, uid, advice_line, context=context) + payslip_pool.write(cr, uid, slip_ids, {'advice_id': advice.id}, context=context) + return True + + def confirm_sheet(self, cr, uid, ids, context=None): + """ + confirm Advice - confirmed Advice after computing Advice Lines.. + @param cr: the current row, from the database cursor, + @param uid: the current user’s ID for security checks, + @param ids: List of confirm Advice’s IDs + @return: confirmed Advice lines and set sequence of Advice. + @param context: A standard dictionary for contextual values + """ + seq_obj = self.pool.get('ir.sequence') + for advice in self.browse(cr, uid, ids, context=context): + if not advice.line_ids: + raise osv.except_osv(_('Error !'), _('You can not confirm Payment advice without advice lines.')) + advice_date = datetime.strptime(advice.date, DATETIME_FORMAT) + advice_year = advice_date.strftime('%m') + '-' + advice_date.strftime('%Y') + number = seq_obj.get(cr, uid, 'payment.advice') + sequence_num = 'PAY' + '/' + advice_year + '/' + number + self.write(cr, uid, [advice.id], {'number': sequence_num, 'state': 'confirm'}, context=context) + return True + + def set_to_draft(self, cr, uid, ids, context=None): + """Resets Advice as draft. + """ + return self.write(cr, uid, ids, {'state':'draft'}, context=context) + + def cancel_sheet(self, cr, uid, ids, context=None): + """Marks Advice as cancelled. + """ + return self.write(cr, uid, ids, {'state':'cancel'}, context=context) + + def onchange_company_id(self, cr, uid, ids, company_id=False, context=None): + res = {} + if company_id: + company = self.pool.get('res.company').browse(cr, uid, [company_id], context=context)[0] + if company.partner_id.bank_ids: + res.update({'bank_id': company.partner_id.bank_ids[0].bank.id}) + return { + 'value':res + } +payroll_advice() + +class hr_payslip_run(osv.osv): + + _inherit = 'hr.payslip.run' + _description = 'Payslip Batches' + _columns = { + 'available_advice': fields.boolean('Made Payment Advice?', help="If this box is checked which means that Payment Advice exists for current batch", readonly=False), + } + def copy(self, cr, uid, id, default={}, context=None): + if not default: + default = {} + default.update({'available_advice': False}) + return super(hr_payslip_run, self).copy(cr, uid, id, default, context=context) + + def draft_payslip_run(self, cr, uid, ids, context=None): + res = super(hr_payslip_run, self).draft_payslip_run(cr, uid, ids, context=context) + self.write(cr, uid, ids, {'available_advice': False}, context=context) + return res + + def create_advice(self, cr, uid, ids, context=None): + wf_service = netsvc.LocalService("workflow") + payslip_pool = self.pool.get('hr.payslip') + payslip_line_pool = self.pool.get('hr.payslip.line') + advice_pool = self.pool.get('hr.payroll.advice') + advice_line_pool = self.pool.get('hr.payroll.advice.line') + users = self.pool.get('res.users').browse(cr, uid, [uid], context=context) + for run in self.browse(cr, uid, ids, context=context): + if run.available_advice: + raise osv.except_osv(_('Error !'), _("Payment advice already exists for %s, 'Set to Draft' to create a new advice.") %(run.name)) + advice_data = { + 'batch_id': run.id, + 'company_id': users[0].company_id.id, + 'name': run.name, + 'date': run.date_end, + 'bank_id': users[0].company_id.bank_ids and users[0].company_id.bank_ids[0].id or False + } + advice_id = advice_pool.create(cr, uid, advice_data, context=context) + slip_ids = [] + for slip_id in run.slip_ids: + wf_service.trg_validate(uid, 'hr.payslip', slip_id.id, 'hr_verify_sheet', cr) + wf_service.trg_validate(uid, 'hr.payslip', slip_id.id, 'process_sheet', cr) + slip_ids.append(slip_id.id) + + for slip in payslip_pool.browse(cr, uid, slip_ids, context=context): + if not slip.employee_id.bank_account_id or not slip.employee_id.bank_account_id.acc_number: + raise osv.except_osv(_('Error !'), _('Please define bank account for the %s employee') % (slip.employee_id.name)) + line_ids = payslip_line_pool.search(cr, uid, [('slip_id', '=', slip.id), ('code', '=', 'NET')], context=context) + if line_ids: + line = payslip_line_pool.browse(cr, uid, line_ids, context=context)[0] + advice_line = { + 'advice_id': advice_id, + 'name': slip.employee_id.bank_account_id.acc_number, + 'employee_id': slip.employee_id.id, + 'bysal': line.total + } + 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): + ''' + Bank Advice Lines + ''' + def onchange_employee_id(self, cr, uid, ids, employee_id=False, context=None): + res = {} + hr_obj = self.pool.get('hr.employee') + if not employee_id: + return {'value': res} + employee = hr_obj.browse(cr, uid, [employee_id], context=context)[0] + res.update({'name': employee.bank_account_id.acc_number , 'ifsc_code': employee.bank_account_id.bank_bic or ''}) + return {'value': res} + + _name = 'hr.payroll.advice.line' + _description = 'Bank Advice Lines' + _columns = { + 'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice'), + 'name': fields.char('Bank Account No.', size=25, required=True), + 'ifsc_code': fields.char('IFSC Code', size=16), + 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), + 'bysal': fields.float('By Salary', digits_compute=dp.get_precision('Payroll')), + 'debit_credit': fields.char('C/D', size=3, required=False), + 'company_id': fields.related('advice_id', 'company_id', type='many2one', required=False, relation='res.company', string='Company', store=True), + 'ifsc': fields.related('advice_id', 'neft', type='boolean', string='IFSC'), + } + _defaults = { + 'debit_credit': 'C', + } + +payroll_advice_line() + +class hr_payslip(osv.osv): + ''' + Employee Pay Slip + ''' + _inherit = 'hr.payslip' + _description = 'Pay Slips' + _columns = { + 'advice_id': fields.many2one('hr.payroll.advice', 'Bank Advice') + } + + def copy(self, cr, uid, id, default={}, context=None): + if not default: + default = {} + default.update({'advice_id' : False}) + return super(hr_payslip, self).copy(cr, uid, id, default, context=context) + +hr_payslip() + +class res_company(osv.osv): + + _inherit = 'res.company' + _columns = { + 'dearness_allowance': fields.boolean('Dearness Allowance', help="Check this box if your company provide Dearness Allowance to employee") + } + _defaults = { + 'dearness_allowance': True, + } + +res_company() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_demo.xml b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_demo.xml new file mode 100644 index 00000000000..bcb168467a9 --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_demo.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <!-- Salary Structure --> + + <record id="hr_payroll_salary_structure_ind_emp" model="hr.payroll.structure"> + <field name="code">NE</field> + <field name="name">Non-Executive Employee</field> + <field eval="[(6, 0, [ref('hr_salary_rule_medical'), ref('hr_salary_rule_da'), ref('hr_salary_rule_lta'), ref('hr_salary_rule_telephone'), ref('hr_salary_rule_internet'), ref('hr_payroll_rule_child1')])]" name="rule_ids"/> + <field name="company_id" ref="base.main_company"/> + <field name="parent_id" ref="hr_payroll.structure_base"/> + </record> + + </data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_report.xml b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_report.xml new file mode 100644 index 00000000000..7f27291051f --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_report.xml @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <report + auto="False" + id="payslip_details_report" + model="hr.payslip" + name="paylip.details.in" + rml="l10n_in_hr_payroll/report/report_payslip_details.rml" + string="PaySlip Details" /> + + <report + auto="False" + id="payroll_advice" + model="hr.payroll.advice" + name="payroll.advice" + rml="l10n_in_hr_payroll/report/report_payroll_advice.rml" + string="Print Advice" /> + + <report + auto="False" + id="hr_salary_employee_bymonth" + model="hr.salary.employee.month" + name="salary.employee.bymonth" + rml="l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml" + string="Yearly Salary by Head" /> + + <report + auto="False" + id="yearly_salary" + model="yearly.salary.detail" + name="salary.detail.byyear" + rml="l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.rml" + string="Yearly Salary by Employee" /> + + </data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_sequence.xml b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_sequence.xml new file mode 100644 index 00000000000..3941d6461c9 --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_sequence.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data noupdate="1"> + + <record id="seq_type_payment_advice" model="ir.sequence.type"> + <field name="name">Payment Advice</field> + <field name="code">payment.advice</field> + </record> + + <record id="seq_payment_advice" model="ir.sequence"> + <field name="name">Payment Advice</field> + <field name="code">payment.advice</field> + <field name="padding">3</field> + </record> + + </data> +</openerp>
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_view.xml b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_view.xml new file mode 100644 index 00000000000..37d2f865770 --- /dev/null +++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll_view.xml @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <record id="hr_contract_form_in_inherit" model="ir.ui.view"> + <field name="name">hr.contract.form.in.inherit</field> + <field name="model">hr.contract</field> + <field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//field[@name='struct_id']" position="after"> + <group col="2" colspan="2" name="right_column"> + <separator colspan="2" string="Allowance"/> + <field name="driver_salay"/> + <field name="house_rent_allowance_metro_nonmetro"/> + <field name="supplementary_allowance"/> + </group> + <group col="2" colspan="2" name="left_column"> + <separator colspan="2" string="Deduction"/> + <field name="tds"/> + <field name="voluntary_provident_fund"/> + <field name="medical_insurance"/> + </group> + </xpath> + </data> + </field> + </record> + + <record id="hr_payslip_run_form_inherit" model="ir.ui.view"> + <field name="name">hr.payslip.run.form.inherit</field> + <field name="model">hr.payslip.run</field> + <field name="inherit_id" ref="hr_payroll.hr_payslip_run_form"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//field[@name='credit_note']" position="after"> + <newline/> + <field name="available_advice"/> + </xpath> + <xpath expr="//button[@string='Set to Draft']" position="after"> + <button name="create_advice" string="Create Advice" type="object" states="close" /> + </xpath> + </data> + </field> + </record> + + <record id="hr_payslip_run_search_inherit" model="ir.ui.view"> + <field name="name">hr.payslip.run.search.inherit</field> + <field name="model">hr.payslip.run</field> + <field name="inherit_id" ref="hr_payroll.hr_payslip_run_filter"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//field[@name='date_end']" position="after"> + <separator orientation="vertical"/> + <filter icon="terp-gtk-go-back-rtl" string="To Advice" domain="[('available_advice','=', False)]" help="Payslip Batches ready to be Adviced"/> + </xpath> + </data> + </field> + </record> + + <record id="view_hr_bank_advice_tree" model="ir.ui.view"> + <field name="name">hr.payroll.advice.tree</field> + <field name="model">hr.payroll.advice</field> + <field name="arch" type="xml"> + <tree string="Bank Advice"> + <field name="date"/> + <field name="number"/> + <field name="name"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + <field name="bank_id"/> + <field name="state"/> + </tree> + </field> + </record> + + <record id="view_hr_bank_advice_form" model="ir.ui.view"> + <field name="name">hr.payroll.advice.form</field> + <field name="model">hr.payroll.advice</field> + <field name="arch" type="xml"> + <form string="Bank Advice" version="7.0"> + <header> + <button name="confirm_sheet" string="Confirm Sheet" states="draft" type="object" class="oe_highlight"/> + <button name="compute_advice" string="Compute Advice" states="draft" type="object" class="oe_highlight"/> + <button name="cancel_sheet" string="Cancel" states="draft" type="object"/> + <button name="set_to_draft" string="Set to Draft" states="cancel,confirm" type="object"/> + <div class="oe_right"> + <field name="state" widget="statusbar" nolabel="1" statusbar_visible="draft"/> + </div> + <div class="oe_clear"/> + </header> + <sheet> + <group> + <label for="Name" class="oe_edit_only" string="Name"/> + <h2><field name="name"/></h2> + <label for="date" class="oe_edit_only"/> + <h2><field name="date"/></h2> + <group colspan="3" col="6"> + <field name="bank_id"/> + <field name="number"/> + </group> + </group> + <notebook colspan="4"> + <page string="Payment Lines"> + <field name="line_ids" colspan="4" nolabel="1"> + <tree string="Payment Lines" editable="bottom"> + <field name="employee_id" on_change="onchange_employee_id(employee_id)"/> + <field name="name"/> + <field name="ifsc" invisible="1"/> + <field name="ifsc_code" attrs="{'invisible':[('ifsc','=', False)]}" /> + <field name="bysal"/> + <field name="debit_credit"/> + </tree> + </field> + </page> + <page string="Other Information"> + <group colspan="4" col="6"> + <field name="company_id" on_change="onchange_company_id(company_id)" groups="base.group_multi_company" widget="selection"/> + <field name="chaque_nos"/> + <newline/> + <field name="neft"/> + <field name="batch_id"/> + </group> + <separator colspan="4" string="Letter Details"/> + <field name="note" colspan="4" nolabel="1"/> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + + <record id="view_hr_payroll_advice_filter" model="ir.ui.view"> + <field name="name">hr.payroll.advice.select</field> + <field name="model">hr.payroll.advice</field> + <field name="arch" type="xml"> + <search string="Search Payment advice"> + <group> + <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Draft Advices"/> + <filter icon="terp-camera_test" string="Confirm" domain="[('state','=','confirm')]" help="Confirm Advices"/> + <separator orientation="vertical"/> + <field name="date"/> + <field name="number"/> + <field name="name"/> + <field name="bank_id"/> + <field name="batch_id"/> + </group> + <newline/> + <group expand="0" string="Group By..."> + <filter string="Bank" name="bank_id" icon="terp-go-home" context="{'group_by':'bank_id'}"/> + <separator orientation="vertical"/> + <filter string="Status" name="state" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/> + <separator orientation="vertical" /> + <filter string="Company" name="company_id" groups="base.group_multi_company" icon="terp-go-home" context="{'group_by':'company_id'}"/> + </group> + </search> + </field> + </record> + + <record id="action_view_hr_bank_advice_tree" model="ir.actions.act_window"> + <field name="name">Payment Advices</field> + <field name="res_model">hr.payroll.advice</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="view_id" ref="view_hr_bank_advice_tree"/> + <field name="search_view_id" ref="view_hr_payroll_advice_filter"/> + </record> + + <menuitem + action="action_view_hr_bank_advice_tree" + id="hr_menu_payment_advice" + parent="hr_payroll.menu_hr_root_payroll" + /> + + <record id="view_advice_line_form" model="ir.ui.view"> + <field name="name">advice.line.form</field> + <field name="model">hr.payroll.advice.line</field> + <field name="arch" type="xml"> + <form string="Advice Lines" version="7.0"> + <sheet> + <label for="advice_id" class="oe_edit_only"/> + <h1><field name="advice_id"/></h1> + <group> + <field name="employee_id" on_change="onchange_employee_id(employee_id)" /> + <field name="name" /> + <field name="ifsc" invisible="1"/> + <field name="ifsc_code" attrs="{'invisible':[('ifsc','=', False)]}" /> + <field name="bysal" /> + <field name="debit_credit" /> + </group> + </sheet> + </form> + </field> + </record> + + <record id="view_res_company_da" model="ir.ui.view"> + <field name="name">res.company.inherit</field> + <field name="model">res.company</field> + <field name="inherit_id" ref="base.view_company_form"/> + <field name="arch" type="xml"> + <data> + <page string="Configuration" position="inside"> + <group name="rule" string="Payroll"> + <field name="dearness_allowance"/> + </group> + </page> + </data> + </field> + </record> + + <!-- Shortcuts --> + + <act_window name="Payslips" + domain="[('advice_id', '=', active_id)]" + res_model="hr.payslip" + src_model="hr.payroll.advice" + view_id ="hr_payroll.view_hr_payslip_tree" + id="act_hr_emp_payslip_list" + /> + + </data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/report/__init__.py b/addons/l10n_in_hr_payroll/report/__init__.py new file mode 100644 index 00000000000..8a20170c384 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/__init__.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# d$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import report_payslip_details +import report_payroll_advice +import report_hr_salary_employee_bymonth +import payment_advice_report +import report_hr_yearly_salary_detail +import payslip_report + +# 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 new file mode 100644 index 00000000000..838334913a5 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payment_advice_report.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import tools +from osv import fields, osv + +class payment_advice_report(osv.osv): + _name = "payment.advice.report" + _description = "Payment Advice Analysis" + _auto = False + _columns = { + 'name':fields.char('Name', size=32, readonly=True), + 'date': fields.date('Date', readonly=True,), + 'year': fields.char('Year', size=4, readonly=True), + 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), + ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), + ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), + 'day': fields.char('Day', size=128, readonly=True), + 'state':fields.selection([ + ('draft', 'Draft'), + ('confirm', 'Confirmed'), + ('cancel', 'Cancelled'), + ], 'State', select=True, readonly=True), + 'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True), + 'nbr': fields.integer('# Payment Lines', readonly=True), + 'number':fields.char('Number', size=16, readonly=True), + 'bysal': fields.float('By Salary', readonly=True), + 'bank_id':fields.many2one('res.bank', 'Bank', readonly=True), + 'company_id':fields.many2one('res.company', 'Company', readonly=True), + 'cheque_nos':fields.char('Cheque Numbers', size=256, readonly=True), + 'neft': fields.boolean('NEFT Transaction', readonly=True), + 'ifsc_code': fields.char('IFSC Code', size=32, readonly=True), + 'employee_bank_no': fields.char('Employee Bank Account', size=32, required=True), + } + def init(self, cr): + tools.drop_view_if_exists(cr, 'payment_advice_report') + cr.execute(""" + create or replace view payment_advice_report as ( + select + min(l.id) as id, + sum(l.bysal) as bysal, + p.name, + p.state, + p.date, + p.number, + p.company_id, + p.bank_id, + p.chaque_nos as cheque_nos, + p.neft, + l.employee_id, + l.ifsc_code, + l.name as employee_bank_no, + to_char(p.date, 'YYYY') as year, + to_char(p.date, 'MM') as month, + to_char(p.date, 'YYYY-MM-DD') as day, + 1 as nbr + from + hr_payroll_advice as p + left join hr_payroll_advice_line as l on (p.id=l.advice_id) + where + l.employee_id IS NOT NULL + group by + p.number,p.name,p.date,p.state,p.company_id,p.bank_id,p.chaque_nos,p.neft, + 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/payment_advice_report_view.xml b/addons/l10n_in_hr_payroll/report/payment_advice_report_view.xml new file mode 100644 index 00000000000..7a023f09608 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payment_advice_report_view.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> +<data> + + <record id="view_payment_advice_tree" model="ir.ui.view"> + <field name="name">payment.advice.report.tree</field> + <field name="model">payment.advice.report</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree colors="blue:state == 'draft';black:state == 'confirm';gray:state == 'cancel' " string="Advices Analysis"> + <field name="nbr" sum="# Payment Lines"/> + <field name="name" invisible="1"/> + <field name="employee_id" invisible="1"/> + <field name="date" invisible="1"/> + <field name="bank_id" invisible="1"/> + <field name="state" invisible="1"/> + <field name="number" invisible="1"/> + <field name="bysal" sum="Total Salary"/> + <field name="year" invisible="1"/> + <field name="day" invisible="1"/> + <field name="month" invisible="1"/> + <field name="company_id" invisible="1"/> + <field name="cheque_nos" invisible="1"/> + <field name="neft" invisible="1"/> + <field name="ifsc_code" invisible="1"/> + </tree> + </field> + </record> + + + <record id="view_payment_advice_search" model="ir.ui.view"> + <field name="name">payment.advice.report.search</field> + <field name="model">payment.advice.report</field> + <field name="type">search</field> + <field name="arch" type="xml"> + <search string="Advices Analysis"> + <group> + <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Payment Advices which are in draft state"/> + <filter icon="terp-check" string="Confirm" name="confirm" domain="[('state','=','confirm')]" help="Payment Advices which are in confirm state"/> + <separator orientation="vertical"/> + <filter icon="terp-camera_test" string="NEFT" domain="[('neft','=',True)]" help="Advices which are paid using NEFT transfer"/> + <separator orientation="vertical"/> + <field name="date"/> + <separator orientation="vertical"/> + <field name="number"/> + <field name="name"/> + <field name="employee_id"/> + <field name="cheque_nos"/> + </group> + <newline/> + <group expand="0" string="Extended Filters..."> + <field name="ifsc_code"/> + <field name="bank_id" widget="selection"/> + <field name="employee_bank_no"/> + <separator orientation="vertical"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + </group> + <newline/> + <group expand="1" string="Group By..."> + <filter string="Employee" icon="terp-personal" context="{'group_by':'employee_id'}" /> + <filter string="Bank" icon="terp-go-home" context="{'group_by':'bank_id'}"/> + <separator orientation="vertical"/> + <filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/> + <separator orientation="vertical"/> + <filter string="Company" icon="terp-go-home" groups="base.group_multi_company" context="{'group_by':'company_id'}"/> + <separator orientation="vertical"/> + <filter string="Day" icon="terp-go-today" context="{'group_by':'day'}" help="Day of Payment Advices"/> + <filter string="Month" name="order_month" icon="terp-go-month" context="{'group_by':'month'}" help="Month of Payment Advices"/> + <filter string="Year" icon="terp-go-year" context="{'group_by':'year'}" help="Year of Payment Advices"/> + </group> + </search> + </field> + </record> + + <record id="action_payment_advice_report_all" model="ir.actions.act_window"> + <field name="name">Advices Analysis</field> + <field name="res_model">payment.advice.report</field> + <field name="view_type">form</field> + <field name="view_mode">tree</field> + <field name="view_id" ref="view_payment_advice_tree"/> + <field name="context">{'search_default_confirm':1,'group_by_no_leaf':1,'group_by':[]}</field> + <field name="help">This report performs analysis on Payment Advices</field> + </record> + + <menuitem action="action_payment_advice_report_all" id="menu_reporting_payment_advice" parent="hr.menu_hr_reporting" sequence="5" groups="base.group_hr_manager"/> + +</data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/report/payslip_report.py b/addons/l10n_in_hr_payroll/report/payslip_report.py new file mode 100644 index 00000000000..b688af4329b --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payslip_report.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-Today OpenERP SA (<http://www.openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import tools +from osv import fields, osv + +class payslip_report(osv.osv): + _name = "payslip.report" + _description = "Payslip Analysis" + _auto = False + _columns = { + 'name':fields.char('Name', size=32, readonly=True), + 'date_from': fields.date('Date From', readonly=True,), + 'date_to': fields.date('Date To', readonly=True,), + 'year': fields.char('Year', size=4, readonly=True), + 'month': fields.selection([('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), + ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), + ('10', 'October'), ('11', 'November'), ('12', 'December')], 'Month', readonly=True), + 'day': fields.char('Day', size=128, readonly=True), + 'state': fields.selection([ + ('draft', 'Draft'), + ('done', 'Done'), + ('cancel', 'Rejected'), + ], 'State', readonly=True), + 'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True), + 'nbr': fields.integer('# Payslip lines', readonly=True), + 'number': fields.char('Number', size=16, readonly=True), + 'struct_id': fields.many2one('hr.payroll.structure', 'Structure', readonly=True), + 'company_id':fields.many2one('res.company', 'Company', readonly=True), + 'paid': fields.boolean('Made Payment Order ? ', readonly=True), + 'total': fields.float('Total', readonly=True), + 'category_id':fields.many2one('hr.salary.rule.category', 'Category', readonly=True), + } + def init(self, cr): + tools.drop_view_if_exists(cr, 'payslip_report') + cr.execute(""" + create or replace view payslip_report as ( + select + min(l.id) as id, + l.name, + p.struct_id, + p.state, + p.date_from, + p.date_to, + p.number, + p.company_id, + p.paid, + l.category_id, + l.employee_id, + sum(l.total) as total, + to_char(p.date_from, 'YYYY') as year, + to_char(p.date_from, 'MM') as month, + to_char(p.date_from, 'YYYY-MM-DD') as day, + to_char(p.date_to, 'YYYY') as to_year, + to_char(p.date_to, 'MM') as to_month, + to_char(p.date_to, 'YYYY-MM-DD') as to_day, + 1 AS nbr + from + hr_payslip as p + left join hr_payslip_line as l on (p.id=l.slip_id) + where + l.employee_id IS NOT NULL + group by + p.number,l.name,p.date_from,p.date_to,p.state,p.company_id,p.paid, + 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/report/payslip_report_view.xml b/addons/l10n_in_hr_payroll/report/payslip_report_view.xml new file mode 100644 index 00000000000..5588fc6edca --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/payslip_report_view.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> +<data> + + <record id="view_payslip_tree" model="ir.ui.view"> + <field name="name">payslip.report.tree</field> + <field name="model">payslip.report</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree colors="blue:state == 'draft';black:state == 'done';gray:state == 'cancel' " string="Payslip Analysis"> + <field name="nbr" sum="# Payslip Lines"/> + <field name="name"/> + <field name="employee_id" invisible="1"/> + <field name="date_to" invisible="1"/> + <field name="date_from" invisible="1"/> + <field name="state" invisible="1"/> + <field name="number" invisible="1"/> + <field name="struct_id" invisible="1"/> + <field name="year" invisible="1"/> + <field name="day" invisible="1"/> + <field name="month" invisible="1"/> + <field name="company_id" invisible="1"/> + <field name="paid" invisible="1"/> + <field name="total" /> + <field name="category_id" invisible="1"/> + </tree> + </field> + </record> + + <record id="view_payslip_search" model="ir.ui.view"> + <field name="name">payslip.report.search</field> + <field name="model">payslip.report</field> + <field name="type">search</field> + <field name="arch" type="xml"> + <search string="Payslip Analysis"> + <group> + <filter icon="terp-document-new" string="Draft" domain="[('state','=','draft')]" help="Payslips which are in draft state"/> + <filter icon="terp-check" string="Done" name="done" domain="[('state','=','done')]" help="Payslips which are in done state"/> + <separator orientation="vertical"/> + <filter icon="terp-camera_test" string="Paid" domain="[('paid','=',True)]" help="Payslips which are paid"/> + <separator orientation="vertical"/> + <field name="date_from"/> + <field name="date_to"/> + <separator orientation="vertical"/> + <field name="number"/> + <field name="name"/> + <field name="employee_id"/> + </group> + <newline/> + <group expand="1" string="Extended Filters..."> + <separator orientation="vertical"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + </group> + <newline/> + <group expand="1" string="Group By..."> + <filter string="Employee" name="employee" icon="terp-personal" context="{'group_by':'employee_id'}" /> + <filter string="Structure" context="{'group_by':'struct_id'}" /> + <filter string="Category" name="category" context="{'group_by':'category_id'}" /> + <filter string="Payslip Line" context="{'group_by':'name'}" /> + <separator orientation="vertical"/> + <filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/> + <separator orientation="vertical"/> + <filter string="Company" icon="terp-go-home" groups="base.group_multi_company" context="{'group_by':'company_id'}"/> + <separator orientation="vertical"/> + <filter string="Day" icon="terp-go-today" context="{'group_by':'day'}" help="Day of Payslip"/> + <filter string="Month" name="order_month" icon="terp-go-month" context="{'group_by':'month'}" help="Month of Payslip"/> + <filter string="Year" icon="terp-go-year" context="{'group_by':'year'}" help="Year of Payslip"/> + + </group> + </search> + </field> + </record> + + <record id="action_payslip_report_all" model="ir.actions.act_window"> + <field name="name">Payslip Analysis</field> + <field name="res_model">payslip.report</field> + <field name="view_type">form</field> + <field name="view_mode">tree</field> + <field name="view_id" ref="view_payslip_tree"/> + <field name="context">{'search_default_employee':1,'search_default_category':1,'group_by_no_leaf':uid,'group_by':[]}</field> + <field name="help">This report performs analysis on Payslip</field> + </record> + + <menuitem action="action_payslip_report_all" id="menu_reporting_payslip" parent="hr.menu_hr_reporting" sequence="5" groups="base.group_hr_manager"/> + +</data> +</openerp> diff --git a/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py new file mode 100644 index 00000000000..54b0daa7749 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.py @@ -0,0 +1,133 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import datetime +import time + +from report import report_sxw + +class report_hr_salary_employee_bymonth(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(report_hr_salary_employee_bymonth, self).__init__(cr, uid, name, context=context) + + self.localcontext.update({ + 'time': time, + 'get_employee': self.get_employee, + 'get_periods': self.get_periods, + 'get_months_tol': self.get_months_tol, + 'get_total': self.get_total, + }) + + self.context = context + self.mnths = [] + self.mnths_total = [] + self.total = 0.0 + + def get_periods(self, form): +# Get start year-month-date and end year-month-date + first_year = int(form['start_date'][0:4]) + last_year = int(form['end_date'][0:4]) + + first_month = int(form['start_date'][5:7]) + last_month = int(form['end_date'][5:7]) + no_months = (last_year-first_year) * 12 + last_month - first_month + 1 + current_month = first_month + current_year = first_year + +# Get name of the months from integer + mnth_name = [] + for count in range(0, no_months): + m = datetime.date(current_year, current_month, 1).strftime('%b') + mnth_name.append(m) + self.mnths.append(str(current_month) + '-' + str(current_year)) + if current_month == 12: + current_month = 0 + current_year = last_year + current_month = current_month + 1 + for c in range(0, (12-no_months)): + mnth_name.append('None') + self.mnths.append('None') + return [mnth_name] + + def get_salary(self, form, emp_id, emp_salary, total_mnths): + category_id = form.get('category_id', []) + category_id = category_id and category_id[0] or False + self.cr.execute("select to_char(date_to,'mm-yyyy') as to_date ,sum(pl.total) \ + from hr_payslip_line as pl \ + left join hr_payslip as p on pl.slip_id = p.id \ + left join hr_employee as emp on emp.id = p.employee_id \ + left join resource_resource as r on r.id = emp.resource_id \ + where p.state = 'done' and p.employee_id = %s and pl.category_id = %s \ + group by r.name, p.date_to,emp.id",(emp_id, category_id,)) + sal = self.cr.fetchall() + salary = dict(sal) + total = 0.0 + cnt = 1 + + for month in self.mnths: + if month <> 'None': + if len(month) != 7: + month = '0' + str(month) + if month in salary and salary[month]: + emp_salary.append(salary[month]) + total += salary[month] + total_mnths[cnt] = total_mnths[cnt] + salary[month] + else: + emp_salary.append(0.00) + else: + emp_salary.append('') + total_mnths[cnt] = '' + cnt = cnt + 1 + return emp_salary, total, total_mnths + + def get_employee(self, form): + emp_salary = [] + salary_list = [] + total_mnths=['Total', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + emp_obj = self.pool.get('hr.employee') + emp_ids = form.get('employee_ids', []) + employees = emp_obj.browse(self.cr, self.uid, emp_ids, context=self.context) + + for emp_id in employees: + emp_salary.append(emp_id.name) + total = 0.0 + emp_salary, total, total_mnths = self.get_salary(form, emp_id.id, emp_salary, total_mnths) + emp_salary.append(total) + salary_list.append(emp_salary) + emp_salary = [] + self.mnths_total.append(total_mnths) + return salary_list + + def get_months_tol(self): + return self.mnths_total + + def get_total(self): + for item in self.mnths_total: + for count in range(1, len(item)): + if item[count] == '': + continue + self.total += item[count] + return self.total + +report_sxw.report_sxw('report.salary.employee.bymonth', 'hr.salary.employee.month', 'l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml', parser=report_hr_salary_employee_bymonth, header='internal') + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml new file mode 100644 index 00000000000..8f2a57d2018 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_salary_employee_bymonth.rml @@ -0,0 +1,293 @@ +<?xml version="1.0"?> +<document filename="yearly salary report.pdf"> + <template pageSize="(620.0,842.0)" title="Yearly Salary Report" author="OpenERP" allowSplitting="50"> + <pageTemplate id="first"> + <frame id="first" x1="28.0" y1="28.0" width="539" height="772"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="6,0" stop="6,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="7,0" stop="7,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="8,0" stop="8,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="9,0" stop="9,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="10,0" stop="10,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="11,0" stop="11,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="12,0" stop="12,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="13,0" stop="13,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="5,0" stop="5,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="6,0" stop="6,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="7,0" stop="7,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="8,0" stop="8,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="9,0" stop="9,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="10,0" stop="10,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="11,0" stop="11,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="12,0" stop="12,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="13,0" stop="13,0"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P2" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P3" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P4" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P5" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P6" fontName="Helvetica" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Standard" fontName="Helvetica"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Text body" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Caption" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Helvetica"/> + <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_header_Centre" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="7.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="7.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_1" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Table Contents" fontName="Helvetica"/> + <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/> + <paraStyle name="terp_default_9" fontName="Helvetica" fontSize="5.0" leading="5" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_8" fontName="Helvetica" fontSize="5.0" leading="2" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9" fontName="Helvetica" fontSize="7.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9_Bold" fontName="Helvetica-Bold" fontSize="5.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_tblheader_General_Right" fontName="Helvetica-Bold" fontSize="5.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <images/> + </stylesheet> + <story> + <para style="P1">Yearly Salary Details </para> + <para style="P5">From <u>[[ formatLang(data['form']['start_date'], date=True) ]]</u> To <u>[[ formatLang(data['form']['end_date'], date=True) ]]</u> of <u>[[ (data['form']['category_id'][1]) ]]</u> Category</para> + <para style="P6"> + <font color="white"> </font> + </para> + <section> + <para style="terp_default_1">[[ repeatIn(get_periods(data['form']),'m') ]]</para> + <blockTable colWidths="55.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,57.0" style="Table1"> + <tr> + <td> + <para style="terp_tblheader_Details">Name</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[0] != 'None' and m[0] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[1] != 'None' and m[1] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[2] != 'None' and m[2] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[3] != 'None' and m[3] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[4] != 'None' and m[4] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[5] != 'None' and m[5] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[6] != 'None' and m[6] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[7] != 'None' and m[7] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[8] != 'None' and m[8] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[9] != 'None' and m[9] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[10] != 'None' and m[10] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[11] != 'None' and m[11] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">Total</para> + </td> + </tr> + </blockTable> + <section> + <para style="terp_default_1">[[ repeatIn(get_employee(data['form']),'e') ]]</para> + <blockTable colWidths="55.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,57.0" style="Table2"> + <tr> + <td> + <para style="terp_default_9">[[ e[0] ]]</para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[1]!='' and formatLang(e[1])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[2]!='' and formatLang(e[2])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[3]!='' and formatLang(e[3])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[4]!='' and formatLang(e[4])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[5]!='' and formatLang(e[5])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[6]!='' and formatLang(e[6])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[7]!='' and formatLang(e[7])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[8]!='' and formatLang(e[8])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[9]!='' and formatLang(e[9])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[10]!='' and formatLang(e[10])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[11]!='' and formatLang(e[11])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica" size="5.0">[[ (e[12]!='' and formatLang(e[12])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_9_Bold">[[ formatLang(e[13]) ]] [[company.currency_id.symbol]]</para> + </td> + </tr> + </blockTable> + </section> + </section> + <section> + <para style="terp_default_1">[[ repeatIn(get_months_tol(),'t') ]]</para> + <blockTable colWidths="55.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,40.0,57.0" style="Table6"> + <tr> + <td> + <para style="terp_tblheader_Details">Total</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[1]) or removeParentNode('para') ]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[2]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[3]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[4]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[5]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[6]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[7]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[8]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[9]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[10]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[11]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(t[12]) or removeParentNode('para')]] [[company.currency_id.symbol]]</para> + </td> + <td> + <para style="terp_tblheader_General_Right">[[ formatLang(get_total()) ]] [[company.currency_id.symbol]]</para> + </td> + </tr> + </blockTable> + </section> + </story> +</document> diff --git a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py new file mode 100644 index 00000000000..576fd302f6f --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.py @@ -0,0 +1,164 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time +import datetime +from report import report_sxw + +class employees_yearly_salary_report(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(employees_yearly_salary_report, self).__init__(cr, uid, name, context) + + self.localcontext.update({ + 'time': time, + 'get_employee': self.get_employee, + 'get_employee_detail': self.get_employee_detail, + 'cal_monthly_amt': self.cal_monthly_amt, + 'get_periods': self.get_periods, + 'get_total': self.get_total, + 'get_allow': self.get_allow, + 'get_deduct': self.get_deduct, + }) + + self.context = context + + def get_periods(self, form): + self.mnths = [] +# Get start year-month-date and end year-month-date + first_year = int(form['date_from'][0:4]) + last_year = int(form['date_to'][0:4]) + + first_month = int(form['date_from'][5:7]) + last_month = int(form['date_to'][5:7]) + no_months = (last_year-first_year) * 12 + last_month - first_month + 1 + current_month = first_month + current_year = first_year + +# Get name of the months from integer + mnth_name = [] + for count in range(0, no_months): + m = datetime.date(current_year, current_month, 1).strftime('%b') + mnth_name.append(m) + self.mnths.append(str(current_month) + '-' + str(current_year)) + if current_month == 12: + current_month = 0 + current_year = last_year + current_month = current_month + 1 + for c in range(0, (12-no_months)): + mnth_name.append('None') + self.mnths.append('None') + return [mnth_name] + + def get_employee(self, form): + return self.pool.get('hr.employee').browse(self.cr,self.uid, form.get('employee_ids', []), context=self.context) + + def get_employee_detail(self, form, obj): + self.allow_list = [] + self.deduct_list = [] + self.total = 0.00 + gross = False + net = False + + payslip_lines = self.cal_monthly_amt(form, obj.id) + for line in payslip_lines: + for line[0] in line: + if line[0][0] == "Gross": + gross = line[0] + elif line[0][0] == "Net": + net = line[0] + elif line[0][13] > 0.0 and line[0][0] != "Net": + self.total += line[0][len(line[0])-1] + self.allow_list.append(line[0]) + elif line[0][13] < 0.0: + self.total += line[0][len(line[0])-1] + self.deduct_list.append(line[0]) + if gross: + self.allow_list.append(gross) + if net: + self.deduct_list.append(net) + return None + + def cal_monthly_amt(self, form, emp_id): + category_obj = self.pool.get('hr.salary.rule.category') + result = [] + res = [] + salaries = {} + self.cr.execute('''SELECT rc.code, pl.name, sum(pl.total), \ + to_char(date_to,'mm-yyyy') as to_date FROM hr_payslip_line as pl \ + LEFT JOIN hr_salary_rule_category AS rc on (pl.category_id = rc.id) \ + LEFT JOIN hr_payslip as p on pl.slip_id = p.id \ + LEFT JOIN hr_employee as emp on emp.id = p.employee_id \ + WHERE p.employee_id = %s \ + GROUP BY rc.parent_id, pl.sequence, pl.id, pl.category_id,pl.name,p.date_to,rc.code \ + ORDER BY pl.sequence, rc.parent_id''',(emp_id,)) + salary = self.cr.fetchall() + for category in salary: + if category[0] not in salaries: + salaries.setdefault(category[0], {}) + salaries[category[0]].update({category[1]: {category[3]: category[2]}}) + elif category[1] not in salaries[category[0]]: + salaries[category[0]].setdefault(category[1], {}) + salaries[category[0]][category[1]].update({category[3]: category[2]}) + else: + salaries[category[0]][category[1]].update({category[3]: category[2]}) + + category_ids = category_obj.search(self.cr,self.uid, [], context=self.context) + categories = category_obj.read(self.cr, self.uid, category_ids, ['code'], context=self.context) + for code in map(lambda x: x['code'], categories): + if code in salaries: + res = self.salary_list(salaries[code]) + result.append(res) + return result + + def salary_list(self, salaries): + cat_salary_all = [] + for category_name,amount in salaries.items(): + cat_salary = [] + total = 0.0 + cat_salary.append(category_name) + for mnth in self.mnths: + if mnth <> 'None': + if len(mnth) != 7: + mnth = '0' + str(mnth) + if mnth in amount and amount[mnth]: + cat_salary.append(amount[mnth]) + total += amount[mnth] + else: + cat_salary.append(0.00) + else: + cat_salary.append('') + cat_salary.append(total) + cat_salary_all.append(cat_salary) + return cat_salary_all + + def get_allow(self): + return self.allow_list + + def get_deduct(self): + return self.deduct_list + + def get_total(self): + return self.total + +report_sxw.report_sxw('report.salary.detail.byyear', 'yearly.salary.detail', 'hr_payroll/report/report_hr_yearly_salary_detail.rml', parser=employees_yearly_salary_report, header='internal landscape') + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.rml b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.rml new file mode 100644 index 00000000000..7fa127b0bd3 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_hr_yearly_salary_detail.rml @@ -0,0 +1,588 @@ +<?xml version="1.0"?> +<document filename="hr_yearly_salary_detail_report.pdf"> + <template pageSize="(842.0,595.0)" title="hr_yearly_salary_detail_report" author="Openerp S.A." allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="28.0" y1="57.0" width="786" height="481"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table12"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="4,0" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="4,0" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table5"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="4,0" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="5,0" stop="5,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="6,0" stop="6,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="7,0" stop="7,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="8,0" stop="8,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="9,0" stop="9,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="10,0" stop="10,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="11,0" stop="11,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="12,0" stop="12,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="13,0" stop="13,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table7"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table10"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table8"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table11"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table9"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="8,-1" stop="8,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="9,-1" stop="9,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="10,-1" stop="10,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="11,-1" stop="11,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="12,-1" stop="12,-1"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="13,-1" stop="13,-1"/> + </blockTableStyle> + <blockTableStyle id="Table1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="5,0" stop="5,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="6,0" stop="6,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="7,0" stop="7,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="8,0" stop="8,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="9,0" stop="9,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="10,0" stop="10,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="11,0" stop="11,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="12,0" stop="12,0"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="13,0" stop="13,0"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="Standard" fontName="Helvetica"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Text body" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Caption" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Helvetica"/> + <paraStyle name="Table Contents" fontName="Helvetica"/> + <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/> + <paraStyle name="Drawing" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Header" fontName="Helvetica"/> + <paraStyle name="Endnote" rightIndent="0.0" leftIndent="14.0" fontName="Helvetica" fontSize="10.0" leading="13"/> + <paraStyle name="Addressee" fontName="Helvetica" spaceBefore="0.0" spaceAfter="3.0"/> + <paraStyle name="Signature" fontName="Helvetica"/> + <paraStyle name="Heading 9" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 8" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 7" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 6" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 5" fontName="Helvetica-Bold" fontSize="85%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 4" fontName="Helvetica-BoldOblique" fontSize="85%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 1" fontName="Helvetica-Bold" fontSize="115%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 10" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 2" fontName="Helvetica-BoldOblique" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="First line indent" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Hanging indent" rightIndent="0.0" leftIndent="28.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Salutation" fontName="Helvetica"/> + <paraStyle name="Text body indent" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Heading 3" fontName="Helvetica-Bold" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="List Indent" rightIndent="0.0" leftIndent="142.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Marginalia" rightIndent="0.0" leftIndent="113.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_8" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_9" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details" rightIndent="0.0" leftIndent="-3.0" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Footer" fontName="Helvetica"/> + <paraStyle name="Horizontal Line" fontName="Helvetica" fontSize="6.0" leading="8" spaceBefore="0.0" spaceAfter="14.0"/> + <paraStyle name="terp_tblheader_General_Right" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Centre" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_header_Right" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_address" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_1" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9_Bold" fontName="Helvetica-Bold" fontSize="7.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_8_Italic" fontName="Helvetica-Oblique" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_space" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="9.0" spaceAfter="0.0"/> + <images/> + </stylesheet> + <story> + <para style="terp_default_8">[[ repeatIn(get_employee(data['form']), 'o') ]] </para> + <blockTable colWidths="785.0" style="Table12"> + <tr> + <td> + <para style="terp_default_Centre_9">From <u>[[ formatLang(data['form']['date_from'], date=True) ]]</u> To <u>[[ formatLang(data['form']['date_to'], date=True) ]]</u></para> + </td> + </tr> + </blockTable> + <para style="terp_default_Centre_9"> + <font color="white"> </font> + </para> + <blockTable colWidths="100.0,162.0,92.0,161.0,103.0,167.0" style="Table2"> + <tr> + <td> + <para style="terp_tblheader_Details">Employee Code</para> + </td> + <td> + <para style="terp_default_9">[[ o.identification_id ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">Department</para> + </td> + <td> + <para style="terp_default_9">[[ o.department_id and o.department_id.name or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">Bank</para> + </td> + <td> + <para style="terp_default_9">[[ o.bank_account_id and o.bank_account_id.bank.name or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="100.0,162.0,92.0,161.0,103.0,167.0" style="Table3"> + <tr> + <td> + <para style="terp_tblheader_Details">Employee Name</para> + </td> + <td> + <para style="terp_default_9">[[ o.name ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">Other No.</para> + </td> + <td> + <para style="terp_default_9">[[ o.otherid or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">Address</para> + </td> + <td> + <para style="terp_default_9">[[o.address_home_id and o.address_home_id.name or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="100.0,162.0,92.0,161.0,103.0,167.0" style="Table5"> + <tr> + <td> + <para style="terp_tblheader_Details">Designation</para> + </td> + <td> + <para style="terp_default_9">[[ o.job_id and o.job_id.name or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">Phone No.</para> + </td> + <td> + <para style="terp_default_9">[[ o.work_phone or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details">E-mail Address</para> + </td> + <td> + <para style="terp_default_9">[[o.work_email or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="terp_default_space"> + <font color="white"> </font> + </para> + <section> + <para style="terp_default_1">[[ repeatIn(get_periods(data['form']),'m') ]]</para> + <para style="terp_default_1">[[ get_employee_detail((data['form']),o) ]]</para> + <blockTable colWidths="195.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,56.0" style="Table4"> + <tr> + <td> + <para style="terp_tblheader_Details">Title</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[0] != 'None' and m[0] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[1] != 'None' and m[1] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[2] != 'None' and m[2] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[3] != 'None' and m[3] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[4] != 'None' and m[4] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[5] != 'None' and m[5] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[6] != 'None' and m[6] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[7] != 'None' and m[7] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[8] != 'None' and m[8] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[9] != 'None' and m[9] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[10] != 'None' and m[10] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">[[ m[11] != 'None' and m[11] or '' ]]</para> + </td> + <td> + <para style="terp_tblheader_Details_Right">Total</para> + </td> + </tr> + </blockTable> + <para style="terp_default_1"> + <font color="white"> </font> + </para> + <section> + <blockTable colWidths="803.0" style="Table7"> + <tr> + <td> + <para style="terp_tblheader_Details">Allowances with Basic: </para> + </td> + </tr> + </blockTable> + <section> + <para style="terp_default_1">[[ repeatIn(get_allow(),'e1') ]]</para> + <blockTable colWidths="195.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,56.0" style="Table6"> + <tr> + <td> + <para style="terp_default_9"><b>[[ e1[0] in ["Basic","Gross"] and e1[0] ]]</b> </para> + <para style="terp_default_9">[[ e1[0] not in ["Basic","Gross"] and e1[0] ]]</para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[1]!='' and formatLang(e1[1])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[1]!='' and formatLang(e1[1])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[2]!='' and formatLang(e1[2])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[2]!='' and formatLang(e1[2])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[3]!='' and formatLang(e1[3])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[3]!='' and formatLang(e1[3])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[4]!='' and formatLang(e1[4])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[4]!='' and formatLang(e1[4])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[5]!='' and formatLang(e1[5])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[5]!='' and formatLang(e1[5])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[6]!='' and formatLang(e1[6])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[6]!='' and formatLang(e1[6])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[7]!='' and formatLang(e1[7])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[7]!='' and formatLang(e1[7])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[8]!='' and formatLang(e1[8])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[8]!='' and formatLang(e1[8])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[9]!='' and formatLang(e1[9])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[9]!='' and formatLang(e1[9])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[10]!='' and formatLang(e1[10])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[10]!='' and formatLang(e1[10])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[11]!='' and formatLang(e1[11])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[11]!='' and formatLang(e1[11])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e1[0] in ["Basic","Gross"] and e1[12]!='' and formatLang(e1[12])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e1[0] not in ["Basic","Gross"] and e1[12]!='' and formatLang(e1[12])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_9_Bold">[[ formatLang(e1[13]) ]] [[ company.currency_id.symbol ]]</para> + </td> + </tr> + </blockTable> + </section> + <blockTable colWidths="803.0" style="Table10"> + <tr> + <td> + <para style="terp_tblheader_Details">Deductions: </para> + </td> + </tr> + </blockTable> + <section> + <para style="terp_default_1">[[ repeatIn(get_deduct(),'e2') ]]</para> + <blockTable colWidths="195.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,46.0,56.0" style="Table8"> + <tr> + <td> + <para style="terp_default_9"><b>[[ e2[0] in ["Net"] and e2[0] ]]</b> </para> + <para style="terp_default_9">[[ e2[0] not in ["Net"] and e2[0] ]]</para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[1]!='' and formatLang(e2[1])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[1]!='' and formatLang(e2[1])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[2]!='' and formatLang(e2[2])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[2]!='' and formatLang(e2[2])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[3]!='' and formatLang(e2[3])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[3]!='' and formatLang(e2[3])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[4]!='' and formatLang(e2[4])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[4]!='' and formatLang(e2[4])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[5]!='' and formatLang(e2[5])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[5]!='' and formatLang(e2[5])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[6]!='' and formatLang(e2[6])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[6]!='' and formatLang(e2[6])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[7]!='' and formatLang(e2[7])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[7]!='' and formatLang(e2[7])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[8]!='' and formatLang(e2[8])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[8]!='' and formatLang(e2[8])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[9]!='' and formatLang(e2[9])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[9]!='' and formatLang(e2[9])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[10]!='' and formatLang(e2[10])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[10]!='' and formatLang(e2[10])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[11]!='' and formatLang(e2[11])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[11]!='' and formatLang(e2[11])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_8"> + <font face="Helvetica-Bold" size="7.0">[[ (e2[0] in ["Net"] and e2[12]!='' and formatLang(e2[12])) or removeParentNode('font') ]]</font> + <font face="Helvetica" size="7.0">[[ (e2[0] not in ["Net"] and e2[12]!='' and formatLang(e2[12])) or removeParentNode('font') ]]</font> + </para> + </td> + <td> + <para style="terp_default_Right_9_Bold">[[ formatLang(e2[13]) ]] [[ company.currency_id.symbol ]]</para> + </td> + </tr> + </blockTable> + </section> + </section> + <para style="terp_default_1"> + <font color="white"> </font> + </para> + </section> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + </story> +</document> diff --git a/addons/l10n_in_hr_payroll/report/report_payroll_advice.py b/addons/l10n_in_hr_payroll/report/report_payroll_advice.py new file mode 100644 index 00000000000..d4eeaf3d218 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payroll_advice.py @@ -0,0 +1,82 @@ +#-*- coding:utf-8 -*- + +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# d$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time +from datetime import datetime + +from report import report_sxw +from tools import amount_to_text_en + +class payroll_advice_report(report_sxw.rml_parse): + + def __init__(self, cr, uid, name, context): + super(payroll_advice_report, self).__init__(cr, uid, name, context=context) + + self.localcontext.update({ + 'time': time, + 'get_month': self.get_month, + 'convert': self.convert, + 'get_detail': self.get_detail, + 'get_bysal_total': self.get_bysal_total, + }) + self.context = context + + def get_month(self, input_date): + payslip_pool = self.pool.get('hr.payslip') + res = { + 'from_name': '', 'to_name': '' + } + slip_ids = payslip_pool.search(self.cr, self.uid, [('date_from','<=',input_date), ('date_to','>=',input_date)], context=self.context) + if slip_ids: + slip = payslip_pool.browse(self.cr, self.uid, slip_ids, context=self.context)[0] + from_date = datetime.strptime(slip.date_from, '%Y-%m-%d') + to_date = datetime.strptime(slip.date_to, '%Y-%m-%d') + res['from_name']= from_date.strftime('%d')+'-'+from_date.strftime('%B')+'-'+from_date.strftime('%Y') + res['to_name']= to_date.strftime('%d')+'-'+to_date.strftime('%B')+'-'+to_date.strftime('%Y') + return res + + def convert(self, amount, cur): + return amount_to_text_en.amount_to_text(amount, 'en', cur); + + def get_bysal_total(self): + return self.total_bysal + + def get_detail(self, line_ids): + result = [] + self.total_bysal = 0.00 + for l in line_ids: + res = {} + res.update({ + 'name': l.employee_id.name, + 'acc_no': l.name, + 'ifsc_code': l.ifsc_code, + 'bysal': l.bysal, + 'debit_credit': l.debit_credit, + }) + self.total_bysal += l.bysal + result.append(res) + return result + +report_sxw.report_sxw('report.payroll.advice', 'hr.payroll.advice', 'l10n_in_hr_payroll/report/report_payroll_advice.rml', parser=payroll_advice_report, header="external") + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/report/report_payroll_advice.rml b/addons/l10n_in_hr_payroll/report/report_payroll_advice.rml new file mode 100644 index 00000000000..8ca1bae5ba5 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payroll_advice.rml @@ -0,0 +1,425 @@ +<?xml version="1.0"?> +<document filename="test.pdf"> + <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="28.0" y1="28.0" width="539" height="786"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table5"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="5,0" stop="5,0"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#000000" start="4,0" stop="4,0"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/> + </blockTableStyle> + <blockTableStyle id="Table7"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P2" fontName="Helvetica-Bold" fontSize="6.0" leading="8" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P3" rightIndent="-0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P4" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P5" fontName="Helvetica" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P6" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P7" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P9" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P10" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P11" fontName="Helvetica" alignment="LEFT"/> + <paraStyle name="P12" fontName="Helvetica" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P13" rightIndent="-0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="JUSTIFY" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Standard" fontName="Helvetica"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Text body" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Caption" fontName="Helvetica-Oblique" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Helvetica"/> + <paraStyle name="Table Contents" fontName="Helvetica"/> + <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/> + <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_8" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_9" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_9" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Footer" fontName="Helvetica"/> + <paraStyle name="Horizontal Line" fontName="Helvetica" fontSize="6.0" leading="8" spaceBefore="0.0" spaceAfter="14.0"/> + <paraStyle name="Heading 9" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_tblheader_General_Right" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_header_Right" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_header_Centre" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="CENTER" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_0.30cmspace" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="9.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_1" fontName="Helvetica" fontSize="4.0" leading="5" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Right_9_Bold" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_8_space" fontName="Helvetica-Oblique" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="1.0"/> + <paraStyle name="Drawing" fontName="Helvetica-Oblique" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Header" fontName="Helvetica"/> + <paraStyle name="Endnote" rightIndent="0.0" leftIndent="14.0" fontName="Helvetica" fontSize="10.0" leading="13" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Addressee" fontName="Helvetica" spaceBefore="0.0" spaceAfter="3.0"/> + <paraStyle name="Signature" fontName="Helvetica"/> + <paraStyle name="Heading 8" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 7" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 6" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 5" fontName="Helvetica-Bold" fontSize="85%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 4" fontName="Helvetica-BoldOblique" fontSize="85%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 1" fontName="Helvetica-Bold" fontSize="115%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 10" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Heading 2" fontName="Helvetica-BoldOblique" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="First line indent" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Hanging indent" rightIndent="0.0" leftIndent="28.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Salutation" fontName="Helvetica"/> + <paraStyle name="Text body indent" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Heading 3" fontName="Helvetica-Bold" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="List Indent" rightIndent="0.0" leftIndent="142.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Marginalia" rightIndent="0.0" leftIndent="113.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="0.0"/> + <images/> + </stylesheet> + <story> + <para style="terp_default_8">[[repeatIn(objects,'o')]]</para> + <blockTable colWidths="234.0,305.0" style="Table1"> + <tr> + <td> + <para style="P7">[[ formatLang(time.strftime('%Y-%m-%d'),date=True) ]]</para> + <para style="P9"/> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P6"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_Bold_9">To, </para> + <para style="terp_default_Bold_9">The Manager</para> + </td> + <td> + <para style="terp_default_Bold_9"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_Bold_9">[[ o.bank_id.name ]] Bank</para> + </td> + <td> + <para style="terp_default_Bold_9"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_1"> + <font color="white"> </font> + </para> + <para style="terp_default_9">Dear Sir/Madam,</para> + </td> + <td> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + <blockTable colWidths="539.0" style="Table2"> + <tr> + <td> + <para style="P10">Payment Advice from [[ o.name ]] A/C no. [[ o.company_id.bank_ids and o.company_id.bank_ids[0].acc_number ]] form period [[ get_month(o.date)['from_name'] ]] to [[ get_month(o.date)['to_name'] ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="539.0" style="Table3"> + <tr> + <td> + <para style="terp_default_9">[[ o.note ]] </para> + </td> + </tr> + </blockTable> + <para style="terp_default_8_space"> + <font color="white"> </font> + </para> + <blockTable colWidths="47.0,146.0,158.0,105.0,83.0" style="Table4">[[ o.neft == True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="P1">SI. No.</para> + </td> + <td> + <para style="P1">Name of the Employe</para> + </td> + <td> + <para style="P3">Bank Account No.</para> + </td> + <td> + <para style="P3">By Salary</para> + </td> + <td> + <para style="P3">C/D</para> + </td> + </tr> + </blockTable> + <section> + <para style="terp_default_8">[[ repeatIn(get_detail(o.line_ids),'line') ]]</para> + <blockTable colWidths="47.0,146.0,158.0,105.0,83.0" style="Table5">[[ o.neft == True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="P11" leftIndent="15" bulletIndent="0"> + <bullet><seq id="L1"/>.</bullet> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P8">[[ line['name'] ]]</para> + </td> + <td> + <para style="P5">[[ line['acc_no'] ]]</para> + </td> + <td> + <para style="P8">[[formatLang(line['bysal'])]] [[ (company.currency_id and company.currency_id.symbol) or '' ]]</para> + </td> + <td> + <para style="P5">[[ line['debit_credit'] ]]</para> + </td> + </tr> + </blockTable> + </section> + <blockTable colWidths="40.0,129.0,141.0,104.0,77.0,49.0" style="Table4">[[ o.neft != True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="P1">SI. No.</para> + </td> + <td> + <para style="P1">Name of the Employe</para> + </td> + <td> + <para style="P3">Bank Account No.</para> + </td> + <td> + <para style="P3">IFSC Code</para> + </td> + <td> + <para style="P3">By Salary</para> + </td> + <td> + <para style="P3">C/D</para> + </td> + </tr> + </blockTable> + <section> + <para style="terp_default_8">[[ repeatIn(get_detail(o.line_ids),'line') ]]</para> + <blockTable colWidths="39.0,129.0,140.0,104.0,78.0,49.0" style="Table5">[[ o.neft !=True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="P11" leftIndent="15" bulletIndent="0"> + <bullet><seq id="L1"/>.</bullet> + </para> + </td> + <td> + <para style="P8">[[ line['name'] ]]</para> + </td> + <td> + <para style="P5">[[ line['acc_no'] ]]</para> + </td> + <td> + <para style="P5">[[ line['ifsc_code'] ]]</para> + </td> + <td> + <para style="P8">[[formatLang(line['bysal'])]] [[ (company.currency_id and company.currency_id.symbol) or '' ]]</para> + </td> + <td> + <para style="P5">[[ line['debit_credit'] ]]</para> + </td> + </tr> + </blockTable> + </section> + <blockTable colWidths="50.0,144.0,158.0,107.0,80.0" style="Table6">[[ o.neft == True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P2"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P1">Total : <font face="Helvetica" size="6.0">[[ o.line_ids==[] and removeParentNode('para') ]]</font></para> + <para style="P4"/> + </td> + <td> + <para style="P4"><font face="Helvetica" size="6.0">[[ o.line_ids==[] and removeParentNode('para') ]]</font>[[ formatLang(get_bysal_total()) ]] [[ (company.currency_id and company.currency_id.symbol) or '' ]]</para> + </td> + <td> + <para style="P4"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + <blockTable colWidths="50.0,119.0,244.0,99.0,27.0" style="Table6">[[ o.neft !=True and removeParentNode('blockTable') ]] + <tr> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P2"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P1">Total : <font face="Helvetica" size="6.0">[[ o.line_ids==[] and removeParentNode('para') ]]</font></para> + <para style="P4"/> + </td> + <td> + <para style="P4"><font face="Helvetica" size="6.0">[[ o.line_ids==[] and removeParentNode('para') ]]</font>[[ formatLang(get_bysal_total()) ]] [[ (company.currency_id and company.currency_id.symbol) or '' ]]</para> + </td> + <td> + <para style="P4"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + <para style="terp_default_0.30cmspace"> + <font color="white"> </font> + </para> + <blockTable colWidths="269.0,269.0" style="Table7"> + <tr> + <td> + <para style="terp_default_9">Yours Sincerely</para> + </td> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_9">For [[ company.name ]]</para> + </td> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + </tr> + <tr> + <td> + <para style="terp_default_9">Authorized Signature</para> + </td> + <td> + <para style="terp_tblheader_Details"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + <para style="terp_default_9"> + <font color="white"> </font> + </para> + <para> + <seqReset id="L1"/> + </para> + </story> +</document> diff --git a/addons/l10n_in_hr_payroll/report/report_payslip_details.py b/addons/l10n_in_hr_payroll/report/report_payslip_details.py new file mode 100644 index 00000000000..2732d5d09ae --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payslip_details.py @@ -0,0 +1,35 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +from report import report_sxw +from hr_payroll import report + +class payslip_details_report_in(report.report_payslip_details.payslip_details_report): + + def __init__(self, cr, uid, name, context): + super(payslip_details_report_in, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'get_details_by_rule_category': self.get_details_by_rule_category, + }) + +report_sxw.report_sxw('report.paylip.details.in', 'hr.payslip', 'l10n_in_hr_payroll/report/report_payslip_details.rml', parser=payslip_details_report_in) + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/report/report_payslip_details.rml b/addons/l10n_in_hr_payroll/report/report_payslip_details.rml new file mode 100644 index 00000000000..a7ab3db3185 --- /dev/null +++ b/addons/l10n_in_hr_payroll/report/report_payslip_details.rml @@ -0,0 +1,372 @@ +<?xml version="1.0"?> +<document filename="test.pdf"> + <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="28.0" y1="28.0" width="539" height="786"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + </blockTableStyle> + <blockTableStyle id="Table4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table5"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/> + <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> + <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table10"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table11"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table12"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/> + </blockTableStyle> + <blockTableStyle id="Table8"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Table7"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#000000" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table16"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/> + <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="5,-1" stop="5,-1"/> + </blockTableStyle> + <blockTableStyle id="Table13"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Helvetica" fontSize="2.0" leading="3"/> + <paraStyle name="P2" fontName="Helvetica" fontSize="2.0" leading="3"/> + <paraStyle name="P3" rightIndent="-56.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P4" rightIndent="-56.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P5" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P6" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P7" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P8" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P9" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P10" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P11" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P12" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P13" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT"/> + <paraStyle name="P14" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT"/> + <paraStyle name="P15" fontName="Helvetica-Bold" fontSize="8.0" leading="10"/> + <paraStyle name="P16" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P17" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P18" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P19" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT"/> + <paraStyle name="P20" fontName="Helvetica" fontSize="2.0" leading="3"/> + <paraStyle name="P21" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P22" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT"/> + <paraStyle name="P23" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT"/> + <paraStyle name="P24" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P25" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="P26" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Standard" fontName="Helvetica"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="Text body" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Caption" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Helvetica"/> + <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_8" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_space" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_header_Centre" fontName="Helvetica-Bold" fontSize="14.0" leading="17" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_8" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Centre_9" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_9" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_8" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Bold_9" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_10" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="Table Contents" fontName="Helvetica"/> + <paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/> + <paraStyle name="payslip_adj" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> + <images/> + </stylesheet> + <story> + <para style="P3">[[repeatIn(objects,'o')]]</para> + <blockTable colWidths="539.0" style="Table1"> + <tr> + <td> + <para style="P8">Pay Slip Details</para> + </td> + </tr> + </blockTable> + <para style="terp_header_Centre"> + <font face="Helvetica" size="6.0">[[o.credit_note==False and removeParentNode('para')]]</font> + <font face="Helvetica-Bold" size="14.0">Credit</font> + <font face="Helvetica" size="14.0"/> + <font face="Helvetica-Bold" size="14.0">Note</font> + </para> + <para style="P9">([[o.name or removeParentNode('para')]])</para> + <blockTable colWidths="63.0,206.0,89.0,181.0" style="Table2"> + <tr> + <td> + <para style="P16">Name</para> + </td> + <td> + <para style="P16">[[o.employee_id.name]]</para> + </td> + <td> + <para style="P16">Designation </para> + </td> + <td> + <para style="P5">[[ o.employee_id.job_id.name or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="63.0,476.0" style="Table3"> + <tr> + <td> + <para style="terp_default_Bold_8"> + <font face="Helvetica">Address </font> + </para> + </td> + <td> + <para style="P5">[[o.employee_id.address_home_id and o.employee_id.address_home_id.name or '' ]],[[o.employee_id.address_home_id and o.employee_id.address_home_id.street or '' ]],[[o.employee_id.address_home_id and o.employee_id.address_home_id.street2 or '' ]],[[o.employee_id.address_home_id and o.employee_id.address_home_id.zip or '' ]],[[o.employee_id.address_home_id and o.employee_id.address_home_id.city or '' ]],[[o.employee_id.address_home_id and o.employee_id.address_home_id.state_id and o.employee_id.address_home_id.state_id.name or '' ]] [[o.employee_id.address_home_id and o.employee_id.address_home_id.country_id and o.employee_id.address_home_id.country_id.name or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="63.0,206.0,89.0,181.0" style="Table4"> + <tr> + <td> + <para style="P16">Email</para> + </td> + <td> + <para style="P5">[[ o.employee_id.work_email or '' ]]</para> + </td> + <td> + <para style="terp_default_Bold_8"> + <font face="Helvetica">Identification No</font> + </para> + </td> + <td> + <para style="P5">[[ o.employee_id.identification_id or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="63.0,206.0,89.0,181.0" style="Table5"> + <tr> + <td> + <para style="P16">Reference</para> + </td> + <td> + <para style="P5">[[ o.number or '' ]]</para> + </td> + <td> + <para style="P16">Bank Account</para> + </td> + <td> + <para style="P5">[[ o.employee_id.otherid or '' ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="63.0,206.0,89.0,181.0" style="Table6"> + <tr> + <td> + <para style="P16">Date From</para> + </td> + <td> + <para style="P5">[[ o.date_from or '']]</para> + </td> + <td> + <para style="terp_default_Bold_8"> + <font face="Helvetica" size="8.0">Date To</font> + </para> + </td> + <td> + <para style="P5">[[ o.date_to or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="P7"/> + <para style="P11"> + <font color="white"> </font> + </para> + <para style="P5"> + <font color="white"> </font> + </para> + <blockTable colWidths="539.0" style="Table10"> + <tr> + <td> + <para style="P10">Details by Salary Rule Category: </para> + </td> + </tr> + </blockTable> + <blockTable colWidths="54.0,388.0,97.0" style="Table11"> + <tr> + <td> + <para style="P15">Code</para> + </td> + <td> + <para style="P15">Salary Rule Category</para> + </td> + <td> + <para style="P14">Total</para> + </td> + </tr> + </blockTable> + <para style="P1"> + <font color="white"> </font> + </para> + <section> + <para style="P16">[[repeatIn(get_details_by_rule_category(o.details_by_salary_rule_category),'h') ]]</para> + <blockTable colWidths="54.0,388.0,97.0" style="Table12"> + <tr> + <td> + <para style="P16"> + <font face="Helvetica">[[ h['code'] ]]</font> + <font face="Helvetica">[[ h['level']!=0 and ( setTag('para','para',{'style':'terp_default_8'})) or removeParentNode('font')]]</font> + </para> + </td> + <td> + <para style="P17"><font face="Helvetica" color="white">[[ '..'*h['level'] ]]</font>[[ h['rule_category'] ]]<font face="Helvetica">[[ h['level']!=0 and ( setTag('para','para',{'style':'terp_default_8'})) or removeParentNode('font') ]]</font></para> + </td> + <td> + <para style="P6">[[ formatLang(h['total']) ]] [[o.company_id and o.company_id.currency_id.symbol or '']] <font face="Helvetica" size="8.0">[[ h['level']==0 and ( setTag('para','para',{'style':'terp_default_10'})) or removeParentNode('font') ]]</font></para> + </td> + </tr> + </blockTable> + </section> + <para style="P7"> + <font color="white"> </font> + </para> + + <blockTable colWidths="269.0,269.0" style="Table13"> + <tr> + <td> + <para style="P5"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P12"> + <font color="white"> </font> + </para> + <para style="P12"> + <font color="white"> </font> + </para> + <para style="P12"> + <font color="white"> </font> + </para> + <para style="P12">Authorized Signature </para> + </td> + </tr> + </blockTable> + <para style="P4"> + <font color="white"> </font> + </para> + </story> +</document> + diff --git a/addons/l10n_in_hr_payroll/security/ir.model.access.csv b/addons/l10n_in_hr_payroll/security/ir.model.access.csv new file mode 100644 index 00000000000..7ca0834449b --- /dev/null +++ b/addons/l10n_in_hr_payroll/security/ir.model.access.csv @@ -0,0 +1,5 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_hr_payroll_advice_user","hr.payroll.advice","model_hr_payroll_advice","base.group_hr_user",1,1,1,1 +"access_hr_payroll_advice_line_user","hr.payroll.advice.line","model_hr_payroll_advice_line","base.group_hr_user",1,1,1,1 +"access_hr_payroll_advice_report_user","payment.advice.report","model_payment_advice_report","base.group_hr_manager",1,1,1,1 +"access_hr_payroll_payslip_report_user","payslip.report","model_payslip_report","base.group_hr_manager",1,1,1,1 diff --git a/addons/l10n_in_hr_payroll/static/src/img/icon.png b/addons/l10n_in_hr_payroll/static/src/img/icon.png Binary files differnew file mode 100644 index 00000000000..ddaf38f8b6d --- /dev/null +++ b/addons/l10n_in_hr_payroll/static/src/img/icon.png diff --git a/addons/l10n_in_hr_payroll/test/payment_advice.yml b/addons/l10n_in_hr_payroll/test/payment_advice.yml new file mode 100644 index 00000000000..20b45830c5e --- /dev/null +++ b/addons/l10n_in_hr_payroll/test/payment_advice.yml @@ -0,0 +1,41 @@ +- + In order to test Payment Advice I create a new Payment Advice +- + I create a new Payment Advice with NEFT Transaction Enable +- + !record {model: hr.payroll.advice, id: payment_advice_1}: + name: 'NEFT Advice' + bank_id: base.res_bank_1 + line_ids: + - employee_id: hr.employee_fp + name: '90125452552' + ifsc_code: 'abn45215145' + bysal: 25000.00 + - employee_id: hr.employee_al + name: '00014521111232' + ifsc_code: 'sbi45452145' + bysal: 20000.00 +- + I check that the Payment Advice is in "Draft" +- + !assert {model: hr.payroll.advice, id: payment_advice_1}: + - state == 'draft' +- + Now I confirm Payment Advice +- + !python {model: hr.payroll.advice}: | + self.confirm_sheet(cr, uid, [ref('payment_advice_1')]) +- + I check that the Payment Advice state is "Confirmed" +- + !python {model: hr.payroll.advice}: | + advice = self.browse(cr, uid, ref("payment_advice_1")) + assert (advice.state == 'confirm') +- + In order to test the PDF report defined on a Payment Advice, we will print a Print Advice Report when NEFT is checked +- + !python {model: hr.payroll.advice}: | + import netsvc, tools, os + (data, format) = netsvc.LocalService('report.payroll.advice').create(cr, uid, [ref('l10n_in_hr_payroll.payment_advice_1')], {}) + if tools.config['test_report_directory']: + file(os.path.join(tools.config['test_report_directory'], 'l10n_in_hr_payroll_summary report'+format), 'wb+').write(data)
\ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml b/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml new file mode 100644 index 00000000000..d1a1436b50b --- /dev/null +++ b/addons/l10n_in_hr_payroll/test/payment_advice_batch.yml @@ -0,0 +1,61 @@ +- + In order to test Payment Advice Creation from Payslip Bacth I create New Payslip Batch +- + I Create a bank record +- + !record {model: res.partner.bank, id: res_partner_bank_0}: + acc_number: '3025632343043' + partner_id: base.res_partner_21 + state: bank + bank: base.res_bank_1 +- + I create a new employee “Rahul” +- + !record {model: hr.employee, id: hr_employee_rahul0}: + country_id: base.in + department_id: hr.dep_rd + name: Rahul + bank_account_id: res_partner_bank_0 +- + I want to generate a payslip from Payslip Batch. +- + !record {model: hr.payslip.run, id: hr_payslip_run_forAdvice}: + name: Payslip Batch +- + I create record for generating the Payslip for Payslip Batch. +- + !record {model: hr.payslip.employees, id: hr_payslip_employees}: + employee_ids: + - hr_employee_rahul0 +- + I generate the payslip by clicking on Generate button wizard. +- + !python {model: hr.payslip.employees}: | + self.compute_sheet(cr, uid, [ref('hr_payslip_employees')], context={'active_id': ref('hr_payslip_run_forAdvice')}) +- + I check that the Payslip Batch is in "Draft" +- + !assert {model: hr.payslip.run, id: hr_payslip_run_forAdvice}: + - state == 'draft' +- + Now I close Payslip Batch +- + !python {model: hr.payslip.run}: | + self.close_payslip_run(cr, uid, [ref('hr_payslip_run_forAdvice')]) +- + I check that the Payslip Batch is "Close" +- + !python {model: hr.payslip.run}: | + batch = self.browse(cr, uid, ref("hr_payslip_run_forAdvice")) + assert (batch.state == 'close') +- + I create Advice from Payslip Batch using Create Advice button +- + !python {model: hr.payslip.run}: | + self.create_advice(cr, uid, [ref('hr_payslip_run_forAdvice')]) +- + I check for Advice is created from Payslip Batch +- + !python {model: hr.payroll.advice}: | + advice_ids = self.search(cr, uid, [('batch_id','=',ref('hr_payslip_run_forAdvice'))]) + assert advice_ids,"Advice is not created from Payslip Batch."
\ No newline at end of file diff --git a/addons/project_mailgate/__init__.py b/addons/l10n_in_hr_payroll/wizard/__init__.py index 0f82ebd1b41..36635aec3c2 100644 --- a/addons/project_mailgate/__init__.py +++ b/addons/l10n_in_hr_payroll/wizard/__init__.py @@ -19,6 +19,7 @@ # ############################################################################## -import project_mailgate +import hr_salary_employee_bymonth +import hr_yearly_salary_detail -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +# 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_salary_employee_bymonth.py b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py new file mode 100644 index 00000000000..25cc615a1df --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth.py @@ -0,0 +1,71 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time + +from osv import osv, fields + +class hr_salary_employee_bymonth(osv.osv_memory): + + _name = 'hr.salary.employee.month' + _description = 'Hr Salary Employee By Month Report' + _columns = { + 'start_date': fields.date('Start Date', required=True), + 'end_date': fields.date('End Date', required=True), + 'employee_ids': fields.many2many('hr.employee', 'payroll_year_rel', 'payroll_year_id', 'employee_id', 'Employees', required=True), + 'category_id': fields.many2one('hr.salary.rule.category', 'Category', required=True), + } + + def _get_default_category(self, cr, uid, context=None): + category_ids = self.pool.get('hr.salary.rule.category').search(cr, uid, [('code', '=', 'NET')], context=context) + return category_ids and category_ids[0] or False + + _defaults = { + 'start_date': lambda *a: time.strftime('%Y-01-01'), + 'end_date': lambda *a: time.strftime('%Y-%m-%d'), + 'category_id': _get_default_category + } + + def print_report(self, cr, uid, ids, context=None): + """ + To get the date and print the report + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return: return report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + + res = self.read(cr, uid, ids, context=context) + res = res and res[0] or {} + datas.update({'form': res}) + return { + 'type': 'ir.actions.report.xml', + 'report_name': 'salary.employee.bymonth', + '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_salary_employee_bymonth_view.xml b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth_view.xml new file mode 100644 index 00000000000..e05519b08db --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_salary_employee_bymonth_view.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="view_salary_employee_month" model="ir.ui.view"> + <field name="name">Hr monthly Employee Salary</field> + <field name="model">hr.salary.employee.month</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Employee Pay Head Breakup" version="7.0"> + <label string="This wizard will print report which displays employees break-up of Net Head for a specified dates."/> + <footer> + <button name="print_report" string="Print" type="object" class="oe_highlight"/> + or + <button string="Cancel" class="oe_link" special="cancel" /> + </footer> + <group> + <field name="start_date"/> + <field name="end_date"/> + <field name="category_id"/> + <separator string="Employees" /> + <field name="employee_ids" nolabel="1" colspan="4"/> + </group> + </form> + </field> + </record> + + <record id="action_salary_employee_month" model="ir.actions.act_window"> + <field name="name">Yearly Salary by Head</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">hr.salary.employee.month</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <menuitem + name="Yearly Salary by Head" + parent="hr.menu_hr_reporting_timesheet" + sequence="200" icon="STOCK_PRINT" + action="action_salary_employee_month" + id="menu_salary_employee_month" + /> + + </data> +</openerp> 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 new file mode 100644 index 00000000000..16bfd63bdc9 --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail.py @@ -0,0 +1,66 @@ +#-*- coding:utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2011 OpenERP SA (<http://openerp.com>). All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import time + +from osv import fields, osv + +class yearly_salary_detail(osv.osv_memory): + + _name ='yearly.salary.detail' + _description = 'Hr Salary Employee By Category Report' + _columns = { + 'employee_ids': fields.many2many('hr.employee', 'payroll_emp_rel', 'payroll_id', 'employee_id', 'Employees', required=True), + 'date_from': fields.date('Start Date', required=True), + 'date_to': fields.date('End Date', required=True), + } + + _defaults = { + 'date_from': lambda *a: time.strftime('%Y-01-01'), + 'date_to': lambda *a: time.strftime('%Y-%m-%d'), + + } + + def print_report(self, cr, uid, ids, context=None): + """ + To get the date and print the report + @param self: The object pointer. + @param cr: A database cursor + @param uid: ID of the user currently logged in + @param context: A standard dictionary + @return: return report + """ + if context is None: + context = {} + datas = {'ids': context.get('active_ids', [])} + + res = self.read(cr, uid, ids, context=context) + res = res and res[0] or {} + datas.update({'form': res}) + return { + 'type': 'ir.actions.report.xml', + 'report_name': 'salary.detail.byyear', + 'datas': datas, + } + +yearly_salary_detail() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail_view.xml b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail_view.xml new file mode 100644 index 00000000000..ec70e8b41e0 --- /dev/null +++ b/addons/l10n_in_hr_payroll/wizard/hr_yearly_salary_detail_view.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="view_yearly_salary_detail" model="ir.ui.view"> + <field name="name">Employee Yearly Salary</field> + <field name="model">yearly.salary.detail</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Pay Head Employee Breakup" version="7.0"> + <label string="This wizard will print report which display a pay head employee breakup for a specified dates."/> + <footer> + <button name="print_report" string="Print" type="object" class="oe_highlight"/> + or + <button string="Cancel" class="oe_link" special="cancel" /> + </footer> + <group> + <field name="date_from"/> + <field name="date_to"/> + <separator string="Employees" /> + <field name="employee_ids" nolabel="1" colspan="4"/> + </group> + </form> + </field> + </record> + + <record id="action_yearly_salary_detail" model="ir.actions.act_window"> + <field name="name">Yearly Salary by Employee</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">yearly.salary.detail</field> + <field name="view_type">form</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <menuitem + name="Yearly Salary by Employee" + parent="hr.menu_hr_reporting_timesheet" + action="action_yearly_salary_detail" + sequence="250" icon="STOCK_PRINT" + id="menu_yearly_salary_detail" + /> + +</data> +</openerp> diff --git a/addons/l10n_syscohada/l10n_syscohada_data.xml b/addons/l10n_syscohada/l10n_syscohada_data.xml index feeea3a2314..d90988e00e3 100644 --- a/addons/l10n_syscohada/l10n_syscohada_data.xml +++ b/addons/l10n_syscohada/l10n_syscohada_data.xml @@ -1816,15 +1816,15 @@ #
-->
- <record id="base.CFA" model="res.currency">
+ <record id="base.XOF" model="res.currency">
<field name="name">XOF</field>
<field name="symbol">CFA</field>
<field name="rounding">1</field>
<field name="accuracy">4</field>
</record>
- <record id="base.rateCFA" model="res.currency.rate">
+ <record id="base.rateXOF" model="res.currency.rate">
<field name="rate">655.957</field>
- <field name="currency_id" ref="base.CFA"/>
+ <field name="currency_id" ref="base.XOF"/>
<field eval="time.strftime('%Y-01-01')" name="name"/>
</record>
<!--
diff --git a/addons/l10n_th/account_data.xml b/addons/l10n_th/account_data.xml index 48e7b4f33d6..d5885d4b92b 100644 --- a/addons/l10n_th/account_data.xml +++ b/addons/l10n_th/account_data.xml @@ -26,7 +26,6 @@ <record id="a_root" model="account.account.template"> <field name="code">0</field> <field name="name">Simple chart of accounts</field> - <field name="parent_id" eval="False"/> <field name="type">view</field> <field name="user_type" ref="acc_type_view"/> </record> diff --git a/addons/l10n_th/i18n/nb.po b/addons/l10n_th/i18n/nb.po new file mode 100644 index 00000000000..8810993035f --- /dev/null +++ b/addons/l10n_th/i18n/nb.po @@ -0,0 +1,52 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2011-12-23 09:56+0000\n" +"PO-Revision-Date: 2012-09-09 16:45+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-10 04:41+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: l10n_th +#: model:ir.actions.todo,note:l10n_th.config_call_account_template_th +msgid "" +"Generate Chart of Accounts from a Chart Template. You will be asked to pass " +"the name of the company, the chart template to follow, the no. of digits to " +"generate the code for your accounts and Bank account, currency to create " +"Journals. Thus,the pure copy of chart Template is generated.\n" +"This is the same wizard that runs from Financial " +"Management/Configuration/Financial Accounting/Financial Accounts/Generate " +"Chart of Accounts from a Chart Template." +msgstr "" +"Generere Kontoplan fra en diagrammal. Du vil bli bedt om å passere navnet på " +"selskapet, diagrammal til følge, nei. Sifre som skal generere koden for " +"kontoene og bankkonto, valuta å skape Journals. Dermed er den rene kopi av " +"diagrammal generert.\n" +"Dette er den samme veiviser som går fra økonomistyring / Configuration / " +"Financial Accounting / finansregnskapet / Generer Kontoplan fra en " +"diagrammal." + +#. module: l10n_th +#: model:account.account.type,name:l10n_th.acc_type_other +msgid "Other" +msgstr "Annet" + +#. module: l10n_th +#: model:account.account.type,name:l10n_th.acc_type_reconciled +msgid "Reconciled" +msgstr "Forsonet" + +#. module: l10n_th +#: model:account.account.type,name:l10n_th.acc_type_view +msgid "View" +msgstr "Vis" diff --git a/addons/l10n_uy/account_chart_template.xml b/addons/l10n_uy/account_chart_template.xml index 1548d3ee25a..8308263d8b0 100644 --- a/addons/l10n_uy/account_chart_template.xml +++ b/addons/l10n_uy/account_chart_template.xml @@ -4,7 +4,6 @@ <record id="uy_code_0" model="account.account.template"> <field name="code">0</field> <field name="type">view</field> - <field name="parent_id" eval="False"/> <field name="name">Uruguay - Plan de Cuentas</field> <field name="user_type" ref="type_view"/> </record> diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 289b9080fe7..da517ee17ad 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -20,11 +20,11 @@ ############################################################################## import mail_alias -import mail_message import mail_followers +import mail_message +import mail_mail import mail_thread import mail_group -import ir_needaction import res_partner import res_users import report diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index daec8d03183..99dd6bda6ac 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -64,6 +64,7 @@ The main features of the module are: 'wizard/mail_compose_message_view.xml', 'res_config_view.xml', 'mail_message_view.xml', + 'mail_mail_view.xml', 'mail_followers_view.xml', 'mail_thread_view.xml', 'mail_group_view.xml', diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml index e3d7c2ab1e1..1350fd33547 100644 --- a/addons/mail/data/mail_demo.xml +++ b/addons/mail/data/mail_demo.xml @@ -3,49 +3,43 @@ <data noupdate="1"> <record id="message_blogpost0" model="mail.message"> - <field name="subject">Internal company announce</field> <field name="model">mail.group</field> - <field name="res_id" ref="group_all_employees"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Your monthly meal vouchers arrived. You can get them at Christine's office. -This month you also have 250 EUR of eco-checks for all employees that worked with us since 1 year minimum.]]></field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="body">Your monthly meal vouchers arrived. You can get them at Christine's office. +This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> </record> <record id="message_blogpost0_comment0" model="mail.message"> <field name="model">mail.group</field> <field name="res_id" ref="group_all_employees"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Great.]]></field> + <field name="body"><![CDATA[Great.]]></field> <field name="parent_id" ref="message_blogpost0"/> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> </record> <record id="message_blogpost0_comment1" model="mail.message"> <field name="model">mail.group</field> <field name="res_id" ref="group_all_employees"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Yes, that's a good news.]]></field> + <field name="body">Thanks, but where is Christine's office, if I may ask? (I'm new here)</field> <field name="parent_id" ref="message_blogpost0"/> <field name="type">comment</field> - <field name="user_id" ref="base.user_demo"/> </record> <record id="message_blogpost0_comment2" model="mail.message"> <field name="model">mail.group</field> <field name="res_id" ref="group_all_employees"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Sure: Curabitur tempor bibendum diam, et euismod ante rutrum vel. - -In quis purus neque. Integer sodales dolor eu elit fringilla blandit. Maecenas lacus nisi, facilisis sit amet viverra eu, tristique vel augue. -Donec viverra congue dui eu blandit. In lacinia molestie nulla, ut sagittis risus feugiat eu. - -Check the file in attachment for more information ! (third comment)]]></field> + <field name="body">Great news, I need to buy a new fridge, I think I can pay it with the eco-vouchers!</field> <field name="parent_id" ref="message_blogpost0"/> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + </record> + + <record id="message_blogpost0_comment1_2" model="mail.message"> + <field name="model">mail.group</field> + <field name="res_id" ref="group_all_employees"/> + <field name="body">Building B3, second floor on the right :-)</field> + <field name="parent_id" ref="message_blogpost0_comment1"/> + <field name="type">comment</field> </record> <record model="ir.config_parameter" id="user_mail_alias"> diff --git a/addons/mail/data/mail_group_data.xml b/addons/mail/data/mail_group_data.xml index 2152706d056..53720926a5c 100644 --- a/addons/mail/data/mail_group_data.xml +++ b/addons/mail/data/mail_group_data.xml @@ -13,16 +13,16 @@ </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Welcome to OpenERP!</value> - <value>Your homepage is a summary of messages you received and key information about documents you follow. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Welcome to OpenERP!</field> + <field name="body">Your homepage is a summary of messages you received and key information about documents you follow. The top menu bar contains all applications you installed. You can use this <i>Settings</i> menu to install more applications, activate others features or give access to new users. -To setup your preferences (name, email signature, avatar), click on the top right corner.</value> - </function> - +To setup your preferences (name, email signature, avatar), click on the top right corner.</field> + </record> </data> </openerp> diff --git a/addons/mail/doc/mail_openchatter_howto.rst b/addons/mail/doc/mail_openchatter_howto.rst index 789acdd87eb..e4c86add7bc 100644 --- a/addons/mail/doc/mail_openchatter_howto.rst +++ b/addons/mail/doc/mail_openchatter_howto.rst @@ -57,7 +57,7 @@ Use the thread viewer widget inside your form view by using the mail_thread widg Send notifications +++++++++++++++++++ -When sending a notification is required in your workflow or business logic, use the ``message_append_note`` method. This method is a shortcut to the ``message_append`` method that takes all ``mail.message`` fields as arguments. This latter method calls ``message_create`` that +When sending a notification is required in your workflow or business logic, use the ``message_post`` method. This method is a shortcut to the ``message_append`` method that takes all ``mail.message`` fields as arguments. This latter method calls ``message_create`` that - creates the message - parses the body to find users you want to push the message to (finding and parsing ``@login`` in the message body) @@ -74,7 +74,7 @@ You should therefore not worry about subscriptions or anything else than sending return res def do_something_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, _('My subject'), + self.message_post(cr, uid, ids, _('My subject'), _("has received a <b>notification</b> and is happy for it."), context=context) Notifications guidelines @@ -147,7 +147,7 @@ The best way to direct the messages that will be displayed in the OpenChatter wi # add: search in the current task project messages '&', '&', ('res_id', '=', my_task.project_id.id), ('model', '=', 'project.project'), # ... containing the task name - '|', ('body_text', 'like', '%s' % (my_task.name)), ('body_html', 'like', '%s' % (my_task.name)) + '|', ('body', 'like', '%s' % (my_task.name)), ('body_html', 'like', '%s' % (my_task.name)) ] + domain, limit=limit, offset=offset, context=context) # if asked: add ancestor ids to have complete threads if (ascent): msg_ids = self._message_add_ancestor_ids(cr, uid, ids, msg_ids, root_ids, context=context) diff --git a/addons/mail/doc/mail_state.rst b/addons/mail/doc/mail_state.rst index 527a31ba835..fc2772bf094 100644 --- a/addons/mail/doc/mail_state.rst +++ b/addons/mail/doc/mail_state.rst @@ -1,15 +1,15 @@ .. _mail_state: -message_state +message_unread ============= -``message_state`` is a boolean field that states whether the document +``message_unread`` is a boolean field that states whether the document has unread messages. In previous versions, some documents were going back to ``pending`` state when receiving an email through the mail gateway. Now the state related to messages differs from the state or stage of the document itself. -message_state and need action mechanism +message_unread and need action mechanism +++++++++++++++++++++++++++++++++++++++ The ``mail`` module introduces a default behavior for the need_action @@ -23,6 +23,6 @@ mechanism [REF]. """ result = super(ir_needaction_mixin, self).get_needaction_user_ids(cr, uid, ids, context=context) for obj in self.browse(cr, uid, ids, context=context): - if obj.message_state == False and obj.user_id: + if obj.message_unread == False and obj.user_id: result[obj.id].append(obj.user_id.id) return result diff --git a/addons/mail/i18n/nl.po b/addons/mail/i18n/nl.po index a5e2fee8155..0fef9761c2f 100644 --- a/addons/mail/i18n/nl.po +++ b/addons/mail/i18n/nl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-09 00:36+0000\n" -"PO-Revision-Date: 2012-05-10 17:39+0000\n" -"Last-Translator: Raphael Collet (OpenERP) <Unknown>\n" +"PO-Revision-Date: 2012-09-03 17:14+0000\n" +"Last-Translator: Erwin <Unknown>\n" "Language-Team: Dutch <nl@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: mail #: field:mail.compose.message,subtype:0 field:mail.message,subtype:0 @@ -196,7 +196,7 @@ msgstr "Ontvangers" #. module: mail #: model:ir.model,name:mail.model_mail_compose_message msgid "Email composition wizard" -msgstr "" +msgstr "E-mail samenstellen wizard" #. module: mail #: field:mail.compose.message,res_id:0 field:mail.message,res_id:0 diff --git a/addons/mail/ir_needaction.py b/addons/mail/ir_needaction.py deleted file mode 100644 index 8193ac4f385..00000000000 --- a/addons/mail/ir_needaction.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2012-today OpenERP SA (<http://www.openerp.com>) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -############################################################################## - -from osv import osv - -class ir_needaction_mixin(osv.Model): - """ Update of ir.needaction_mixin class - - override the get_needaction_user_ids method to define the default - mail gateway need_action: when the object is unread, the object - responsible has an action to perform. - """ - _name = 'ir.needaction_mixin' - _inherit = ['ir.needaction_mixin'] - - def get_needaction_user_ids(self, cr, uid, ids, context=None): - """ Returns the user_ids that have to perform an action. It the - document mail state is unread (False), return object.user_id.id - as need_action uid. - :return: dict { record_id: [user_ids], } - """ - result = super(ir_needaction_mixin, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if obj.message_state == False and obj.user_id: - result[obj.id].append(obj.user_id.id) - return result - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/mail_alias.py b/addons/mail/mail_alias.py index 5bcce0b8927..27eb29051b9 100644 --- a/addons/mail/mail_alias.py +++ b/addons/mail/mail_alias.py @@ -92,9 +92,8 @@ class mail_alias(osv.Model): _defaults = { 'alias_defaults': '{}', 'alias_user_id': lambda self,cr,uid,context: uid, - # looks better when creating new aliases - even if the field is informative only - 'alias_domain': lambda self,cr,uid,context: self._get_alias_domain(cr,1,[1],None,None)[1] + 'alias_domain': lambda self,cr,uid,context: self._get_alias_domain(cr, SUPERUSER_ID,[1],None,None)[1] } _sql_constraints = [ diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index fcdc78d5f0a..2aba685c4fa 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -21,43 +21,113 @@ from osv import osv from osv import fields +import tools class mail_followers(osv.Model): """ mail_followers holds the data related to the follow mechanism inside - OpenERP. Users can choose to follow documents (records) of any kind that - inherits from mail.thread. Following documents allow to receive - notifications for new messages. - A subscription is characterized by: - :param: res_model: model of the followed objects - :param: res_id: ID of resource (may be 0 for every objects) - :param: user_id: user_id of the follower + OpenERP. Partners can choose to follow documents (records) of any kind + that inherits from mail.thread. Following documents allow to receive + notifications for new messages. + A subscription is characterized by: + :param: res_model: model of the followed objects + :param: res_id: ID of resource (may be 0 for every objects) """ _name = 'mail.followers' - _rec_name = 'id' + _rec_name = 'partner_id' _log_access = False - _order = 'res_model asc' - _description = 'Mail Document Followers' + _description = 'Document Followers' _columns = { 'res_model': fields.char('Related Document Model', size=128, required=True, select=1, help='Model of the followed resource'), 'res_id': fields.integer('Related Document ID', select=1, help='Id of the followed resource'), - 'user_id': fields.many2one('res.users', string='Related User', + 'partner_id': fields.many2one('res.partner', string='Related Partner', ondelete='cascade', required=True, select=1), } + class mail_notification(osv.Model): - """ mail_notification is a relational table modeling messages pushed to users. - """ + """ Class holding notifications pushed to partners. Followers and partners + added in 'contacts to notify' receive notifications. """ _name = 'mail.notification' - _rec_name = 'id' + _rec_name = 'partner_id' _log_access = False - _order = 'message_id desc' - _description = 'Mail notification' + _description = 'Notifications' + _columns = { - 'user_id': fields.many2one('res.users', string='User', - ondelete='cascade', required=True, select=1), + 'partner_id': fields.many2one('res.partner', string='Contact', + ondelete='cascade', required=True), + 'read': fields.boolean('Read'), 'message_id': fields.many2one('mail.message', string='Message', - ondelete='cascade', required=True, select=1), + ondelete='cascade', required=True), + } + + _defaults = { + 'read': False, } + + def init(self, cr): + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_message_id',)) + if not cr.fetchone(): + cr.execute('CREATE INDEX mail_notification_partner_id_read_message_id ON mail_notification (partner_id, read, message_id)') + + def create(self, cr, uid, vals, context=None): + """ Override of create to check that we can not create a notification + for a message the user can not read. """ + if self.pool.get('mail.message').check_access_rights(cr, uid, 'read'): + return super(mail_notification, self).create(cr, uid, vals, context=context) + return False + + def set_message_read(self, cr, uid, msg_id, context=None): + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + notif_ids = self.search(cr, uid, [('partner_id', '=', partner_id), ('message_id', '=', msg_id)], context=context) + return self.write(cr, uid, notif_ids, {'read': True}, context=context) + + def notify(self, cr, uid, partner_ids, msg_id, context=None): + """ Send by email the notification depending on the user preferences """ + context = context or {} + # mail_noemail (do not send email) or no partner_ids: do not send, return + if context.get('mail_noemail') or not partner_ids: + return True + + mail_mail = self.pool.get('mail.mail') + msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context) + + # add signature + body_html = msg.body + signature = msg.author_id and msg.author_id.user_ids[0].signature or '' + if signature: + body_html = tools.append_content_to_html(body_html, signature) + + mail_values = { + 'mail_message_id': msg.id, + 'email_to': [], + 'auto_delete': True, + 'body_html': body_html, + 'state': 'outgoing', + } + + for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): + # Do not send an email to the writer + if partner.user_ids and partner.user_ids[0].id == uid: + continue + # Do not send to partners without email address defined + if not partner.email: + continue + # Partner does not want to receive any emails + if partner.notification_email_send == 'none': + continue + # Partner wants to receive only emails and comments + if partner.notification_email_send == 'comment' and msg.type not in ('email', 'comment'): + continue + # Partner wants to receive only emails + if partner.notification_email_send == 'email' and msg.type != 'email': + continue + if partner.email not in mail_values['email_to']: + mail_values['email_to'].append(partner.email) + if mail_values['email_to']: + mail_values['email_to'] = ', '.join(mail_values['email_to']) + email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) + mail_mail.send(cr, uid, [email_notif_id], context=context) + return True diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml index 62e93430675..36f08b3814a 100644 --- a/addons/mail/mail_followers_view.xml +++ b/addons/mail/mail_followers_view.xml @@ -6,13 +6,12 @@ <record model="ir.ui.view" id="view_followers_tree"> <field name="name">mail.followers.tree</field> <field name="model">mail.followers</field> - <field name="type">tree</field> <field name="priority">10</field> <field name="arch" type="xml"> <tree string="Followers"> <field name="res_model"/> <field name="res_id"/> - <field name="user_id"/> + <field name="partner_id"/> </tree> </field> </record> @@ -24,7 +23,7 @@ <field name="priority">10</field> <field name="arch" type="xml"> <tree string="Notifications"> - <field name="user_id"/> + <field name="partner_id"/> <field name="message_id"/> </tree> </field> @@ -38,7 +37,7 @@ </record> <record id="action_view_notifications" model="ir.actions.act_window"> - <field name="name">Pushed notif</field> + <field name="name">Notifications</field> <field name="res_model">mail.notification</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 6a14fef83aa..175ce650f06 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -28,13 +28,8 @@ from osv import fields from tools.translate import _ class mail_group(osv.Model): - """ - A mail_group is a collection of users sharing messages in a discussion - group. Group users are users that follow the mail group, using the - subscription/follow mechanism of OpenSocial. A mail group has nothing - in common with res.users.group. - """ - + """ A mail_group is a collection of users sharing messages in a discussion + group. The group mechanics are based on the followers. """ _description = 'Discussion group' _name = 'mail.group' _inherit = ['mail.thread'] @@ -45,68 +40,54 @@ class mail_group(osv.Model): 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 _get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None): - result = {} - for id in ids: - lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) - result[id] = self.message_search(cr, uid, [id], limit=None, domain=[('date', '>=', lower_date)], count=True, context=context) - return result - - def _get_default_image(self, cr, uid, context=None): - image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') - return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) - + _columns = { 'description': fields.text('Description'), 'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"), - 'responsible_id': fields.many2one('res.users', string='Responsible', - ondelete='set null', required=True, select=1, - help="Responsible of the group that has all rights on the record."), - 'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], - string='Privacy', required=True, - help='This group is visible by non members. '\ - 'Invisible groups can add members through the invite button.'), + 'public': fields.selection([('public','Public'),('private','Private'),('groups','Selected Group Only')], 'Privacy', required=True, + help='This group is visible by non members. \ + Invisible groups can add members through the invite button.'), 'group_public_id': fields.many2one('res.groups', string='Authorized Group'), 'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel', id1='mail_group_id', id2='groups_id', string='Auto Subscription', help="Members of those groups will automatically added as followers. "\ "Note that they will be able to manage their subscription manually "\ "if necessary."), + # 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 "\ - "user. The image is base64 encoded, and PIL-supported. "\ - "It is limited to a 12024x1024 px image."), + 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 = { + store={ 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Medium-sized photo of the group. It is automatically "\ - "resized as a 180x180px image, with aspect ratio preserved. "\ + "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 = { + store={ 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Small-sized photo of the group. It is automatically "\ - "resized as a 50x50px image, with aspect ratio preserved. "\ + "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), - 'last_month_msg_nbr': fields.function(_get_last_month_msg_nbr, type='integer', - string='Messages count for last month'), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", - help="The email address associated with this group. New emails received will automatically " - "create new topics."), + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + help="The email address associated with this group. New emails received will automatically " + "create new topics."), } def _get_default_employee_group(self, cr, uid, context=None): ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user') return ref and ref[1] or False + def _get_default_image(self, cr, uid, context=None): + image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png') + return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64')) + def _get_menu_parent(self, cr, uid, context=None): ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root') return ref and ref[1] or False @@ -114,29 +95,23 @@ class mail_group(osv.Model): _defaults = { 'public': 'groups', 'group_public_id': _get_default_employee_group, - 'responsible_id': (lambda s, cr, uid, ctx: uid), 'image': _get_default_image, 'parent_id': _get_menu_parent, - 'alias_domain': False, # always hide alias during creation + 'alias_domain': False, # always hide alias during creation } - def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None): - # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} - user_group_ids = [command[1] for command in group_ids_command if command[0] == 4] - user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]] - # retrieve the user member of those groups - user_ids = [] - res_groups_obj = self.pool.get('res.groups') - for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context): - user_ids += [user.id for user in group.users] - # subscribe the users - return self.message_subscribe(cr, uid, ids, user_ids, context=context) + def _subscribe_users(self, cr, uid, ids, context=None): + for mail_group in self.browse(cr, uid, ids, context=context): + partner_ids = [] + for group in mail_group.group_ids: + partner_ids += [user.partner_id.id for user in group.users] + self.message_subscribe(cr, uid, ids, partner_ids, context=context) def create(self, cr, uid, vals, context=None): mail_alias = self.pool.get('mail.alias') if not vals.get('alias_id'): vals.pop('alias_name', None) # prevent errors during copy() - alias_id = mail_alias.create_unique_alias(cr, uid, + alias_id = mail_alias.create_unique_alias(cr, uid, # Using '+' allows using subaddressing for those who don't # have a catchall domain setup. {'alias_name': "group+"+vals['name']}, @@ -148,23 +123,22 @@ class mail_group(osv.Model): # Create client action for this group and link the menu to it ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds') if ref: - search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search_wall') + search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search') params = { 'search_view_id': search_ref and search_ref[1] or False, - 'domain': [('model','=','mail.group'),('res_id','=',mail_group_id)], - 'res_model': 'mail.group', - 'res_id': mail_group_id, - 'thread_level': 2 + 'domain': [('model','=','mail.group'), ('res_id','=',mail_group_id)], + 'context': {'default_model': 'mail.group', 'default_res_id': mail_group_id}, + 'res_model': 'mail.message', + 'thread_level': 1, } cobj = self.pool.get('ir.actions.client') newref = cobj.copy(cr, uid, ref[1], default={'params': str(params), 'name': vals['name']}, context=context) self.write(cr, uid, [mail_group_id], {'action': 'ir.actions.client,'+str(newref), 'mail_group_id': mail_group_id}, context=context) mail_alias.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context) - - if vals.get('group_ids'): - self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context) + if vals.get('group_ids'): + self._subscribe_users(cr, uid, [mail_group_id], context=context) return mail_group_id def unlink(self, cr, uid, ids, context=None): @@ -176,21 +150,17 @@ class mail_group(osv.Model): return res def write(self, cr, uid, ids, vals, context=None): + result = super(mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('group_ids'): - self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context) - return super(mail_group, self).write(cr, uid, ids, vals, context=context) - - def action_group_join(self, cr, uid, ids, context=None): - return self.message_subscribe(cr, uid, ids, context=context) - - def action_group_leave(self, cr, uid, ids, context=None): - return self.message_unsubscribe(cr, uid, ids, context=context) + self._subscribe_users(cr, uid, ids, vals.get('group_ids'), context=context) + return result - # ---------------------------------------- - # OpenChatter methods and notifications - # ---------------------------------------- + def action_follow(self, cr, uid, ids, context=None): + """ Wrapper because message_subscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_subscribe_users(cr, uid, ids, context=context) - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'responsible_id' to the monitored fields """ - res = super(mail_group, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['responsible_id'] + def action_unfollow(self, cr, uid, ids, context=None): + """ Wrapper because message_unsubscribe_users take a user_ids=None + that receive the context without the wrapper. """ + return self.message_unsubscribe_users(cr, uid, ids, context=context) diff --git a/addons/mail/mail_group_menu.py b/addons/mail/mail_group_menu.py index 8625285d640..25b85bb7b22 100644 --- a/addons/mail/mail_group_menu.py +++ b/addons/mail/mail_group_menu.py @@ -40,11 +40,12 @@ class ir_ui_menu(osv.osv): """ Override to take off menu entries (mail.group) the user is not following. """ ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0, limit=None, order=order, context=context, count=False) + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id follower_obj = self.pool.get('mail.followers') for menu in self.browse(cr, uid, ids, context=context): if menu.mail_group_id: sub_ids = follower_obj.search(cr, uid, [ - ('user_id', '=', uid), ('res_model', '=', 'mail.group'), + ('partner_id', '=', partner_id), ('res_model', '=', 'mail.group'), ('res_id', '=', menu.mail_group_id.id) ], context=context) if not sub_ids: diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml index d21d32a3893..31beba80ecc 100644 --- a/addons/mail/mail_group_view.xml +++ b/addons/mail/mail_group_view.xml @@ -1,10 +1,11 @@ <?xml version="1.0"?> <openerp> <data> + <!-- this record will be dynamically adapted to each new menu created --> <record id="action_mail_group_feeds" model="ir.actions.client"> <field name="name">Discussion Group</field> <field name="tag">mail.wall</field> - <field name="params" eval="{'search_view_id': ref('view_message_search_wall')}"/> + <field name="res_model">mail.message</field> </record> <!-- Group Kanban View !--> @@ -14,8 +15,9 @@ <field name="priority" eval="10"/> <field name="arch" type="xml"> <kanban> - <field name="message_follower_ids"/> <field name="message_is_follower"/> + <field name="message_follower_ids"/> + <field name="message_summary"/> <templates> <t t-name="kanban-description"> <div class="oe_group_description" t-if="record.description.raw_value"> @@ -23,20 +25,16 @@ </div> </t> <t t-name="kanban-box"> - <div t-attf-class="{record.message_is_follower.raw_value} oe_group_vignette"> + <div t-attf-class="{record.message_is_follower.raw_value} oe_group_vignette oe_semantic_html_override"> <div class="oe_group_image"> <a type="open"><img t-att-src="kanban_image('mail.group', 'image_medium', record.id.value)" class="oe_group_photo" tooltip="kanban-description"/></a> </div> <div class="oe_group_details"> <h4><a type="open"><field name="name"/></a></h4> <ul> - <!-- <li><field name="message_follower_count"/> member(s)</li> --> - <li> - <t t-raw="record.message_follower_ids.raw_value.length"/> member(s) - </li> - <li t-if="! record.message_is_follower.raw_value"><a name="action_group_join" string="Join" type="object" class="oe_group_join">Not following</a></li> - <li t-if="record.message_is_follower.raw_value"><a name="action_group_leave" string="Join" type="object" class="oe_group_leave">Following</a></li> - <li><field name="last_month_msg_nbr"/> messages last month</li> + <li><t t-raw="record.message_summary.raw_value"/></li> + <li t-if="! record.message_is_follower.raw_value"><a name="action_follow" string="Join" type="object" class="oe_group_join">Not following</a></li> + <li t-if="record.message_is_follower.raw_value"><a name="action_unfollow" string="Leave" type="object" class="oe_group_leave">Following</a></li> </ul> </div> </div> @@ -58,11 +56,11 @@ <field name="arch" type="xml"> <form string="Group Form" version="7.0"> <sheet class="openerp_mail_group_sheet"> - <field name="image_small" widget='image' class="oe_avatar oe_left"/> + <field name="image" widget='image' class="oe_avatar oe_left" options='{"preview_image": "image_medium"}'/> <div class="oe_title"> <div class="oe_edit_only"> - <label for="name" string="Group Name"/> - </div> + <label for="name" string="Group Name"/> + </div> <h1><field name="name"/></h1> <div name="alias_box" colspan="4" attrs="{'invisible': [('alias_domain', '=', False)]}"> <field name="alias_id" invisible="1" required="0"/> @@ -80,16 +78,12 @@ /> <field name="group_ids" widget="many2many_tags" class="oe_inline"/> </group> - <group class="oe_edit_only"> - <field name="responsible_id" class="oe_inline"/> - </group> </group> </sheet> <div class="oe_chatter"> <field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/> - <field name="message_follower_ids" widget="mail_followers" - context="{'lapin': 'nouille'}" image="image_small"/> + <field name="message_follower_ids" widget="mail_followers"/> </div> </form> </field> @@ -103,7 +97,6 @@ <field name="arch" type="xml"> <tree string="Groups"> <field name="name" string="Group Name"/> - <field name="responsible_id"/> </tree> </field> </record> @@ -116,14 +109,13 @@ <field name="arch" type="xml"> <search string="Search Groups"> <field name="name" string="Group"/> - <field name="responsible_id"/> </search> </field> </record> <!-- group record !--> <record id="action_view_groups" model="ir.actions.act_window"> - <field name="name">Groups</field> + <field name="name">Join a group</field> <field name="res_model">mail.group</field> <field name="view_type">form</field> <field name="view_mode">kanban,tree,form</field> @@ -132,6 +124,6 @@ <!-- left-side menu: Groups !--> <menuitem id="mail_group_root" parent="mail_feeds_main" sequence="12" name="My Groups"/> - <menuitem id="mail_allgroups" parent="mail_feeds" sequence="12" action="action_view_groups"/> + <menuitem id="mail_allgroups" parent="mail_group_root" sequence="100" action="action_view_groups"/> </data> </openerp> diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py new file mode 100644 index 00000000000..7b593c5b998 --- /dev/null +++ b/addons/mail/mail_mail.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +# import ast +import base64 +import logging +import tools + +from osv import osv +from osv import fields +from tools.translate import _ + +_logger = logging.getLogger(__name__) + +class mail_mail(osv.Model): + """ Model holding RFC2822 email messages to send. This model also provides + facilities to queue and send new email messages. """ + _name = 'mail.mail' + _description = 'Outgoing Mails' + _inherits = {'mail.message': 'mail_message_id'} + _order = 'id desc' + + _columns = { + 'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'), + 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), + 'state': fields.selection([ + ('outgoing', 'Outgoing'), + ('sent', 'Sent'), + ('received', 'Received'), + ('exception', 'Delivery Failed'), + ('cancel', 'Cancelled'), + ], 'Status', readonly=True), + 'auto_delete': fields.boolean('Auto Delete', + help="Permanently delete this email after sending it, to save space"), + 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), + 'email_from': fields.char('From', help='Message sender, taken from user preferences.'), + 'email_to': fields.text('To', help='Message recipients'), + 'email_cc': fields.char('Cc', help='Carbon copy message recipients'), + 'reply_to': fields.char('Reply-To', help='Preferred response address for the message'), + 'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"), + + # Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification + # and during unlink() we will cascade delete the parent and its attachments + 'notification': fields.boolean('Is Notification') + } + + def _get_default_from(self, cr, uid, context=None): + cur = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if not cur.alias_domain: + raise osv.except_osv(_('Invalid Action!'), _('Unable to send email, set an alias domain in your server settings.')) + return cur.alias_name + '@' + cur.alias_domain + + _defaults = { + 'state': 'outgoing', + 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), + } + + def create(self, cr, uid, values, context=None): + if 'notification' not in values and values.get('mail_message_id'): + values['notification'] = True + return super(mail_mail,self).create(cr, uid, values, context=context) + + def unlink(self, cr, uid, ids, context=None): + # cascade-delete the parent message for all mails that are not created for a notification + ids_to_cascade = self.search(cr, uid, [('notification','=',False),('id','in',ids)]) + parent_msg_ids = [m.mail_message_id.id for m in self.browse(cr, uid, ids_to_cascade, context=context)] + res = super(mail_mail,self).unlink(cr, uid, ids, context=context) + self.pool.get('mail.message').unlink(cr, uid, parent_msg_ids, context=context) + return res + + def mark_outgoing(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'outgoing'}, context=context) + + def cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state':'cancel'}, context=context) + + def process_email_queue(self, cr, uid, ids=None, context=None): + """Send immediately queued messages, committing after each + message is sent - this is not transactional and should + not be called during another transaction! + + :param list ids: optional list of emails ids to send. If passed + no search is performed, and these ids are used + instead. + :param dict context: if a 'filters' key is present in context, + this value will be used as an additional + filter to further restrict the outgoing + messages to send (by default all 'outgoing' + messages are sent). + """ + if context is None: + context = {} + if not ids: + filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] + if 'filters' in context: + filters.extend(context['filters']) + ids = self.search(cr, uid, filters, context=context) + res = None + try: + # Force auto-commit - this is meant to be called by + # the scheduler, and we can't allow rolling back the status + # of previously sent emails! + res = self.send(cr, uid, ids, auto_commit=True, context=context) + except Exception: + _logger.exception("Failed processing mail queue") + return res + + def _postprocess_sent_message(self, cr, uid, mail, context=None): + """Perform any post-processing necessary after sending ``mail`` + successfully, including deleting it completely along with its + attachment if the ``auto_delete`` flag of the mail was set. + Overridden by subclasses for extra post-processing behaviors. + + :param browse_record mail: the mail that was just sent + :return: True + """ + if mail.auto_delete: + mail.unlink() + return True + + def _send_get_mail_subject(self, cr, uid, mail, force=False, context=None): + """ if void and related document: '<Author> posted on <Resource>' + :param mail: mail.mail browse_record """ + if force or (not mail.subject and mail.model and mail.res_id): + return '%s posted on %s' % (mail.author_id.name, mail.record_name) + return mail.subject + + def send(self, cr, uid, ids, auto_commit=False, context=None): + """ Sends the selected emails immediately, ignoring their current + state (mails that have already been sent should not be passed + unless they should actually be re-sent). + Emails successfully delivered are marked as 'sent', and those + that fail to be deliver are marked as 'exception', and the + corresponding error mail is output in the server logs. + + :param bool auto_commit: whether to force a commit of the mail status + after sending each mail (meant only for scheduler processing); + should never be True during normal transactions (default: False) + :return: True + """ + ir_mail_server = self.pool.get('ir.mail_server') + for mail in self.browse(cr, uid, ids, context=context): + try: + body = mail.body_html + subject = self._send_get_mail_subject(cr, uid, mail, context=context) + + # handle attachments + attachments = [] + for attach in mail.attachment_ids: + attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + + # use only sanitized html and set its plaintexted version as alternative + body_alternative = tools.html2plaintext(body) + content_subtype_alternative = 'plain' + + # build an RFC2822 email.message.Message object and send it without queuing + msg = ir_mail_server.build_email( + email_from = mail.email_from, + email_to = tools.email_split(mail.email_to), + subject = subject, + body = body, + body_alternative = body_alternative, + email_cc = tools.email_split(mail.email_cc), + reply_to = mail.reply_to, + attachments = attachments, + message_id = mail.message_id, + references = mail.references, + object_id = mail.res_id and ('%s-%s' % (mail.res_id, mail.model)), + subtype = 'html', + subtype_alternative = content_subtype_alternative) + res = ir_mail_server.send_email(cr, uid, msg, + mail_server_id=mail.mail_server_id.id, context=context) + if res: + mail.write({'state':'sent', 'message_id': res}) + else: + mail.write({'state':'exception'}) + mail.refresh() + if mail.state == 'sent': + self._postprocess_sent_message(cr, uid, mail, context=context) + except Exception: + _logger.exception('failed sending mail.mail %s', mail.id) + mail.write({'state':'exception'}) + + if auto_commit == True: + cr.commit() + return True diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml new file mode 100644 index 00000000000..72d41e33a84 --- /dev/null +++ b/addons/mail/mail_mail_view.xml @@ -0,0 +1,127 @@ +<?xml version="1.0"?> +<openerp> + <data> + <record model="ir.ui.view" id="view_mail_form"> + <field name="name">mail.mail.form</field> + <field name="model">mail.mail</field> + <field name="arch" type="xml"> + <form string="Email message" version="7.0"> + <sheet> + <label for="subject" class="oe_edit_only"/> + <h2><field name="subject"/></h2> + <div> + by <field name="author_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/> + <button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied" + context="{'default_composition_mode':'reply', 'default_parent_id': active_id}" states='received,sent,exception,cancel'/> + </div> + <notebook colspan="4"> + <page string="Message Details"> + <group> + <group> + <field name="email_from"/> + <field name="email_to"/> + <field name="email_cc"/> + <field name="reply_to"/> + </group> + <group> + <field name="partner_ids" widget="many2many_tags"/> + </group> + </group> + <notebook> + <page string="Body"> + <field name="body_html"/> + </page> + </notebook> + </page> + <page string="Advanced" groups="base.group_no_one"> + <group> + <group> + <field name="auto_delete"/> + <field name="type"/> + <field name="state" colspan="2"/> + <field name="mail_server_id"/> + <field name="model"/> + <field name="res_id"/> + </group> + <group> + <field name="message_id"/> + <field name="references"/> + </group> + </group> + </page> + <page string="Attachments"> + <field name="attachment_ids"/> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + + <record model="ir.ui.view" id="view_mail_tree"> + <field name="name">mail.mail.tree</field> + <field name="model">mail.mail</field> + <field name="arch" type="xml"> + <tree string="Emails" colors="grey:state in ('sent', 'cancel');blue:state=='outgoing';red:state=='exception';black:state=='received'"> + <field name="date"/> + <field name="subject"/> + <field name="author_id" string="User"/> + <field name="message_id" invisible="1"/> + <field name="partner_ids" invisible="1"/> + <field name="model" invisible="1"/> + <field name="res_id" invisible="1"/> + <field name="email_from" invisible="1"/> + <field name="state" invisible="1"/> + <field name="type" invisible="1"/> + <button name="send" string="Send Now" type="object" icon="gtk-media-play" states='outgoing'/> + <button name="mark_outgoing" string="Retry" type="object" icon="gtk-redo" states='exception,cancel'/> + <button name="cancel" string="Cancel" type="object" icon="terp-gtk-stop" states='outgoing'/> + </tree> + </field> + </record> + + <record model="ir.ui.view" id="view_mail_search"> + <field name="name">mail.mail.search</field> + <field name="model">mail.mail</field> + <field name="arch" type="xml"> + <search string="Email Search"> + <field name="email_from" filter_domain="['|' '|',('email_from','ilike',self), ('email_to','ilike',self), ('subject','ilike',self)]" string="Email"/> + <field name="date"/> + <filter icon="terp-camera_test" name="received" string="Received" domain="[('state','=','received')]"/> + <filter icon="terp-call-start" name="outgoing" string="Outgoing" domain="[('state','=','outgoing')]"/> + <filter icon="terp-check" name="sent" string="Sent" domain="[('state','=','sent')]"/> + <filter icon="terp-gtk-stop" name="exception" string="Failed" domain="[('state','=','exception')]"/> + <separator/> + <filter icon="terp-camera_test" name="type_email" string="Email" domain="[('type','=','email')]"/> + <filter icon="terp-camera_test" name="type_comment" string="Comment" domain="[('type','=','comment')]"/> + <filter icon="terp-camera_test" name="type_notification" string="Notification" domain="[('type','=','notification')]"/> + <group expand="0" string="Extended Filters..."> + <field name="author_id"/> + <field name="partner_ids"/> + <field name="model"/> + <field name="res_id"/> + </group> + <group expand="0" string="Group By..."> + <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> + <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> + <filter string="User" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/> + <filter string="Thread" icon="terp-mail-" domain="[]" context="{'group_by':'message_id'}"/> + <filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/> + </group> + </search> + </field> + </record> + + <record id="action_view_mail_mail" model="ir.actions.act_window"> + <field name="name">Emails</field> + <field name="res_model">mail.mail</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="context">{'search_default_outgoing': 1, 'search_default_type_email': 1}</field> + <field name="search_view_id" ref="view_mail_search"/> + </record> + + <!-- Add menu entry in Settings/Email --> + <menuitem name="Emails" id="menu_mail_mail" parent="base.menu_email" action="action_view_mail_mail" /> + </data> +</openerp> diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 7801a9b42d9..e4862ca9fb9 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -19,25 +19,13 @@ # ############################################################################## -import ast -import base64 -import dateutil.parser -import email import logging -import re -import time -import datetime -from email.header import decode_header -from email.message import Message - -from openerp import SUPERUSER_ID -from osv import osv -from osv import fields -import pytz -from tools import DEFAULT_SERVER_DATETIME_FORMAT -from tools.translate import _ import tools +from email.header import decode_header +from operator import itemgetter +from osv import osv, fields + _logger = logging.getLogger(__name__) """ Some tools for parsing / creating email fields """ @@ -47,633 +35,309 @@ def decode(text): text = decode_header(text.replace('\r', '')) return ''.join([tools.ustr(x[0], x[1]) for x in text]) -def mail_tools_to_email(text): - """Return a list of the email addresses found in ``text``""" - if not text: return [] - return re.findall(r'([^ ,<@]+@[^> ,]+)', text) +class mail_message(osv.Model): + """ Messages model: system notification (replacing res.log notifications), + comments (OpenChatter discussion) and incoming emails. """ + _name = 'mail.message' + _description = 'Message' + _inherit = ['ir.needaction_mixin'] + _order = 'id desc' -# TODO: remove that after cleaning -def to_email(text): - return mail_tools_to_email(text) + _message_read_limit = 10 + _message_record_name_length = 18 -class mail_message_common(osv.TransientModel): - """ Common abstract class for holding the main attributes of a - message object. It could be reused as parent model for any - database model or wizard screen that needs to hold a kind of - message. - All internal logic should be in another model while this - model holds the basics of a message. For example, a wizard for writing - emails should inherit from this class and not from mail.message.""" + def _shorten_name(self, name): + if len(name) <= (self._message_record_name_length + 3): + return name + return name[:self._message_record_name_length] + '...' - def get_body(self, cr, uid, ids, name, arg, context=None): - """ get correct body version: body_html for html messages, and - body_text for plain text messages - """ - result = dict.fromkeys(ids, '') - for message in self.browse(cr, uid, ids, context=context): - if message.content_subtype == 'html': - result[message.id] = message.body_html - else: - result[message.id] = message.body_text - return result - - def search_body(self, cr, uid, obj, name, args, context=None): - # will receive: - # - obj: mail.message object - # - name: 'body' - # - args: [('body', 'ilike', 'blah')] - return ['|', '&', ('content_subtype', '=', 'html'), ('body_html', args[0][1], args[0][2]), ('body_text', args[0][1], args[0][2])] - - def get_record_name(self, cr, uid, ids, name, arg, context=None): + def _get_record_name(self, cr, uid, ids, name, arg, context=None): + """ Return the related document name, using get_name. """ result = dict.fromkeys(ids, '') for message in self.browse(cr, uid, ids, context=context): if not message.model or not message.res_id: continue - result[message.id] = self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1] + result[message.id] = self._shorten_name(self.pool.get(message.model).name_get(cr, uid, [message.res_id], context=context)[0][1]) return result + def _get_unread(self, cr, uid, ids, name, arg, context=None): + """ Compute if the message is unread by the current user. """ + res = dict((id, {'unread': False}) for id in ids) + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', 'in', [partner_id]), + ('message_id', 'in', ids), + ('read', '=', False) + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.id]['unread'] = True + return res + + def _search_unread(self, cr, uid, obj, name, domain, context=None): + """ Search for messages unread by the current user. Condition is + inversed because we search unread message on a read column. """ + if domain[0][2]: + read_cond = '(read = false or read is null)' + else: + read_cond = 'read = true' + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + cr.execute("SELECT message_id FROM mail_notification "\ + "WHERE partner_id = %%s AND %s" % read_cond, + (partner_id,)) + return [('id', 'in', [r[0] for r in cr.fetchall()])] + def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list if isinstance(ids, (int, long)): ids = [ids] res = [] for message in self.browse(cr, uid, ids, context=context): - name = '' - if message.subject: - name = '%s: ' % (message.subject) - if message.body_text: - name = '%s%s ' % (name, message.body_text[0:20]) - if message.date: - name = '%s(%s)' % (name, message.date) - res.append((message.id, name)) + name = '%s: %s' % (message.subject or '', message.body or '') + res.append((message.id, self._shorten_name(name.lstrip(' :')))) return res - _name = 'mail.message.common' - _rec_name = 'subject' _columns = { - 'subject': fields.char('Subject', size=512), + 'type': fields.selection([ + ('email', 'Email'), + ('comment', 'Comment'), + ('notification', 'System notification'), + ], 'Type', + help="Message type: email for email message, notification for system "\ + "message, comment for other messages such as user replies"), + 'author_id': fields.many2one('res.partner', 'Author', required=True), + 'partner_ids': fields.many2many('res.partner', 'mail_notification', 'message_id', 'partner_id', 'Recipients'), + 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', + 'message_id', 'attachment_id', 'Attachments'), + 'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."), + 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), 'model': fields.char('Related Document Model', size=128, select=1), 'res_id': fields.integer('Related Document ID', select=1), - 'record_name': fields.function(get_record_name, type='string', + 'record_name': fields.function(_get_record_name, type='string', string='Message Record Name', help="Name get of the related document."), + 'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'), + 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), - 'email_from': fields.char('From', size=128, help='Message sender, taken from user preferences.'), - 'email_to': fields.char('To', size=256, help='Message recipients'), - 'email_cc': fields.char('Cc', size=256, help='Carbon copy message recipients'), - 'email_bcc': fields.char('Bcc', size=256, help='Blind carbon copy message recipients'), - 'reply_to':fields.char('Reply-To', size=256, help='Preferred response address for the message'), - 'headers': fields.text('Message Headers', readonly=1, - help="Full message headers, e.g. SMTP session headers (usually available on inbound messages only)"), - 'message_id': fields.char('Message-Id', size=256, help='Message unique identifier', select=1, readonly=1), - 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), - 'content_subtype': fields.char('Message content subtype', size=32, - oldname="subtype", readonly=1, - help="Type of message, usually 'html' or 'plain', used to select "\ - "plain-text or rich-text contents accordingly"), - 'body_text': fields.text('Text Contents', help="Plain-text version of the message"), - 'body_html': fields.html('Rich-text Contents', help="Rich-text/HTML version of the message"), - 'body': fields.function(get_body, fnct_search = search_body, type='text', - string='Message Content', store=True, - help="Content of the message. This content equals the body_text field "\ - "for plain-test messages, and body_html for rich-text/HTML "\ - "messages. This allows having one field if we want to access "\ - "the content matching the message content_subtype."), - 'parent_id': fields.many2one('mail.message.common', 'Parent Message', - select=True, ondelete='set null', - help="Parent message, used for displaying as threads with hierarchy"), + 'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1), + 'body': fields.html('Contents', help='Automatically sanitized HTML contents'), + 'unread': fields.function(_get_unread, fnct_search=_search_unread, + type='boolean', string='Unread', + help='Functional field to search for unread messages linked to uid'), } + def _needaction_domain_get(self, cr, uid, context=None): + if self._needaction: + return [('unread', '=', True)] + return [] + + def _get_default_author(self, cr, uid, context=None): + return self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + _defaults = { - 'content_subtype': 'plain', - 'date': (lambda *a: fields.datetime.now()), + 'type': 'email', + 'date': lambda *a: fields.datetime.now(), + 'author_id': lambda self, cr, uid, ctx={}: self._get_default_author(cr, uid, ctx), + 'body': '', } -class mail_message(osv.Model): - """Model holding messages: system notification (replacing res.log - notifications), comments (for OpenChatter feature) and - RFC2822 email messages. This model also provides facilities to - parse, queue and send new email messages. Type of messages - are differentiated using the 'type' column. """ - - _name = 'mail.message' - _inherit = 'mail.message.common' - _description = 'Mail Message (email, comment, notification)' - _order = 'date desc' + #------------------------------------------------------ + # Message loading for web interface + #------------------------------------------------------ - def open_document(self, cr, uid, ids, context=None): - """ Open the message related document. Note that only the document of - ids[0] will be opened. - TODO: how to determine the action to use ? + def _message_dict_get(self, cr, uid, msg, context=None): + """ Return a dict representation of the message browse record. """ + attachment_ids = self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context) + author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0] + author_user_id = self.pool.get('res.users').name_get(cr, uid, [msg.author_id.user_ids[0].id], context=context)[0] + partner_ids = self.pool.get('res.partner').name_get(cr, uid, [x.id for x in msg.partner_ids], context=context) + return { + 'id': msg.id, + 'type': msg.type, + 'attachment_ids': attachment_ids, + 'body': msg.body, + 'model': msg.model, + 'res_id': msg.res_id, + 'record_name': msg.record_name, + 'subject': msg.subject, + 'date': msg.date, + 'author_id': author_id, + 'author_user_id': author_user_id, + 'partner_ids': partner_ids, + 'child_ids': [], + } + + def message_read_tree_flatten(self, cr, uid, messages, current_level, level, context=None): + """ Given a tree with several roots of following structure : + [ {'id': 1, 'child_ids': [ + {'id': 11, 'child_ids': [...] },], + {...} ] + Flatten it to have a maximum number of levels, 0 being flat and + sort messages in a level according to a key of the messages. + Perform the flattening at leafs if above the maximum depth, then get + back in the tree. + :param context: ``sort_key``: key for sorting (id by default) + :param context: ``sort_reverse``: reverser order for sorting (True by default) """ - action_data = False - if not ids: - return action_data - msg = self.browse(cr, uid, ids[0], context=context) - ir_act_window = self.pool.get('ir.actions.act_window') - action_ids = ir_act_window.search(cr, uid, [('res_model', '=', msg.model)], context=context) - if action_ids: - action_data = ir_act_window.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain' : "[('id', '=', %d)]" % (msg.res_id), - 'nodestroy': True, - 'context': {} - }) - return action_data - - def open_attachment(self, cr, uid, ids, context=None): - """ Open the message related attachments. - TODO: how to determine the action to use ? + def _flatten(msg_dict): + """ from {'id': x, 'child_ids': [{child1}, {child2}]} + get [{'id': x, 'child_ids': []}, {child1}, {child2}] + """ + child_ids = msg_dict.pop('child_ids', []) + msg_dict['child_ids'] = [] + return [msg_dict] + child_ids + # return sorted([msg_dict] + child_ids, key=itemgetter('id'), reverse=True) + context = context or {} + # Depth-first flattening + for message in messages: + if message.get('type') == 'expandable': + continue + message['child_ids'] = self.message_read_tree_flatten(cr, uid, message['child_ids'], current_level + 1, level, context=context) + # Flatten if above maximum depth + if current_level < level: + return_list = messages + else: + return_list = [] + for message in messages: + for flat_message in _flatten(message): + return_list.append(flat_message) + return sorted(return_list, key=itemgetter(context.get('sort_key', 'id')), reverse=context.get('sort_reverse', True)) + + def message_read(self, cr, uid, ids=False, domain=[], thread_level=0, limit=None, context=None): + """ If IDs are provided, fetch these records. Otherwise use the domain + to fetch the matching records. + After having fetched the records provided by IDs, it will fetch the + parents to have well-formed threads. + :return list: list of trees of messages """ - action_data = False + limit = limit or self._message_read_limit + context = context or {} if not ids: - return action_data - action_pool = self.pool.get('ir.actions.act_window') + ids = self.search(cr, uid, domain, context=context, limit=limit) messages = self.browse(cr, uid, ids, context=context) - att_ids = [x.id for message in messages for x in message.attachment_ids] - action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')], context=context) - if action_ids: - action_data = action_pool.read(cr, uid, action_ids[0], context=context) - action_data.update({ - 'domain': [('id', 'in', att_ids)], - 'nodestroy': True + + result = [] + tree = {} # key: ID, value: record + for msg in messages: + if len(result) < (limit - 1): + record = self._message_dict_get(cr, uid, msg, context=context) + if thread_level and msg.parent_id: + while msg.parent_id: + if msg.parent_id.id in tree: + record_parent = tree[msg.parent_id.id] + else: + record_parent = self._message_dict_get(cr, uid, msg.parent_id, context=context) + if msg.parent_id.parent_id: + tree[msg.parent_id.id] = record_parent + if record['id'] not in [x['id'] for x in record_parent['child_ids']]: + record_parent['child_ids'].append(record) + record = record_parent + msg = msg.parent_id + if msg.id not in tree: + result.append(record) + tree[msg.id] = record + else: + result.append({ + 'type': 'expandable', + 'domain': [('id', '<=', msg.id)] + domain, + 'context': context, + 'thread_level': thread_level, # should be improve accodting to level of records + 'id': -1, }) - return action_data - - _columns = { - 'type': fields.selection([ - ('email', 'email'), - ('comment', 'Comment'), - ('notification', 'System notification'), - ], 'Type', - help="Message type: email for email message, notification for system "\ - "message, comment for other messages such as user replies"), - 'partner_id': fields.many2one('res.partner', 'Related partner', - help="Deprecated field. Use partner_ids instead."), - 'partner_ids': fields.many2many('res.partner', - 'mail_message_res_partner_rel', - 'message_id', 'partner_id', 'Destination partners', - help="When sending emails through the social network composition wizard"\ - "you may choose to send a copy of the mail to partners."), - 'user_id': fields.many2one('res.users', 'Related User', readonly=1), - 'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', - 'message_id', 'attachment_id', 'Attachments'), - 'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing mail server', readonly=1), - 'state': fields.selection([ - ('outgoing', 'Outgoing'), - ('sent', 'Sent'), - ('received', 'Received'), - ('exception', 'Delivery Failed'), - ('cancel', 'Cancelled'), - ], 'Status', readonly=True), - 'auto_delete': fields.boolean('Auto Delete', - help="Permanently delete this email after sending it, to save space"), - 'original': fields.binary('Original', readonly=1, - help="Original version of the message, as it was sent on the network"), - 'parent_id': fields.many2one('mail.message', 'Parent Message', - select=True, ondelete='set null', - help="Parent message, used for displaying as threads with hierarchy"), - 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), - } - - _defaults = { - 'type': 'email', - 'state': 'received', - } - + break + + # Flatten the result + if thread_level > 0: + result = self.message_read_tree_flatten(cr, uid, result, 0, thread_level, context=context) + return result + #------------------------------------------------------ # Email api #------------------------------------------------------ - + def init(self, cr): cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""") if not cr.fetchone(): cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""") - def check(self, cr, uid, ids, mode, context=None, values=None): - """Restricts the access to a mail.message, according to referred model - """ - if not ids: - return - res_ids = {} + def check_access_rule(self, cr, uid, ids, operation, context=None): + """ mail.message access rule check + - message received (a notification exists) -> ok + - check rules of related document if exists + - fallback on normal mail.message check """ if isinstance(ids, (int, long)): ids = [ids] - cr.execute('SELECT DISTINCT model, res_id FROM mail_message WHERE id = ANY (%s)', (ids,)) - for rmod, rid in cr.fetchall(): + + # check messages for which you have a notification + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + not_obj = self.pool.get('mail.notification') + not_ids = not_obj.search(cr, uid, [ + ('partner_id', '=', partner_id), + ('message_id', 'in', ids), + ], context=context) + notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, uid, not_ids, context=context) + if notification.message_id.id in ids] + + # check messages linked to an existing document + model_record_ids = {} + document_ids = [] + cr.execute('SELECT DISTINCT id, model, res_id FROM mail_message WHERE id = ANY (%s)', (ids,)) + for id, rmod, rid in cr.fetchall(): if not (rmod and rid): continue - res_ids.setdefault(rmod,set()).add(rid) - if values: - if 'res_model' in values and 'res_id' in values: - res_ids.setdefault(values['res_model'],set()).add(values['res_id']) + document_ids.append(id) + model_record_ids.setdefault(rmod, set()).add(rid) + for model, mids in model_record_ids.items(): + model_obj = self.pool.get(model) + mids = model_obj.exists(cr, uid, mids) + model_obj.check_access_rights(cr, uid, operation) + model_obj.check_access_rule(cr, uid, mids, operation, context=context) + + # fall back on classic operation for other ids + other_ids = set(ids).difference(set(notified_ids), set(document_ids)) + super(mail_message, self).check_access_rule(cr, uid, other_ids, operation, context=None) - ima_obj = self.pool.get('ir.model.access') - for model, mids in res_ids.items(): - # ignore mail messages that are not attached to a resource anymore when checking access rights - # (resource was deleted but message was not) - mids = self.pool.get(model).exists(cr, uid, mids) - ima_obj.check(cr, uid, model, mode) - self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context) - def create(self, cr, uid, values, context=None): - self.check(cr, uid, [], mode='create', context=context, values=values) - return super(mail_message, self).create(cr, uid, values, context) + if not values.get('message_id') and values.get('res_id') and values.get('model'): + values['message_id'] = tools.generate_tracking_message_id('%(model)s-%(res_id)s' % values) + newid = super(mail_message, self).create(cr, uid, values, context) + self.notify(cr, uid, newid, context=context) + return newid - def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'): - self.check(cr, uid, ids, 'read', context=context) - return super(mail_message, self).read(cr, uid, ids, fields_to_read, context, load) + def unlink(self, cr, uid, ids, context=None): + # cascade-delete attachments that are directly attached to the message (should only happen + # for mail.messages that act as parent for a standalone mail.mail record. + attachments_to_delete = [] + for mail in self.browse(cr, uid, ids, context=context): + for attach in mail.attachment_ids: + if attach.res_model == 'mail.message' and attach.res_id == mail.id: + attachments_to_delete.append(attach.id) + if attachments_to_delete: + self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context) + return super(mail_message,self).unlink(cr, uid, ids, context=context) + + def notify(self, cr, uid, newid, context=None): + """ Add the related record followers to the destination partner_ids. + Call mail_notification.notify to manage the email sending + """ + message = self.browse(cr, uid, newid, context=context) + partners_to_notify = set([]) + # add all partner_ids of the message + if message.partner_ids: + partners_to_notify |= set(partner.id for partner in message.partner_ids) + # add all followers and set add them in partner_ids + if message.model and message.res_id: + record = self.pool.get(message.model).browse(cr, uid, message.res_id, context=context) + extra_notified = set(partner.id for partner in record.message_follower_ids) + missing_notified = extra_notified - partners_to_notify + if missing_notified: + message.write({'partner_ids': [(4, p_id) for p_id in missing_notified]}) + partners_to_notify |= extra_notified + self.pool.get('mail.notification').notify(cr, uid, list(partners_to_notify), newid, context=context) def copy(self, cr, uid, id, default=None, context=None): """Overridden to avoid duplicating fields that are unique to each email""" if default is None: default = {} - self.check(cr, uid, [id], 'read', context=context) - default.update(message_id=False, original=False, headers=False) - return super(mail_message,self).copy(cr, uid, id, default=default, context=context) - - def write(self, cr, uid, ids, vals, context=None): - self.check(cr, uid, ids, 'write', context=context, values=vals) - return super(mail_message, self).write(cr, uid, ids, vals, context) - - def unlink(self, cr, uid, ids, context=None): - self.check(cr, uid, ids, 'unlink', context=context) - return super(mail_message, self).unlink(cr, uid, ids, context) - - def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, type='email', - email_cc=None, email_bcc=None, reply_to=False, partner_ids=None, attachments=None, - message_id=False, references=False, res_id=False, content_subtype='plain', - headers=None, mail_server_id=False, auto_delete=False, context=None): - """ Schedule sending a new email message, to be sent the next time the - mail scheduler runs, or the next time :meth:`process_email_queue` is - called explicitly. - - :param string email_from: sender email address - :param list email_to: list of recipient addresses (to be joined with commas) - :param string subject: email subject (no pre-encoding/quoting necessary) - :param string body: email body, according to the ``content_subtype`` - (by default, plaintext). If html content_subtype is used, the - message will be automatically converted to plaintext and wrapped - in multipart/alternative. - :param list email_cc: optional list of string values for CC header - (to be joined with commas) - :param list email_bcc: optional list of string values for BCC header - (to be joined with commas) - :param string model: optional model name of the document this mail - is related to (this will also be used to generate a tracking id, - used to match any response related to the same document) - :param int res_id: optional resource identifier this mail is related - to (this will also be used to generate a tracking id, used to - match any response related to the same document) - :param string reply_to: optional value of Reply-To header - :param partner_ids: destination partner_ids - :param string content_subtype: optional mime content_subtype for - the text body (usually 'plain' or 'html'), must match the format - of the ``body`` parameter. Default is 'plain', making the content - part of the mail "text/plain". - :param dict attachments: map of filename to filecontents, where - filecontents is a string containing the bytes of the attachment - :param dict headers: optional map of headers to set on the outgoing - mail (may override the other headers, including Subject, - Reply-To, Message-Id, etc.) - :param int mail_server_id: optional id of the preferred outgoing - mail server for this mail - :param bool auto_delete: optional flag to turn on auto-deletion of - the message after it has been successfully sent (default to False) - """ - if context is None: - context = {} - if attachments is None: - attachments = {} - if partner_ids is None: - partner_ids = [] - attachment_obj = self.pool.get('ir.attachment') - for param in (email_to, email_cc, email_bcc): - if param and not isinstance(param, list): - param = [param] - msg_vals = { - 'subject': subject, - 'date': fields.datetime.now(), - 'user_id': uid, - 'model': model, - 'res_id': res_id, - 'type': type, - 'body_text': body if content_subtype != 'html' else False, - 'body_html': body if content_subtype == 'html' else False, - 'email_from': email_from, - 'email_to': email_to and ','.join(email_to) or '', - 'email_cc': email_cc and ','.join(email_cc) or '', - 'email_bcc': email_bcc and ','.join(email_bcc) or '', - 'partner_ids': partner_ids, - 'reply_to': reply_to, - 'message_id': message_id, - 'references': references, - 'content_subtype': content_subtype, - 'headers': headers, # serialize the dict on the fly - 'mail_server_id': mail_server_id, - 'state': 'outgoing', - 'auto_delete': auto_delete - } - email_msg_id = self.create(cr, uid, msg_vals, context) - attachment_ids = [] - for attachment in attachments: - fname, fcontent = attachment - attachment_data = { - 'name': fname, - 'datas_fname': fname, - 'datas': fcontent and fcontent.encode('base64'), - 'res_model': self._name, - 'res_id': email_msg_id, - } - if context.has_key('default_type'): - del context['default_type'] - attachment_ids.append(attachment_obj.create(cr, uid, attachment_data, context)) - if attachment_ids: - self.write(cr, uid, email_msg_id, { 'attachment_ids': [(6, 0, attachment_ids)]}, context=context) - return email_msg_id - - def mark_outgoing(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state':'outgoing'}, context=context) - - def cancel(self, cr, uid, ids, context=None): - return self.write(cr, uid, ids, {'state':'cancel'}, context=context) - - def process_email_queue(self, cr, uid, ids=None, context=None): - """Send immediately queued messages, committing after each - message is sent - this is not transactional and should - not be called during another transaction! - - :param list ids: optional list of emails ids to send. If passed - no search is performed, and these ids are used - instead. - :param dict context: if a 'filters' key is present in context, - this value will be used as an additional - filter to further restrict the outgoing - messages to send (by default all 'outgoing' - messages are sent). - """ - if context is None: - context = {} - if not ids: - filters = ['&', ('state', '=', 'outgoing'), ('type', '=', 'email')] - if 'filters' in context: - filters.extend(context['filters']) - ids = self.search(cr, uid, filters, context=context) - res = None - try: - # Force auto-commit - this is meant to be called by - # the scheduler, and we can't allow rolling back the status - # of previously sent emails! - res = self.send(cr, uid, ids, auto_commit=True, context=context) - except Exception: - _logger.exception("Failed processing mail queue") - return res - - def parse_message(self, message, save_original=False, context=None): - """Parses a string or email.message.Message representing an - RFC-2822 email, and returns a generic dict holding the - message details. - - :param message: the message to parse - :type message: email.message.Message | string | unicode - :param bool save_original: whether the returned dict - should include an ``original`` entry with the base64 - encoded source of the message. - :rtype: dict - :return: A dict with the following structure, where each - field may not be present if missing in original - message:: - - { 'message-id': msg_id, - 'subject': subject, - 'from': from, - 'to': to, - 'cc': cc, - 'headers' : { 'X-Mailer': mailer, - #.. all X- headers... - }, - 'content_subtype': msg_mime_subtype, - 'body_text': plaintext_body - 'body_html': html_body, - 'attachments': [('file1', 'bytes'), - ('file2', 'bytes') } - # ... - 'original': source_of_email, - } - """ - msg_txt = message - if isinstance(message, str): - msg_txt = email.message_from_string(message) - - # Warning: message_from_string doesn't always work correctly on unicode, - # we must use utf-8 strings here :-( - if isinstance(message, unicode): - message = message.encode('utf-8') - msg_txt = email.message_from_string(message) - - message_id = msg_txt.get('message-id', False) - msg = {} - - if save_original: - # save original, we need to be able to read the original email sometimes - msg['original'] = message.as_string() if isinstance(message, Message) \ - else message - msg['original'] = base64.b64encode(msg['original']) # binary fields are b64 - - if not message_id: - # Very unusual situation, be we should be fault-tolerant here - message_id = time.time() - msg_txt['message-id'] = message_id - _logger.info('Parsing Message without message-id, generating a random one: %s', message_id) - - msg_fields = msg_txt.keys() - msg['id'] = message_id - msg['message-id'] = message_id - - if 'Subject' in msg_fields: - msg['subject'] = decode(msg_txt.get('Subject')) - - if 'Content-Type' in msg_fields: - msg['content-type'] = msg_txt.get('Content-Type') - - if 'From' in msg_fields: - msg['from'] = decode(msg_txt.get('From') or msg_txt.get_unixfrom()) - - if 'To' in msg_fields: - msg['to'] = decode(msg_txt.get('To')) - - if 'Delivered-To' in msg_fields: - msg['to'] = decode(msg_txt.get('Delivered-To')) - - if 'CC' in msg_fields: - msg['cc'] = decode(msg_txt.get('CC')) - - if 'Cc' in msg_fields: - msg['cc'] = decode(msg_txt.get('Cc')) - - if 'Reply-To' in msg_fields: - msg['reply'] = decode(msg_txt.get('Reply-To')) - - if 'Date' in msg_fields: - date_hdr = decode(msg_txt.get('Date')) - # convert from email timezone to server timezone - date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) - date_server_datetime_str = date_server_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - msg['date'] = date_server_datetime_str - - if 'Content-Transfer-Encoding' in msg_fields: - msg['encoding'] = msg_txt.get('Content-Transfer-Encoding') - - if 'References' in msg_fields: - msg['references'] = msg_txt.get('References') - - if 'In-Reply-To' in msg_fields: - msg['in-reply-to'] = msg_txt.get('In-Reply-To') - - msg['headers'] = {} - msg['content_subtype'] = 'plain' - for item in msg_txt.items(): - if item[0].startswith('X-'): - msg['headers'].update({item[0]: item[1]}) - if not msg_txt.is_multipart() or 'text/plain' in msg.get('content-type', ''): - encoding = msg_txt.get_content_charset() - body = msg_txt.get_payload(decode=True) - if 'text/html' in msg.get('content-type', ''): - msg['body_html'] = body - msg['content_subtype'] = 'html' - if body: - body = tools.html2plaintext(body) - msg['body_text'] = tools.ustr(body, encoding) - - attachments = [] - if msg_txt.is_multipart() or 'multipart/alternative' in msg.get('content-type', ''): - body = "" - if 'multipart/alternative' in msg.get('content-type', ''): - msg['content_subtype'] = 'alternative' - else: - msg['content_subtype'] = 'mixed' - for part in msg_txt.walk(): - if part.get_content_maintype() == 'multipart': - continue - - encoding = part.get_content_charset() - filename = part.get_filename() - if part.get_content_maintype()=='text': - content = part.get_payload(decode=True) - if filename: - attachments.append((filename, content)) - content = tools.ustr(content, encoding) - if part.get_content_subtype() == 'html': - msg['body_html'] = content - msg['content_subtype'] = 'html' # html version prevails - body = tools.ustr(tools.html2plaintext(content)) - body = body.replace(' ', '') - elif part.get_content_subtype() == 'plain': - body = content - elif part.get_content_maintype() in ('application', 'image'): - if filename : - attachments.append((filename,part.get_payload(decode=True))) - else: - res = part.get_payload(decode=True) - body += tools.ustr(res, encoding) - - msg['body_text'] = body - msg['attachments'] = attachments - - # for backwards compatibility: - msg['body'] = msg['body_text'] - msg['sub_type'] = msg['content_subtype'] or 'plain' - return msg - - def _postprocess_sent_message(self, cr, uid, message, context=None): - """Perform any post-processing necessary after sending ``message`` - successfully, including deleting it completely along with its - attachment if the ``auto_delete`` flag of the message was set. - Overridden by subclasses for extra post-processing behaviors. - - :param browse_record message: the message that was just sent - :return: True - """ - if message.auto_delete: - self.pool.get('ir.attachment').unlink(cr, uid, - [x.id for x in message.attachment_ids - if x.res_model == self._name and x.res_id == message.id], - context=context) - message.unlink() - return True - - def send(self, cr, uid, ids, auto_commit=False, context=None): - """Sends the selected emails immediately, ignoring their current - state (mails that have already been sent should not be passed - unless they should actually be re-sent). - Emails successfully delivered are marked as 'sent', and those - that fail to be deliver are marked as 'exception', and the - corresponding error message is output in the server logs. - - :param bool auto_commit: whether to force a commit of the message - status after sending each message (meant - only for processing by the scheduler), - should never be True during normal - transactions (default: False) - :return: True - """ - ir_mail_server = self.pool.get('ir.mail_server') - self.write(cr, uid, ids, {'state': 'outgoing'}, context=context) - for message in self.browse(cr, uid, ids, context=context): - try: - attachments = [] - for attach in message.attachment_ids: - attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) - - body = message.body_html if message.content_subtype == 'html' else message.body_text - body_alternative = None - content_subtype_alternative = None - if message.content_subtype == 'html' and message.body_text: - # we have a plain text alternative prepared, pass it to - # build_message instead of letting it build one - body_alternative = message.body_text - content_subtype_alternative = 'plain' - - # handle destination_partners - partner_ids_email_to = '' - for partner in message.partner_ids: - partner_ids_email_to += '%s ' % (partner.email or '') - message_email_to = '%s %s' % (partner_ids_email_to, message.email_to or '') - - # build an RFC2822 email.message.Message object and send it - # without queuing - msg = ir_mail_server.build_email( - email_from=message.email_from, - email_to=mail_tools_to_email(message_email_to), - subject=message.subject, - body=body, - body_alternative=body_alternative, - email_cc=mail_tools_to_email(message.email_cc), - email_bcc=mail_tools_to_email(message.email_bcc), - reply_to=message.reply_to, - attachments=attachments, message_id=message.message_id, - references = message.references, - object_id=message.res_id and ('%s-%s' % (message.res_id,message.model)), - subtype=message.content_subtype, - subtype_alternative=content_subtype_alternative, - headers=message.headers and ast.literal_eval(message.headers)) - res = ir_mail_server.send_email(cr, uid, msg, - mail_server_id=message.mail_server_id.id, - context=context) - if res: - message.write({'state':'sent', 'message_id': res, 'email_to': message_email_to}) - else: - message.write({'state':'exception', 'email_to': message_email_to}) - message.refresh() - if message.state == 'sent': - self._postprocess_sent_message(cr, uid, message, context=context) - except Exception: - _logger.exception('failed sending mail.message %s', message.id) - message.write({'state':'exception'}) - - if auto_commit == True: - cr.commit() - return True - - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + default.update(message_id=False, headers=False) + return super(mail_message, self).copy(cr, uid, id, default=default, context=context) diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 0aa5261d7e7..914937a34f3 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -2,15 +2,15 @@ <openerp> <data> <!-- mail.message tree: short view !--> - <record model="ir.ui.view" id="view_message_tree_short"> - <field name="name">mail.message.tree.short</field> + <record model="ir.ui.view" id="view_message_tree"> + <field name="name">mail.message.tree</field> <field name="model">mail.message</field> <field name="priority">20</field> <field name="arch" type="xml"> <tree string="Messages"> <field name="date"/> <field name="subject"/> - <field name="user_id"/> + <field name="author_id"/> <field name="model"/> <field name="res_id"/> </tree> @@ -18,8 +18,8 @@ </record> <!-- mail.message form: short view !--> - <record model="ir.ui.view" id="view_message_form_short"> - <field name="name">mail.message.form.short</field> + <record model="ir.ui.view" id="view_message_form"> + <field name="name">mail.message.form</field> <field name="model">mail.message</field> <field name="priority">20</field> <field name="arch" type="xml"> @@ -28,10 +28,9 @@ <group> <group> <field name="subject"/> - <field name="user_id"/> + <field name="author_id"/> <field name="date"/> <field name="type"/> - <field name="content_subtype"/> </group> <group> <field name="model"/> @@ -40,45 +39,22 @@ <field name="partner_ids" widget="many2many_tags"/> </group> </group> - <notebook> - <page string="Body (Rich)"> - <field name="body_html"/> - </page> - <page string="Body (Plain)"> - <field name="body_text" widget="text"/> - </page> - </notebook> + <field name="body"/> </sheet> </form> </field> </record> - <!-- mail.message search: short view !--> - <record model="ir.ui.view" id="view_message_search_short"> - <field name="name">mail.message.search.short</field> - <field name="model">mail.message</field> - <field name="priority">20</field> - <field name="arch" type="xml"> - <search string="Messages Search"> - <field name="model" string="Message"/> - <field name="date"/> - <field name="user_id"/> - </search> - </field> - </record> - <!-- mail.message search: wall view !--> - <record model="ir.ui.view" id="view_message_search_wall"> - <field name="name">mail.message.search.wall</field> + <record model="ir.ui.view" id="view_message_search"> + <field name="name">mail.message.search</field> <field name="model">mail.message</field> <field name="priority">25</field> <field name="arch" type="xml"> <search string="Messages Search"> <field name="subject" string="Content" filter_domain="['|', ('subject', 'ilike', self), ('body', 'ilike', self)]" /> <field name="type"/> - <filter icon="terp-personal+" string="My Feeds" - name="my_feeds" help="My Feeds" - domain="[('user_id','=',uid)]"/> + <field name="author_id"/> <filter icon="terp-personal+" string="Comments" name="comments" help="Comments" domain="[('type', '=', 'comment')]"/> @@ -88,159 +64,17 @@ <filter icon="terp-personal+" string="Emails" name="emails" help="Emails" domain="[('type', '=', 'email')]"/> - <filter icon="terp-go-today" string="Today" - name="today" help="Today" - domain="[ ('date', '<=', datetime.date.today().strftime('%%Y-%%m-%%d 23:59:59')), - ('date', '>=', datetime.date.today().strftime('%%Y-%%m-%%d 00:00:00')) - ]"/> - <filter icon="terp-go-week" string="This week" - name="7_days" help="This week" - domain="[ ('date', '<=', datetime.date.today().strftime('%%Y-%%m-%%d 23:59:59')), - ('date', '>=', (datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d 00:00:00')) - ]"/> - <field name="user_id"/> - </search> - </field> - </record> - - <record model="ir.ui.view" id="view_email_message_form"> - <field name="name">mail.message.form</field> - <field name="model">mail.message</field> - <field name="arch" type="xml"> - <form string="Email message" version="7.0"> - <sheet> - <label for="subject" class="oe_edit_only"/> - <h2><field name="subject"/></h2> - <div> - by <field name="user_id" class="oe_inline" string="User"/> on <field name="date" class="oe_inline"/> - <button name="%(action_email_compose_message_wizard)d" string="Reply" type="action" icon="terp-mail-replied" - context="{'mail.compose.message.mode':'reply', 'message_id':active_id}" states='received,sent,exception,cancel'/> - </div> - <notebook colspan="4"> - <page string="Message Details"> - <group> - <group> - <field name="email_from"/> - <field name="email_to"/> - <field name="email_cc"/> - <field name="email_bcc"/> - <field name="reply_to"/> - </group> - <group> - <field name="partner_id" readonly="1"/> - <field name="partner_ids" widget="many2many_tags"/> - </group> - </group> - <notebook> - <page string="Body (Rich)"> - <field name="body_html"/> - </page> - <page string="Body (Plain)"> - <field name="body_text" widget="text"/> - </page> - </notebook> - </page> - <page string="Advanced" groups="base.group_no_one"> - <group> - <group> - <field name="auto_delete"/> - <field name="type"/> - <field name="content_subtype"/> - <field name="state" colspan="2"/> - <field name="mail_server_id"/> - <field name="original"/> - <field name="model"/> - <field name="res_id"/> - <button name="open_document" string="Open" type="object" icon="gtk-jump-to" colspan="2" - attrs="{'invisible':['|', ('model', '=', ''), ('res_id', '=', False)]}"/> - </group> - <group> - <field name="message_id"/> - <field name="references"/> - <field name="headers"/> - </group> - </group> - </page> - <page string="Attachments"> - <field name="attachment_ids"/> - </page> - </notebook> - </sheet> - </form> - </field> - </record> - - <record model="ir.ui.view" id="view_email_message_tree"> - <field name="name">mail.message.tree</field> - <field name="model">mail.message</field> - <field name="arch" type="xml"> - <tree string="Emails" colors="grey:state in ('sent', 'cancel');blue:state=='outgoing';red:state=='exception';black:state=='received'"> - <field name="date"/> - <field name="subject"/> - <field name="email_from"/> - <field name="user_id" string="User"/> - <field name="message_id" invisible="1"/> - <field name="partner_id" invisible="1"/> - <field name="model" invisible="1"/> - <field name="res_id" invisible="1"/> - <field name="state"/> - <button name="send" string="Send Now" type="object" icon="gtk-media-play" states='outgoing'/> - <button name="mark_outgoing" string="Retry" type="object" icon="gtk-redo" states='exception,cancel'/> - <button name="cancel" string="Cancel" type="object" icon="terp-gtk-stop" states='outgoing'/> - <button name="open_document" string="Open Related Document" type="object" icon="gtk-jump-to"/> - <button name="open_attachment" string="Open Attachments" type="object" icon="gtk-jump-to"/> - </tree> - </field> - </record> - - <record model="ir.ui.view" id="view_email_message_search"> - <field name="name">mail.message.search</field> - <field name="model">mail.message</field> - <field name="arch" type="xml"> - <search string="Email Search"> - <field name="email_from" filter_domain="['|' '|',('email_from','ilike',self), ('email_to','ilike',self), ('subject','ilike',self)]" string="Email"/> - <field name="date"/> - <filter icon="terp-camera_test" name="received" string="Received" domain="[('state','=','received')]"/> - <filter icon="terp-call-start" name="outgoing" string="Outgoing" domain="[('state','=','outgoing')]"/> - <filter icon="terp-check" name="sent" string="Sent" domain="[('state','=','sent')]"/> - <filter icon="terp-gtk-stop" name="exception" string="Failed" domain="[('state','=','exception')]"/> - <separator/> - <filter icon="terp-camera_test" name="type_email" string="Email" domain="[('type','=','email')]"/> - <filter icon="terp-camera_test" name="type_comment" string="Comment" domain="[('type','=','comment')]"/> - <filter icon="terp-camera_test" name="type_notification" string="Notification" domain="[('type','=','notification')]"/> - <group expand="0" string="Extended Filters..."> - <field name="user_id" string="User"/> - <field name="partner_id" string="Partner Name"/> - <field name="model"/> - <field name="res_id"/> - </group> - <group expand="0" string="Group By..."> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> - <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/> - <filter string="User" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/> - <filter string="Thread" icon="terp-mail-" domain="[]" context="{'group_by':'message_id'}"/> - <filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/> - </group> + <field name="author_id"/> </search> </field> </record> - <record id="action_view_all_messages_short" model="ir.actions.act_window"> - <field name="name">Messages</field> - <field name="res_model">mail.message</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="context">{'tree_view_ref': 'mail.view_message_tree_short', 'form_view_ref': 'mail.view_message_form_short'}</field> - <field name="search_view_id" ref="view_message_search_short"/> - </record> - <record id="action_view_mail_message" model="ir.actions.act_window"> <field name="name">Messages</field> <field name="res_model">mail.message</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> - <field name="context">{'search_default_received': 1, 'search_default_type_email': 1}</field> - <field name="search_view_id" ref="view_email_message_search"/> + <field name="search_view_id" ref="view_message_search"/> </record> <act_window domain="[('partner_id', '=', active_id), ('email_from', '!=', False)]" @@ -248,24 +82,23 @@ id="act_res_partner_emails" name="Emails" res_model="mail.message" src_model="res.partner" - view_id="view_email_message_tree"/> - - <!-- Add menu entry in Settings/Email --> - <menuitem name="Messages" id="menu_email_message" parent="base.menu_email" action="action_view_mail_message" /> + view_id="view_message_tree"/> <!-- Add menu entry in Settings/Email --> - <menuitem name="Feeds" id="menu_email_message_all" parent="base.menu_email" action="action_view_all_messages_short" groups="base.group_no_one"/> + <menuitem name="Messages" id="menu_mail_message" parent="base.menu_email" action="action_view_mail_message"/> <record id="action_mail_all_feeds" model="ir.actions.client"> <field name="name">News Feed</field> <field name="tag">mail.wall</field> - <field name="params" eval="{'search_view_id': ref('view_message_search_wall')}"/> + <field name="params" eval=""{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid])], + 'context': {'default_model': 'res.users', 'default_res_id': uid} }""/> </record> <record id="action_mail_my_feeds" model="ir.actions.client"> <field name="name">My Feeds</field> <field name="tag">mail.wall</field> - <field name="params" eval="{'search_view_id': ref('view_message_search_wall'), 'my_feeds': True}"/> + <field name="params" eval=""{'domain': [('author_id.user_ids', 'in', [uid])], + 'context': {'default_model': 'res.users', 'default_res_id': uid} }""/> </record> </data> </openerp> diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index f25212487d8..c0d6ac90fe6 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -20,314 +20,197 @@ ############################################################################## import base64 +import dateutil import email import logging -import re +import pytz import time +import tools import xmlrpclib -from email.utils import parsedate -from email.message import Message +from email.message import Message +from mail_message import decode from osv import osv, fields -from mail_message import decode, to_email -import tools -from tools.translate import _ from tools.safe_eval import safe_eval as eval _logger = logging.getLogger(__name__) def decode_header(message, header, separator=' '): - return separator.join(map(decode,message.get_all(header, []))) + return separator.join(map(decode, message.get_all(header, []))) class many2many_reference(fields.many2many): - """ many2many_reference is an override of fields.many2many. It manages - many2many-like table where one id is given by two fields, res_model - and res_id. - """ - + """ many2many_reference manages many2many fields where one id is found + by a reference-like key (a char column in addition to the foreign id). + The reference_column attribute on the many2many fields is used; + if not defined, ``res_model`` is used. """ + def _get_query_and_where_params(self, cr, model, ids, values, where_params): - """ Add in where: - - mail_followers.res_model = 'crm.lead' - """ + """ Add in where condition like mail_followers.res_model = 'crm.lead' """ + reference_column = self.reference_column if self.reference_column else 'res_model' + values.update(reference_column=reference_column, reference_value=model._name) query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \ FROM %(rel)s, %(from_c)s \ WHERE %(rel)s.%(id1)s IN %%s \ AND %(rel)s.%(id2)s = %(tbl)s.id \ - AND %(rel)s.res_model = %%s \ + AND %(rel)s.%(reference_column)s = \'%(reference_value)s\' \ %(where_c)s \ %(order_by)s \ %(limit)s \ OFFSET %(offset)d' \ % values - where_params = [model._name] + where_params return query, where_params def set(self, cr, model, id, name, values, user=None, context=None): - """ Override to add the res_model field in queries. """ + """ Override to add the reference field in queries. """ if not values: return rel, id1, id2 = self._sql_names(model) obj = model.pool.get(self._obj) + # reference column name: given by attribute or res_model + reference_column = self.reference_column if self.reference_column else 'res_model' for act in values: if not (isinstance(act, list) or isinstance(act, tuple)) or not act: continue if act[0] == 0: idnew = obj.create(cr, user, act[2], context=context) - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, idnew, model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, idnew, model._name)) elif act[0] == 3: - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name)) + cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name)) elif act[0] == 4: # following queries are in the same transaction - so should be relatively safe - cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND res_model=%s', (id, act[1], model._name)) + cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name)) if not cr.fetchone(): - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act[1], model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act[1], model._name)) + elif act[0] == 5: + cr.execute('delete from '+rel+' where '+id1+' = %s AND '+reference_column+'=%s', (id, model._name)) elif act[0] == 6: d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context) if d1: d1 = ' and ' + ' and '.join(d1) else: d1 = '' - cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND res_model=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2) + cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+reference_column+'=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2) for act_nbr in act[2]: - cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+',res_model) VALUES (%s,%s,%s)', (id, act_nbr, model._name)) + cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act_nbr, model._name)) + # cases 1, 2: performs write and unlink -> default implementation is ok else: return super(many2many_reference, self).set(cr, model, id, name, values, user, context) -class mail_thread(osv.Model): - '''Mixin model, meant to be inherited by any model that needs to - act as a discussion topic on which messages can be attached. - Public methods are prefixed with ``message_`` in order to avoid - name collisions with methods of the models that will inherit - from this mixin. - - ``mail.thread`` is designed to work without adding any field - to the extended models. All functionalities and expected behavior - are managed by mail.thread, using model name and record ids. - A widget has been designed for the 6.1 and following version of OpenERP - web-client. However, due to technical limitations, ``mail.thread`` - adds a simulated one2many field, to display the web widget by - overriding the default field displayed. Using this field - is not recommanded has it will disappeear in future version - of OpenERP, leading to a pure mixin class. - - Inheriting classes are not required to implement any method, as the - default implementation will work for any model. However it is common - to override at least the ``message_new`` and ``message_update`` - methods (calling ``super``) to add model-specific behavior at - creation and update of a thread. - - #TODO: UPDATE WITH SUBTYPE / NEW FOLLOW MECHANISM +class mail_thread(osv.AbstractModel): + ''' mail_thread model is meant to be inherited by any model that needs to + act as a discussion topic on which messages can be attached. Public + methods are prefixed with ``message_`` in order to avoid name + collisions with methods of the models that will inherit from this class. + + ``mail.thread`` defines fields used to handle and display the + communication history. ``mail.thread`` also manages followers of + inheriting classes. All features and expected behavior are managed + by mail.thread. Widgets has been designed for the 7.0 and following + versions of OpenERP. + + Inheriting classes are not required to implement any method, as the + default implementation will work for any model. However it is common + to override at least the ``message_new`` and ``message_update`` + methods (calling ``super``) to add model-specific behavior at + creation and update of a thread when processing incoming emails. ''' _name = 'mail.thread' _description = 'Email Thread' - def _get_message_data(self, cr, uid, ids, field_names, args, context=None): - res = dict.fromkeys(ids) - for id in ids: - res[id] = {'message_ids': self.message_search(cr, uid, [id], context=context)} + def _get_message_data(self, cr, uid, ids, name, args, context=None): + res = dict((id, dict(message_unread=False, message_summary='')) for id in ids) + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id.user_ids', 'in', [uid]), + ('message_id.res_id', 'in', ids), + ('message_id.model', '=', self._name), + ('read', '=', False) + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.res_id]['message_unread'] = True + for thread in self.browse(cr, uid, ids, context=context): - message_follower_ids = [follower.id for follower in thread.message_follower_ids] - res[thread.id].update({ - 'message_is_follower': uid in message_follower_ids, - 'message_summary': "<span><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % - (len(res[thread.id]['message_ids']), len(thread.message_follower_ids)) - }) + cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or '' + res[thread.id]['message_summary'] = "<span%s><span class='oe_e'>9</span> %d</span> <span><span class='oe_e'>+</span> %d</span>" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids)) + res[thread.id]['message_is_follower'] = user.partner_id.id in [follower.id for follower in thread.message_follower_ids] return res - def _search_message_ids(self, cr, uid, obj, name, args, context=None): - msg_obj = self.pool.get('mail.message') - msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', args[0][2]), ('model', '=', self._name)], context=context) - return [('id', 'in', msg_ids)] + def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + res = {} + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', '=', partner_id), + ('message_id.model', '=', self._name), + ('read', '=', False) + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.res_id] = True + return [('id', 'in', res.keys())] _columns = { - 'message_ids': fields.function(_get_message_data, - fnct_search=_search_message_ids, - type='one2many', obj='mail.message', _fields_id = 'res_id', - string='Messages', multi="_get_message_data", - help="Field holding discussion about the current document."), - 'message_follower_ids': many2many_reference('res.users', - rel='mail_followers', id1='res_id', id2='user_id', string="Followers", - help="Followers of the document. The followers have full access to " \ - "the document details, as well as the conversation."), - 'message_is_follower': fields.function(_get_message_data, method=True, - type='boolean', string='I am Follower', multi='_get_message_data', - help='True if the current user is following the current document.'), - 'message_state': fields.boolean('Read', - help="When checked, new messages require your attention."), + 'message_is_follower': fields.function(_get_message_data, + type='boolean', string='Is a Follower', multi='_get_message_data'), + 'message_follower_ids': many2many_reference('res.partner', + 'mail_followers', 'res_id', 'partner_id', + reference_column='res_model', string='Followers'), + 'message_comment_ids': fields.one2many('mail.message', 'res_id', + domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))], + string='Comments and emails', + help="Comments and emails"), + 'message_ids': fields.one2many('mail.message', 'res_id', + domain=lambda self: [('model', '=', self._name)], + string='Messages', + help="Messages and communication history"), + 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, + type='boolean', string='Unread Messages', multi="_get_message_data", + help="If checked new messages require your attention."), 'message_summary': fields.function(_get_message_data, method=True, - type='text', string='Summary', multi='_get_message_data', + type='text', string='Summary', multi="_get_message_data", help="Holds the Chatter summary (number of messages, ...). "\ "This summary is directly in html format in order to "\ "be inserted in kanban views."), } - _defaults = { - 'message_state': True, - } - #------------------------------------------------------ - # Automatic subscription when creating/reading + # Automatic subscription when creating #------------------------------------------------------ def create(self, cr, uid, vals, context=None): - """ Override of create to subscribe : - - the writer - - followers given by the monitored fields - """ + """ Override to subscribe the current user. """ thread_id = super(mail_thread, self).create(cr, uid, vals, context=context) - followers_command = self.message_get_automatic_followers(cr, uid, thread_id, vals, fetch_missing=False, context=context) - if followers_command: - self.write(cr, uid, [thread_id], {'message_follower_ids': followers_command}, context=context) + self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context) return thread_id - def write(self, cr, uid, ids, vals, context=None): - """ Override of write to subscribe : - - the writer - - followers given by the monitored fields - """ - if isinstance(ids, (int, long)): - ids = [ids] - for id in ids: - # copy original vals because we are going to modify it - specific_vals = dict(vals) - # we modify followers: do not subscribe the uid - if specific_vals.get('message_follower_ids'): - followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, add_uid=False, context=context) - specific_vals['message_follower_ids'] += followers_command - else: - followers_command = self.message_get_automatic_followers(cr, uid, id, specific_vals, context=context) - specific_vals['message_follower_ids'] = followers_command - write_res = super(mail_thread, self).write(cr, uid, ids, specific_vals, context=context) - return True - def unlink(self, cr, uid, ids, context=None): - """Override unlink, to automatically delete messages - that are linked with res_model and res_id, not through - a foreign key with a 'cascade' ondelete attribute. - Notifications will be deleted with messages - """ + """ Override unlink to delete messages and followers. This cannot be + cascaded, because link is done through (res_model, res_id). """ msg_obj = self.pool.get('mail.message') + fol_obj = self.pool.get('mail.followers') # delete messages and notifications - msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) - msg_obj.unlink(cr, uid, msg_to_del_ids, context=context) + msg_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) + msg_obj.unlink(cr, uid, msg_ids, context=context) + # delete followers + fol_ids = fol_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + fol_obj.unlink(cr, uid, fol_ids, context=context) return super(mail_thread, self).unlink(cr, uid, ids, context=context) - def message_get_automatic_followers(self, cr, uid, id, record_vals, add_uid=True, fetch_missing=False, context=None): - """ Return the command for the many2many follower_ids field to manage - subscribers. Behavior : - - get the monitored fields (ex: ['user_id', 'responsible_id']); those - fields should be relationships to res.users (#TODO: res.partner) - - if this field is in the record_vals: it means it has been modified - thus add its value to the followers - - if this fields is not in record_vals, but fetch_missing paramter - is set to True: fetch the value in the record (use: at creation - for default values, not present in record_vals) - - if add_uid: add the current user (for example: writer is subscriber) - - generate the command and return it - This method has to be used on 1 id, because otherwise it would imply - to track which user.id is used for which record.id. - - :param record_vals: values given to the create method of the new - record, or values updated in a write. - :param monitored_fields: a list of fields that are monitored. Those - fields must be many2one fields to the res.users model. - :param fetch_missing: is set to True, the method will read the - record to find values that are not present in record_vals. - - #TODO : UPDATE WHEN MERGING TO PARTNERS - """ - # get monitored fields - monitored_fields = self.message_get_monitored_follower_fields(cr, uid, [id], context=context) - modified_fields = [field for field in monitored_fields if field in record_vals.iterkeys()] - other_fields = [field for field in monitored_fields if field not in record_vals.iterkeys()] if fetch_missing else [] - # for each monitored field: if in record_vals, it has been modified/added - follower_ids = [] - for field in modified_fields: - # do not add 'False' - if record_vals.get(fields): - follower_ids.append(record_vals.get(field)) - # for other fields: read in record if fetch_missing (otherwise list is void) - for field in other_fields: - record = self.browse(cr, uid, id, context=context) - value = getattr(record, field) - if value: - follower_ids.append(value) - # add uid if asked and not already present - if add_uid and uid not in follower_ids: - follower_ids.append(uid) - return self.message_subscribe_get_command(cr, uid, follower_ids, context=context) - #------------------------------------------------------ # mail.message wrappers and tools #------------------------------------------------------ - def message_create(self, cr, uid, thread_id, vals, context=None): - """ OpenChatter: wrapper of mail.message create method - - creates the mail.message - - automatically subscribe the message writer - - push the message to followers - """ - if context is None: - context = {} - - # create message - msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context) - - # automatically subscribe the writer of the message - if vals.get('user_id'): - record = self.browse(cr, uid, thread_id, context=context) - follower_ids = [follower.id for follower in record.message_follower_ids] - if vals.get('user_id') not in follower_ids: - self.message_subscribe(cr, uid, [thread_id], [vals.get('user_id')], context=context) - - # Set as unread if writer is not the document responsible - self.message_create_set_unread(cr, uid, [thread_id], context=context) - - # special: if install mode, do not push demo data - if context.get('install_mode', False): - return msg_id - - # get users that will get a notification pushed - notification_obj = self.pool.get('mail.notification') - user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, context=context) - for id in user_to_push_ids: - notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context) - - # create the email to send - self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context) - - return msg_id - - def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None): - # get body - body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '') - - # get subscribers - subscr_obj = self.pool.get('mail.followers') - subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', thread_ids)], context=context) - notif_user_ids = [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)] - - # add users requested to perform an action (need_action mechanism) - if hasattr(self, 'get_needaction_user_ids') and self._columns.get('user_id'): - user_ids_dict = self.get_needaction_user_ids(cr, uid, thread_ids, context=context) - for id, user_ids in user_ids_dict.iteritems(): - notif_user_ids += user_ids - - # add users notified of the parent messages (because: if parent message contains @login, login must receive the replies) - if new_msg_vals.get('parent_id'): - notif_obj = self.pool.get('mail.notification') - parent_notif_ids = notif_obj.search(cr, uid, [('message_id', '=', new_msg_vals.get('parent_id'))], context=context) - parent_notifs = notif_obj.read(cr, uid, parent_notif_ids, context=context) - notif_user_ids += [parent_notif['user_id'][0] for parent_notif in parent_notifs] - - # remove duplicate entries - notif_user_ids = list(set(notif_user_ids)) - return notif_user_ids + def _needaction_domain_get(self, cr, uid, context=None): + if self._needaction: + return [('message_unread', '=', True)] + return [] #------------------------------------------------------ - # Generic message api + # Mail gateway #------------------------------------------------------ def message_capable_models(self, cr, uid, context=None): + """ 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) @@ -335,343 +218,20 @@ class mail_thread(osv.Model): ret_dict[model_name] = model._description return ret_dict - def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, - type='email', email_date=None, parent_id=False, - content_subtype='plain', state=None, - partner_ids=None, email_from=False, email_to=False, - email_cc=None, email_bcc=None, reply_to=None, - headers=None, message_id=False, references=None, - attachments=None, original=None, context=None): - """ Creates a new mail.message through message_create. The new message - is attached to the current mail.thread, containing all the details - passed as parameters. All attachments will be attached to the - thread record as well as to the actual message. - - This method calls message_create that will handle management of - subscription and notifications, and effectively create the message. - - If ``email_from`` is not set or ``type`` not set as 'email', - a note message is created (comment or system notification), - without the usual envelope attributes (sender, recipients, etc.). - - :param threads: list of thread ids, or list of browse_records - representing threads to which a new message should be attached - :param subject: subject of the message, or description of the event; - this is totally optional as subjects are not important except - for specific messages (blog post, job offers) or for emails - :param body_text: plaintext contents of the mail or log message - :param body_html: html contents of the mail or log message - :param type: type of message: 'email', 'comment', 'notification'; - email by default - :param email_date: email date string if different from now, in - server timezone - :param parent_id: id of the parent message (threaded messaging model) - :param content_subtype: optional content_subtype of message: 'plain' - or 'html', corresponding to the main body contents (body_text or - body_html). - :param state: state of message - :param partner_ids: destination partners of the message, in addition - to the now fully optional email_to; this method is supposed to - received a list of ids is not None. The specific many2many - instruction will be generated by this method. - :param email_from: Email From / Sender address if any - :param email_to: Email-To / Recipient address - :param email_cc: Comma-Separated list of Carbon Copy Emails To - addresses if any - :param email_bcc: Comma-Separated list of Blind Carbon Copy Emails To - addresses if any - :param reply_to: reply_to header - :param headers: mail headers to store - :param message_id: optional email identifier - :param references: optional email references - :param dict attachments: map of attachment filenames to binary - contents, if any. - :param str original: optional full source of the RFC2822 email, for - reference - :param dict context: if a ``thread_model`` value is present in the - context, its value will be used to determine the model of the - thread to update (instead of the current model). - """ - if context is None: - context = {} - if attachments is None: - attachments = {} - - if email_date: - edate = parsedate(email_date) - if edate is not None: - email_date = time.strftime('%Y-%m-%d %H:%M:%S', edate) - - if all(isinstance(thread_id, (int, long)) for thread_id in threads): - model = context.get('thread_model') or self._name - model_pool = self.pool.get(model) - threads = model_pool.browse(cr, uid, threads, context=context) - - ir_attachment = self.pool.get('ir.attachment') - - new_msg_ids = [] - for thread in threads: - to_attach = [] - for attachment in attachments: - fname, fcontent = attachment - if isinstance(fcontent, unicode): - fcontent = fcontent.encode('utf-8') - data_attach = { - 'name': fname, - 'datas': base64.b64encode(str(fcontent)), - 'datas_fname': fname, - 'description': _('Mail attachment'), - 'res_model': thread._name, - 'res_id': thread.id, - } - to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context)) - # find related partner: partner_id column in thread object, or self is res.partner model - partner_id = ('partner_id' in thread._columns.keys()) and (thread.partner_id and thread.partner_id.id or False) or False - if not partner_id and thread._name == 'res.partner': - partner_id = thread.id - # destination partners - if partner_ids is None: - partner_ids = [] - mail_partner_ids = [(6, 0, partner_ids)] - - data = { - 'subject': subject, - 'body_text': body_text or thread._model._columns.get('description') and thread.description or '', - 'body_html': body_html or '', - 'parent_id': parent_id, - 'date': email_date or fields.datetime.now(), - 'type': type, - 'content_subtype': content_subtype, - 'state': state, - 'message_id': message_id, - 'partner_ids': mail_partner_ids, - 'attachment_ids': [(6, 0, to_attach)], - 'user_id': uid, - 'model' : thread._name, - 'res_id': thread.id, - 'partner_id': partner_id, - } - - if email_from or type == 'email': - for param in (email_to, email_cc, email_bcc): - if isinstance(param, list): - param = ", ".join(param) - data.update({ - 'email_to': email_to, - 'email_from': email_from or \ - thread._model._columns.get('user_id') and thread.user_id and thread.user_id.user_email, - 'email_cc': email_cc, - 'email_bcc': email_bcc, - 'references': references, - 'headers': headers, - 'reply_to': reply_to, - 'original': original, }) - - new_msg_ids.append(self.message_create(cr, uid, thread.id, data, context=context)) - return new_msg_ids - - def message_append_dict(self, cr, uid, ids, msg_dict, context=None): - """Creates a new mail.message attached to the given threads (``ids``), - with the contents of ``msg_dict``, by calling ``message_append`` - with the mail details. All attachments in msg_dict will be - attached to the object record as well as to the actual - mail message. - - :param dict msg_dict: a map containing the email details and - attachments. See ``message_process()`` and - ``mail.message.parse()`` for details on - the dict structure. - :param dict context: if a ``thread_model`` value is present - in the context, its value will be used - to determine the model of the thread to - update (instead of the current model). - """ - return self.message_append(cr, uid, ids, - subject = msg_dict.get('subject'), - body_text = msg_dict.get('body_text'), - body_html= msg_dict.get('body_html'), - parent_id = msg_dict.get('parent_id', False), - type = msg_dict.get('type', 'email'), - content_subtype = msg_dict.get('content_subtype'), - state = msg_dict.get('state'), - partner_ids = msg_dict.get('partner_ids'), - email_from = msg_dict.get('from', msg_dict.get('email_from')), - email_to = msg_dict.get('to', msg_dict.get('email_to')), - email_cc = msg_dict.get('cc', msg_dict.get('email_cc')), - email_bcc = msg_dict.get('bcc', msg_dict.get('email_bcc')), - reply_to = msg_dict.get('reply', msg_dict.get('reply_to')), - email_date = msg_dict.get('date'), - message_id = msg_dict.get('message-id', msg_dict.get('message_id')), - references = msg_dict.get('references')\ - or msg_dict.get('in-reply-to'), - attachments = msg_dict.get('attachments'), - headers = msg_dict.get('headers'), - original = msg_dict.get('original'), - context = context) - - #------------------------------------------------------ - # Message loading - #------------------------------------------------------ - - def _message_search_ancestor_ids(self, cr, uid, ids, child_ids, ancestor_ids, context=None): - """ Given message child_ids ids, find their ancestors until ancestor_ids - using their parent_id relationship. - - :param child_ids: the first nodes of the search - :param ancestor_ids: list of ancestors. When the search reach an - ancestor, it stops. - """ - def _get_parent_ids(message_list, ancestor_ids, child_ids): - """ Tool function: return the list of parent_ids of messages - contained in message_list. Parents that are in ancestor_ids - or in child_ids are not returned. """ - return [message['parent_id'][0] for message in message_list - if message['parent_id'] - and message['parent_id'][0] not in ancestor_ids - and message['parent_id'][0] not in child_ids - ] - - message_obj = self.pool.get('mail.message') - messages_temp = message_obj.read(cr, uid, child_ids, ['id', 'parent_id'], context=context) - parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) - child_ids += parent_ids - cur_iter = 0; max_iter = 100; # avoid infinite loop - while (parent_ids and (cur_iter < max_iter)): - cur_iter += 1 - messages_temp = message_obj.read(cr, uid, parent_ids, ['id', 'parent_id'], context=context) - parent_ids = _get_parent_ids(messages_temp, ancestor_ids, child_ids) - child_ids += parent_ids - if (cur_iter > max_iter): - _logger.warning("Possible infinite loop in _message_search_ancestor_ids. "\ - "Note that this algorithm is intended to check for cycle in "\ - "message graph, leading to a curious error. Have fun.") - return child_ids - - def message_search_get_domain(self, cr, uid, ids, context=None): - """ OpenChatter feature: get the domain to search the messages related - to a document. mail.thread defines the default behavior as - being messages with model = self._name, id in ids. - This method should be overridden if a model has to implement a - particular behavior. - """ - return ['&', ('res_id', 'in', ids), ('model', '=', self._name)] - - def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, count=False, context=None): - """ OpenChatter feature: return thread messages ids according to the - search domain given by ``message_search_get_domain``. - - It is possible to add in the search the parent of messages by - setting the fetch_ancestors flag to True. In that case, using - the parent_id relationship, the method returns the id list according - to the search domain, but then calls ``_message_search_ancestor_ids`` - that will add to the list the ancestors ids. The search is limited - to parent messages having an id in ancestor_ids or having - parent_id set to False. - - If ``count==True``, the number of ids is returned instead of the - id list. The count is done by hand instead of passing it as an - argument to the search call because we might want to perform - a research including parent messages until some ancestor_ids. - - :param fetch_ancestors: performs an ascended search; will add - to fetched msgs all their parents until - ancestor_ids - :param ancestor_ids: used when fetching ancestors - :param domain: domain to add to the search; especially child_of - is interesting when dealing with threaded display. - Note that the added domain is anded with the - default domain. - :param limit, offset, count, context: as usual - """ - search_domain = self.message_search_get_domain(cr, uid, ids, context=context) - if domain: - search_domain += domain - message_obj = self.pool.get('mail.message') - message_res = message_obj.search(cr, uid, search_domain, limit=limit, offset=offset, count=count, context=context) - if not count and fetch_ancestors: - message_res += self._message_search_ancestor_ids(cr, uid, ids, message_res, ancestor_ids, context=context) - return message_res - - def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, context=None): - """ OpenChatter feature: read the messages related to some threads. - This method is used mainly the Chatter widget, to directly have - read result instead of searching then reading. - - Please see message_search for more information about the parameters. - """ - message_ids = self.message_search(cr, uid, ids, fetch_ancestors, ancestor_ids, - limit, offset, domain, context=context) - messages = self.pool.get('mail.message').read(cr, uid, message_ids, context=context) - - """ Retrieve all attachments names """ - map_id_to_name = dict((attachment_id, '') for message in messages for attachment_id in message['attachment_ids']) - - ids = map_id_to_name.keys() - names = self.pool.get('ir.attachment').name_get(cr, uid, ids, context=context) - - # convert the list of tuples into a dictionnary - for name in names: - map_id_to_name[name[0]] = name[1] - - # give corresponding ids and names to each message - for msg in messages: - msg["attachments"] = [] - - for attach_id in msg["attachment_ids"]: - msg["attachments"].append({'id': attach_id, 'name': map_id_to_name[attach_id]}) - - # Set the threads as read - self.message_check_and_set_read(cr, uid, ids, context=context) - # Sort and return the messages - messages = sorted(messages, key=lambda d: (-d['id'])) - return messages - - def message_get_pushed_messages(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, msg_search_domain=[], context=None): - """ OpenChatter: wall: get the pushed notifications and used them - to fetch messages to display on the wall. - - :param fetch_ancestors: performs an ascended search; will add - to fetched msgs all their parents until - ancestor_ids - :param ancestor_ids: used when fetching ancestors - :param domain: domain to add to the search; especially child_of - is interesting when dealing with threaded display - :param ascent: performs an ascended search; will add to fetched msgs - all their parents until root_ids - :param root_ids: for ascent search - :return: list of mail.messages sorted by date - """ - notification_obj = self.pool.get('mail.notification') - msg_obj = self.pool.get('mail.message') - # update message search - for arg in msg_search_domain: - if isinstance(arg, (tuple, list)): - arg[0] = 'message_id.' + arg[0] - # compose final domain - domain = [('user_id', '=', uid)] + msg_search_domain - # get notifications - notification_ids = notification_obj.search(cr, uid, domain, limit=limit, offset=offset, context=context) - notifications = notification_obj.browse(cr, uid, notification_ids, context=context) - msg_ids = [notification.message_id.id for notification in notifications] - # get messages - msg_ids = msg_obj.search(cr, uid, [('id', 'in', msg_ids)], context=context) - if (fetch_ancestors): msg_ids = self._message_search_ancestor_ids(cr, uid, ids, msg_ids, ancestor_ids, context=context) - msgs = msg_obj.read(cr, uid, msg_ids, context=context) - return msgs + def _message_find_partners(self, cr, uid, message, header_fields=['From'], context=None): + """ Find partners related to some header fields of the message. """ + s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)]) + return [partner_id for email in tools.email_split(s) + for partner_id in self.pool.get('res.partner').search(cr, uid, [('email', 'ilike', email)], context=context)] def _message_find_user_id(self, cr, uid, message, context=None): - from_local_part = to_email(decode(message.get('From')))[0] - user_ids = self.pool.get('res.users').search(cr, uid, [('login', '=', from_local_part)], context=context) + 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, ['|', + ('login', '=', from_local_part), + ('email', '=', from_local_part)], context=context) return user_ids[0] if user_ids else uid - #------------------------------------------------------ - # Mail gateway - #------------------------------------------------------ - # message_process will call either message_new or message_update. - def message_route(self, cr, uid, message, model=None, thread_id=None, custom_values=None, context=None): """Attempt to figure out the correct target model, thread_id, @@ -679,11 +239,11 @@ class mail_thread(osv.Model): Multiple values may be returned, if a message had multiple recipients matching existing mail.aliases, for example. - The following heuristics are used, in this order: + The following heuristics are used, in this order: 1. If the message replies to an existing thread_id, and properly contains the thread model in the 'In-Reply-To' header, use this model/thread_id pair, and ignore - custom_value (not needed as no creation will take place) + custom_value (not needed as no creation will take place) 2. Look for a mail.alias entry matching the message recipient, and use the corresponding model, thread_id, custom_values and user_id. @@ -719,7 +279,7 @@ class mail_thread(osv.Model): _logger.debug('Routing mail with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s', message_id, model, thread_id, custom_values, uid) return [(model, thread_id, custom_values, uid)] - + # 2. Look for a matching mail.alias entry # Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values # for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value. @@ -728,7 +288,7 @@ class mail_thread(osv.Model): decode_header(message, 'Cc'), decode_header(message, 'Resent-To'), decode_header(message, 'Resent-Cc')]) - local_parts = [e.split('@')[0] for e in to_email(rcpt_tos)] + local_parts = [e.split('@')[0] for e in tools.email_split(rcpt_tos)] if local_parts: mail_alias = self.pool.get('mail.alias') alias_ids = mail_alias.search(cr, uid, [('alias_name', 'in', local_parts)]) @@ -742,7 +302,7 @@ class mail_thread(osv.Model): eval(alias.alias_defaults), user_id)) _logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes) return routes - + # 3. Fallback to the provided parameters, if they work model_pool = self.pool.get(model) if not thread_id: @@ -760,19 +320,16 @@ class mail_thread(osv.Model): message_id, model, thread_id, custom_values, uid) return [(model, thread_id, custom_values, uid)] - def message_process(self, cr, uid, model, message, custom_values=None, save_original=False, strip_attachments=False, thread_id=None, context=None): """Process an incoming RFC2822 email message, relying on ``mail.message.parse()`` for the parsing operation, - and ``message_route()`` to figure out the target model. - + and ``message_route()`` to figure out the target model. + Once the target model is known, its ``message_new`` method is called with the new message (if the thread record did not exist) - or its ``message_update`` method (if it did). Finally, - ``message_forward`` is called to automatically notify other - people that should receive this message. + or its ``message_update`` method (if it did). :param string model: the fallback model to use if the message does not match any of the currently configured mail aliases @@ -807,11 +364,9 @@ class mail_thread(osv.Model): routes = self.message_route(cr, uid, msg_txt, model, thread_id, custom_values, context=context) - msg = self.pool.get('mail.message').parse_message(msg_txt, save_original=save_original, context=context) - msg['state'] = 'received' - if strip_attachments and 'attachments' in msg: - del msg['attachments'] - for model, thread_id, custom_values, user_id in routes: + msg = self.message_parse(cr, uid, msg_txt, save_original=save_original, context=context) + if strip_attachments: msg.pop('attachments', None) + for model, thread_id, custom_values, user_id in routes: if self._name != model: context.update({'thread_model': model}) model_pool = self.pool.get(model) @@ -822,10 +377,7 @@ class mail_thread(osv.Model): model_pool.message_update(cr, user_id, [thread_id], msg, context=context) else: thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context) - - # Forward the email to other followers - self.message_forward(cr, uid, model, [thread_id], msg_txt, context=context) - model_pool.message_mark_as_unread(cr, uid, [thread_id], context=context) + self.message_post(cr, uid, [thread_id], context=context, **msg) return True def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): @@ -833,9 +385,7 @@ class mail_thread(osv.Model): for a given thread model, if the message did not belong to an existing thread. The default behavior is to create a new record of the corresponding - model (based on some very basic info extracted from the message), - then attach the message to the newly created record - (by calling ``message_append_dict``). + model (based on some very basic info extracted from the message). Additional behavior may be implemented by overriding this method. :param dict msg_dict: a map containing the email details and @@ -864,14 +414,12 @@ class mail_thread(osv.Model): if custom_values and isinstance(custom_values, dict): data.update(custom_values) res_id = model_pool.create(cr, uid, data, context=context) - self.message_append_dict(cr, uid, [res_id], msg_dict, context=context) return res_id def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None): """Called by ``message_process`` when a new message is received - for an existing thread. The default behavior is to create a - new mail.message in the given thread (by calling - ``message_append_dict``) + for an existing thread. The default behavior is to update the record + with update_vals taken from the incoming email. Additional behavior may be implemented by overriding this method. :param dict msg_dict: a map containing the email details and @@ -883,285 +431,248 @@ class mail_thread(osv.Model): """ if update_vals: self.write(cr, uid, ids, update_vals, context=context) - return self.message_append_dict(cr, uid, ids, msg_dict, context=context) - - def message_thread_followers(self, cr, uid, ids, context=None): - """ Returns a list of email addresses of the people following - this thread, including the sender of each mail, and the - people who were in CC of the messages, if any. - """ - res = {} - if isinstance(ids, (str, int, long)): - ids = [long(ids)] - for thread in self.browse(cr, uid, ids, context=context): - l = set() - for message in thread.message_ids: - l.add((message.user_id and message.user_id.email) or '') - l.add(message.email_from or '') - l.add(message.email_cc or '') - res[thread.id] = filter(None, l) - return res - - def message_forward(self, cr, uid, model, thread_ids, msg, email_error=False, context=None): - """Sends an email to all people following the given threads. - The emails are forwarded immediately, not queued for sending, - and not archived. - - :param str model: thread model - :param list thread_ids: ids of the thread records - :param msg: email.message.Message object to forward - :param email_error: optional email address to notify in case - of any delivery error during the forward. - :return: True - """ - model_pool = self.pool.get(model) - smtp_server_obj = self.pool.get('ir.mail_server') - for res in model_pool.browse(cr, uid, thread_ids, context=context): - if hasattr(model_pool, 'message_thread_followers'): - followers = model_pool.message_thread_followers(cr, uid, [res.id])[res.id] - else: - followers = self.message_thread_followers(cr, uid, [res.id])[res.id] - message_followers_emails = to_email(','.join(filter(None, followers))) - message_recipients = to_email(','.join(filter(None, - [decode(msg['from']), - decode(msg['to']), - decode(msg['cc'])]))) - forward_to = [i for i in message_followers_emails if (i and (i not in message_recipients))] - if forward_to: - # TODO: we need an interface for this for all types of objects, not just leads - if model_pool._columns.get('section_id'): - del msg['reply-to'] - msg['reply-to'] = res.section_id.reply_to - - smtp_from, = to_email(msg['from']) - msg['from'] = smtp_from - msg['to'] = ", ".join(forward_to) - msg['message-id'] = tools.generate_tracking_message_id(res.id) - if not smtp_server_obj.send_email(cr, uid, msg) and email_error: - subj = msg['subject'] - del msg['subject'], msg['to'], msg['cc'], msg['bcc'] - msg['subject'] = _('[OpenERP-Forward-Failed] %s') % subj - msg['to'] = email_error - smtp_server_obj.send_email(cr, uid, msg) return True - def message_partner_by_email(self, cr, uid, email, context=None): - """Attempts to return the id of a partner address matching - the given ``email``, and the corresponding partner id. - Can be used by classes using the ``mail.thread`` mixin - to lookup the partner and use it in their implementation - of ``message_new`` to link the new record with a - corresponding partner. - The keys used in the returned dict are meant to map - to usual names for relationships towards a partner - and one of its addresses. - - :param email: email address for which a partner - should be searched for. + def _message_extract_payload(self, message, save_original=False): + """Extract body as HTML and attachments from the mail message""" + attachments = [] + body = u'' + if save_original: + attachments.append(('original_email.eml', message.as_string())) + if not message.is_multipart() or 'text/' in message.get('content-type', ''): + encoding = message.get_content_charset() + body = message.get_payload(decode=True) + body = tools.ustr(body, encoding, errors='replace') + if message.get_content_type() == 'text/plain': + # text/plain -> <pre/> + body = tools.append_content_to_html(u'', body) + else: + alternative = (message.get_content_type() == 'multipart/alternative') + for part in message.walk(): + if part.get_content_maintype() == 'multipart': + continue # skip container + filename = part.get_filename() # None if normal part + encoding = part.get_content_charset() # None if attachment + # 1) Explicit Attachments -> attachments + if filename or part.get('content-disposition', '').strip().startswith('attachment'): + attachments.append((filename or 'attachment', part.get_payload(decode=True))) + continue + # 2) text/plain -> <pre/> + if part.get_content_type() == 'text/plain' and (not alternative or not body): + body = tools.append_content_to_html(body, tools.ustr(part.get_payload(decode=True), + encoding, errors='replace')) + # 3) text/html -> raw + elif part.get_content_type() == 'text/html': + html = tools.ustr(part.get_payload(decode=True), encoding, errors='replace') + if alternative: + body = html + else: + body = tools.append_content_to_html(body, html, plaintext=False) + # 4) Anything else -> attachment + else: + attachments.append((filename or 'attachment', part.get_payload(decode=True))) + return body, attachments + + def message_parse(self, cr, uid, message, save_original=False, context=None): + """Parses a string or email.message.Message representing an + RFC-2822 email, and returns a generic dict holding the + message details. + + :param message: the message to parse + :type message: email.message.Message | string | unicode + :param bool save_original: whether the returned dict + should include an ``original`` attachment containing + the source of the message :rtype: dict - :return: a map of the following form:: - - { 'partner_address_id': id or False, - 'partner_id': pid or False } + :return: A dict with the following structure, where each + field may not be present if missing in original + message:: + + { 'message-id': msg_id, + 'subject': subject, + 'from': from, + 'to': to, + 'cc': cc, + 'body': unified_body, + 'attachments': [('file1', 'bytes'), + ('file2', 'bytes')} + } """ - partner_pool = self.pool.get('res.partner') - res = {'partner_id': False} - if email: - email = to_email(email)[0] - contact_ids = partner_pool.search(cr, uid, [('email', '=', email)]) - if contact_ids: - contact = partner_pool.browse(cr, uid, contact_ids[0]) - res['partner_id'] = contact.id - return res - - # for backwards-compatibility with old scripts - process_email = message_process + msg_dict = {} + if not isinstance(message, Message): + if isinstance(message, unicode): + # Warning: message_from_string doesn't always work correctly on unicode, + # we must use utf-8 strings here :-( + message = message.encode('utf-8') + message = email.message_from_string(message) + + message_id = message['message-id'] + if not message_id: + # Very unusual situation, be we should be fault-tolerant here + message_id = "<%s@localhost>" % time.time() + _logger.debug('Parsing Message without message-id, generating a random one: %s', message_id) + msg_dict['message_id'] = message_id + + if 'Subject' in message: + msg_dict['subject'] = decode(message.get('Subject')) + + # Envelope fields not stored in mail.message but made available for message_new() + msg_dict['from'] = decode(message.get('from')) + msg_dict['to'] = decode(message.get('to')) + msg_dict['cc'] = decode(message.get('cc')) + + if 'From' in message: + author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context) + if author_ids: + msg_dict['author_id'] = author_ids[0] + partner_ids = self._message_find_partners(cr, uid, message, ['From', 'To', 'Cc'], context=context) + msg_dict['partner_ids'] = partner_ids + + if 'Date' in message: + date_hdr = decode(message.get('Date')) + # convert from email timezone to server timezone + date_server_datetime = dateutil.parser.parse(date_hdr).astimezone(pytz.timezone(tools.get_server_timezone())) + date_server_datetime_str = date_server_datetime.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + msg_dict['date'] = date_server_datetime_str + + if 'In-Reply-To' in message: + parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', decode(message['In-Reply-To']))]) + if parent_ids: + msg_dict['parent_id'] = parent_ids[0] + + if 'References' in message and 'parent_id' not in msg_dict: + parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', 'in', + [x.strip() for x in decode(message['References']).split()])]) + if parent_ids: + msg_dict['parent_id'] = parent_ids[0] + + msg_dict['body'], msg_dict['attachments'] = self._message_extract_payload(message) + return msg_dict #------------------------------------------------------ # Note specific #------------------------------------------------------ def log(self, cr, uid, id, message, secondary=False, context=None): - _logger.warning("log() is deprecated. As this module inherit from \ - mail.thread, the message will be managed by this \ - module instead of by the res.log mechanism. Please \ - use the mail.thread OpenChatter API instead of the \ - now deprecated res.log.") - self.message_append_note(cr, uid, [id], 'res.log', message, context=context) + _logger.warning("log() is deprecated. As this module inherit from "\ + "mail.thread, the message will be managed by this "\ + "module instead of by the res.log mechanism. Please "\ + "use mail_thread.message_post() instead of the "\ + "now deprecated res.log.") + self.message_post(cr, uid, [id], message, context=context) + + def message_post(self, cr, uid, thread_id, body='', subject=False, + type='notification', parent_id=False, attachments=None, context=None, **kwargs): + """ Post a new message in an existing thread, returning the new + mail.message ID. Extra keyword arguments will be used as default + column values for the new mail.message record. + + :param int thread_id: thread ID to post into, or list with one ID + :param str body: body of the message, usually raw HTML that will + be sanitized + :param str subject: optional subject + :param str type: mail_message.type + :param int parent_id: optional ID of parent message in this thread + :param tuple(str,str) attachments: list of attachment tuples in the form + ``(name,content)``, where content is NOT base64 encoded + :return: ID of newly created mail.message + """ + context = context or {} + attachments = attachments or [] + assert (not thread_id) or isinstance(thread_id, (int,long)) or \ + (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id" + if isinstance(thread_id, (list, tuple)): + thread_id = thread_id and thread_id[0] + + attachment_ids = [] + for name, content in attachments: + if isinstance(content, unicode): + content = content.encode('utf-8') + data_attach = { + 'name': name, + 'datas': base64.b64encode(str(content)), + 'datas_fname': name, + 'description': name, + 'res_model': context.get('thread_model') or self._name, + 'res_id': thread_id, + } + attachment_ids.append((0, 0, data_attach)) - def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False, - type='notification', content_subtype='html', context=None): - if content_subtype == 'html': - body_html = body - body_text = body - else: - body_html = body - body_text = body - return self.message_append(cr, uid, ids, subject, body_html, body_text, - type, parent_id=parent_id, - content_subtype=content_subtype, context=context) + values = kwargs + values.update({ + 'model': context.get('thread_model', self._name) if thread_id else False, + 'res_id': thread_id or False, + 'body': body, + 'subject': subject, + 'type': type, + 'parent_id': parent_id, + 'attachment_ids': attachment_ids, + }) + for x in ('from', 'to', 'cc'): values.pop(x, None) # Avoid warnings + return self.pool.get('mail.message').create(cr, uid, values, context=context) #------------------------------------------------------ - # Subscription mechanism + # Followers API #------------------------------------------------------ - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Returns a list of fields containing a res.user.id. Those fields - will be checked to automatically subscribe those users. + def message_subscribe_users(self, cr, uid, ids, user_ids=None, context=None): + """ Wrapper on message_subscribe, using users. If user_ids is not + provided, subscribe uid instead. """ + if not user_ids: user_ids = [uid] + partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] + return self.message_subscribe(cr, uid, ids, partner_ids, context=context) + + def message_subscribe(self, cr, uid, ids, partner_ids, context=None): + """ Add partners to the records followers. + :param partner_ids: a list of partner_ids to subscribe + :param return: new value of followers if read_back key in context """ + self.write(cr, uid, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context) + if context and context.get('read_back'): + return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] return [] - def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): - """ Subscribe the user (or user_ids) to the current document. - - :param user_ids: a list of user_ids; if not set, subscribe - uid instead - :param return: new value of followers, for Chatter - """ - to_subscribe_uids = [uid] if user_ids is None else user_ids - write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_subscribe_get_command(cr, uid, to_subscribe_uids, context)}, context=context) - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] - - def message_subscribe_get_command(self, cr, uid, follower_ids, context=None): - """ Generate the many2many command to add followers. """ - return [(4, id) for id in follower_ids] - - def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): - """ Unsubscribe the user (or user_ids) from the current document. - - :param user_ids: a list of user_ids; if not set, subscribe - uid instead - :param return: new value of followers, for Chatter - """ - to_unsubscribe_uids = [uid] if user_ids is None else user_ids - write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_unsubscribe_get_command(cr, uid, to_unsubscribe_uids, context)}, context=context) - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] - - def message_unsubscribe_get_command(self, cr, uid, follower_ids, context=None): - """ Generate the many2many command to remove followers. """ - return [(3, id) for id in follower_ids] - - #------------------------------------------------------ - # Notification API - #------------------------------------------------------ - - def message_create_notify_by_email(self, cr, uid, new_msg_values, user_to_notify_ids, context=None): - """ When creating a new message and pushing notifications, emails - must be send if users have chosen to receive notifications - by email via the notification_email_pref field. - - ``notification_email_pref`` can have 3 values : - - all: receive all notification by email (for example for shared - users) - - to_me: messages send directly to me (@login, messages on res.users) - - never: never receive notifications - Note that an user should never receive notifications for messages - he has created. - - :param new_msg_values: dictionary of message values, those that - are given to the create method - :param user_to_notify_ids: list of user_ids, user that will - receive a notification on their Wall + def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): + """ Wrapper on message_subscribe, using users. If user_ids is not + provided, unsubscribe uid instead. """ + if not user_ids: user_ids = [uid] + partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] + return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context) + + def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None): + """ Remove partners from the records followers. + :param partner_ids: a list of partner_ids to unsubscribe + :param return: new value of followers if read_back key in context """ - message_obj = self.pool.get('mail.message') - res_users_obj = self.pool.get('res.users') - body = new_msg_values.get('body_html', '') if new_msg_values.get('content_subtype') == 'html' else new_msg_values.get('body_text', '') - - # remove message writer - if user_to_notify_ids.count(new_msg_values.get('user_id')) > 0: - user_to_notify_ids.remove(new_msg_values.get('user_id')) - - # try to find an email_to - email_to = '' - for user in res_users_obj.browse(cr, uid, user_to_notify_ids, context=context): - # TO BE REFACTORED BY FP, JUSTE REMOVED TO_ME, NOT SURE WHAT S NEW BEHAVIOR - if not user.notification_email_pref == 'all': - continue - if not user.email: - continue - email_to = '%s, %s' % (email_to, user.email) - email_to = email_to.lstrip(', ') - - # did not find any email address: not necessary to create an email - if not email_to: - return - - # try to find an email_from - current_user = res_users_obj.browse(cr, uid, [uid], context=context)[0] - email_from = new_msg_values.get('email_from') - if not email_from: - email_from = current_user.email - - # get email content, create it (with mail_message.create) - email_values = self.message_create_notify_get_email_dict(cr, uid, new_msg_values, email_from, email_to, context) - email_id = message_obj.create(cr, uid, email_values, context=context) - return email_id - - def message_create_notify_get_email_dict(self, cr, uid, new_msg_values, email_from, email_to, context=None): - values = dict(new_msg_values) - - body_html = new_msg_values.get('body_html', '') - if body_html: - body_html += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.' - body_text = new_msg_values.get('body_text', '') - if body_text: - body_text += '\n\n----------\nThis email was send automatically by OpenERP, because you have subscribed to a document.' - values.update({ - 'type': 'email', - 'state': 'outgoing', - 'email_from': email_from, - 'email_to': email_to, - 'subject': 'New message', - 'content_subtype': new_msg_values.get('content_subtype', 'plain'), - 'body_html': body_html, - 'body_text': body_text, - 'auto_delete': True, - 'res_model': '', - 'res_id': False, - }) - return values - - def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None): - notif_obj = self.pool.get('mail.notification') - msg_obj = self.pool.get('mail.message') - if remove_childs: - notif_msg_ids = msg_obj.search(cr, uid, [('id', 'child_of', msg_ids)], context=context) - else: - notif_msg_ids = msg_ids - to_del_notif_ids = notif_obj.search(cr, uid, ['&', ('user_id', '=', uid), ('message_id', 'in', notif_msg_ids)], context=context) - return notif_obj.unlink(cr, uid, to_del_notif_ids, context=context) + self.write(cr, uid, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context) + if context and context.get('read_back'): + return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + return [] #------------------------------------------------------ - # Thread_state + # Thread state #------------------------------------------------------ - def message_create_set_unread(self, cr, uid, ids, context=None): - """ When creating a new message, set as unread if uid is not the - object responsible. """ - for obj in self.browse(cr, uid, ids, context=context): - if obj.message_state and ('user_id' in obj._columns.keys()) and (not obj.user_id or obj.user_id.id != uid) : - self.message_mark_as_unread(cr, uid, [obj.id], context=context) - - def message_check_and_set_unread(self, cr, uid, ids, context=None): - """ Set unread if uid is the object responsible or if the object has - no responsible. """ - for obj in self.browse(cr, uid, ids, context=context): - if obj.message_state and self._columns.get('user_id') and (not obj.user_id or obj.user_id.id == uid): - self.message_mark_as_unread(cr, uid, [obj.id], context=context) - def message_mark_as_unread(self, cr, uid, ids, context=None): """ Set as unread. """ - return self.write(cr, uid, ids, {'message_state': False}, context=context) + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + cr.execute(''' + UPDATE mail_notification SET + read=false + WHERE + message_id IN (SELECT id from mail_message where res_id=any(%s) and model=%s limit 1) and + partner_id = %s + ''', (ids, self._name, partner_id)) + return True - def message_check_and_set_read(self, cr, uid, ids, context=None): - """ Set read if uid is the object responsible. """ - for obj in self.browse(cr, uid, ids, context=context): - if not obj.message_state and self._columns.get('user_id') and obj.user_id and obj.user_id.id == uid: - self.message_mark_as_read(cr, uid, [obj.id], context=context) - def message_mark_as_read(self, cr, uid, ids, context=None): """ Set as read. """ - return self.write(cr, uid, ids, {'message_state': True}, context=context) - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + partner_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).partner_id.id + cr.execute(''' + UPDATE mail_notification SET + read=true + WHERE + message_id IN (SELECT id FROM mail_message WHERE res_id=ANY(%s) AND model=%s) AND + partner_id = %s + ''', (ids, self._name, partner_id)) + return True diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index f6ef5bc2ee9..34efa05f812 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -3,7 +3,7 @@ <data> <!-- Top menu item --> - <menuitem name="Feeds" + <menuitem name="Home" id="mail_feeds_main" groups="base.group_user" sequence="10"/> @@ -11,8 +11,18 @@ <!-- Left-side menu: Feeds --> <menuitem id="mail_feeds" name="Feeds" parent="mail_feeds_main" groups="base.group_user" sequence="10"/> - <menuitem id="mail_wallfeeds" name="News Feed" parent="mail_feeds" action="action_mail_all_feeds" sequence="10"/> - <menuitem id="mail_myfeeds" name="My Feeds" parent="mail_feeds" action="action_mail_my_feeds" sequence="11"/> + <record id="mail_wallfeeds" model="ir.ui.menu"> + <field name="name">My Feeds</field> + <field name="sequence" eval="10"/> + <field name="action" ref="action_mail_all_feeds"/> + <field name="parent_id" ref="mail_feeds"/> + </record> + <record id="mail_myfeeds" model="ir.ui.menu"> + <field name="name">My Posts</field> + <field name="sequence" eval="11"/> + <field name="action" ref="action_mail_my_feeds"/> + <field name="parent_id" ref="mail_feeds"/> + </record> </data> </openerp> diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index d3a61b31a8a..2988cee3d9c 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -19,29 +19,27 @@ # ############################################################################## -from osv import osv +from osv import osv, fields class res_partner_mail(osv.Model): - """ Inherits partner and adds CRM information in the partner form """ + """ Update partner to add a field about notification preferences """ _name = "res.partner" _inherit = ['res.partner', 'mail.thread'] - def message_search_get_domain(self, cr, uid, ids, context=None): - """ Override of message_search_get_domain for partner discussion page. - The purpose is to add messages directly sent to the partner. It also - adds messages pushed to the related user, if any, using @login. - """ - initial_domain = super(res_partner_mail, self).message_search_get_domain(cr, uid, ids, context=context) - # to avoid models inheriting from res.partner - if self._name != 'res.partner': - return initial_domain - # add message linked to the partner - search_domain = ['|'] + initial_domain + ['|', ('partner_id', 'in', ids), ('partner_ids', 'in', ids)] - # if partner is linked to a user: find @login - res_users_obj = self.pool.get('res.users') - user_ids = res_users_obj.search(cr, uid, [('partner_id', 'in', ids)], context=context) - for user in res_users_obj.browse(cr, uid, user_ids, context=context): - search_domain = ['|'] + search_domain + ['|', ('body_text', 'like', '@%s' % (user.login)), ('body_html', 'like', '@%s' % (user.login))] - return search_domain + _columns = { + 'notification_email_send': fields.selection([ + ('all', 'All feeds'), + ('comment', 'Comments and Emails'), + ('email', 'Emails only'), + ('none', 'Never') + ], 'Receive Feeds by Email', required=True, + help="Choose in which case you want to receive an email when you "\ + "receive new feeds."), + } + + _defaults = { + 'notification_email_send': lambda *args: 'comment' + } + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index d9c77cb83f4..f9ca54c478d 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -34,38 +34,41 @@ class res_users(osv.Model): _inherits = {'mail.alias': 'alias_id'} _columns = { - 'notification_email_pref': fields.selection([ - ('all', 'All Feeds'), - ('to_me', 'Only send directly to me'), - ('none', 'Never') - ], 'Receive Feeds by Email', required=True, - help="Choose in which case you want to receive an email when you "\ - "receive new feeds."), 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, help="Email address internally associated with this user. Incoming "\ "emails will appear in the user's notifications."), } _defaults = { - 'notification_email_pref': 'to_me', 'alias_domain': False, # always hide alias during creation } def __init__(self, pool, cr): - """ Override of __init__ to add access rights on notification_email_pref + """ Override of __init__ to add access rights on notification_email_send field. Access rights are disabled by default, but allowed on fields defined in self.SELF_WRITEABLE_FIELDS. """ init_res = super(res_users, self).__init__(pool, cr) # duplicate list to avoid modifying the original reference self.SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS) - self.SELF_WRITEABLE_FIELDS.append('notification_email_pref') + self.SELF_WRITEABLE_FIELDS.append('notification_email_send') return init_res def _auto_init(self, cr, context=None): - """Installation hook to 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, + """ 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, self._columns['alias_id'], 'login', alias_force_key='id', context=context) + # make already existing users follow themselves, using SQL to avoid using the ORM during the auto_init + cr.execute(""" SELECT p.id FROM res_partner p + LEFT JOIN mail_followers n + ON (n.partner_id = p.id AND n.res_model = 'res.partner' AND n.res_id = p.id) + WHERE n.id IS NULL + """) + params = [(res[0], res[0]) for res in cr.fetchall()] + cr.executemany(""" INSERT INTO mail_followers (partner_id, res_model, res_id) + VALUES (%s, 'res.partner', %s) + """, params) def create(self, cr, uid, data, context=None): # create default alias same as the login @@ -73,23 +76,23 @@ class res_users(osv.Model): alias_id = mail_alias.create_unique_alias(cr, uid, {'alias_name': data['login']}, model_name=self._name, context=context) data['alias_id'] = alias_id data.pop('alias_name', None) # prevent errors during copy() + # create user that follows its related partner user_id = super(res_users, self).create(cr, uid, data, context=context) user = self.browse(cr, uid, user_id, context=context) - self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user_id], context=context) + self.pool.get('res.partner').message_subscribe(cr, uid, [user.partner_id.id], [user.partner_id.id], context=context) # alias mail_alias.write(cr, SUPERUSER_ID, [alias_id], {"alias_force_thread_id": user_id}, context) # create a welcome message - self.create_welcome_message(cr, uid, user, context=context) + self._create_welcome_message(cr, uid, user, context=context) return user_id - def create_welcome_message(self, cr, uid, user, context=None): + def _create_welcome_message(self, cr, uid, user, context=None): company_name = user.company_id.name if user.company_id else _('the company') - subject = '''%s has joined %s.''' % (user.name, company_name) - body = '''Welcome to OpenERP !''' - # TODO change 1 into user.id but catch errors - return self.pool.get('res.partner').message_append_note(cr, 1, [user.partner_id.id], - subject=subject, body=body, type='comment', content_subtype='html', context=context) + body = _('%s has joined %s.') % (user.name, company_name) + # TODO change SUPERUSER_ID into user.id but catch errors + return self.pool.get('res.partner').message_post(cr, SUPERUSER_ID, [user.partner_id.id], + body=body, context=context) def write(self, cr, uid, ids, vals, context=None): # User alias is sync'ed with login @@ -104,52 +107,19 @@ class res_users(osv.Model): alias_pool.unlink(cr, uid, alias_ids, context=context) return res - # -------------------------------------------------- - # Wrappers on partner methods for Chatter - # #FIXME: another branch holds a refactoring of mail.thread - # that should help cleaning those wrappers - # -------------------------------------------------- - - def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, - type='email', email_date=None, parent_id=False, - content_subtype='plain', state=None, - partner_ids=None, email_from=False, email_to=False, - email_cc=None, email_bcc=None, reply_to=None, - headers=None, message_id=False, references=None, - attachments=None, original=None, context=None): - for user in self.browse(cr, uid, threads, context=context): - user.partner_id.message_append(subject, body_text, body_html, type, email_date, parent_id, - content_subtype, state, partner_ids, email_from, email_to, email_cc, email_bcc, reply_to, - headers, message_id, references, attachments, original) - - def message_read(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_read(fetch_ancestors, ancestor_ids, limit, offset, domain) - - def message_search(self, cr, uid, ids, fetch_ancestors=False, ancestor_ids=None, - limit=100, offset=0, domain=None, count=False, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_search(fetch_ancestors, ancestor_ids, limit, offset, domain, count) - - def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_subscribe(user_ids) - - def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None): - for user in self.browse(cr, uid, ids, context=context): - return user.partner_id.message_unsubscribe(user_ids) - + def message_post(self, cr, uid, thread_id, **kwargs): + partner_id = self.pool.get('res.users').browse(cr, uid, thread_id)[0].partner_id.id + return self.pool.get('res.partner').message_post(cr, uid, partner_id, **kwargs) class res_users_mail_group(osv.Model): - """ Update of res.groups class - - if adding/removing users from a group, check mail.groups linked to - this user group, and subscribe / unsubscribe them from the discussion - group. This is done by overriding the write method. + """ Update of res.users class + - if adding groups to an user, check mail.groups linked to this user + group, and the user. This is done by overriding the write method. """ _name = 'res.users' _inherit = ['res.users'] + # FP Note: to improve, post processing may be better ? def write(self, cr, uid, ids, vals, context=None): write_res = super(res_users_mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('groups_id'): @@ -158,26 +128,25 @@ class res_users_mail_group(osv.Model): user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]] mail_group_obj = self.pool.get('mail.group') mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', user_group_ids)], context=context) - mail_group_obj.message_subscribe(cr, uid, mail_group_ids, ids, context=context) + mail_group_obj.message_subscribe_users(cr, uid, mail_group_ids, ids, context=context) return write_res class res_groups_mail_group(osv.Model): """ Update of res.groups class - - if adding/removing users from a group, check mail.groups linked to - this user group, and subscribe / unsubscribe them from the discussion - group. This is done by overriding the write method. + - if adding users from a group, check mail.groups linked to this user + group and subscribe them. This is done by overriding the write method. """ _name = 'res.groups' _inherit = 'res.groups' + # FP Note: to improve, post processeing, after the super may be better def write(self, cr, uid, ids, vals, context=None): + write_res = super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) if vals.get('users'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} user_ids = [command[1] for command in vals['users'] if command[0] == 4] user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]] mail_group_obj = self.pool.get('mail.group') mail_group_ids = mail_group_obj.search(cr, uid, [('group_ids', 'in', ids)], context=context) - mail_group_obj.message_subscribe(cr, uid, mail_group_ids, user_ids, context=context) - return super(res_groups_mail_group, self).write(cr, uid, ids, vals, context=context) - -# vim:et: + mail_group_obj.message_subscribe_users(cr, uid, mail_group_ids, user_ids, context=context) + return write_res diff --git a/addons/mail/res_users_view.xml b/addons/mail/res_users_view.xml index 005d71cb09f..a119765e1ec 100644 --- a/addons/mail/res_users_view.xml +++ b/addons/mail/res_users_view.xml @@ -10,7 +10,7 @@ <field name="arch" type="xml"> <data> <field name="email" position="before"> - <field name="notification_email_pref" readonly="0"/> + <field name="notification_email_send" readonly="0"/> </field> </data> </field> @@ -24,7 +24,7 @@ <field name="arch" type="xml"> <data> <field name="email" position="before"> - <field name="notification_email_pref"/> + <field name="notification_email_send"/> </field> <field name="email" position="after"> <field name="alias_domain" invisible="1"/> diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css index 28a32b8c7a3..815f0ec92c4 100644 --- a/addons/mail/static/src/css/mail.css +++ b/addons/mail/static/src/css/mail.css @@ -1,56 +1,46 @@ /* ------------------------------------------------------------ */ -/* Wall +/* Reset because of ugly display of end of August /* ------------------------------------------------------------ */ -.openerp div.oe_mail_wall { - overflow: auto; +.openerp .oe_mail_wall ul, .openerp .oe_mail_wall li { + list-style-type: none; padding: 0; - background: white; -} - -.openerp div.oe_mail_wall_main { - float: left; - width: 560px; - margin: 8px; + margin: 0; } -.openerp div.oe_mail_wall_aside { - margin-left: 565px; - margin: 8px; +.openerp .oe_chatter ul, .openerp .oe_chatter li { + list-style-type: none; + padding: 0; + margin: 0; } -.openerp div.oe_mail_wall_action { - padding: 8px; - background: #eee; - border: 1px solid #ccc; -} -.openerp div.oe_mail_wall_action:after { - content: ""; - display: block; - clear: both; -} +/* ------------------------------------------------------------ */ +/* Wall +/* ------------------------------------------------------------ */ -.openerp div.oe_mail_wall_action .oe_mail_msg_content { - width: 484px; +.openerp div.oe_mail_wall { + overflow: auto; + padding: 0; + background: white; } -.openerp div.oe_mail_wall_action textarea.oe_mail_compose_textarea, -.openerp div.oe_mail_wall_action div.oe_mail_compose_message_body_text textarea { - width: 474px; - height: 60px; - padding: 4px; - margin-bottom: 2px; +.openerp div.oe_mail_wall div.oe_mail_wall_aside { + margin-left: 565px; + margin: 8px; } -.openerp ul.oe_mail_wall_threads { - margin-top: 8px; +.openerp div.oe_mail_wall ul.oe_mail_wall_threads { + float: left; + width: 560px; + margin: 8px; + list-style-type: none; } /* Specific display of threads in the wall */ /* ------------------------------------------------------------ */ -.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea { +.openerp ul.oe_mail_wall_threads .oe_mail_msg_content textarea.oe_mail_compose_textarea { width: 434px; height: 30px; padding: 4px; @@ -79,11 +69,6 @@ width: 440px; } -.openerp div.oe_mail_wall_more { - text-align: center; - display: none; -} - /* ------------------------------------------------------------ */ /* RecordThread @@ -93,10 +78,6 @@ overflow: auto; } -.openerp div.oe_mail_recthread { - /*overflow: auto;*/ -} - .openerp div.oe_mail_recthread_main { float: left; width: 560px; @@ -165,9 +146,9 @@ clear: both; } -/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */ +/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */ .openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea, -.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea { +.openerp .oe_mail_msg_content div.oe_mail_compose_message_body textarea { width: 474px; height: 60px; padding: 4px; @@ -175,9 +156,9 @@ border: 1px solid #cccccc; } -/* default textarea (oe_mail_compose_textarea), and body_text textarea for compose form view */ +/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */ .openerp .oe_mail_msg_content textarea.oe_mail_compose_textarea:focus, -.openerp .oe_mail_msg_content div.oe_mail_compose_message_body_text textarea:focus { +.openerp .oe_mail_msg_content div.oe_mail_compose_message_body textarea:focus { outline: 0; border-color: rgba(82, 168, 236, 0.8); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); @@ -341,11 +322,7 @@ text-decoration: none; } -.openerp .oe_mail_msg_subtitle { - margin: 0 0 .5em 0; -} -.openerp .oe_mail_msg_footer, -.openerp .oe_mail_msg_subtitle { +.openerp .oe_mail_msg_footer { color: #888; } @@ -359,12 +336,13 @@ text-align: justify; } -.openerp .oe_mail_msg_body span.oe_mail_msg_tail { - display: inline; +.openerp .oe_mail_msg_record_body pre { + font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif; + margin: 0px; } -.openerp .oe_mail_oe_bold { - font-weight: bold; +.openerp .oe_mail_msg_body span.oe_mail_msg_tail { + display: inline; } /* Read more/less link */ diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css index 4cd1339ded4..ba7ed36a695 100644 --- a/addons/mail/static/src/css/mail_compose_message.css +++ b/addons/mail/static/src/css/mail_compose_message.css @@ -2,10 +2,6 @@ /* Compose Message Wizard Form */ /* ------------------------------ */ -.openerp tr td .oe_form_field.oe_mail_compose_message_invisible { - display: none; -} - .openerp .oe_mail_compose_message_icons { text-align: right; } diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index e3d688e3337..3f23e65f00d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -18,11 +18,14 @@ openerp.mail = function(session) { session.web.FormView = session.web.FormView.extend({ do_action: function(action, on_close) { - if (action.res_model == 'mail.compose.message' && this.fields && this.fields.message_ids && this.fields.message_ids.view.get("actual_mode") != 'create') { + if (action.res_model == 'mail.compose.message' && + action.context && action.context.redirect == true && + this.fields && this.fields.message_ids && + this.fields.message_ids.view.get("actual_mode") != 'create') { var record_thread = this.fields.message_ids; var thread = record_thread.thread; - thread.instantiate_composition_form('comment', true, false, 0, action.context); - return false; + thread.refresh_composition_form(action.context); + return true; } else { return this._super(action, on_close); @@ -30,7 +33,6 @@ openerp.mail = function(session) { }, }); - /** * ------------------------------------------------------------ * ChatterUtils @@ -38,181 +40,37 @@ openerp.mail = function(session) { * * This class holds a few tools method that will be used by * the various Chatter widgets. + * + * Some regular expressions not used anymore, kept because I want to + * - (^|\s)@((\w|@|\.)*): @login@log.log + * 1. '(void)' + * 2. login@log.log + * - (^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]: [ir.attachment,3|My Label], + * for internal links + * 1. '(void)' + * 2. 'ir' + * 3. 'attachment' + * 4. '3' + * 5. 'My Label' */ mail.ChatterUtils = { - /** - * mail_int_mapping: structure to keep a trace of internal links mapping - * mail_int_mapping['model'] = { - * 'name_get': [[id,label], [id,label], ...] - * 'fetch_ids': [id, id, ...] } */ - //var mail_int_mapping = {}; - - /** - * mail_msg_struct: structure to orrganize chatter messages - */ - //var mail_msg_struct = {}; // TODO: USE IT OR NOT :) - - /* generic chatter events binding */ - bind_events: function(widget) { - // event: click on an internal link to a document: model, login - widget.$el.delegate('a.oe_mail_internal_link', 'click', function (event) { - event.preventDefault(); - // lazy implementation: fetch data and try to redirect - if (! event.srcElement.dataset.resModel) return false; - else var res_model = event.srcElement.dataset.resModel; - var res_login = event.srcElement.dataset.resLogin; - if (! res_login) return false; - var ds = new session.web.DataSet(widget, res_model); - var defer = ds.call('search', [[['login', '=', res_login]]]).pipe(function (records) { - if (records[0]) { - widget.do_action({ type: 'ir.actions.act_window', res_model: res_model, res_id: parseInt(records[0]), views: [[false, 'form']]}); - } - else return false; - }); - }); - }, - /** get an image in /web/binary/image?... */ get_image: function(session_prefix, session_id, model, field, id) { return session_prefix + '/web/binary/image?session_id=' + session_id + '&model=' + model + '&field=' + field + '&id=' + (id || ''); }, - /** checks if tue current user is the message author */ + /** check if the current user is the message author */ is_author: function (widget, message_user_id) { return (widget.session && widget.session.uid != 0 && widget.session.uid == message_user_id); }, - /** - * Add records to comments_structure array - * @param {Array} records records from mail.message sorted by date desc - * @returns {Object} cs comments_structure: dict - * cs.model_to_root_ids = {model: [root_ids], } - * cs.new_root_ids = [new_root_ids] - * cs.root_ids = [root_ids] - * cs.msgs = {record.id: record,} - * cs.tree_struct = {record.id: { - * 'level': record_level in hierarchy, 0 is root, - * 'msg_nbr': number of childs, - * 'direct_childs': [msg_ids], - * 'all_childs': [msg_ids], - * 'for_thread_msgs': [records], - * 'ancestors': [msg_ids], } } - */ - records_struct_add_records: function(cs, records, parent_id) { - var cur_iter = 0; var max_iter = 10; var modif = true; - while ( modif && (cur_iter++) < max_iter) { - modif = false; - _(records).each(function (record) { - // root and not yet recorded - if ( (record.parent_id == false || record.parent_id[0] == parent_id) && ! cs['msgs'][record.id]) { - // add to model -> root_list ids - if (! cs['model_to_root_ids'][record.model]) cs['model_to_root_ids'][record.model] = [record.id]; - else cs['model_to_root_ids'][record.model].push(record.id); - // add root data - cs['new_root_ids'].push(record.id); - // add record - cs['tree_struct'][record.id] = {'level': 0, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [record], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; - } - // not yet recorded, but parent is recorded - else if (! cs['msgs'][record.id] && cs['msgs'][record.parent_id[0]]) { - var parent_level = cs['tree_struct'][record.parent_id[0]]['level']; - // update parent structure - cs['tree_struct'][record.parent_id[0]]['direct_childs'].push(record.id); - cs['tree_struct'][record.parent_id[0]]['for_thread_msgs'].push(record); - // update ancestors structure - for (ancestor_id in cs['tree_struct'][record.parent_id[0]]['ancestors']) { - cs['tree_struct'][ancestor_id]['all_childs'].push(record.id); - } - // add record - cs['tree_struct'][record.id] = {'level': parent_level+1, 'direct_childs': [], 'all_childs': [], 'for_thread_msgs': [], 'msg_nbr': -1, 'ancestors': []}; - cs['msgs'][record.id] = record; - modif = true; - } - }); - } - return cs; - }, - - /* copy cs.new_root_ids into cs.root_ids */ - records_struct_update_after_display: function(cs) { - // update TODO - cs['root_ids'] = _.union(cs['root_ids'], cs['new_root_ids']); - cs['new_root_ids'] = []; - return cs; - }, - - /** - * CONTENT MANIPULATION - * - * Regular expressions - * - (^|\s)@((\w|@|\.)*): @login@log.log, supports inner '@' for - * logins that are emails - * 1. '(void)' - * 2. login@log.log - * - (^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]: [ir.attachment,3|My Label], - * for internal links to model ir.attachment, id=3, and with - * optional label 'My Label'. Note that having a '|Label' is not - * mandatory, because the regex should still be correct. - * 1. '(void)' - * 2. 'ir' - * 3. 'attachment' - * 4. '3' - * 5. 'My Label' - */ - - /** Removes html tags, except b, em, br, ul, li */ - do_text_remove_html_tags: function (string) { - var html = $('<div/>').text(string.replace(/\s+/g, ' ')).html().replace(new RegExp('<(/)?(b|em|br|br /|ul|li|div)\\s*>', 'gi'), '<$1$2>'); - return html; - }, - - /** Replaces line breaks by html line breaks (br) */ - do_text_nl2br: function (str, is_xhtml) { - var break_tag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>'; - return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2'); - }, - - /* Add a prefix before each new line of the original string */ - do_text_quote: function (str, prefix) { - return str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ break_tag +'$2' + prefix || '> '); - }, - - /** - * Replaces some expressions - * - @login - shorcut to link to a res.user, given its login - * - [ir.attachment,3|My Label] - shortcut to an internal - * document + /** Replaces some expressions * - :name - shortcut to an image */ do_replace_expressions: function (string) { - var self = this; var icon_list = ['al', 'pinky'] - /* shortcut to user: @login */ - var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var login = regex_res[2]; - string = string.replace(regex_res[0], regex_res[1] + '<a href="#" class="oe_mail_internal_link" data-res-model="res.users" data-res-login = ' + login + '>@' + login + '</a>'); - regex_res = regex_login.exec(string); - } - /* shortcut for internal document */ - var regex_login = new RegExp(/(^|\s)\[(\w+).(\w+),(\d)\|*((\w|[@ .,])*)\]/g); - var regex_res = regex_login.exec(string); - while (regex_res != null) { - var res_model = regex_res[2] + '.' + regex_res[3]; - var res_id = regex_res[4]; - if (! regex_res[5]) { - var label = res_model + ':' + res_id } - else { - var label = regex_res[5]; - } - string = string.replace(regex_res[0], regex_res[1] + '<a href="#model=' + res_model + '&id=' + res_id + '>' + label + '</a>'); - regex_res = regex_login.exec(string); - } /* special shortcut: :name, try to find an icon if in list */ var regex_login = new RegExp(/(^|\s):((\w)*)/g); var regex_res = regex_login.exec(string); @@ -224,46 +82,6 @@ openerp.mail = function(session) { } return string; }, - - /** - * Checks a string to find an expression that will be replaced - * by an internal link and requiring a name_get to replace - * the expression. - * :param mapping: structure to keep a trace of internal links mapping - * mapping['model'] = { - * name_get': [[id,label], [id,label], ...] - * 'to_fetch_ids': [id, id, ...] - * } - * CURRENTLY NOT IMPLEMENTED */ - do_check_for_name_get_mapping: function(string, mapping) { - /* shortcut to user: @login */ - //var regex_login = new RegExp(/(^|\s)@((\w|@|\.)*)/g); - //var regex_res = regex_login.exec(string); - //while (regex_res != null) { - //var login = regex_res[2]; - //if (! ('res.users' in this.map_hash)) { this.map_hash['res.users']['name'] = []; } - //this.map_hash['res.users']['login'].push(login); - //regex_res = regex_login.exec(string); - //} - /* document link with name_get: [res.model,name] */ - /* internal link with id: [res.model,id], or [res.model,id|display_name] */ - //var regex_intlink = new RegExp(/(^|\s)#(\w*[a-zA-Z_]+\w*)\.(\w+[a-zA-Z_]+\w*),(\w+)/g); - //regex_res = regex_intlink.exec(string); - //while (regex_res != null) { - //var res_model = regex_res[2] + '.' + regex_res[3]; - //var res_name = regex_res[4]; - //if (! (res_model in this.map_hash)) { this.map_hash[res_model]['name'] = []; } - //this.map_hash[res_model]['name'].push(res_name); - //regex_res = regex_intlink.exec(string); - //} - }, - - /** - * Updates the mapping; check for to_fetch_ids for each recorded - * model, and perform a name_get to update the mapping. - * CURRENTLY NOT IMPLEMENTED */ - do_update_name_get_mapping: function(mapping) { - }, }; @@ -273,8 +91,7 @@ openerp.mail = function(session) { * ------------------------------------------------------------ * * This widget handles the display of a form to compose a new message. - * This form is an OpenERP form_view, build on a mail.compose.message - * wizard. + * This form is a mail.compose.message form_view. */ mail.ComposeMessage = session.web.Widget.extend({ @@ -282,96 +99,52 @@ openerp.mail = function(session) { /** * @param {Object} parent parent - * @param {Object} [params] - * @param {String} [params.res_model] res_model of document [REQUIRED] - * @param {Number} [params.res_id] res_id of record [REQUIRED] - * @param {Number} [params.email_mode] true/false, tells whether - * we are in email sending mode - * @param {Number} [params.formatting] true/false, tells whether - * we are in advance formatting mode - * @param {String} [params.model] mail.compose.message.mode (see - * composition wizard) - * @param {Number} [params.msg_id] id of a message in case we are in - * reply mode + * @param {Object} [options] + * @param {Object} [options.context] context passed to the + * mail.compose.message DataSetSearch. Please refer to this model + * for more details about fields and default values. */ - init: function(parent, params) { + init: function (parent, options) { var self = this; this._super(parent); // options - this.params = params || {}; - this.params.context = params.context || {}; - this.params.email_mode = params.email_mode || false; - this.params.formatting = params.formatting || false; - this.params.mode = params.mode || 'comment'; - this.params.form_xml_id = params.form_xml_id || 'email_compose_message_wizard_form_chatter'; - this.params.form_view_id = false; - if (this.params.mode == 'reply') { - this.params.active_id = this.params.msg_id; - } else { - this.params.active_id = this.params.res_id; - } - this.email_mode = false; - this.formatting = false; - }, - - /** - * Reinitialize the widget field values to the default values. The - * purpose is to avoid to destroy and re-build a form view. Default - * values are therefore given as for an onchange. */ - reinit: function() { - var self = this; - if (! this.form_view) return; - var call_defer = this.ds_compose.call('default_get', [['subject', 'body_text', 'body_html', 'dest_partner_ids'], this.ds_compose.get_context()]).then( - function (result) { - self.form_view.on_processed_onchange({'value': result}, []); - }); - return call_defer; + this.options = options || {}; + this.options.context = options.context || {}; + this.options.form_xml_id = options.form_xml_id || 'email_compose_message_wizard_form_chatter'; + this.options.form_view_id = options.form_view_id || false; + // debug + // console.groupCollapsed('New ComposeMessage: model', this.options.context.default_res_model, ', id', this.options.context.default_res_id); + // console.log('context:', this.options.context); + // console.groupEnd(); }, - /** - * Override-hack of do_action: clean the form */ - do_action: function(action, on_close) { - // this.init_comments(); - return this._super(action, on_close); - }, - - /** - * Widget start function - * - builds and initializes the form view */ - start: function() { - var self = this; + start: function () { this._super.apply(this, arguments); // customize display: add avatar, clean previous content - var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, - this.session.session_id, 'res.users', 'image_small', this.session.uid); + var user_avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.session.uid); this.$el.find('img.oe_mail_icon').attr('src', user_avatar); this.$el.find('div.oe_mail_msg_content').empty(); - // create a context for the default_get of the compose form - var widget_context = { - 'active_model': this.params.res_model, - 'active_id': this.params.active_id, - 'mail.compose.message.mode': this.params.mode, - }; - var context = _.extend({}, this.params.context, widget_context); + // create a context for the dataset and default_get of the wizard + var context = _.extend({}, this.options.context); this.ds_compose = new session.web.DataSetSearch(this, 'mail.compose.message', context); // find the id of the view to display in the chatter form - var data_ds = new session.web.DataSetSearch(this, 'ir.model.data'); - var deferred_form_id =data_ds.call('get_object_reference', ['mail', this.params.form_xml_id]).then( function (result) { - if (result) { - self.params.form_view_id = result[1]; - } - }).pipe(this.proxy('create_form_view')); - return deferred_form_id; + if (this.options.form_view_id) { + return this.create_form_view(); + } + else { + var data_ds = new session.web.DataSetSearch(this, 'ir.model.data'); + return data_ds.call('get_object_reference', ['mail', this.options.form_xml_id]).pipe(this.proxy('create_form_view')); + } }, - /** - * Create a FormView, then append it to the to widget DOM. */ - create_form_view: function () { + /** Create a FormView, then append it to the to widget DOM. */ + create_form_view: function (new_form_view_id) { var self = this; + this.options.form_view_id = (new_form_view_id && new_form_view_id[1]) || this.options.form_view_id; // destroy previous form_view if any if (this.form_view) { this.form_view.destroy(); } // create the FormView - this.form_view = new session.web.FormView(this, this.ds_compose, this.params.form_view_id, { + this.form_view = new session.web.FormView(this, this.ds_compose, this.options.form_view_id, { action_buttons: false, pager: false, initial_mode: 'edit', @@ -382,13 +155,24 @@ openerp.mail = function(session) { return $.when(this.form_view.appendTo(msg_node)).pipe(function() { self.bind_events(); self.form_view.do_show(); - if (self.params.email_mode) { self.toggle_email_mode(); } - if (self.params.formatting) { self.toggle_formatting_mode(); } }); }, - destroy: function() { - this._super.apply(this, arguments); + /** + * Reinitialize the widget field values to the default values obtained + * using default_get on mail.compose.message. This allows to reinitialize + * the widget without having to rebuild a complete form view. + * @param {Object} new_context: context of the refresh */ + refresh: function (new_context) { + if (! this.form_view) return; + var self = this; + this.options.context = _.extend(this.options.context, new_context || {}); + this.ds_compose.context = _.extend(this.ds_compose.context, this.options.context); + return this.ds_compose.call('default_get', [ + ['subject', 'body_text', 'body', 'attachment_ids', 'partner_ids', 'composition_mode', + 'use_template', 'template_id', 'model', 'res_id', 'parent_id', 'content_subtype'], + this.ds_compose.get_context(), + ]).then( function (result) { self.form_view.on_processed_onchange({'value': result}, []); }); }, /** @@ -396,87 +180,12 @@ openerp.mail = function(session) { * in the function. */ bind_events: function() { var self = this; - this.$el.find('button.oe_form_button').click(function (event) { - event.preventDefault(); - }); - // event: click on 'Send an Email' link that toggles the form for - // sending an email (partner_ids) - this.$el.find('a.oe_mail_compose_message_email').click(function (event) { - event.preventDefault(); - self.toggle_email_mode(); - }); - // event: click on 'Formatting' icon-link that toggles the advanced - // formatting options for writing a message (subject, body_html) - this.$el.find('a.oe_mail_compose_message_formatting').click(function (event) { - event.preventDefault(); - self.toggle_formatting_mode(); - }); // event: click on 'Attachment' icon-link that opens the dialog to // add an attachment. - this.$el.find('a.oe_mail_compose_message_attachment').click(function (event) { - event.preventDefault(); - // not yet implemented - self.set_body_value('attachment', 'attachment'); - }); - // event: click on 'Checklist' icon-link that toggles the options - // for adding checklist. - this.$el.find('a.oe_mail_compose_message_checklist').click(function (event) { - event.preventDefault(); - // not yet implemented - self.set_body_value('checklist', 'checklist'); + this.$el.on('click', 'button.oe_mail_compose_message_attachment', function (event) { + event.stopImmediatePropagation(); }); }, - - /** - * Toggle the formatting mode. */ - toggle_formatting_mode: function() { - var self = this; - this.formatting = ! this.formatting; - // calls onchange - var call_defer = this.ds_compose.call('onchange_formatting', [[], this.formatting, this.params.res_model, this.params.res_id]).then( - function (result) { - self.form_view.on_processed_onchange(result, []); - }); - // update context of datasetsearch - this.ds_compose.context.formatting = this.formatting; - // toggle display - this.$el.find('span.oe_mail_compose_message_subject').toggleClass('oe_mail_compose_message_invisible'); - this.$el.find('div.oe_mail_compose_message_body_text').toggleClass('oe_mail_compose_message_invisible'); - this.$el.find('div.oe_mail_compose_message_body_html').toggleClass('oe_mail_compose_message_invisible'); - }, - - /** - * Toggle the email mode. */ - toggle_email_mode: function() { - var self = this; - this.email_mode = ! this.email_mode; - // calls onchange - var call_defer = this.ds_compose.call('onchange_email_mode', [[], this.email_mode, this.params.res_model, this.params.res_id]).then( - function (result) { - self.form_view.on_processed_onchange(result, []); - }); - // update context of datasetsearch - this.ds_compose.context.email_mode = this.email_mode; - // update 'Post' button -> 'Send' - // update 'Send an Email' link -> 'Post a comment' - if (this.email_mode) { - this.$el.find('button.oe_mail_compose_message_button_send').html('<span>Send</span>'); - this.$el.find('a.oe_mail_compose_message_email').html('Comment'); - } else { - this.$el.find('button.oe_mail_compose_message_button_send').html('<span>Post</span>'); - this.$el.find('a.oe_mail_compose_message_email').html('Send an Email'); - } - // toggle display - this.$el.find('div.oe_mail_compose_message_partner_ids').toggleClass('oe_mail_compose_message_invisible'); - }, - - /** - * Update the values of the composition form; with possible different - * values for body_text and body_html. */ - set_body_value: function(body_text, body_html) { - this.form_view.fields.body_text.set_value(body_text); - this.form_view.fields.body_html.set_value(body_html); - }, }), /** @@ -490,9 +199,6 @@ openerp.mail = function(session) { * - - sub message (parent_id = root message) * - - - sub sub message (parent id = sub message) * - - sub message (parent_id = root message) - * This widget has 2 ways of initialization, either you give records - * to be rendered, either it will fetch [limit] messages related to - * [res_model]:[res_id]. */ mail.Thread = session.web.Widget.extend({ @@ -500,95 +206,67 @@ openerp.mail = function(session) { /** * @param {Object} parent parent - * @param {Object} [params] - * @param {String} [params.res_model] res_model of document [REQUIRED] - * @param {Number} [params.res_id] res_id of record [REQUIRED] - * @param {Number} [params.uid] user id [REQUIRED] - * @param {Bool} [params.parent_id=false] parent_id of message - * @param {Number} [params.thread_level=0] number of levels in the thread - * (only 0 or 1 currently) - * @param {Bool} [params.is_wall=false] thread is displayed in the wall - * @param {Number} [params.msg_more_limit=150] number of character to + * @param {Object} [options] + * @param {Object} [options.context] context of the thread. It should + contain at least default_model, default_res_id. Please refer to + the ComposeMessage widget for more information about it. + * @param {Number} [options.thread_level=0] number of thread levels + * @param {Number} [options.message_ids=null] ids for message_fetch + * @param {Number} [options.message_data=null] already formatted message + data, for subthreads getting data from their parent + * @param {Boolean} [options.composer] use the advanced composer, or + the default basic textarea if not set + * @param {Number} [options.truncate_limit=250] number of character to * display before having a "show more" link; note that the text * will not be truncated if it does not have 110% of the parameter - * (ex: 110 characters needed to be truncated and be displayed as - * a 100-characters message) - * @param {Number} [params.limit=100] maximum number of messages to fetch - * @param {Number} [params.offset=0] offset for fetching messages - * @param {Number} [params.records=null] records to show instead of fetching messages */ - init: function(parent, params) { + init: function(parent, options) { this._super(parent); // options - this.params = params; - this.params.parent_id = this.params.parent_id || false; - this.params.thread_level = this.params.thread_level || 0; - this.params.is_wall = this.params.is_wall || (this.params.records != undefined) || false; - this.params.msg_more_limit = this.params.msg_more_limit || 250; - this.params.limit = this.params.limit || 100; - // this.params.limit = 3; // tmp for testing - this.params.offset = this.params.offset || 0; - this.params.records = this.params.records || null; + this.options = options || {}; + this.options.domain = options.domain || []; + this.options.context = _.extend({ + default_model: 'mail.thread', + default_res_id: 0, + default_parent_id: false }, options.context || {}); + this.options.thread_level = options.thread_level || 0; + this.options.composer = options.composer || false; + this.options.message_ids = options.message_ids || null; + this.options.message_data = options.message_data || null; // datasets and internal vars - this.ds = new session.web.DataSetSearch(this, this.params.res_model); - this.ds_users = new session.web.DataSetSearch(this, 'res.users'); - this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; + this.ds_thread = new session.web.DataSetSearch(this, this.options.context.default_model); + this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification'); + this.ds_message = new session.web.DataSetSearch(this, 'mail.message'); // display customization vars - this.display = {}; - this.display.show_post_comment = this.params.show_post_comment || false; - this.display.show_reply = (this.params.thread_level > 0 && this.params.is_wall); - this.display.show_delete = ! this.params.is_wall; - this.display.show_hide = this.params.is_wall; - this.display.show_reply_by_email = ! this.params.is_wall; - this.display.show_more = (this.params.thread_level == 0); + this.display = { + truncate_limit: options.truncate_limit || 250, + show_header_compose: options.show_header_compose || false, + show_reply: options.show_reply || false, + show_delete: options.show_delete || false, + show_hide: options.show_hide || false, + show_reply_by_email: options.show_reply_by_email || false, + show_more: options.show_more || false, + } }, start: function() { + // TDE TODO: check for deferred, not sure it is correct this._super.apply(this, arguments); - // add events this.bind_events(); - // display user, fetch comments - this.display_current_user(); - if (this.params.records) var display_done = this.display_comments_from_parameters(this.params.records); - else var display_done = this.init_comments(); - // customize display - $.when(display_done).then(this.proxy('do_customize_display')); + // fetch and display message, using message_ids if set + var display_done = $.when(this.message_fetch(true, [], {})).then(this.proxy('do_customize_display')); // add message composition form view - if (this.display.show_post_comment) { + if (this.display.show_header_compose && this.options.composer) { var compose_done = this.instantiate_composition_form(); } return display_done && compose_done; }, - /** - * Override-hack of do_action: automatically reload the chatter. - * Normally it should be called only when clicking on 'Post/Send' - * in the composition form. */ - do_action: function(action, on_close) { - this.init_comments(); - if (this.compose_message_widget) { - this.compose_message_widget.reinit(); } - return this._super(action, on_close); - }, - - instantiate_composition_form: function(mode, email_mode, formatting, msg_id, context) { - if (this.compose_message_widget) { - this.compose_message_widget.destroy(); - } - this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.params.uid, 'res_model': this.params.res_model, - 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id, - 'email_mode': email_mode || false, 'formatting': formatting || false, - 'context': context || false } ); - var composition_node = this.$el.find('div.oe_mail_thread_action'); - composition_node.empty(); - var compose_done = this.compose_message_widget.appendTo(composition_node); - return compose_done; - }, - + /** Customize the display + * - show_header_compose: show the composition form in the header */ do_customize_display: function() { - if (this.display.show_post_comment) { this.$el.find('div.oe_mail_thread_action').eq(0).show(); } + this.display_user_avatar(); + if (this.display.show_header_compose) { this.$el.find('div.oe_mail_thread_action').eq(0).show(); } }, /** @@ -596,171 +274,188 @@ openerp.mail = function(session) { * in the function. */ bind_events: function() { var self = this; - // generic events from Chatter Mixin - mail.ChatterUtils.bind_events(this); // event: click on 'more' at bottom of thread this.$el.find('button.oe_mail_button_more').click(function () { - self.do_more(); + self.do_message_fetch(); }); // event: writing in basic textarea of composition form (quick reply) this.$el.find('textarea.oe_mail_compose_textarea').keyup(function (event) { var charCode = (event.which) ? event.which : window.event.keyCode; if (event.shiftKey && charCode == 13) { this.value = this.value+"\n"; } - else if (charCode == 13) { return self.do_comment(); } + else if (charCode == 13) { return self.message_post(); } }); // event: click on 'Reply' in msg - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply', 'click', function (event) { - var act_dom = $(this).parents('div.oe_mail_thread_display').find('div.oe_mail_thread_action:first'); - act_dom.toggle(); + this.$el.on('click', 'a.oe_mail_msg_reply', function (event) { event.preventDefault(); + event.stopPropagation(); + var act_dom = $(this).parents('li.oe_mail_thread_msg').eq(0).find('div.oe_mail_thread_action:first'); + act_dom.toggle(); }); // event: click on 'attachment(s)' in msg - this.$el.delegate('a.oe_mail_msg_view_attachments', 'click', function (event) { + this.$el.on('click', 'a.oe_mail_msg_view_attachments', function (event) { + event.preventDefault(); + event.stopPropagation(); var act_dom = $(this).parent().parent().parent().find('.oe_mail_msg_attachments'); act_dom.toggle(); - event.preventDefault(); }); // event: click on 'Delete' in msg side menu - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_delete', 'click', function (event) { + this.$el.on('click', 'a.oe_mail_msg_delete', function (event) { + event.preventDefault(); + event.stopPropagation(); if (! confirm(_t("Do you really want to delete this message?"))) { return false; } var msg_id = event.srcElement.dataset.id; if (! msg_id) return false; - var call_defer = self.ds_msg.unlink([parseInt(msg_id)]); - $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); - if (self.params.thread_level > 0) { - $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); - } - event.preventDefault(); - return call_defer; + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).remove(); + return self.ds_msg.unlink([parseInt(msg_id)]); }); // event: click on 'Hide' in msg side menu - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_hide', 'click', function (event) { - if (! confirm(_t("Do you really want to hide this thread ?"))) { return false; } + this.$el.on('click', 'a.oe_mail_msg_hide', function (event) { + event.preventDefault(); + event.stopPropagation(); var msg_id = event.srcElement.dataset.id; if (! msg_id) return false; - var call_defer = self.ds.call('message_remove_pushed_notifications', [[self.params.res_id], [parseInt(msg_id)], true]); - $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).hide(); - if (self.params.thread_level > 0) { - $(event.srcElement).parents('.oe_mail_thread').eq(0).hide(); - } - event.preventDefault(); - return call_defer; + $(event.srcElement).parents('li.oe_mail_thread_msg').eq(0).remove(); + return self.ds_notif.call('set_message_read', [parseInt(msg_id)]); }); - // event: click on "Reply" in msg side menu (email style) - this.$el.find('div.oe_mail_thread_display').delegate('a.oe_mail_msg_reply_by_email', 'click', function (event) { + // event: click on "Reply by email" in msg side menu (email style) + this.$el.on('click', 'a.oe_mail_msg_reply_by_email', function (event) { + event.preventDefault(); + event.stopPropagation(); var msg_id = event.srcElement.dataset.msg_id; - var email_mode = (event.srcElement.dataset.type == 'email'); - var formatting = (event.srcElement.dataset.formatting == 'html'); if (! msg_id) return false; - self.instantiate_composition_form('reply', email_mode, formatting, msg_id); - event.preventDefault(); + self.compose_message_widget.refresh({ + 'default_composition_mode': 'reply', + 'default_parent_id': parseInt(msg_id), + 'default_content_subtype': 'html'} ); }); }, - - destroy: function () { - this._super.apply(this, arguments); - }, - - init_comments: function() { - var self = this; - this.params.offset = 0; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - this.$el.find('div.oe_mail_thread_display').empty(); - var domain = this.get_fetch_domain(this.comments_structure); - return this.fetch_comments(this.params.limit, this.params.offset, domain).then(); + + /** + * Override-hack of do_action: automatically reload the chatter. + * Normally it should be called only when clicking on 'Post/Send' + * in the composition form. */ + do_action: function(action, on_close) { + this.message_clean(); + this.message_fetch(); + if (this.compose_message_widget) { + this.compose_message_widget.refresh({ + 'default_composition_mode': 'comment', + 'default_parent_id': this.options.default_parent_id, + 'default_content_subtype': 'plain'} ); + } + return this._super(action, on_close); }, - - fetch_comments: function (limit, offset, domain) { - var self = this; - var defer = this.ds.call('message_read', [[this.params.res_id], (this.params.thread_level > 0), (this.comments_structure['root_ids']), - (limit+1) || (this.params.limit+1), offset||this.params.offset, domain||undefined ]).then(function (records) { - if (records.length <= self.params.limit) self.display.show_more = false; - // else { self.display.show_more = true; records.pop(); } - // else { self.display.show_more = true; records.splice(0, 1); } - else { self.display.show_more = true; } - self.display_comments(records); - // TODO: move to customize display - if (self.display.show_more == true) self.$el.find('div.oe_mail_thread_more:last').show(); - else self.$el.find('div.oe_mail_thread_more:last').hide(); + + /** Instantiate the composition form, with every parameters in context + or in the widget context. */ + instantiate_composition_form: function(context) { + if (this.compose_message_widget) { + this.compose_message_widget.destroy(); + } + this.compose_message_widget = new mail.ComposeMessage(this, { + 'context': _.extend(context || {}, this.options.context), }); - - return defer; + var composition_node = this.$el.find('div.oe_mail_thread_action'); + composition_node.empty(); + var compose_done = this.compose_message_widget.appendTo(composition_node); + return compose_done; }, - display_comments_from_parameters: function (records) { - if (records.length > 0 && records.length < (records[0].child_ids.length+1) ) this.display.show_more = true; - else this.display.show_more = false; - var defer = this.display_comments(records); - // TODO: move to customize display - if (this.display.show_more == true) $('div.oe_mail_thread_more').eq(-2).show(); - else $('div.oe_mail_thread_more').eq(-2).hide(); - return defer; + refresh_composition_form: function (context) { + if (! this.compose_message_widget) return; + return this.compose_message_widget.refresh(context); }, - - display_comments: function (records) { + + /** Clean the thread */ + message_clean: function() { + this.$el.find('div.oe_mail_thread_display').empty(); + }, + + /** Fetch messages + * @param {Bool} initial_mode: initial mode: try to use message_data or + * message_ids, if nothing available perform a message_read; otherwise + * directly perform a message_read + * @param {Array} additional_domain: added to options.domain + * @param {Object} additional_context: added to options.context + */ + message_fetch: function (initial_mode, additional_domain, additional_context) { var self = this; - // sort the records - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, this.params.parent_id); - //build attachments download urls and compute time-relative from dates - for (var k in records) { - records[k].timerelative = $.timeago(records[k].date); - if (records[k].attachments) { - for (var l in records[k].attachments) { - var url = self.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id='+records[k].attachments[l].id; - records[k].attachments[l].url = url; - } - } + // domain and context: options + additional + fetch_domain = _.flatten([this.options.domain, additional_domain || []], true) + fetch_context = _.extend(this.options.context, additional_context || {}) + // if message_ids is set: try to use it + if (initial_mode && this.options.message_data) { + return this.message_display(this.options.message_data); } + return this.ds_message.call('message_read', + [(initial_mode && this.options.message_ids) || false, fetch_domain, this.options.thread_level, undefined, fetch_context] + ).then(this.proxy('message_display')); + }, + + /* Display a list of records + * A specific case is done for 'expandable' messages that are messages + displayed under a 'show more' button form + */ + message_display: function (records) { + var self = this; + var _expendable = false; _(records).each(function (record) { - var sub_msgs = []; - if ((record.parent_id == false || record.parent_id[0] == self.params.parent_id) && self.params.thread_level > 0 ) { - var sub_list = self.comments_structure['tree_struct'][record.id]['direct_childs']; - _(records).each(function (record) { - //if (record.parent_id == false || record.parent_id[0] == self.params.parent_id) return; - if (_.indexOf(sub_list, record.id) != -1) { - sub_msgs.push(record); - } + if (record.type == 'expandable') { + _expendable = true; + self.update_fetch_more(true); + self.fetch_more_domain = record.domain; + self.fetch_more_context = record.context; + } + else { + self.display_record(record); + // if (self.options.thread_level >= 0) { + self.thread = new mail.Thread(self, { + 'context': { + 'default_model': record.model, + 'default_res_id': record.res_id, + 'default_parent_id': record.id }, + 'message_data': record.child_ids, 'thread_level': self.options.thread_level-1, + 'show_header_compose': false, 'show_reply': self.options.thread_level > 1, + 'show_hide': self.display.show_hide, 'show_delete': self.display.show_delete, }); - self.display_comment(record); - self.thread = new mail.Thread(self, {'res_model': self.params.res_model, 'res_id': self.params.res_id, 'uid': self.params.uid, - 'records': sub_msgs, 'thread_level': (self.params.thread_level-1), 'parent_id': record.id, - 'is_wall': self.params.is_wall}); self.$el.find('li.oe_mail_thread_msg:last').append('<div class="oe_mail_thread_subthread"/>'); self.thread.appendTo(self.$el.find('div.oe_mail_thread_subthread:last')); - } - else if (self.params.thread_level == 0) { - self.display_comment(record); + // } } }); - mail.ChatterUtils.records_struct_update_after_display(this.comments_structure); - // update offset for "More" buttons - if (this.params.thread_level == 0) this.params.offset += records.length; + if (! _expendable) { + this.update_fetch_more(false); + } }, - /** Displays a record, performs text/link formatting */ - display_comment: function (record) { - record.body = mail.ChatterUtils.do_text_nl2br($.trim(record.body), true); - // if (record.type == 'email' && record.state == 'received') { + /** Displays a record and performs some formatting on the record : + * - record.date: formatting according to the user timezone + * - record.timerelative: relative time givein by timeago lib + * - record.avatar: image url + * - record.attachments[].url: url of each attachment + * - record.is_author: is the current user the author of the record */ + display_record: function (record) { + // formatting and additional fields + record.date = session.web.format_value(record.date, {type:"datetime"}); + record.timerelative = $.timeago(record.date); if (record.type == 'email') { - record.mini_url = ('/mail/static/src/img/email_icon.png'); + record.avatar = ('/mail/static/src/img/email_icon.png'); } else { - record.mini_url = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', record.user_id[0]); + record.avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.partner', 'image_small', record.author_id[0]); } - // body text manipulation - if (record.subtype == 'plain') { - record.body = mail.ChatterUtils.do_text_remove_html_tags(record.body); + //TDE: FIX + if (record.attachments) { + for (var l in record.attachments) { + var url = self.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id='+records[k].attachments[l].id; + record.attachments[l].url = url; + } } - record.body = mail.ChatterUtils.do_replace_expressions(record.body); - // format date according to the user timezone - record.date = session.web.format_value(record.date, {type:"datetime"}); - // is the user the author ? - record.is_author = mail.ChatterUtils.is_author(this, record.user_id[0]); - // render - var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.params, 'display': this.display}); - // expand feature + record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]); + // render, add the expand feature + var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'params': this.options, 'display': this.display}); $(rendered).appendTo(this.$el.children('div.oe_mail_thread_display:first')); this.$el.find('div.oe_mail_msg_record_body').expander({ - slicePoint: this.params.msg_more_limit, + slicePoint: this.options.msg_more_limit, expandText: 'read more', userCollapseText: '[^]', detailClass: 'oe_mail_msg_tail', @@ -769,54 +464,68 @@ openerp.mail = function(session) { }); }, - display_current_user: function () { - var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.params.uid); - return this.$el.find('img.oe_mail_icon').attr('src', avatar); + /** Display 'show more' button */ + update_fetch_more: function (new_value) { + if (new_value) { + this.$el.find('div.oe_mail_thread_more:last').show(); + } else { + this.$el.find('div.oe_mail_thread_more:last').hide(); + } }, - - do_comment: function () { - var comment_node = this.$el.find('textarea'); - var body_text = comment_node.val(); - comment_node.val(''); - return this.ds.call('message_append_note', [[this.params.res_id], '', body_text, this.params.parent_id, 'comment', 'plain']).then( - this.proxy('init_comments')); + + display_user_avatar: function () { + var avatar = mail.ChatterUtils.get_image(this.session.prefix, this.session.session_id, 'res.users', 'image_small', this.session.uid); + return this.$el.find('img.oe_mail_icon').attr('src', avatar); }, - /** - * Create a domain to fetch new comments according to - * comment already present in comments_structure - * @param {Object} comments_structure (see chatter utils) - * @returns {Array} fetch_domain (OpenERP domain style) - */ - get_fetch_domain: function (comments_structure) { - var domain = []; - var ids = comments_structure.root_ids.slice(); - var ids2 = []; - // must be child of current parent - if (this.params.parent_id) { domain.push(['id', 'child_of', this.params.parent_id]); } - _(comments_structure.root_ids).each(function (id) { // each record - ids.push(id); - ids2.push(id); - }); - if (this.params.parent_id != false) { - ids2.push(this.params.parent_id); - } - // must not be children of already fetched messages - if (ids.length > 0) { - domain.push('&'); - domain.push('!'); - domain.push(['id', 'child_of', ids]); - } - if (ids2.length > 0) { - domain.push(['id', 'not in', ids2]); + message_post: function (body) { + var self = this; + if (! body) { + var comment_node = this.$el.find('textarea'); + var body = comment_node.val(); + comment_node.val(''); } - return domain; - }, - - do_more: function () { - domain = this.get_fetch_domain(this.comments_structure); - return this.fetch_comments(this.params.limit, this.params.offset, domain); - }, + return this.ds_thread.call('message_post', [ + [this.options.context.default_res_id], body, false, 'comment', this.options.context.default_parent_id, undefined] + ).then(self.message_fetch()); + }, + + /** Action: 'shows more' to fetch new messages */ + do_message_fetch: function () { + return this.message_fetch(false, this.fetch_more_domain, this.fetch_more_context); + }, + + // TDE: keep currently because need something similar + // /** + // * Create a domain to fetch new comments according to + // * comment already present in comments_structure + // * @param {Object} comments_structure (see chatter utils) + // * @returns {Array} fetch_domain (OpenERP domain style) + // */ + // get_fetch_domain: function (comments_structure) { + // var domain = []; + // var ids = comments_structure.root_ids.slice(); + // var ids2 = []; + // // must be child of current parent + // if (this.options.parent_id) { domain.push(['id', 'child_of', this.options.parent_id]); } + // _(comments_structure.root_ids).each(function (id) { // each record + // ids.push(id); + // ids2.push(id); + // }); + // if (this.options.parent_id != false) { + // ids2.push(this.options.parent_id); + // } + // // must not be children of already fetched messages + // if (ids.length > 0) { + // domain.push('&'); + // domain.push('!'); + // domain.push(['id', 'child_of', ids]); + // } + // if (ids2.length > 0) { + // domain.push(['id', 'not in', ids2]); + // } + // return domain; + // }, }); @@ -833,62 +542,60 @@ openerp.mail = function(session) { /** mail_thread widget: thread of comments */ mail.RecordThread = session.web.form.AbstractField.extend({ - // QWeb template to use when rendering the object template: 'mail.record_thread', - init: function() { + init: function() { this._super.apply(this, arguments); - this.params = this.options; - this.params.thread_level = this.params.thread_level || 0; - this.thread = null; - this.ds = new session.web.DataSet(this, this.view.model); - this.ds_users = new session.web.DataSet(this, 'res.users'); + this.options.domain = this.options.domain || []; + this.options.context = {'default_model': 'mail.thread', 'default_res_id': false}; + this.options.thread_level = this.options.thread_level || 0; }, start: function() { + this._super.apply(this, arguments); // NB: all the widget should be modified to check the actual_mode property on view, not use // any other method to know if the view is in create mode anymore this.view.on("change:actual_mode", this, this._check_visibility); this._check_visibility(); - mail.ChatterUtils.bind_events(this); - this.$el.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }); - if (! this.params.see_subscribers_options) { - this.$el.find('button.oe_mail_button_followers').hide(); } - this.$el.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }); - this.$el.find('button.oe_mail_button_unfollow').click(function () { self.do_unfollow(); }) - .mouseover(function () { $(this).html('Unfollow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) - .mouseleave(function () { $(this).html('Following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); }, - + _check_visibility: function() { this.$el.toggle(this.view.get("actual_mode") !== "create"); }, - - destroy: function () { + + destroy: function() { + if (this.thread) { this.thread.destroy(); } this._super.apply(this, arguments); }, - + set_value: function() { + var self = this; this._super.apply(this, arguments); - if (! this.view.datarecord.id || - session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { - this.$el.find('.oe_mail_thread').hide(); + if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) { + this.$el.find('oe_mail_thread').hide(); return; } + // update context + _.extend(this.options.context, { + default_res_id: this.view.datarecord.id, + default_model: this.view.model }); // create and render Thread widget this.$el.find('div.oe_mail_recthread_main').empty(); - if (this.thread) this.thread.destroy(); - this.thread = new mail.Thread(this, {'res_model': this.view.model, 'res_id': this.view.datarecord.id, 'uid': this.session.uid, - 'thread_level': this.params.thread_level, 'show_post_comment': true, 'limit': 15}); - var thread_done = this.thread.appendTo(this.$el.find('div.oe_mail_recthread_main')); - return thread_done; + if (this.thread) { this.thread.destroy(); } + var thread = new mail.Thread(self, { + 'context': this.options.context, + 'thread_level': this.options.thread_level, 'show_header_compose': true, + 'message_ids': this.get_value(), + 'show_delete': true, 'composer': true }); + this.thread = thread; + return thread.appendTo(this.$el.find('div.oe_mail_recthread_main')); }, }); /** * ------------------------------------------------------------ - * WallView Widget + * Wall Widget * ------------------------------------------------------------ * * This widget handles the display of the Chatter on the Wall. @@ -903,92 +610,45 @@ openerp.mail = function(session) { /** * @param {Object} parent parent - * @param {Object} [params] - * @param {Number} [params.limit=20] number of messages to show and fetch - * @param {Number} [params.search_view_id=false] search view id for messages - * @var {Array} comments_structure (see chatter utils) + * @param {Object} [options] + * @param {Number} [options.domain] domain on the Wall, is an array. + * @param {Number} [options.domain] context, is an object. It should + * contain default_model, default_res_id, to give it to the threads. */ - init: function (parent, params) { + init: function (parent, options) { this._super(parent); - this.params = {}; - this.params.limit = params.limit || 25; - this.params.domain = params.domain || []; - this.params.context = params.context || {}; - this.params.res_model = params.res_model || false; - this.params.res_id = params.res_id || false; - this.params.search_view_id = params.search_view_id || false; - this.params.thread_level = params.thread_level || 1; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; - this.display_show_more = true; + this.options = options || {}; + this.options.domain = options.domain || []; + this.options.context = options.context || {}; + this.options.thread_level = options.thread_level || 1; this.thread_list = []; + this.ds_msg = new session.web.DataSetSearch(this, 'mail.message'); + // for search view this.search = {'domain': [], 'context': {}, 'groupby': {}} this.search_results = {'domain': [], 'context': {}, 'groupby': {}} - // datasets - this.ds_msg = new session.web.DataSet(this, 'mail.message'); - this.ds_thread = new session.web.DataSet(this, 'mail.thread'); - this.ds_users = new session.web.DataSet(this, 'res.users'); }, start: function () { this._super.apply(this, arguments); - this.display_current_user(); - // add events - this.add_event_handlers(); - // load mail.message search view - var search_view_ready = this.load_search_view(this.params.search_view_id, {}, false); - // load composition form - var compose_done = this.instantiate_composition_form(); - // fetch first threads - var comments_ready = this.init_and_fetch_comments(this.params.limit, 0); - return (search_view_ready && comments_ready && compose_done); - }, - - /** - * Override-hack of do_action: automatically reload the chatter. - * Normally it should be called only when clicking on 'Post/Send' - * in the composition form. */ - do_action: function(action, on_close) { - this.init_and_fetch_comments(); - if (this.compose_message_widget) { - this.compose_message_widget.reinit(); } - return this._super(action, on_close); + var search_view_ready = this.load_search_view({}, false); + var thread_displayed = this.message_display(); + return (search_view_ready && thread_displayed); }, destroy: function () { + for (var i in this.thread_list) { this.thread_list[i].destroy(); } this._super.apply(this, arguments); }, - instantiate_composition_form: function(mode, msg_id) { - if (this.compose_message_widget) { - this.compose_message_widget.destroy(); - } - this.compose_message_widget = new mail.ComposeMessage(this, { - 'extended_mode': false, 'uid': this.session.uid, 'res_model': this.params.res_model, - 'res_id': this.params.res_id, 'mode': mode || 'comment', 'msg_id': msg_id }); - var composition_node = this.$el.find('div.oe_mail_wall_action'); - composition_node.empty(); - var compose_done = this.compose_message_widget.appendTo(composition_node); - return compose_done; - }, - - /** Add events */ - add_event_handlers: function () { - var self = this; - // display more threads - this.$el.find('button.oe_mail_wall_button_more').click(function () { return self.do_more(); }); - }, - /** - * Loads the mail.message search view - * @param {Number} view_id id of the search view to load + * Load the mail.message search view * @param {Object} defaults ?? * @param {Boolean} hidden some kind of trick we do not care here */ - load_search_view: function (view_id, defaults, hidden) { + load_search_view: function (defaults, hidden) { var self = this; - this.searchview = new session.web.SearchView(this, this.ds_msg, view_id || false, defaults || {}, hidden || false); - var search_view_loaded = this.searchview.appendTo(this.$el.find('.oe_view_manager_view_search')); - return $.when(search_view_loaded).then(function () { + this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false); + return this.searchview.appendTo(this.$el.find('.oe_view_manager_view_search')).then(function () { self.searchview.on_search.add(self.do_searchview_search); }); }, @@ -1011,100 +671,30 @@ openerp.mail = function(session) { self.search_results['context'] = results.context; self.search_results['domain'] = results.domain; self.search_results['groupby'] = results.group_by; - return self.init_and_fetch_comments(); + self.message_clean(); + return self.message_display(); }); }, - display_current_user: function () { - //return this.$el.find('img.oe_mail_msg_image').attr('src', this.thread_get_avatar('res.users', 'avatar', this.session.uid)); - }, - - /** - * Initializes the wall and calls fetch_comments - * @param {Number} limit: number of notifications to fetch - * @param {Number} offset: offset in notifications search - * @param {Array} domain - * @param {Array} context - */ - init_and_fetch_comments: function() { - this.search['domain'] = _.union(this.params.domain, this.search_results.domain); - this.search['context'] = _.extend(this.params.context, this.search_results.context); - this.display_show_more = true; - this.comments_structure = {'root_ids': [], 'new_root_ids': [], 'msgs': {}, 'tree_struct': {}, 'model_to_root_ids': {}}; + /** Clean the wall */ + message_clean: function() { this.$el.find('ul.oe_mail_wall_threads').empty(); - return this.fetch_comments(this.params.limit, 0); - }, - - /** - * Fetches wall messages - * @param {Number} limit: number of notifications to fetch - * @param {Number} offset: offset in notifications search - * @param {Array} domain - * @param {Array} context - */ - fetch_comments: function (limit, offset, additional_domain, additional_context) { - var self = this; - if (additional_domain) var fetch_domain = this.search['domain'].concat(additional_domain); - else var fetch_domain = this.search['domain']; - if (additional_context) var fetch_context = _.extend(this.search['context'], additional_context); - else var fetch_context = this.search['context']; - return this.ds_thread.call('message_get_pushed_messages', - [[this.session.uid], true, [], (limit || 0), (offset || 0), fetch_domain, fetch_context]).then(this.proxy('display_comments')); }, - /** - * @param {Array} records records to show in threads - */ - display_comments: function (records) { - var self = this; - this.do_update_show_more(records.length >= self.params.limit); - mail.ChatterUtils.records_struct_add_records(this.comments_structure, records, false); - _(this.comments_structure['new_root_ids']).each(function (root_id) { - var records = self.comments_structure.tree_struct[root_id]['for_thread_msgs']; - var model_name = self.comments_structure.msgs[root_id]['model']; - var res_id = self.comments_structure.msgs[root_id]['res_id']; - var render_res = session.web.qweb.render('mail.wall_thread_container', {}); - $('<li class="oe_mail_wall_thread">').html(render_res).appendTo(self.$el.find('ul.oe_mail_wall_threads')); - var thread = new mail.Thread(self, { - 'res_model': model_name, 'res_id': res_id, 'uid': self.session.uid, 'records': records, - 'parent_id': false, 'thread_level': self.params.thread_level, 'show_hide': true, 'is_wall': true} - ); - self.thread_list.push(thread); - return thread.appendTo(self.$el.find('li.oe_mail_wall_thread:last')); - }); - // update TODO - this.comments_structure['root_ids'] = _.union(this.comments_structure['root_ids'], this.comments_structure['new_root_ids']); - this.comments_structure['new_root_ids'] = []; - }, - - /** - * Create a domain to fetch new comments according to - * comments already present in comments_structure - * - for each model: - * -- should not be child of already displayed ids - * @returns {Array} fetch_domain (OpenERP domain style) - */ - get_fetch_domain: function () { - var self = this; - var model_to_root = {}; - var fetch_domain = []; - _(this.comments_structure['model_to_root_ids']).each(function (sc_model, model_name) { - fetch_domain.push('|', ['model', '!=', model_name], '!', ['id', 'child_of', sc_model]); - }); - return fetch_domain; - }, - - /** Display update: show more button */ - do_update_show_more: function (new_value) { - if (new_value != undefined) this.display_show_more = new_value; - if (this.display_show_more) this.$el.find('div.oe_mail_wall_more:last').show(); - else this.$el.find('div.oe_mail_wall_more:last').hide(); - }, - - /** Action: Shows more discussions */ - do_more: function () { - var domain = this.get_fetch_domain(); - return this.fetch_comments(this.params.limit, 0, domain); + /** Display the Wall threads */ + message_display: function () { + var render_res = session.web.qweb.render('mail.wall_thread_container', {}); + $('<li class="oe_mail_wall_thread">').html(render_res).appendTo(this.$el.find('ul.oe_mail_wall_threads')); + var thread = new mail.Thread(this, { + 'domain': this.options.domain, 'context': this.options.context, + 'thread_level': this.options.thread_level, 'composer': true, + // display options + 'show_header_compose': true, 'show_reply': this.options.thread_level > 0, + 'show_hide': true, 'show_reply_by_email': true, + } + ); + thread.appendTo(this.$el.find('li.oe_mail_wall_thread:last')); + this.thread_list.push(thread); }, }); }; diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js index eda56231d69..e5d216ed3d2 100644 --- a/addons/mail/static/src/js/mail_followers.js +++ b/addons/mail/static/src/js/mail_followers.js @@ -9,7 +9,7 @@ openerp_mail_followers = function(session, mail) { * mail_followers Widget * ------------------------------------------------------------ * - * This widget handles the display of a list of records as a vetical + * This widget handles the display of a list of records as a vertical * list, with an image on the left. The widget itself is a floatting * right-sided box. * This widget is mainly used to display the followers of records @@ -24,12 +24,8 @@ openerp_mail_followers = function(session, mail) { init: function() { this._super.apply(this, arguments); - this.params = {}; - this.params.image = this.node.attrs.image || 'image_small'; - this.params.title = this.node.attrs.title || 'Followers'; - this.params.display_followers = true; - this.params.display_control = this.node.attrs.display_control || false; - this.params.display_actions = this.node.attrs.display_actions || false; + this.options.image = this.node.attrs.image || 'image_small'; + this.options.title = this.node.attrs.title || 'Followers'; this.ds_model = new session.web.DataSetSearch(this, this.view.model); this.ds_follow = new session.web.DataSetSearch(this, this.field.relation); }, @@ -40,9 +36,6 @@ openerp_mail_followers = function(session, mail) { // any other method to know if the view is in create mode anymore this.view.on("change:actual_mode", this, this._check_visibility); this._check_visibility(); - this.$el.find('button.oe_mail_button_followers').click(function () { self.do_toggle_followers(); }); - if (! this.params.display_control) { - this.$el.find('button.oe_mail_button_followers').hide(); } this.$el.find('button.oe_mail_button_follow').click(function () { self.do_follow(); }) .mouseover(function () { $(this).html('Follow').removeClass('oe_mail_button_mouseout').addClass('oe_mail_button_mouseover'); }) .mouseleave(function () { $(this).html('Not following').removeClass('oe_mail_button_mouseover').addClass('oe_mail_button_mouseout'); }); @@ -61,10 +54,6 @@ openerp_mail_followers = function(session, mail) { }, reinit: function() { - this.params.display_followers = true; - this.params.display_control = this.node.attrs.display_control || false; - this.params.display_actions = this.node.attrs.display_actions || false; - this.$el.find('button.oe_mail_button_followers').html('Hide followers') this.$el.find('button.oe_mail_button_follow').hide(); this.$el.find('button.oe_mail_button_unfollow').hide(); }, @@ -76,27 +65,24 @@ openerp_mail_followers = function(session, mail) { this.$el.find('div.oe_mail_recthread_aside').hide(); return; } - return this.fetch_subscribers(value_); + return this.fetch_followers(value_); }, - fetch_subscribers: function (value_) { - return this.ds_follow.call('read', [value_ || this.get_value(), ['name', this.params.image]]).then(this.proxy('display_subscribers')); + fetch_followers: function (value_) { + return this.ds_follow.call('read', [value_ || this.get_value(), ['name', 'user_ids']]).then(this.proxy('display_followers')); }, - /** - * Display the followers. - * TODO: replace the is_subscriber check by fields read */ - display_subscribers: function (records) { + /** Display the followers, evaluate is_follower directly */ + display_followers: function (records) { var self = this; - this.is_subscriber = false; - var user_list = this.$el.find('ul.oe_mail_followers_display').empty(); - this.$el.find('div.oe_mail_recthread_followers h4').html(this.params.title + ' (' + records.length + ')'); + this.message_is_follower = _.indexOf(_.flatten(_.pluck(records, 'user_ids')), this.session.uid) != -1; + var node_user_list = this.$el.find('ul.oe_mail_followers_display').empty(); + this.$el.find('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')'); _(records).each(function (record) { - if (record.id == self.session.uid) { self.is_subscriber = true; } - record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.users', 'image_small', record.id); - $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(user_list); + record.avatar_url = mail.ChatterUtils.get_image(self.session.prefix, self.session.session_id, 'res.partner', 'image_small', record.id); + $(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list); }); - if (this.is_subscriber) { + if (this.message_is_follower) { this.$el.find('button.oe_mail_button_follow').hide(); this.$el.find('button.oe_mail_button_unfollow').show(); } else { @@ -105,18 +91,13 @@ openerp_mail_followers = function(session, mail) { }, do_follow: function () { - return this.ds_model.call('message_subscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); + var context = new session.web.CompoundContext(this.build_context(), {'read_back': true}); + return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], undefined, context]).pipe(this.proxy('set_value')); }, do_unfollow: function () { - return this.ds_model.call('message_unsubscribe', [[this.view.datarecord.id]]).pipe(this.proxy('set_value')); - }, - - do_toggle_followers: function () { - this.params.see_subscribers = ! this.params.see_subscribers; - if (this.params.see_subscribers) { this.$el.find('button.oe_mail_button_followers').html('Hide followers'); } - else { this.$el.find('button.oe_mail_button_followers').html('Show followers'); } - this.$el.find('div.oe_mail_recthread_followers').toggle(); + var context = new session.web.CompoundContext(this.build_context(), {'read_back': true}); + return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], undefined, context]).pipe(this.proxy('set_value')); }, }); }; diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index f67c4e8676f..66b82d5a36c 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -17,8 +17,9 @@ <td colspan="2"> <h2 class="oe_view_title"> <span class="oe_view_title_text"> - <t t-if="! widget.params.res_model">News Feeds</t> - <t t-if="widget.params.res_model" t-esc="widget.params.res_model"/> + News Feed + <t t-if="widget.options.res_model"> / </t> + <t t-if="widget.options.res_model" t-esc="widget.options.res_model"/> </span> </h2> </td> @@ -26,19 +27,9 @@ </tr> </tbody> </table> - <div class="oe_mail_wall_main"> - <div class="oe_mail_wall_action"> - <!-- call the composition form --> - <t t-call ="mail.compose_message"/> - </div> - <div class="oe_clear"/> - <ul class="oe_mail_wall_threads"> - <!-- contains threads --> - </ul> - <div class="oe_mail_wall_more"> - <button class="oe_mail_wall_button_more" type="button">See more...</button> - </div> - </div> + <ul class="oe_mail_wall_threads"> + <!-- contains threads --> + </ul> <div class="oe_mail_wall_aside"> <!-- contains currently nothing --> </div> @@ -56,22 +47,11 @@ Template used to display the communication history in documents form view. --> - <div t-name="mail.record_thread" class="oe_mail_recthread"> + <div t-name="mail.record_thread"> <!-- <h4>History and Comments</h4> --> <div class="oe_mail_recthread_main"> <!-- contains the document thread --> </div> - <div class="oe_mail_recthread_aside"> - <div class="oe_mail_recthread_actions"> - <button type="button" class="oe_mail_button_follow">Follow</button> - <button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button> - <button type="button" class="oe_mail_button_followers">Show followers</button> - </div> - <div class="oe_mail_recthread_followers"> - <h4>Followers</h4> - <ul class="oe_mail_followers_display"></ul> - </div> - </div> </div> <!-- @@ -99,7 +79,7 @@ container, holding the composition form. Then come the various messages. Then comes the 'more' button. --> - <ul t-name="mail.thread" class="oe_mail oe_mail_thread"> + <ul t-name="mail.thread" class="oe_mail oe_mail_thread oe_semantic_html_override"> <div class="oe_mail_thread_action"> <!-- contains the composition box (form + image) --> <t t-call="mail.compose_message"/> @@ -114,21 +94,21 @@ <!-- default layout --> <li t-name="mail.thread.message" class="oe_mail oe_mail_thread_msg"> - <div t-attf-class="oe_mail_msg_#{record.type}"> - <img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="record.mini_url"/> + <div t-attf-class="oe_mail_msg_#{record.type} oe_semantic_html_override"> + <img class="oe_mail_icon oe_mail_frame oe_left" t-att-src="record.avatar"/> <div class="oe_mail_msg_content"> <!-- dropdown menu with message options and actions --> <span class="oe_dropdown_toggle oe_dropdown_arrow"> <ul class="oe_dropdown_menu"> - <t t-if="display['show_delete']"> - <li t-if="record.is_author"><a href="#" class="oe_mail_msg_delete" t-attf-data-id='{record.id}'>Delete</a></li> + <t t-if="record.is_author"> + <li t-if="display['show_delete']"><a href="#" class="oe_mail_msg_delete" t-attf-data-id='{record.id}'>Delete</a></li> </t> - <li t-if="display['show_hide']"><a href="#" class="oe_mail_msg_hide" t-attf-data-id='{record.id}'>Hide</a></li> + <li t-if="display['show_hide']"><a href="#" class="oe_mail_msg_hide" t-attf-data-id='{record.id}'>Remove notification</a></li> <!-- Uncomment when adding subtype hiding <li t-if="display['show_hide']"> <a href="#" class="oe_mail_msg_hide_type" t-attf-data-subtype='{record.subtype}'>Hide '<t t-esc="record.subtype"/>' for this document</a> </li> --> - <li><a href="#" t-attf-data-msg_id="{record.id}" t-attf-data-type="{record.type}" t-attf-data-formatting="{record.content_subtype}" class="oe_mail_msg_reply_by_email">Reply by email</a></li> + <li><a href="#" t-attf-data-msg_id="{record.id}" class="oe_mail_msg_reply_by_email">Quote and reply</a></li> <li t-if="record.type == 'email'"><a t-attf-href="#model=mail.message&id=#{record.id}" class="oe_mail_msg_details">Details</a></li> </ul> </span> @@ -138,16 +118,15 @@ <t t-raw="record.subject"/> </h1> <div class="oe_mail_msg_body"> - <div class="oe_mail_msg_record_body"> - <a t-attf-href="#model=#{params.res_model}&id=#{params.res_id}" t-if="(params.thread_level > 0) & (!record.subject)"><t t-raw="record.record_name"/></a> + <a t-attf-href="#model=#{record.model}&id=#{record.res_id}" t-if="(params.thread_level > 0) & (!record.subject)"><t t-raw="record.record_name"/></a> <t t-raw="record.body"/> </div> </div> <div class="oe_clear"/> <ul class="oe_mail_msg_footer"> - <li t-if="record.subject & params.thread_level > 0"><a t-attf-href="#model=#{params.res_model}&id=#{params.res_id}"><t t-raw="record.record_name"/></a></li> - <li><a t-attf-href="#model=res.users&id=#{record.user_id[0]}"><t t-raw="record.user_id[1]"/></a></li> + <li t-if="record.subject & params.thread_level > 0"><a t-attf-href="#model=#{record.model}&id=#{record.res_id}"><t t-raw="record.record_name"/></a></li> + <li><a t-attf-href="#model=res.partner&id=#{record.author_id[0]}"><t t-raw="record.author_id[1]"/></a></li> <li><span t-att-title="record.date"><t t-raw="record.timerelative"/></span></li> <li t-if="display['show_reply']"><a href="#" class="oe_mail_msg_reply">Reply</a></li> <!-- uncomment when merging vote diff --git a/addons/mail/static/src/xml/mail_followers.xml b/addons/mail/static/src/xml/mail_followers.xml index 53c899b9569..39622f42abf 100644 --- a/addons/mail/static/src/xml/mail_followers.xml +++ b/addons/mail/static/src/xml/mail_followers.xml @@ -5,15 +5,14 @@ followers main template Template used to display the followers and the actions in a record. --> - <div t-name="mail.followers" class="oe_mail_recthread_aside"> + <div t-name="mail.followers" class="oe_mail_recthread_aside oe_semantic_html_override"> <div class="oe_mail_recthread_actions"> <button type="button" class="oe_mail_button_follow oe_mail_button_mouseout">Not following</button> <button type="button" class="oe_mail_button_unfollow oe_mail_button_mouseout">Following</button> - <button type="button" class="oe_mail_button_followers">Show followers</button> </div> <div class="oe_mail_recthread_followers"> - <t t-if="widget.params.title"> - <h4><t t-raw="widget.params.title"/></h4> + <t t-if="widget.options.title"> + <h4><t t-raw="widget.options.title"/></h4> </t> <ul class="oe_mail_followers_display"></ul> </div> @@ -25,7 +24,7 @@ --> <li t-name="mail.followers.partner"> <img class="oe_mail_thumbnail oe_mail_frame" t-attf-src="{record.avatar_url}"/> - <a t-attf-href="#model=res.users&id=#{record.id}"><t t-raw="record.name"/></a> + <a t-attf-href="#model=res.partner&id=#{record.id}"><t t-raw="record.name"/></a> </li> </template> diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index 0940d72c490..f45745f31dc 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.tests import common +from openerp.tools.html_sanitize import html_sanitize MAIL_TEMPLATE = """Return-Path: <whatever-2a840@postmaster.twitter.com> To: {to} @@ -62,51 +63,544 @@ Content-Transfer-Encoding: quoted-printable ------=_Part_4200734_24778174.1344608186754-- """ +MAIL_TEMPLATE_PLAINTEXT = """Return-Path: <whatever-2a840@postmaster.twitter.com> +To: {to} +Received: by mail1.openerp.com (Postfix, from userid 10002) + id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST) +From: Sylvie Lelitre <sylvie.lelitre@agrolait.com> +Subject: {subject} +MIME-Version: 1.0 +Content-Type: text/plain +Date: Fri, 10 Aug 2012 14:16:26 +0000 +Message-ID: {msg_id} +{extra} + +Please call me as soon as possible this afternoon! + +-- +Sylvie +""" + class test_mail(common.TransactionCase): + def _mock_smtp_gateway(self, *args, **kwargs): + return True + + def _mock_build_email(self, *args, **kwargs): + self._build_email_args = args + self._build_email_kwargs = kwargs + return self.build_email_real(*args, **kwargs) + def setUp(self): super(test_mail, self).setUp() self.ir_model = self.registry('ir.model') self.mail_alias = self.registry('mail.alias') self.mail_thread = self.registry('mail.thread') self.mail_group = self.registry('mail.group') + self.mail_mail = self.registry('mail.mail') + self.mail_message = self.registry('mail.message') + self.mail_notification = self.registry('mail.notification') + self.mail_followers = self.registry('mail.followers') self.res_users = self.registry('res.users') + self.res_partner = self.registry('res.partner') + + # Install mock SMTP gateway + self.build_email_real = self.registry('ir.mail_server').build_email + self.registry('ir.mail_server').build_email = self._mock_build_email + self.registry('ir.mail_server').send_email = self._mock_smtp_gateway # groups@.. will cause the creation of new mail groups - self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model','=', 'mail.group')])[0] + self.mail_group_model_id = self.ir_model.search(self.cr, self.uid, [('model', '=', 'mail.group')])[0] self.mail_alias.create(self.cr, self.uid, {'alias_name': 'groups', 'alias_model_id': self.mail_group_model_id}) - - # tech@... will append new messages to the 'tech' group - self.group_tech_id = self.mail_group.create(self.cr, self.uid, {'name': 'tech'}) + # create a 'pigs' group that will be used through the various tests + self.group_pigs_id = self.mail_group.create(self.cr, self.uid, + {'name': 'Pigs', 'description': 'Fans of Pigs, unite !'}) - def test_message_process(self): + def test_00_message_process(self): + cr, uid = self.cr, self.uid # Incoming mail creates a new mail_group "frogs" - self.assertEqual(self.mail_group.search(self.cr, self.uid, [('name','=','frogs')]), []) + self.assertEqual(self.mail_group.search(cr, uid, [('name', '=', 'frogs')]), []) mail_frogs = MAIL_TEMPLATE.format(to='groups@example.com, other@gmail.com', subject='frogs', extra='') - self.mail_thread.message_process(self.cr, self.uid, None, mail_frogs) - frog_groups = self.mail_group.search(self.cr, self.uid, [('name','=','frogs')]) + self.mail_thread.message_process(cr, uid, None, mail_frogs) + frog_groups = self.mail_group.search(cr, uid, [('name', '=', 'frogs')]) self.assertTrue(len(frog_groups) == 1) # Previously-created group can be emailed now - it should have an implicit alias group+frogs@... - frog_group = self.mail_group.browse(self.cr, self.uid, frog_groups[0]) + frog_group = self.mail_group.browse(cr, uid, frog_groups[0]) group_messages = frog_group.message_ids self.assertTrue(len(group_messages) == 1, 'New group should only have the original message') mail_frog_news = MAIL_TEMPLATE.format(to='Friendly Frogs <group+frogs@example.com>', subject='news', extra='') - self.mail_thread.message_process(self.cr, self.uid, None, mail_frog_news) + self.mail_thread.message_process(cr, uid, None, mail_frog_news) frog_group.refresh() self.assertTrue(len(frog_group.message_ids) == 2, 'Group should contain 2 messages now') # Even with a wrong destination, a reply should end up in the correct thread mail_reply = MAIL_TEMPLATE.format(to='erroneous@example.com>', subject='Re: news', - extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n'%frog_group.id) - self.mail_thread.message_process(self.cr, self.uid, None, mail_reply) + extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id) + self.mail_thread.message_process(cr, uid, None, mail_reply) frog_group.refresh() self.assertTrue(len(frog_group.message_ids) == 3, 'Group should contain 3 messages now') - + # No model passed and no matching alias must raise mail_spam = MAIL_TEMPLATE.format(to='noone@example.com', subject='spam', extra='') self.assertRaises(Exception, self.mail_thread.message_process, - self.cr, self.uid, None, mail_spam) + cr, uid, None, mail_spam) + + # plain text content should be wrapped and stored as html + test_msg_id = '<deadcafe.1337@smtp.agrolait.com>' + mail_text = MAIL_TEMPLATE_PLAINTEXT.format(to='groups@example.com', subject='frogs', extra='', msg_id=test_msg_id) + self.mail_thread.message_process(cr, uid, None, mail_text) + new_mail = self.mail_message.browse(cr, uid, self.mail_message.search(cr, uid, [('message_id','=',test_msg_id)])[0]) + self.assertEqual(new_mail.body, '\n<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>\n', + 'plaintext mail incorrectly parsed') + + def test_10_many2many_reference_field(self): + """ Tests designed for the many2many_reference field (follower_ids). + We will test to perform writes using the many2many commands 0, 3, 4, + 5 and 6. """ + cr, uid = self.cr, self.uid + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # Create partner Bert Poilu + partner_bert_id = self.res_partner.create(cr, uid, {'name': 'Bert Poilu'}) + + # Create 'disturbing' values in mail.followers: same res_id, other res_model; same res_model, other res_id + group_dummy_id = self.mail_group.create(cr, uid, + {'name': 'Dummy group'}) + self.mail_followers.create(cr, uid, + {'res_model': 'mail.thread', 'res_id': self.group_pigs_id, 'partner_id': partner_bert_id}) + self.mail_followers.create(cr, uid, + {'res_model': 'mail.group', 'res_id': group_dummy_id, 'partner_id': partner_bert_id}) + + # Pigs just created: should be only Admin as follower + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan') + + # Subscribe Bert through a '4' command + group_pigs.write({'message_follower_ids': [(4, partner_bert_id)]}) + group_pigs.refresh() + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the only Pigs fans') + + # Unsubscribe Bert through a '3' command + group_pigs.write({'message_follower_ids': [(3, partner_bert_id)]}) + group_pigs.refresh() + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertEqual(follower_ids, set([user_admin.partner_id.id]), 'Admin should be the only Pigs fan') + + # Set followers through a '6' command + group_pigs.write({'message_follower_ids': [(6, 0, [partner_bert_id])]}) + group_pigs.refresh() + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the only Pigs fan') + + # Add a follower created on the fly through a '0' command + group_pigs.write({'message_follower_ids': [(0, 0, {'name': 'Patrick Fiori'})]}) + partner_patrick_id = self.res_partner.search(cr, uid, [('name', '=', 'Patrick Fiori')])[0] + group_pigs.refresh() + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertEqual(follower_ids, set([partner_bert_id, partner_patrick_id]), 'Bert and Patrick should be the only Pigs fans') + + # Finally, unlink through a '5' command + group_pigs.write({'message_follower_ids': [(5, 0)]}) + group_pigs.refresh() + follower_ids = set([follower.id for follower in group_pigs.message_follower_ids]) + self.assertFalse(follower_ids, 'Pigs group should not have fans anymore') + + # Test dummy data has not been altered + fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.thread'), ('res_id', '=', self.group_pigs_id)]) + follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)]) + self.assertEqual(follower_ids, set([partner_bert_id]), 'Bert should be the follower of dummy mail.thread data') + fol_obj_ids = self.mail_followers.search(cr, uid, [('res_model', '=', 'mail.group'), ('res_id', '=', group_dummy_id)]) + follower_ids = set([follower.partner_id.id for follower in self.mail_followers.browse(cr, uid, fol_obj_ids)]) + self.assertEqual(follower_ids, set([partner_bert_id, user_admin.partner_id.id]), 'Bert and Admin should be the followers of dummy mail.group data') + + def test_11_message_followers(self): + """ Tests designed for the subscriber API. """ + cr, uid = self.cr, self.uid + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # Create user Raoul + user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'login': 'raoul'}) + user_raoul = self.res_users.browse(cr, uid, user_raoul_id) + + # Subscribe Raoul three times (niak niak) through message_subscribe_users + group_pigs.message_subscribe_users([user_raoul_id, user_raoul_id]) + group_pigs.message_subscribe_users([user_raoul_id]) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertEqual(len(follower_ids), 2, 'There should be 2 Pigs fans') + self.assertEqual(set(follower_ids), set([user_raoul.partner_id.id, user_admin.partner_id.id]), 'Admin and Raoul should be the only 2 Pigs fans') + + # Unsubscribe Raoul twice through message_unsubscribe_users + group_pigs.message_unsubscribe_users([user_raoul_id, user_raoul_id]) + group_pigs.refresh() + follower_ids = [follower.id for follower in group_pigs.message_follower_ids] + self.assertEqual(follower_ids, [user_admin.partner_id.id], 'Admin must be the only Pigs fan') + + def test_20_message_post(self): + """ Tests designed for message_post. """ + cr, uid = self.cr, self.uid + self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + + # 0 - Admin + p_a_id = user_admin.partner_id.id + # 1 - Bert Tartopoils, with email, should receive emails for comments and emails + p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) + # 2 - Carine Poilvache, with email, should never receive emails + p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'}) + # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message + p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'}) + + # Subscribe #1, #2 + group_pigs.message_subscribe([p_b_id, p_c_id]) + + # Mail data + _subject = 'Pigs' + _mail_subject = '%s posted on %s' % (user_admin.name, group_pigs.name) + _body1 = 'Pigs rules' + _mail_body1 = 'Pigs rules\n<pre>Admin</pre>\n' + _mail_bodyalt1 = 'Pigs rules\nAdmin' + _body2 = '<html>Pigs rules</html>' + _mail_body2 = html_sanitize('<html>Pigs rules\n<pre>Admin</pre>\n</html>') + _mail_bodyalt2 = 'Pigs rules\nAdmin' + _attachments = [('First', 'My first attachment'), ('Second', 'My second attachment')] + + # CASE1: post comment, body and subject specified + msg_id = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body1, subject=_subject, type='comment') + message = self.mail_message.browse(cr, uid, msg_id) + sent_email = self._build_email_kwargs + # Test: notifications have been deleted + self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id)]), 'mail.mail notifications should have been auto-deleted!') + # Test: mail_message: subject is _subject, body is _body1 (no formatting done) + self.assertEqual(message.subject, _subject, 'mail.message subject incorrect') + self.assertEqual(message.body, _body1, 'mail.message body incorrect') + # Test: sent_email: email send by server: correct subject, body; body_alternative + self.assertEqual(sent_email['subject'], _subject, 'sent_email subject incorrect') + self.assertEqual(sent_email['body'], _mail_body1, 'sent_email body incorrect') + self.assertEqual(sent_email['body_alternative'], _mail_bodyalt1, 'sent_email body_alternative is incorrect') + # Test: mail_message: partner_ids = group followers + message_pids = set([partner.id for partner in message.partner_ids]) + test_pids = set([p_a_id, p_b_id, p_c_id]) + self.assertEqual(test_pids, message_pids, 'mail.message partners incorrect') + # Test: notification linked to this message = group followers = partner_ids + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) + notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) + self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') + # Test: sent_email: email_to should contain b@b, not c@c (pref email), not a@a (writer) + self.assertEqual(sent_email['email_to'], ['b@b'], 'sent_email email_to is incorrect') + + # CASE2: post an email with attachments, parent_id, partner_ids + # TESTS: automatic subject, signature in body_html, attachments propagation + msg_id2 = self.mail_group.message_post(cr, uid, self.group_pigs_id, body=_body2, type='email', + partner_ids=[(6, 0, [p_d_id])], parent_id=msg_id, attachments=_attachments) + message = self.mail_message.browse(cr, uid, msg_id2) + sent_email = self._build_email_kwargs + self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg_id2)]), 'mail.mail notifications should have been auto-deleted!') + + # Test: mail_message: subject is False, body is _body2 (no formatting done), parent_id is msg_id + self.assertEqual(message.subject, False, 'mail.message subject incorrect') + self.assertEqual(message.body, html_sanitize(_body2), 'mail.message body incorrect') + self.assertEqual(message.parent_id.id, msg_id, 'mail.message parent_id incorrect') + # Test: sent_email: email send by server: correct subject, body, body_alternative + self.assertEqual(sent_email['subject'], _mail_subject, 'sent_email subject incorrect') + self.assertEqual(sent_email['body'], _mail_body2, 'sent_email body incorrect') + self.assertEqual(sent_email['body_alternative'], _mail_bodyalt2, 'sent_email body_alternative incorrect') + # Test: mail_message: partner_ids = group followers + message_pids = set([partner.id for partner in message.partner_ids]) + test_pids = set([p_a_id, p_b_id, p_c_id, p_d_id]) + self.assertEqual(message_pids, test_pids, 'mail.message partners incorrect') + # Test: notifications linked to this message = group followers = partner_ids + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) + notif_pids = set([notif.partner_id.id for notif in self.mail_notification.browse(cr, uid, notif_ids)]) + self.assertEqual(notif_pids, test_pids, 'mail.message notification partners incorrect') + # Test: sent_email: email_to should contain b@b, c@c, not a@a (writer) + self.assertEqual(set(sent_email['email_to']), set(['b@b', 'c@c']), 'sent_email email_to incorrect') + # Test: attachments + for attach in message.attachment_ids: + self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') + self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect') + self.assertIn((attach.name, attach.datas.decode('base64')), _attachments, + 'mail.message attachment name / data incorrect') + + def test_21_message_compose_wizard(self): + """ Tests designed for the mail.compose.message wizard. """ + cr, uid = self.cr, self.uid + mail_compose = self.registry('mail.compose.message') + self.res_users.write(cr, uid, [uid], {'signature': 'Admin', 'email': 'a@a'}) + user_admin = self.res_users.browse(cr, uid, uid) + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + group_bird_id = self.mail_group.create(cr, uid, {'name': 'Bird', 'description': 'Bird resistance'}) + group_bird = self.mail_group.browse(cr, uid, group_bird_id) + + # Mail data + _subject = 'Pigs' + _body_text = 'Pigs rules' + _msg_reply = 'Re: Pigs' + _msg_body = '<pre>Pigs rules</pre>' + _attachments = [ + {'name': 'First', 'datas_fname': 'first.txt', 'datas': 'My first attachment'.encode('base64')}, + {'name': 'Second', 'datas_fname': 'second.txt', 'datas': 'My second attachment'.encode('base64')} + ] + _attachments_test = [('first.txt', 'My first attachment'), ('second.txt', 'My second attachment')] + + # Create partners + # 0 - Admin + p_a_id = user_admin.partner_id.id + # 1 - Bert Tartopoils, with email, should receive emails for comments and emails + p_b_id = self.res_partner.create(cr, uid, {'name': 'Bert Tartopoils', 'email': 'b@b'}) + # 2 - Carine Poilvache, with email, should never receive emails + p_c_id = self.res_partner.create(cr, uid, {'name': 'Carine Poilvache', 'email': 'c@c', 'notification_email_send': 'email'}) + # 3 - Dédé Grosbedon, without email, to test email verification; should receive emails for every message + p_d_id = self.res_partner.create(cr, uid, {'name': 'Dédé Grosbedon', 'notification_email_send': 'all'}) + + # Subscribe #1 + group_pigs.message_subscribe([p_b_id]) + + # ---------------------------------------- + # CASE1: comment on group_pigs + # ---------------------------------------- + + # 1. Comment group_pigs with body_text and subject + compose_id = mail_compose.create(cr, uid, + {'subject': _subject, 'body_text': _body_text, 'partner_ids': [(4, p_c_id), (4, p_d_id)]}, + {'default_composition_mode': 'comment', 'default_model': 'mail.group', 'default_res_id': self.group_pigs_id}) + compose = mail_compose.browse(cr, uid, compose_id) + # Test: mail.compose.message: composition_mode, model, res_id + self.assertEqual(compose.composition_mode, 'comment', 'mail.compose.message incorrect composition_mode') + self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') + self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id') + + # 2. Post the comment, get created message + mail_compose.send_mail(cr, uid, [compose_id]) + group_pigs.refresh() + message = group_pigs.message_ids[0] + # Test: mail.message: subject, body inside pre + self.assertEqual(message.subject, False, 'mail.message incorrect subject') + self.assertEqual(message.body, _msg_body, 'mail.message incorrect body') + # Test: mail.message: partner_ids = entries in mail.notification: group_pigs fans (a, b) + mail.compose.message partner_ids (c, d) + msg_pids = [partner.id for partner in message.partner_ids] + test_pids = [p_a_id, p_b_id, p_c_id, p_d_id] + notif_ids = self.mail_notification.search(cr, uid, [('message_id', '=', message.id)]) + self.assertEqual(len(notif_ids), 4, 'mail.message: too much notifications created') + self.assertEqual(set(msg_pids), set(test_pids), 'mail.message partner_ids incorrect') + + # ---------------------------------------- + # CASE2: reply to last comment with attachments + # ---------------------------------------- + + # 1. Update last comment subject, reply with attachments + message.write({'subject': _subject}) + compose_id = mail_compose.create(cr, uid, + {'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])]}, + {'default_composition_mode': 'reply', 'default_model': 'mail.thread', 'default_res_id': self.group_pigs_id, 'default_parent_id': message.id}) + compose = mail_compose.browse(cr, uid, compose_id) + # Test: model, res_id, parent_id, content_subtype + self.assertEqual(compose.model, 'mail.group', 'mail.compose.message incorrect model') + self.assertEqual(compose.res_id, self.group_pigs_id, 'mail.compose.message incorrect res_id') + self.assertEqual(compose.parent_id.id, message.id, 'mail.compose.message incorrect parent_id') + self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message incorrect content_subtype') + + # 2. Post the comment, get created message + mail_compose.send_mail(cr, uid, [compose_id]) + group_pigs.refresh() + message = group_pigs.message_ids[0] + # Test: mail.message: subject as Re:.., body in html + self.assertEqual(message.subject, _msg_reply, 'mail.message incorrect subject') + self.assertIn('Administrator wrote:<blockquote><pre>Pigs rules</pre></blockquote></div>', message.body, 'mail.message body is incorrect') + # Test: mail.message: attachments + for attach in message.attachment_ids: + self.assertEqual(attach.res_model, 'mail.group', 'mail.message attachment res_model incorrect') + self.assertEqual(attach.res_id, self.group_pigs_id, 'mail.message attachment res_id incorrect') + self.assertIn((attach.name, attach.datas.decode('base64')), _attachments_test, + 'mail.message attachment name / data incorrect') + + # ---------------------------------------- + # CASE3: mass_mail on Pigs and Bird + # ---------------------------------------- + + # 1. mass_mail on pigs and bird + compose_id = mail_compose.create(cr, uid, + {'subject': _subject, 'body': '${object.description}'}, + {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group', 'default_res_id': -1, + 'active_ids': [self.group_pigs_id, group_bird_id]}) + compose = mail_compose.browse(cr, uid, compose_id) + # Test: content_subtype is html + self.assertEqual(compose.content_subtype, 'html', 'mail.compose.message content_subtype incorrect') + + # 2. Post the comment, get created message for each group + mail_compose.send_mail(cr, uid, [compose_id], + context={'default_res_id': -1, 'active_ids': [self.group_pigs_id, group_bird_id]}) + group_pigs.refresh() + group_bird.refresh() + message1 = group_pigs.message_ids[0] + message2 = group_bird.message_ids[0] + # Test: Pigs and Bird did receive their message + test_msg_ids = self.mail_message.search(cr, uid, [], limit=2) + self.assertIn(message1.id, test_msg_ids, 'Pigs did not receive its mass mailing message') + self.assertIn(message2.id, test_msg_ids, 'Bird did not receive its mass mailing message') + # Test: mail.message: subject, body + self.assertEqual(message1.subject, _subject, 'mail.message subject incorrect') + self.assertEqual(message1.body, group_pigs.description, 'mail.message body incorrect') + self.assertEqual(message2.subject, _subject, 'mail.message subject incorrect') + self.assertEqual(message2.body, group_bird.description, 'mail.message body incorrect') + + def test_30_message_read(self): + """ Tests designed for message_read. """ + # TDE NOTE: this test is not finished, as the message_read method is not fully specified. + # It will be updated as soon as we have fixed specs ! + cr, uid = self.cr, self.uid + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + def _compare_structures(struct1, struct2, n=0): + # print '%scompare structure' % ('\t' * n) + self.assertEqual(len(struct1), len(struct2), 'message_read structure number of childs incorrect') + for x in range(len(struct1)): + # print '%s' % ('\t' * n), struct1[x]['id'], struct2[x]['id'], struct1[x].get('subject') or '' + self.assertEqual(struct1[x]['id'], struct2[x]['id'], 'message_read failure %s' % struct1[x].get('subject')) + _compare_structures(struct1[x]['child_ids'], struct2[x]['child_ids'], n + 1) + # print '%send compare' % ('\t' * n) + + # ---------------------------------------- + # CASE1: Flattening test + # ---------------------------------------- + + # Create dummy message structure + import copy + tree = [{'id': 2, 'child_ids': [ + {'id': 6, 'child_ids': [ + {'id': 8, 'child_ids': []}, + ]}, + ]}, + {'id': 1, 'child_ids':[ + {'id': 7, 'child_ids': [ + {'id': 9, 'child_ids': []}, + ]}, + {'id': 4, 'child_ids': [ + {'id': 10, 'child_ids': []}, + {'id': 5, 'child_ids': []}, + ]}, + {'id': 3, 'child_ids': []}, + ]}, + ] + # Test: completely flat + new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 0) + self.assertEqual(len(new_tree), 10, 'message_read_tree_flatten wrong in flat') + # Test: 1 thread level + tree_test = [{'id': 2, 'child_ids': [ + {'id': 8, 'child_ids': []}, {'id': 6, 'child_ids': []}, + ]}, + {'id': 1, 'child_ids': [ + {'id': 10, 'child_ids': []}, {'id': 9, 'child_ids': []}, + {'id': 7, 'child_ids': []}, {'id': 5, 'child_ids': []}, + {'id': 4, 'child_ids': []}, {'id': 3, 'child_ids': []}, + ]}, + ] + new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 1) + _compare_structures(new_tree, tree_test) + # Test: 2 thread levels + new_tree = self.mail_message.message_read_tree_flatten(cr, uid, copy.deepcopy(tree), 0, 2) + _compare_structures(new_tree, tree) + + # ---------------------------------------- + # CASE2: message_read test + # ---------------------------------------- + + # 1. Add a few messages to pigs group + msgid1 = group_pigs.message_post(body='1', subject='1', parent_id=False) + msgid2 = group_pigs.message_post(body='2', subject='1-1', parent_id=msgid1) + msgid3 = group_pigs.message_post(body='3', subject='1-2', parent_id=msgid1) + msgid4 = group_pigs.message_post(body='4', subject='2', parent_id=False) + msgid5 = group_pigs.message_post(body='5', subject='1-1-1', parent_id=msgid2) + msgid6 = group_pigs.message_post(body='6', subject='2-1', parent_id=msgid4) + + # Test: read all messages flat + tree_test = [{'id': msgid6, 'child_ids': []}, {'id': msgid5, 'child_ids': []}, + {'id': msgid4, 'child_ids': []}, {'id': msgid3, 'child_ids': []}, + {'id': msgid2, 'child_ids': []}, {'id': msgid1, 'child_ids': []}] + tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=0, limit=10) + _compare_structures(tree, tree_test) + # Test: read with 1 level of thread + tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]}, + {'id': msgid1, 'child_ids': [ + {'id': msgid5, 'child_ids': []}, {'id': msgid3, 'child_ids': []}, + {'id': msgid2, 'child_ids': []}, + ]}, + ] + tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=1, limit=10) + _compare_structures(tree, tree_test) + # Test: read with 2 levels of thread + tree_test = [{'id': msgid4, 'child_ids': [{'id': msgid6, 'child_ids': []}, ]}, + {'id': msgid1, 'child_ids': [ + {'id': msgid3, 'child_ids': []}, + {'id': msgid2, 'child_ids': [{'id': msgid5, 'child_ids': []}, ]}, + ]}, + ] + tree = self.mail_message.message_read(cr, uid, ids=False, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)], thread_level=2, limit=10) + _compare_structures(tree, tree_test) + + # 2. Test expandables + # TDE FIXME: add those tests when expandables are specified and implemented + + def test_40_needaction(self): + """ Tests for mail.message needaction. """ + cr, uid = self.cr, self.uid + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + user_admin = self.res_users.browse(cr, uid, uid) + + # Demo values: check unread notification = needaction on mail.message + notif_ids = self.mail_notification.search(cr, uid, [ + ('partner_id', '=', user_admin.partner_id.id), + ('read', '=', False) + ]) + na_count = self.mail_message._needaction_count(cr, uid, domain=[]) + self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count') + + # Post 4 message on group_pigs + for dummy in range(4): + group_pigs.message_post(body='My Body') + + # Check there are 4 new needaction on mail.message + notif_ids = self.mail_notification.search(cr, uid, [ + ('partner_id', '=', user_admin.partner_id.id), + ('read', '=', False) + ]) + na_count = self.mail_message._needaction_count(cr, uid, domain=[]) + self.assertEqual(len(notif_ids), na_count, 'unread notifications count does not match needaction count') + + # Check there are 4 needaction on mail.message with particular domain + na_count = self.mail_message._needaction_count(cr, uid, domain=[('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)]) + self.assertEqual(na_count, 4, 'posted message count does not match needaction count') + + def test_50_thread_parent_resolution(self): + """Verify parent/child relationships are correctly established when processing incoming mails""" + cr, uid = self.cr, self.uid + group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id) + msg1 = group_pigs.message_post(body='My Body', subject='1') + msg2 = group_pigs.message_post(body='My Body', subject='2') + msg1, msg2 = self.mail_message.browse(cr, uid, [msg1, msg2]) + self.assertTrue(msg1.message_id, "New message should have a proper message_id") + + # Reply to msg1, make sure the reply is properly attached using the various reply identification mechanisms + # 1. In-Reply-To header + reply_msg = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: 1', + extra='In-Reply-To: %s' % msg1.message_id) + self.mail_thread.message_process(cr, uid, None, reply_msg) + # 2. References header + reply_msg2 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', subject='Re: Re: 1', + extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % msg1.message_id) + self.mail_thread.message_process(cr, uid, None, reply_msg2) + # 3. Subject contains [<ID>] + model passed to message+process -> only attached to group, not to mail + reply_msg3 = MAIL_TEMPLATE.format(to='Pretty Pigs <group+pigs@example.com>, other@gmail.com', + extra='', subject='Re: [%s] 1' % self.group_pigs_id) + self.mail_thread.message_process(cr, uid, 'mail.group', reply_msg3) + group_pigs.refresh() + msg1.refresh() + self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages') + self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now') diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index fe87b7584e0..3153ccc2069 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -19,417 +19,289 @@ # ############################################################################## -import ast +import base64 import re - import tools + from osv import osv from osv import fields from tools.safe_eval import safe_eval as eval from tools.translate import _ -from ..mail_message import to_email - # main mako-like expression pattern EXPRESSION_PATTERN = re.compile('(\$\{.+?\})') class mail_compose_message(osv.TransientModel): - """Generic Email composition wizard. This wizard is meant to be inherited - at model and view level to provide specific wizard features. - - The behavior of the wizard can be modified through the use of context - parameters, among which are: - - * mail.compose.message.mode: if set to 'reply', the wizard is in - reply to a previous message mode and pre-populated with the original - quote. If set to 'comment', it means you are writing a new message to - be attached to a document. If set to 'mass_mail', the wizard is in - mass mailing where the mail details can contain template placeholders - that will be merged with actual data before being sent to each - recipient. - * active_model: model name of the document to which the mail being - composed is related - * active_id: id of the document to which the mail being composed is - related, or id of the message to which user is replying, - in case ``mail.compose.message.mode == 'reply'`` - * active_ids: ids of the documents to which the mail being composed is - related, in case ``mail.compose.message.mode == 'mass_mail'``. + """ Generic message composition wizard. You may inherit from this wizard + at model and view levels to provide specific features. + + The behavior of the wizard depends on the composition_mode field: + - 'reply': reply to a previous message. The wizard is pre-populated + via ``get_message_data``. + - 'comment': new post on a record. The wizard is pre-populated via + ``get_record_data`` + - 'mass_mail': wizard in mass mailing mode where the mail details can + contain template placeholders that will be merged with actual data + before being sent to each recipient. """ _name = 'mail.compose.message' - _inherit = 'mail.message.common' + _inherit = 'mail.message' _description = 'Email composition wizard' + _log_access = True def default_get(self, cr, uid, fields, context=None): - """ Overridden to provide specific defaults depending on the context - parameters. - - Composition mode - - comment: default mode; active_model, active_id = model and ID of a - document we are commenting, - - reply: active_id = ID of a mail.message to which we are replying. - From this message we can find the related model and res_id, - - mass_mailing mode: active_model, active_id = model and ID of a - document we are commenting, - - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. + """ Handle composition mode. Some details about context keys: + - comment: default mode, model and ID of a record the user comments + - default_model or active_model + - default_res_id or active_id + - reply: active_id of a message the user replies to + - default_parent_id or message_id or active_id: ID of the + mail.message we reply to + - message.res_model or default_model + - message.res_id or default_res_id + - mass_mail: model and IDs of records the user mass-mails + - active_ids: record IDs + - default_model or active_model """ if context is None: context = {} - compose_mode = context.get('mail.compose.message.mode', 'comment') - active_model = context.get('active_model') - active_id = context.get('active_id') result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context) + # get some important values from context + composition_mode = context.get('default_composition_mode', context.get('mail.compose.message.mode')) + model = context.get('default_model', context.get('active_model')) + res_id = context.get('default_res_id', context.get('active_id')) + message_id = context.get('default_parent_id', context.get('message_id', context.get('active_id'))) + active_ids = context.get('active_ids') + # get default values according to the composition mode - vals = {} - if compose_mode in ['reply']: - vals = self.get_message_data(cr, uid, int(context['active_id']), context=context) - elif compose_mode in ['comment', 'mass_mail'] and active_model and active_id: - vals = self.get_value(cr, uid, active_model, active_id, context) + if composition_mode == 'reply': + vals = self.get_message_data(cr, uid, message_id, context=context) + elif composition_mode == 'comment' and model and res_id: + vals = self.get_record_data(cr, uid, model, res_id, context=context) + elif composition_mode == 'mass_mail' and model and active_ids: + vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'} + else: + vals = {'model': model, 'res_id': res_id} + if composition_mode: + vals['composition_mode'] = composition_mode + for field in vals: if field in fields: result[field] = vals[field] - - # link to model and record if not done yet - if not result.get('model') and active_model: - result['model'] = active_model - if not result.get('res_id') and active_id: - result['res_id'] = active_id - - # Try to provide default email_from if not specified yet - if not result.get('email_from'): - current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - result['email_from'] = current_user.email or False return result + def _get_composition_mode_selection(self, cr, uid, context=None): + return [('comment', 'Comment a document'), ('reply', 'Reply to a message'), ('mass_mail', 'Mass mailing')] + _columns = { - 'dest_partner_ids': fields.many2many('res.partner', - 'email_message_send_partner_rel', - 'wizard_id', 'partner_id', 'Destination partners', - help="When sending emails through the social network composition wizard"\ - "you may choose to send a copy of the mail to partners."), - 'attachment_ids': fields.many2many('ir.attachment','email_message_send_attachment_rel', 'wizard_id', 'attachment_id', 'Attachments'), - 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete emails after sending"), + 'composition_mode': fields.selection( + lambda s, *a, **k: s._get_composition_mode_selection(*a, **k), + string='Composition mode'), + 'partner_ids': fields.many2many('res.partner', + 'mail_compose_message_res_partner_rel', + 'wizard_id', 'partner_id', 'Additional contacts'), + 'attachment_ids': fields.many2many('ir.attachment', + 'mail_compose_message_ir_attachments_rel', + 'wizard_id', 'attachment_id', 'Attachments'), 'filter_id': fields.many2one('ir.filters', 'Filters'), + 'body_text': fields.text('Plain-text Contents'), + 'content_subtype': fields.char('Message content subtype', size=32, readonly=1, + help="Type of message, usually 'html' or 'plain', used to select "\ + "plain-text or rich-text contents accordingly"), + } + + _defaults = { + 'composition_mode': 'comment', + 'content_subtype': lambda self, cr, uid, ctx={}: 'plain', + 'body_text': lambda self, cr, uid, ctx={}: False, + 'body': lambda self, cr, uid, ctx={}: '', + 'subject': lambda self, cr, uid, ctx={}: False, } - def get_value(self, cr, uid, model, res_id, context=None): + def notify(self, cr, uid, newid, context=None): + """ Override specific notify method of mail.message, because we do + not want that feature in the wizard. """ + return + + def get_record_data(self, cr, uid, model, res_id, context=None): """ Returns a defaults-like dict with initial values for the composition wizard when sending an email related to the document record identified by ``model`` and ``res_id``. - The default implementation returns an empty dictionary, and is meant - to be overridden by subclasses. - :param str model: model name of the document record this mail is related to. - :param int res_id: id of the document record this mail is related to. - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. + :param int res_id: id of the document record this mail is related to """ - result = {} - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - result.update({ - 'model': model, - 'res_id': res_id, - 'email_from': user.email or tools.config.get('email_from', False), - 'body_html': False, - 'body_text': False, - 'subject': False, - 'dest_partner_ids': [], - }) - return result - - def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None): - """ email_mode (values: True or False). This onchange on the email mode - allows to have some specific behavior when going in email mode, or - when going out of email mode. - Basically, dest_partner_ids is reset when going out of email - mode. - This method can be overridden for models that want to have their - specific behavior. - Note that currently, this onchange is used in mail.js and called - manually on the form instantiated in the Chatter. - """ - if not value: - return {'value': {'dest_partner_ids': []}} - return {'value': {}} - - def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): - """ onchange_formatting (values: True or False). This onchange on the - formatting allows to have some specific behavior when going in - formatting mode, or when going out of formatting. - Basically, subject is reset when going out of formatting mode. - This method can be overridden for models that want to have their - specific behavior. - Note that currently, this onchange is used in mail.js and called - manually on the form instantiated in the Chatter. - """ - if not value: - return {'value': {'subject': False}} - return {'value': {}} + return {'model': model, 'res_id': res_id} def get_message_data(self, cr, uid, message_id, context=None): """ Returns a defaults-like dict with initial values for the composition wizard when replying to the given message (e.g. including the quote - of the initial message, and the correct recipient). It should not be - called unless ``context['mail.compose.message.mode'] == 'reply'``. + of the initial message, and the correct recipients). :param int message_id: id of the mail.message to which the user is replying. - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. """ + if not message_id: + return {} if context is None: context = {} - result = {} - if not message_id: - return result + message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context=context) - current_user = self.pool.get('res.users').browse(cr, uid, uid, context) - message_data = self.pool.get('mail.message').browse(cr, uid, message_id, context) - # Form the subject - re_prefix = _("Re:") + # create subject + re_prefix = _('Re:') reply_subject = tools.ustr(message_data.subject or '') if not (reply_subject.startswith('Re:') or reply_subject.startswith(re_prefix)): reply_subject = "%s %s" % (re_prefix, reply_subject) - # Form the bodies (text and html). We use the plain text version of the - # original mail, by default, as it is easier to quote than the HTML - # version. TODO: make it possible to switch to HTML on the fly - sent_date = _('On %(date)s, ') % {'date': message_data.date} if message_data.date else '' - sender = _('%(sender_name)s wrote:') % {'sender_name': tools.ustr(message_data.email_from or _('You'))} - body_text = message_data.body_text or '' - body_html = message_data.body_html or '' - quoted_body_text = '> %s' % tools.ustr(body_text.replace('\n', "\n> ") or '') - quoted_body_html = '<blockquote>%s</blockquote>' % (tools.ustr(body_html)), - reply_body_text = '\n%s%s\n%s\n%s' % (sent_date, sender, quoted_body_text, current_user.signature) - reply_body_html = '<br /><br />%s%s<br />%s<br />%s' % (sent_date, sender, quoted_body_html, current_user.signature) - # form dest_partner_ids - dest_partner_ids = [partner.id for partner in message_data.partner_ids] - # Update header and references - reply_headers = {} - reply_references = message_data.references and tools.ustr(message_data.references) or False - reply_message_id = message_data.message_id or False - if reply_message_id: - reply_references = (reply_references or '') + " " + message_data.message_id - reply_headers['In-Reply-To'] = message_data.message_id + # create the reply in the body + reply_body = _('<div>On %(date)s, %(sender_name)s wrote:<blockquote>%(body)s</blockquote></div>') % { + 'date': message_data.date if message_data.date else '', + 'sender_name': message_data.author_id.name, + 'body': message_data.body, + } + # get partner_ids from original message + partner_ids = [partner.id for partner in message_data.partner_ids] if message_data.partner_ids else [] + # update the result - result.update({ - 'body_text': reply_body_text, - 'body_html': reply_body_html, + result = { + 'model': message_data.model, + 'res_id': message_data.res_id, + 'parent_id': message_data.id, + 'body': reply_body, 'subject': reply_subject, - 'attachment_ids': [], - 'dest_partner_ids': dest_partner_ids, - 'model': message_data.model or False, - 'res_id': message_data.res_id or False, - 'email_from': current_user.email or message_data.email_to or False, - 'email_to': message_data.reply_to or message_data.email_from or False, - 'email_cc': message_data.email_cc or False, - 'user_id': uid, - # pass msg-id and references of mail we're replying to, to construct the - # new ones later when sending - 'message_id': reply_message_id, - 'references': reply_references, - 'headers': reply_headers, - }) + 'partner_ids': partner_ids, + 'content_subtype': 'html', + } return result + def toggle_content_subtype(self, cr, uid, ids, context=None): + """ hit toggle formatting mode button: calls onchange_formatting to + emulate an on_change, then writes the value to update the form. """ + for record in self.browse(cr, uid, ids, context=context): + content_st_new_value = 'plain' if record.content_subtype == 'html' else 'html' + onchange_res = self.onchange_content_subtype(cr, uid, ids, content_st_new_value, record.model, record.res_id, context=context) + self.write(cr, uid, [record.id], onchange_res['value'], context=context) + return True + + def onchange_content_subtype(self, cr, uid, ids, value, model, res_id, context=None): + """ onchange_content_subtype (values: 'plain' or 'html'). This onchange + on the subtype allows to have some specific behavior when switching + between text or html mode. + This method can be overridden for models that want to have their + specific behavior. """ + return {'value': {'content_subtype': value}} + + def _verify_partner_email(self, cr, uid, partner_ids, context=None): + """ Verify that selected partner_ids have an email_address defined. + Otherwise throw a warning. """ + partner_wo_email_lst = [] + for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids, context=context): + if not partner.email: + partner_wo_email_lst.append(partner) + if not partner_wo_email_lst: + return {} + warning_msg = _('The following partners chosen as recipients for the email have no email address linked :') + for partner in partner_wo_email_lst: + warning_msg += '\n- %s' % (partner.name) + return {'warning': { + 'title': _('Partners email addresses not found'), + 'message': warning_msg, + } + } + + def onchange_partner_ids(self, cr, uid, ids, value, context=None): + """ onchange_partner_ids (value format: [[6, 0, [3, 4]]]). The + basic purpose of this method is to check that destination partners + effectively have email addresses. Otherwise a warning is thrown. + """ + res = {'value': {}} + if not value or not value[0] or not value[0][0] == 6: + return + res.update(self._verify_partner_email(cr, uid, value[0][2], context=context)) + return res + + def unlink(self, cr, uid, ids, context=None): + # Cascade delete all attachments, as they are owned by the composition wizard + for wizard in self.read(cr, uid, ids, ['attachment_ids'], context=context): + self.pool.get('ir.attachment').unlink(cr, uid, wizard['attachment_ids'], context=context) + return super(mail_compose_message, self).unlink(cr, uid, ids, context=context) + + def dummy(self, cr, uid, ids, context=None): + """ TDE: defined to have buttons that do basically nothing. It is + currently impossible to have buttons that do nothing special + in views (if type not specified, considered as 'object'). """ + return True + + #------------------------------------------------------ + # Wizard validation and send + #------------------------------------------------------ + def send_mail(self, cr, uid, ids, context=None): - '''Process the wizard contents and proceed with sending the corresponding - email(s), rendering any template patterns on the fly if needed. - If the wizard is in mass-mail mode (context['mail.compose.message.mode'] is - set to ``'mass_mail'``), the resulting email(s) are scheduled for being - sent the next time the mail.message scheduler runs, or the next time - ``mail.message.process_email_queue`` is called. - Otherwise the new message is sent immediately. - - :param dict context: several context values will modify the behavior - of the wizard, cfr. the class description. - ''' + """ Process the wizard content and proceed with sending the related + email(s), rendering any template patterns on the fly if needed. """ if context is None: context = {} - # composition wizard options - email_mode = context.get('email_mode') - formatting = context.get('formatting') - mass_mail_mode = context.get('mail.compose.message.mode') == 'mass_mail' - - mail_message_obj = self.pool.get('mail.message') - for mail_wiz in self.browse(cr, uid, ids, context=context): - # attachments - attachment = {} - for attach in mail_wiz.attachment_ids: - attachment[attach.datas_fname] = attach.datas and attach.datas.decode('base64') - - # default values, according to the wizard options - subject = mail_wiz.subject if formatting else False - content_subtype = 'html' if formatting else 'plain' - type = 'email' if email_mode else 'comment' - state = 'outgoing' if email_mode else False - partner_ids = [partner.id for partner in mail_wiz.dest_partner_ids] - references = None - headers = {} - body = mail_wiz.body_html if content_subtype == 'html' else mail_wiz.body_text - - # get model, active_ids, and check if model is openchatter-enabled - if mass_mail_mode and context.get('active_ids') and context.get('active_model'): - active_ids = context['active_ids'] - active_model = context['active_model'] - elif mass_mail_mode: - active_model = mail_wiz.model - active_model_pool = self.pool.get(active_model) - active_ids = active_model_pool.search(cr, uid, ast.literal_eval(mail_wiz.filter_id.domain), context=ast.literal_eval(mail_wiz.filter_id.context)) - else: - active_model = mail_wiz.model - active_ids = [mail_wiz.res_id] - active_model_pool = self.pool.get(active_model) - mail_thread_enabled = hasattr(active_model_pool, 'message_append') - - if context.get('mail.compose.message.mode') == 'mass_mail': - # Mass mailing: must render the template patterns - for active_id in active_ids: - rendered_subject = self.render_template(cr, uid, subject, active_model, active_id) - rendered_body_html = self.render_template(cr, uid, mail_wiz.body_html, active_model, active_id) - rendered_body_text = self.render_template(cr, uid, mail_wiz.body_text, active_model, active_id) - email_from = self.render_template(cr, uid, mail_wiz.email_from, active_model, active_id) - email_to = self.render_template(cr, uid, mail_wiz.email_to, active_model, active_id) - email_cc = self.render_template(cr, uid, mail_wiz.email_cc, active_model, active_id) - email_bcc = self.render_template(cr, uid, mail_wiz.email_bcc, active_model, active_id) - reply_to = self.render_template(cr, uid, mail_wiz.reply_to, active_model, active_id) - - # in mass-mailing mode we only schedule the mail for sending, it will be - # processed as soon as the mail scheduler runs. - if mail_thread_enabled: - active_model_pool.message_append(cr, uid, [active_id], rendered_subject, rendered_body_text, rendered_body_html, - type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids, - email_from=email_from, email_to=email_to, email_cc=email_cc, email_bcc=email_bcc, - reply_to=reply_to, references=references, attachments=attachment, headers=headers, context=context) - else: - mail_message_obj.schedule_with_attach(cr, uid, email_from, to_email(email_to), subject, rendered_body_text, - model=mail_wiz.model, email_cc=to_email(email_cc), email_bcc=to_email(email_bcc), reply_to=reply_to, - attachments=attachment, references=references, res_id=active_id, partner_ids=partner_ids, - content_subtype=mail_wiz.content_subtype, headers=headers, context=context) - else: - # normal mode - no mass-mailing - if mail_thread_enabled: - msg_ids = active_model_pool.message_append(cr, uid, active_ids, subject, mail_wiz.body_text, mail_wiz.body_html, - type=type, content_subtype=content_subtype, state=state, partner_ids=partner_ids, - email_from=mail_wiz.email_from, email_to=mail_wiz.email_to, email_cc=mail_wiz.email_cc, email_bcc=mail_wiz.email_bcc, - reply_to=mail_wiz.reply_to, references=references, attachments=attachment, headers=headers, context=context) - else: - msg_ids = [mail_message_obj.schedule_with_attach(cr, uid, mail_wiz.email_from, to_email(mail_wiz.email_to), subject, mail_wiz.body_text, - type=type, model=mail_wiz.model, email_cc=to_email(mail_wiz.email_cc), email_bcc=to_email(mail_wiz.email_bcc), reply_to=mail_wiz.reply_to, - attachments=attachment, references=references, res_id=int(mail_wiz.res_id), partner_ids=partner_ids, - content_subtype=mail_wiz.content_subtype, headers=headers, context=context)] - # in normal mode, we send the email immediately, as the user expects us to (delay should be sufficiently small) - if type == 'email': - mail_message_obj.send(cr, uid, msg_ids, context=context) + active_ids = context.get('active_ids') + + for wizard in self.browse(cr, uid, ids, context=context): + mass_mail_mode = wizard.composition_mode == 'mass_mail' + active_model_pool = self.pool.get(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 + post_values = { + 'subject': wizard.subject if wizard.content_subtype == 'html' else False, + 'body': wizard.body if wizard.content_subtype == 'html' else '<pre>%s</pre>' % tools.ustr(wizard.body_text), + 'partner_ids': [(4, 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], + } + # 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'] += [(4, partner_id) for partner_id in new_partner_ids] + new_attachments = email_dict.pop('attachments', []) + post_values['attachments'] += new_attachments + post_values.update(email_dict) + # post the message + active_model_pool.message_post(cr, uid, [res_id], type='comment', context=context, **post_values) return {'type': 'ir.actions.act_window_close'} + def render_message(self, cr, uid, wizard, res_id, context=None): + """ Generate an email from the template for given (wizard.model, res_id) + pair. This method is meant to be inherited by email_template that + will produce a more complete dictionary. """ + return { + 'subject': self.render_template(cr, uid, wizard.subject, wizard.model, res_id, context), + 'body': self.render_template(cr, uid, wizard.body, wizard.model, res_id, context), + } + def render_template(self, cr, uid, template, model, res_id, context=None): - """Render the given template text, replace mako-like expressions ``${expr}`` - with the result of evaluating these expressions with an evaluation context - containing: + """ Render the given template text, replace mako-like expressions ``${expr}`` + with the result of evaluating these expressions with an evaluation context + containing: * ``user``: browse_record of the current user * ``object``: browse_record of the document record this mail is related to * ``context``: the context passed to the mail composition wizard - :param str template: the template text to render - :param str model: model name of the document record this mail is related to. - :param int res_id: id of the document record this mail is related to. + :param str template: the template text to render + :param str model: model name of the document record this mail is related to. + :param int res_id: id of the document record this mail is related to. """ if context is None: context = {} def merge(match): 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), - 'context': dict(context), # copy context to prevent side-effects of eval - }) - if result in (None, False): - return "" - return tools.ustr(result) + 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), + 'context': dict(context), # copy context to prevent side-effects of eval + }) + return result and tools.ustr(result) or '' return template and EXPRESSION_PATTERN.sub(merge, template) - - -class mail_compose_message_extended(osv.TransientModel): - """ Extension of 'mail.compose.message' to support default field values related - to CRM-like models that follow the following conventions: - - 1. The model object must have an attribute '_mail_compose_message' equal to True. - - 2. The model should define the following fields: - - 'name' as subject of the message (required); - - 'email_from' as destination email address (required); - - 'email_cc' as cc email addresses (required); - - 'section_id.reply_to' as reply-to address (optional). - """ - _inherit = 'mail.compose.message' - - def get_value(self, cr, uid, model, res_id, context=None): - """ Overrides the default implementation to provide more default field values - related to the corresponding CRM case. - """ - result = super(mail_compose_message_extended, self).get_value(cr, uid, model, res_id, context=context) - model_obj = self.pool.get(model) - if getattr(model_obj, '_mail_compose_message', False) and res_id: - data = model_obj.browse(cr, uid , res_id, context) - result.update({ - 'email_to': data.email_from or False, - 'email_cc': tools.ustr(data.email_cc or ''), - 'subject': data.name or False, - }) - if hasattr(data, 'section_id'): - result['reply_to'] = data.section_id and data.section_id.reply_to or False - return result - - def onchange_email_mode(self, cr, uid, ids, value, model, res_id, context=None): - """ Overrides the default implementation to provide default values for - dest_partner_ids. This method checks that a partner maching the - ``email_from`` of the record exists. It it does not exist, it - creates a new partner. The found or created partner is then added - in dest_partner_ids. - Partner check/creation valid inly if the value is True, and if - the model has the ``_mail_compose_message`` attribute. - """ - result = super(mail_compose_message_extended, self).onchange_email_mode(cr, uid, ids, value, model, res_id, context=context) - model_obj = self.pool.get(model) - if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): - return result - data = model_obj.browse(cr, uid , res_id, context=context) - partner_obj = self.pool.get('res.partner') - partner_ids = partner_obj.search(cr, uid, [('email', '=', data.email_from)], context=context) - if partner_ids: - partner_id = partner_ids[0] - else: - partner_id = partner_obj.name_create(cr, uid, data.email_from, context=context)[0] - result['value'].update({ - 'dest_partner_ids': [partner_id], - 'email_cc': tools.ustr(data.email_cc or ''), - }) - if hasattr(data, 'section_id'): - result['value']['reply_to'] = data.section_id and data.section_id.reply_to or False - return result - - def onchange_formatting(self, cr, uid, ids, value, model, res_id, context=None): - """ Overrides the default implementation to provide default values for - the subject. - Subject re-creation valid only if the value is True, and if the - model has the ``_mail_compose_message`` attribute. - """ - result = super(mail_compose_message_extended, self).onchange_formatting(cr, uid, ids, value, model, res_id, context=context) - model_obj = self.pool.get(model) - if not value or not (getattr(model_obj, '_mail_compose_message', False) and res_id): - return result - data = model_obj.browse(cr, uid , res_id, context=context) - result['value'].update({ - 'subject': data.name or False, - }) - return result - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml index 73fb03cdc28..2899c0c9a41 100644 --- a/addons/mail/wizard/mail_compose_message_view.xml +++ b/addons/mail/wizard/mail_compose_message_view.xml @@ -6,73 +6,87 @@ <field name="model">mail.compose.message</field> <field name="arch" type="xml"> <form string="Compose Email" version="7.0"> + <field name="composition_mode" nolabel="1" invisible="1"/> + <field name="model" nolabel="1" invisible="1"/> + <field name="res_id" nolabel="1" invisible="1"/> + <field name="parent_id" nolabel="1" invisible="1"/> + <field name="content_subtype" nolabel="1" invisible="1"/> <group> - <field name="model" invisible="1"/> - <field name="res_id" invisible="1"/> - <field name='filter_id' invisible="context.get('active_model',False)"/> - <field name="email_from" required="1"/> - <field name="email_to" required="1"/> - <field name="email_cc"/> - <field name="email_bcc"/> - <field name="reply_to"/> - <field name="subject" widget="char" size="512"/> - <field name="references"/> - <field name="message_id"/> + <field name="subject" placeholder="Subject..."/> + <field name="partner_ids" widget="many2many_tags" placeholder="Add contacts to notify..." + context="{'force_email':True}" + on_change="onchange_partner_ids(partner_ids)"/> </group> - <notebook colspan="4"> + <notebook> <page string="Body"> - <field name="body_text" colspan="4" nolabel="1" height="300" width="300"/> + <field name="body_text" nolabel="1" + attrs="{'invisible':[('content_subtype', '=', 'html')]}"/> + <field name="body" nolabel="1" + attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> </page> <page string="Attachments"> <field name="attachment_ids" colspan="4" nolabel="1"/> </page> </notebook> <footer> - <button name="send_mail" string="Send" type="object" class="oe_highlight" /> + <button string="Send" name="send_mail" type="object" class="oe_highlight" /> or <button string="Cancel" class="oe_link" special="cancel" /> + <div class="oe_right"> + <button string="" name="toggle_content_subtype" type="object" icon="/mail/static/src/img/formatting.png" + help="Toggle advanced formatting mode"/> + </div> </footer> </form> </field> </record> <record model="ir.ui.view" id="email_compose_message_wizard_form_chatter"> - <field name="name">mail.compose.message.form</field> + <field name="name">mail.compose.message.form.chatter</field> <field name="model">mail.compose.message</field> <field name="priority">18</field> <field name="arch" type="xml"> <form string="Compose Email" version="7.0" > <group> + <!-- truly invisible fields for control and options --> + <field name="composition_mode" colspan="2" nolabel="1" invisible="1"/> + <field name="model" colspan="2" nolabel="1" invisible="1"/> + <field name="res_id" colspan="2" nolabel="1" invisible="1"/> + <field name="parent_id" colspan="2" nolabel="1" invisible="1"/> + <field name="content_subtype" colspan="2" nolabel="1" invisible="1"/> + <!-- visible wizard --> <field name="subject" colspan="2" nolabel="1" placeholder="Subject..." - class="oe_mail_compose_message_subject oe_mail_compose_message_invisible"/> + class="oe_mail_compose_message_subject" + attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> <field name="body_text" colspan="2" nolabel="1" placeholder="What are you working on ?" - class="oe_mail_compose_message_body_text"/> - <field name="body_html" colspan="2" nolabel="1" placeholder="What are you working on HTML ?" - class="oe_mail_compose_message_body_html oe_mail_compose_message_invisible"/> - <field name="dest_partner_ids" colspan="2" nolabel="1" widget="many2many_tags" - placeholder="Add contacts to notify..." - class="oe_mail_compose_message_partner_ids oe_mail_compose_message_invisible"/> + class="oe_mail_compose_message_body" + attrs="{'invisible':[('content_subtype', '=', 'html')]}"/> + <field name="body" colspan="2" nolabel="1" placeholder="What are you working on ?" + class="oe_mail_compose_message_body_html" + attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/> + <field name="partner_ids" colspan="2" nolabel="1" widget="many2many_tags" placeholder="Add contacts to notify..." + context="{'force_email':True}" + on_change="onchange_partner_ids(partner_ids)" + class="oe_mail_compose_message_partner_ids"/> + <field name="attachment_ids" colspan="2" nolabel="1" widget="many2many_tags" + placeholder="Add attachments..." invisible="1" + class="oe_mail_compose_message_attachment_ids"/> + <!-- void div to display attachments, Chatter-controlled --> + <div colspan="2" class="oe_mail_compose_message_attachments"/> + <!-- buttons, with as few Chatter logic as possible --> <div> <button name="send_mail" string="Post" type="object" class="oe_mail_compose_message_button_send"/> - or <a href="#" class="oe_mail_compose_message_email">Send an Email</a> </div> <div class='oe_mail_compose_message_icons'> -<!-- <button icon="../../../../../mail/static/src/img/checklist" - type="object" name="checklist" string="" - help="Add a checklist"/> --> - <a href="#" class="oe_mail_compose_message_checklist"> - <img src='/mail/static/src/img/checklist.png/' alt='Checklist' - title='Add a checklist'/> - </a> - <a href="#" class="oe_mail_compose_message_attachment"> - <img src='/mail/static/src/img/attachment.png/' alt='Attachment' - title='Add an attachment'/> - </a> - <a href="#" class="oe_mail_compose_message_formatting"> - <img src='/mail/static/src/img/formatting.png/' alt='Formatting' - title='Switch to advanced formatting mode'/> - </a> + <button icon="/mail/static/src/img/attachment.png" + class="oe_mail_compose_message_attachment" string="" + name="dummy" + help="Add an attachment"/> + <button icon="/mail/static/src/img/formatting.png" + class="oe_mail_compose_message_formatting" string="" + type="object" name="toggle_content_subtype" + help="Toggle advanced formatting mode"/> </div> </group> </form> @@ -97,6 +111,6 @@ target="new" key2="client_action_multi" id="base.action_partner_mass_mail" - context="{'mail.compose.message.mode':'mass_mail'}"/> + context="{'default_composition_mode': 'mass_mail'}"/> </data> </openerp> diff --git a/addons/marketing_campaign/marketing_campaign_demo.xml b/addons/marketing_campaign/marketing_campaign_demo.xml index 54897cd111a..92443fe987d 100644 --- a/addons/marketing_campaign/marketing_campaign_demo.xml +++ b/addons/marketing_campaign/marketing_campaign_demo.xml @@ -3,29 +3,29 @@ <data> <!-- Email tempalte --> <record id="email_template_1" model="email.template"> - <field name="name">Template for New Partner</field> + <field name="name">welcome new partner</field> <field name="email_from">info@openerp.com</field> - <field name="subject">Welcome in OpenERP Partner Channel!</field> + <field name="subject">Welcome to the OpenERP Partner Channel!</field> <field name="email_to">${object.email or ''}</field> <field name="model_id" ref="base.model_res_partner"/> - <field name="body_text">Hello, We are very happy to send Welcome message.</field> + <field name="body_html">Hello, you will receive your welcome pack via email shortly.</field> </record> <record id="email_template_2" model="email.template"> - <field name="name">Template for Silver Partner</field> + <field name="name">congrats silver partner</field> <field name="email_from">info@openerp.com</field> - <field name="subject">Congratulation! You become now our Silver Partner.</field> + <field name="subject">Congratulations! You are now a Silver Partner!</field> <field name="email_to">${object.email or ''}</field> <field name="model_id" ref="base.model_res_partner"/> - <field name="body_text">Hello, We are happy to announce that you now become our Silver Partner.</field> + <field name="body_html">Hi, we are delighted to welcome you among our Silver Partners as of today!</field> </record> <record id="email_template_3" model="email.template"> - <field name="name">Template for Gold Partner</field> + <field name="name">congrats gold partner</field> <field name="email_from">info@openerp.com</field> - <field name="subject">Congratulation! You become our Gold Partner.</field> + <field name="subject">Congratulations! You are now one of our Gold Partners!</field> <field name="email_to">${object.email or ''}</field> <field name="model_id" ref="base.model_res_partner"/> - <field name="body_text">Hello, We are happy to announce that you become our Gold Partner.</field> + <field name="body_html">Hi, we are delighted to let you know that you have entered the select circle of our Gold Partners</field> </record> @@ -64,7 +64,7 @@ </record> - <!-- Tranisition --> + <!-- Transition --> <record id="marketing_campaign_transition_0" model="marketing.campaign.transition"> <field model="marketing.campaign.activity" name="activity_from_id" ref = "marketing_campaign_activity_0"/> <field model="marketing.campaign.activity" name="activity_to_id" ref = "marketing_campaign_activity_1"/> diff --git a/addons/marketing_campaign/test/marketing_campaign.yml b/addons/marketing_campaign/test/marketing_campaign.yml index 04063a6fbc1..3d540b4ccb7 100644 --- a/addons/marketing_campaign/test/marketing_campaign.yml +++ b/addons/marketing_campaign/test/marketing_campaign.yml @@ -42,7 +42,7 @@ record = self.browse(cr, uid, ids[0]) assert record.state == 'todo' or record.state == 'done' , 'Marketing Workitem shoud be in draft state.' - - I check follow-up detail of first activity. + I process follow-up of first activity. - !python {model: marketing.campaign.workitem}: | ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')), @@ -50,12 +50,6 @@ assert ids, 'Follow-up item is not created for first activity.' work_item_id = self.browse(cr ,uid ,ids[0] ,context) assert work_item_id.res_name, 'Resource Name is not defined.' -- - I process follow-up of first activity. -- - !python {model: marketing.campaign.workitem}: | - ids = self.search(cr, uid, [('segment_id', '=', ref('marketing_campaign_segment0')), - ('campaign_id', '=', ref('marketing_campaign_openerppartnerchannel')), ('activity_id', '=', ref('marketing_campaign_activity_0'))]) self.process(cr, uid, ids) record = self.browse(cr, uid, ids)[0] assert record.state == "done", "Follow-up item should be closed after process." diff --git a/addons/marketing_campaign_crm_demo/i18n/nb.po b/addons/marketing_campaign_crm_demo/i18n/nb.po new file mode 100644 index 00000000000..036051e492d --- /dev/null +++ b/addons/marketing_campaign_crm_demo/i18n/nb.po @@ -0,0 +1,192 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:33+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: marketing_campaign_crm_demo +#: model:ir.actions.report.xml,name:marketing_campaign_crm_demo.mc_crm_lead_demo_report +msgid "Marketing campaign demo report" +msgstr "Markedsføringskampanje demo rapport." + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_1 +msgid "" +"Hello,Thanks for generous interest you have shown in the " +"openERP.Regards,OpenERP Team," +msgstr "" +"Hei, Takk for sjenerøs interessen du har vist i openERP.Regards, OpenERP lag," + +#. module: marketing_campaign_crm_demo +#: report:crm.lead.demo:0 +msgid "Company :" +msgstr "Firma :" + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_4 +msgid "Thanks for buying the OpenERP book" +msgstr "Takk for at du kjøpte OpenERP boken." + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_6 +msgid "" +"Hello, We have very good offer that might suit you.\n" +" For our silver partners,We are paid technical training on " +"june,2010.\n" +" If any further information required kindly revert back.\n" +" I really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Vi har veldig godt tilbud som kan passe for deg.\n" +" For våre sølv partnere, Vi betalte teknisk trening på juni " +"2010.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Jeg setter pris på ditt samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_5 +msgid "Propose a free technical training to Gold partners" +msgstr "Foreslå en gratis teknisk opplæring til Gold partnere." + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_3 +msgid "Thanks for subscribing to the OpenERP Discovery Day" +msgstr "Takk for at du abonnerte på OpenERP Discovery dag." + +#. module: marketing_campaign_crm_demo +#: model:ir.actions.server,name:marketing_campaign_crm_demo.action_dummy +msgid "Dummy Action" +msgstr "Dummy handling." + +#. module: marketing_campaign_crm_demo +#: report:crm.lead.demo:0 +msgid "Partner :" +msgstr "Partner :" + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_1 +msgid "Thanks for showing interest in OpenERP" +msgstr "Takk for at du viser interesse for OpenERP." + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_2 +msgid "Propose to subscribe to the OpenERP Discovery Day on May 2010" +msgstr "Foreslår å abonnere på OpenERP Discovery Day mai 2010." + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_7 +msgid "" +"Hello, We have very good offer that might suit you.\n" +" For our silver partners,We are offering Gold partnership.\n" +" If any further information required kindly revert back.\n" +" I really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Vi har veldig godt tilbud som kan passe for deg.\n" +" For våre sølv partnere, tilbyr vi Gold partnerskap.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Jeg setter pris på ditt samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_7 +msgid "Propose gold partnership to silver partners" +msgstr "Foreslå gull partnerskap til sølv partnere." + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_4 +msgid "" +"Hello,Thanks for showing intrest and buying the OpenERP book.\n" +" If any further information required kindly revert back.\n" +" I really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Takk for at du viser interesse og kjøpe OpenERP boken.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Jeg setter pris på ditt samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_5 +msgid "" +"Hello, We have very good offer that might suit you.\n" +" For our gold partners,We are arranging free technical training " +"on june,2010.\n" +" If any further information required kindly revert back.\n" +" I really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Vi har veldig godt tilbud som kan passe for deg.\n" +" For våre gull partnere, Vi arrangerer gratis teknisk trening på " +"juni 2010.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Jeg setter pris på ditt samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_3 +msgid "" +"Hello,Thanks for showing intrest and for subscribing to the OpenERP " +"Discovery Day.\n" +" If any further information required kindly revert back.\n" +" I really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Takk for at du viser interesse og for å abonnere på OpenERP Discovery " +"Day.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Jeg setter pris på ditt samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_8 +msgid "Thanks for subscribing to technical training" +msgstr "Takk for at du abonnerte på teknisk trening." + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_2 +msgid "" +"Hello,We have very good offer that might suit you.\n" +" We propose you to subscribe to the OpenERP Discovery Day on May " +"2010.\n" +" If any further information required kindly revert back.\n" +" We really appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Vi har veldig godt tilbud som kan passe for deg.\n" +" Vi foreslår at du abonnere på OpenERP Discovery Day mai 2010.\n" +" Hvis noen ytterligere opplysninger som kreves ber gå tilbake.\n" +" Vi setter pris på din samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,body_text:marketing_campaign_crm_demo.email_template_8 +msgid "" +"Hello, Thanks for showing intrest and for subscribing to technical " +"training.If any further information required kindly revert back.I really " +"appreciate your co-operation on this.\n" +" Regards,OpenERP Team," +msgstr "" +"Hei, Takk for at du viser interesse og for å abonnere på teknisk training.If " +"ytterligere informasjon som kreves ber tilbake tilbake.Jeg virkelig setter " +"pris på din samarbeid om dette.\n" +" Hilsen OpenERP Team," + +#. module: marketing_campaign_crm_demo +#: model:email.template,subject:marketing_campaign_crm_demo.email_template_6 +msgid "Propose paid training to Silver partners" +msgstr "Foreslå betalt opplæring til Silver partnere." diff --git a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml index 0946ff6e129..141981a21d6 100644 --- a/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml +++ b/addons/marketing_campaign_crm_demo/marketing_campaign_demo.xml @@ -40,7 +40,10 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello,Thanks for generous interest you have shown in the openERP.Regards,OpenERP Team,</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>Thanks for the genuine interest you have shown in OpenERP.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> <field name="name">For OpenERP OnDemand Free Trial 2010</field> </record> <record id="email_template_2" model="email.template"> @@ -50,11 +53,11 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello,We have very good offer that might suit you. - We propose you to subscribe to the OpenERP Discovery Day on May 2010. - If any further information required kindly revert back. - We really appreciate your co-operation on this. - Regards,OpenERP Team,</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>We have very good offer that might suit you. + We suggest you subscribe to the OpenERP Discovery Day on May 2010.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> <field name="name">For OpenERP Discovery Day on May 2010</field> </record> @@ -65,10 +68,10 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello,Thanks for showing intrest and for subscribing to the OpenERP Discovery Day. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>Thanks for showing interest and for subscribing to the OpenERP Discovery Day.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> <field name="name">For OpenERP Discovery Day</field> </record> @@ -79,10 +82,10 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello,Thanks for showing intrest and buying the OpenERP book. + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>Thanks for showing interest and buying the OpenERP book.</p> If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> + <p>Regards,OpenERP Team,</p>]]></field> <field name="name">For OpenERP book</field> </record> @@ -92,12 +95,12 @@ <field model="ir.actions.act_window" name="ref_ir_act_window" search="[('name', '=', u'For OpenERP OnDemand Free Trial 2010 Mail Form')]"/> <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello, We have very good offer that might suit you. - For our gold partners,We are arranging free technical training on june,2010. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> - <field name="name">For technical training to Gold partners</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>We have very good offer that might suit you. + For our gold partners,We are arranging free technical training on june,2010.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> + <field name="name">technical training to gold partners</field> </record> <record id="email_template_6" model="email.template"> @@ -107,12 +110,12 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello, We have very good offer that might suit you. - For our silver partners,We are paid technical training on june,2010. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> - <field name="name">For training to Silver partners</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>We have very good offer that might suit you. + For our silver partners,We are paid technical training on june,2010.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> + <field name="name">training to silver partners</field> </record> <record id="email_template_7" model="email.template"> @@ -122,12 +125,12 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello, We have very good offer that might suit you. - For our silver partners,We are offering Gold partnership. - If any further information required kindly revert back. - I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> - <field name="name">For gold partnership to silver partners</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>We have very good offer that might suit you. + For our silver partners, we are offering Gold partnership.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> + <field name="name">gold partnership to silver partners</field> </record> <record id="email_template_8" model="email.template"> @@ -137,9 +140,12 @@ <field model="ir.values" name="ref_ir_value" search="[('name', '=', u'Send Mail (For OpenERP OnDemand Free Trial 2010)')]"/> <field name="model_id" ref="crm.model_crm_lead"/> <field eval="0" name="user_signature"/> - <field name="body_text">Hello, Thanks for showing intrest and for subscribing to technical training.If any further information required kindly revert back.I really appreciate your co-operation on this. - Regards,OpenERP Team,</field> - <field name="name">For subscribing to technical training</field> + <field name="body_html"><![CDATA[<p>Hello,</p> + <p>Thanks for showing interest and for subscribing to technical training.</p> + If any further information required kindly revert back.I really appreciate your co-operation on this.</p> + <p>If any further information is required, do not hesitate to reply to this message.</p> + <p>Regards,OpenERP Team,</p>]]></field> + <field name="name">subscribing to technical training</field> </record> <!-- Report --> diff --git a/addons/membership/__openerp__.py b/addons/membership/__openerp__.py index e4b596183af..316a2f0f120 100644 --- a/addons/membership/__openerp__.py +++ b/addons/membership/__openerp__.py @@ -46,7 +46,6 @@ invoice and send propositions for membership renewal. 'membership_view.xml', 'report/report_membership_view.xml', 'process/membership_process.xml', - 'membership_data.xml' ], 'demo': ['membership_demo.xml'], 'test': ['test/test_membership.yml'], diff --git a/addons/membership/membership.py b/addons/membership/membership.py index 3439f97f833..2a006814629 100644 --- a/addons/membership/membership.py +++ b/addons/membership/membership.py @@ -146,7 +146,7 @@ class membership_line(osv.osv): 'date_to': fields.date('To', readonly=True), 'date_cancel': fields.date('Cancel date'), 'date': fields.date('Join Date', help="Date on which member has joined the membership"), - 'member_price': fields.float('Member Price', digits_compute= dp.get_precision('Product Price'), required=True, help='Amount for the membership'), + 'member_price': fields.float('Membership Fee', digits_compute= dp.get_precision('Product Price'), required=True, help='Amount for the membership'), 'account_invoice_line': fields.many2one('account.invoice.line', 'Account Invoice line', readonly=True), 'account_invoice_id': fields.related('account_invoice_line', 'invoice_id', type='many2one', relation='account.invoice', string='Invoice', readonly=True), 'state': fields.function(_state, @@ -444,14 +444,6 @@ class Partner(osv.osv): Partner() -class product_template(osv.osv): - _inherit = 'product.template' - _columns = { - 'member_price': fields.float('Member Price', digits_compute= dp.get_precision('Product Price')), - } - -product_template() - class Product(osv.osv): def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): diff --git a/addons/membership/membership_data.xml b/addons/membership/membership_data.xml deleted file mode 100644 index 14fbb00f9a2..00000000000 --- a/addons/membership/membership_data.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" ?> -<openerp> - <data noupdate="1"> - - <!-- - Price type - --> - <record model="product.price.type" id="product_price_type_memberprice" > - <field name="name">Member Price</field> - <field name="field">member_price</field> - </record> - - <!-- - Price list - --> - <record model="product.pricelist" id="list1m"> - <field name="name">Member Sale Pricelist</field> - <field name="type">sale</field> - </record> - <record model="product.pricelist.version" id="ver1m"> - <field name="pricelist_id" ref="list1m"/> - <field name="name">Member Sale Pricelist Version</field> - </record> - <record model="product.pricelist.item" id="item1m"> - <field name="price_version_id" ref="ver1m"/> - <field name="base" ref="product_price_type_memberprice" /> - <field name="name">Member Sale Pricelist Line</field> - </record> - - </data> -</openerp> diff --git a/addons/membership/membership_view.xml b/addons/membership/membership_view.xml index fb128585fd2..ce0e55e37c9 100644 --- a/addons/membership/membership_view.xml +++ b/addons/membership/membership_view.xml @@ -88,11 +88,11 @@ <field name="arch" type="xml"> <page string="Information" position="after"> <page string="Membership"> - <group> + <group col="1"> <group> <field name="membership" readonly="0"/> </group> - <group> + <group attrs="{'invisible':[('membership','=',False)]}"> <field name="membership_date_from" readonly="0" attrs="{'required':[('membership','=',True)]}"/> <field name="membership_date_to" readonly="0" attrs="{'required':[('membership','=',True)]}"/> </group> @@ -270,17 +270,5 @@ <!-- MEMBERSHIP/REPORTING --> <menuitem name="Reporting" id="base.menu_report_association" parent="base.menu_association" sequence="20"/> - <!-- View for product.product object inherited from product module --> - <record model="ir.ui.view" id="view_product_form_inherit1"> - <field name="name">product.normal.form</field> - <field name="model">product.product</field> - <field name="inherit_id" ref="product.product_normal_form_view"/> - <field name="arch" type="xml"> - <field name="list_price" position="after"> - <field name="member_price" groups="base.group_user"/> - </field> - </field> - </record> - </data> </openerp> diff --git a/addons/mrp/board_manufacturing_view.xml b/addons/mrp/board_manufacturing_view.xml index e7668517a70..cae1fea07dc 100644 --- a/addons/mrp/board_manufacturing_view.xml +++ b/addons/mrp/board_manufacturing_view.xml @@ -9,7 +9,6 @@ <board style="2-1"> <column> <action name="%(procurement.procurement_exceptions)d" string="Procurements in Exception" domain="[('state','=','exception')]"/> - <action name="%(mrp.mrp_production_action2)d" string="Next Production Orders" creatable="true"/> </column> <column> <action name="%(mrp.action_report_in_out_picking_tree)d" string="Stock Value Variation"/> diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 7b8442b4c46..c667daadbec 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -369,7 +369,7 @@ class mrp_bom(osv.osv): prod_obj = self.pool.get('product.product') for obj in self.browse(cr, uid, ids, context=context): for prod in prod_obj.browse(cr, uid, [obj.product_id], context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Bill of Material has been <b>created</b> for <em>%s</em> product.") % (prod.id.name_template), context=context) + self.message_post(cr, uid, [obj.id], body=_("Bill of Material has been <b>created</b> for <em>%s</em> product.") % (prod.id.name_template), context=context) return True mrp_bom() @@ -407,7 +407,7 @@ class mrp_production(osv.osv): _name = 'mrp.production' _description = 'Manufacturing Order' _date_name = 'date_planned' - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _production_calc(self, cr, uid, ids, prop, unknow_none, context=None): """ Calculates total hours and total no. of cycles for a production order. @@ -1046,33 +1046,28 @@ class mrp_production(osv.osv): # OpenChatter methods and notifications # --------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(mrp_production, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def create_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Manufacturing order has been <b>created</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Manufacturing order has been <b>created</b>."), context=context) return True def action_cancel_send_note(self, cr, uid, ids, context=None): message = _("Manufacturing order has been <b>canceled</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def action_ready_send_note(self, cr, uid, ids, context=None): message = _("Manufacturing order is <b>ready to produce</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def action_in_production_send_note(self, cr, uid, ids, context=None): message = _("Manufacturing order is <b>in production</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def action_done_send_note(self, cr, uid, ids, context=None): message = _("Manufacturing order has been <b>done</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def action_confirm_send_note(self, cr, uid, ids, context=None): @@ -1082,7 +1077,7 @@ class mrp_production(osv.osv): obj_datetime = fields.DT.datetime.strptime(obj.date_planned, DEFAULT_SERVER_DATETIME_FORMAT) obj_date_str = fields.datetime.context_timestamp(cr, uid, obj_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)") message = _("Manufacturing order has been <b>confirmed</b> and is <b>scheduled</b> for the <em>%s</em>.") % (obj_date_str) - self.message_append_note(cr, uid, [obj.id], body=message, context=context) + self.message_post(cr, uid, [obj.id], body=message, context=context) return True diff --git a/addons/mrp/mrp_data.xml b/addons/mrp/mrp_data.xml index e5d5380f96a..0bbf2d3b64c 100644 --- a/addons/mrp/mrp_data.xml +++ b/addons/mrp/mrp_data.xml @@ -2,14 +2,16 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module MRP has been installed</value> - <value>Manage your manufacturing process in OpenERP by defining bill of materials (BoM), routings and work centers. This module supports complete integration and planification of stockable goods, consumable, and services. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">MRP application installed!</field> + <field name="body">Manage your manufacturing process with OpenERP by defining your bills of materials (BoM), routings and work centers. +This application supports complete integration and production scheduling for stockable goods, consumables, and services. -From the Manufacturing Settings, you can choose to compute production schedules periodically or just-in-time.</value> - </function> +From the Manufacturing Settings, you can choose to compute production schedules periodically or just-in-time.</field> + </record> <record id="sequence_mrp_prod_type" model="ir.sequence.type"> <field name="name">Production order</field> diff --git a/addons/mrp/mrp_demo.xml b/addons/mrp/mrp_demo.xml index d0f05a2aaff..952a5e0e3de 100644 --- a/addons/mrp/mrp_demo.xml +++ b/addons/mrp/mrp_demo.xml @@ -49,7 +49,7 @@ <field name="costs_hour_account_id" ref="account_assembly_hours"/> <field name="costs_cycle">0.05</field> <field name="costs_cycle_account_id" ref="account_assembly_cycle"/> - <field name="costs_journal_id" ref="account.expenses_journal"/> + <field name="costs_journal_id" ref="account.exp"/> <field name="costs_general_account_id" ref="account.a_expense"/> <field name="costs_general_account_id" ref="account.a_expense"/> <field name="calendar_id" ref="resource.timesheet_group1"/> diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 52b79e2ade0..8121f6e84d1 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -501,7 +501,7 @@ <field name="res_model">mrp.bom</field> <field name="view_type">form</field> <field name="view_id" ref="mrp_bom_component_tree_view"/> - <field name="domain">[('bom_id','!=',False)]</field> + <field name="domain">[]</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to add a component to a bill of material. @@ -524,6 +524,7 @@ action="mrp_bom_form_action2" id="menu_mrp_bom_form_action2" parent="menu_mrp_configuration" + groups="base.group_no_one" sequence="20"/> <record id="action2" model="ir.actions.act_window"> @@ -578,8 +579,8 @@ <field name="name">mrp.production.tree</field> <field name="model">mrp.production</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="blue:state in ('draft','confirmed');red:date_planned<current_date and state not in ('done','cancel');black:date_planned>=current_date;gray:state in ('done','cancel') " string="Manufacturing Orders"> - <field name="needaction_pending" invisible="1"/> + <tree fonts="bold:message_unread==True" colors="blue:state in ('draft','confirmed');red:date_planned<current_date and state not in ('done','cancel');black:date_planned>=current_date;gray:state in ('done','cancel') " string="Manufacturing Orders"> + <field name="message_unread" invisible="1"/> <field name="name"/> <field name="date_planned"/> <field name="product_id"/> @@ -647,51 +648,43 @@ <field name="state" widget="statusbar" statusbar_visible="draft,ready,in_production,done" statusbar_colors='{"picking_except":"red","confirmed":"blue"}'/> </header> <sheet> - <label for="product_id" class="oe_edit_only"/> - <h1> - <field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','<>',False),('bom_ids.bom_id','=',False)]" class="oe_inline"/> - </h1> - <label for="product_qty" class="oe_edit_only"/> - <h2> - <field name="product_qty" class="oe_inline"/> - <field name="product_uom" groups="product.group_uom" class="oe_inline"/> - <button type="action" - icon="terp-accessories-archiver+" - name="%(mrp.action_change_production_qty)d" - string="Change Qty" states="ready,confirmed" class="oe_inline"/> - </h2> - <div groups="mrp.group_mrp_routings"> - <label for="routing_id" class="oe_edit_only"/> - <h2><field name="routing_id" class="oe_inline"/></h2> + <div class="oe_title"> + <h1>Manufacturing Order <field name="name" class="oe_inline"/></h1> </div> <group> <group> - <field name="name"/> - <field name="date_planned"/> - <field name="origin"/> - </group> - <group> - <field name="user_id"/> + <field name="product_id" on_change="product_id_change(product_id)" domain="[('bom_ids','<>',False),('bom_ids.bom_id','=',False)]" class="oe_inline"/> + <label for="product_qty"/> + <div> + <field name="product_qty" class="oe_inline"/> + <field name="product_uom" groups="product.group_uom" class="oe_inline"/> + <button type="action" + icon="terp-accessories-archiver+" + name="%(mrp.action_change_production_qty)d" + string="(Update)" states="ready,confirmed" class="oe_edit_only oe_link"/> + </div> <label for="product_uos_qty" groups="product.group_uos"/> <div groups="product.group_uos"> <field name="product_uos_qty" class="oe_inline"/> <label string="-" attrs="{'invisible':[('product_uos','=',False)]}" class="oe_inline"/> <field name="product_uos" class="oe_inline"/> </div> + <field name="date_planned"/> + </group> + <group> + <field name="bom_id" domain="[('product_id','=',product_id)]" context="{'default_product_id': product_id}" on_change="bom_id_change(bom_id)"/> + <field name="routing_id" class="oe_inline" groups="mrp.group_mrp_routings"/> + <field name="user_id"/> + <field name="origin"/> + </group> + <group groups="stock.group_locations"> + <field name="location_src_id" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/> + <field name="location_dest_id" domain="[('usage','=','internal')]"/> </group> </group> <notebook> <page string="Consumed Products"> <group> - <group> - <field name="bom_id" domain="[('product_id','=',product_id)]" context="{'default_product_id': product_id}" on_change="bom_id_change(bom_id)"/> - </group> - <group groups="stock.group_locations"> - <field name="location_src_id" domain="[('usage','=','internal')]" on_change="location_id_change(location_src_id,location_dest_id)"/> - <field name="location_dest_id" domain="[('usage','=','internal')]"/> - </group> - </group> - <group> <group string="Products to Consume"> <field name="move_lines" domain="[('state','<>', ('done', 'cancel'))]" nolabel="1"> <tree colors="blue:state == 'draft';black:state in ('picking_except','confirmed','ready','in_production');gray:state in ('cancel','done') " string="Products to Consume"> diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index 363f98e35d1..7f417dc18b1 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -43,7 +43,7 @@ class procurement_order(osv.osv): cr.execute('update procurement_order set message=%s where id=%s', (_('No BoM defined for this product !'), procurement.id)) for (id, name) in self.name_get(cr, uid, procurement.id): message = _("Procurement '%s' has an exception: 'No BoM defined for this product !'") % name - self.message_append_note(cr, uid, [procurement.id], body=message, context=context) + self.message_post(cr, uid, [procurement.id], body=message, context=context) return False return True diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index 8692dab5c87..ee2547bc7df 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -224,7 +224,7 @@ class mrp_production_workcenter_line(osv.osv): for workorder in self.browse(cr, uid, ids): for prod in prod_obj.browse(cr, uid, [workorder.production_id]): message = _("Work order has been <b>created</b> for production order <em>%s</em>.") % (prod.id.name) - self.message_append_note(cr, uid, [workorder.id], body=message, context=context) + self.message_post(cr, uid, [workorder.id], body=message, context=context) return True def action_start_send_note(self, cr, uid, ids, context=None): @@ -232,7 +232,7 @@ class mrp_production_workcenter_line(osv.osv): for workorder in self.browse(cr, uid, ids): for prod in prod_obj.browse(cr, uid, [workorder.production_id]): message = _("Work order has been <b>started</b> for production order <em>%s</em>.") % (prod.id.name) - self.message_append_note(cr, uid, [workorder.id], body=message, context=context) + self.message_post(cr, uid, [workorder.id], body=message, context=context) return True def action_done_send_note(self, cr, uid, ids, context=None): @@ -240,7 +240,7 @@ class mrp_production_workcenter_line(osv.osv): for workorder in self.browse(cr, uid, ids): for prod in prod_obj.browse(cr, uid, [workorder.production_id]): message = _("Work order has been <b>done</b> for production order <em>%s</em>.") % (prod.id.name) - self.message_append_note(cr, uid, [workorder.id], body=message, context=context) + self.message_post(cr, uid, [workorder.id], body=message, context=context) return True def action_pending_send_note(self, cr, uid, ids, context=None): @@ -248,7 +248,7 @@ class mrp_production_workcenter_line(osv.osv): for workorder in self.browse(cr, uid, ids): for prod in prod_obj.browse(cr, uid, [workorder.production_id]): message = _("Work order is <b>pending</b> for production order <em>%s</em>.") % (prod.id.name) - self.message_append_note(cr, uid, [workorder.id], body=message, context=context) + self.message_post(cr, uid, [workorder.id], body=message, context=context) return True def action_cancel_send_note(self, cr, uid, ids, context=None): @@ -256,7 +256,7 @@ class mrp_production_workcenter_line(osv.osv): for workorder in self.browse(cr, uid, ids): for prod in prod_obj.browse(cr, uid, [workorder.production_id]): message = _("Work order has been <b>cancelled</b> for production order <em>%s</em>.") % (prod.id.name) - self.message_append_note(cr, uid, [workorder.id], body=message, context=context) + self.message_post(cr, uid, [workorder.id], body=message, context=context) return True mrp_production_workcenter_line() diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py index f41abb58c08..c1ac8f809ce 100644 --- a/addons/mrp_repair/mrp_repair.py +++ b/addons/mrp_repair/mrp_repair.py @@ -571,40 +571,40 @@ class mrp_repair(osv.osv): def create_send_note(self, cr, uid, ids, context=None): for repair in self.browse(cr, uid, ids, context): message = _("Repair Order for <em>%s</em> has been <b>created</b>." % (repair.product_id.name)) - self.message_append_note(cr, uid, [repair.id], body=message, context=context) + self.message_post(cr, uid, [repair.id], body=message, context=context) return True def set_start_send_note(self, cr, uid, ids, context=None): for repair in self.browse(cr, uid, ids, context): message = _("Repair Order for <em>%s</em> has been <b>started</b>." % (repair.product_id.name)) - self.message_append_note(cr, uid, [repair.id], body=message, context=context) + self.message_post(cr, uid, [repair.id], body=message, context=context) return True def set_toinvoiced_send_note(self, cr, uid, ids, context=None): for repair in self.browse(cr, uid, ids, context): message = _("Draft Invoice of %s %s <b>waiting for validation</b>.") % (repair.invoice_id.amount_total, repair.invoice_id.currency_id.symbol) - self.message_append_note(cr, uid, [repair.id], body=message, context=context) + self.message_post(cr, uid, [repair.id], body=message, context=context) return True def set_confirm_send_note(self, cr, uid, ids, context=None): for repair in self.browse(cr, uid, ids, context): message = _( "Repair Order for <em>%s</em> has been <b>accepted</b>." % (repair.product_id.name)) - self.message_append_note(cr, uid, [repair.id], body=message, context=context) + self.message_post(cr, uid, [repair.id], body=message, context=context) return True def set_cancel_send_note(self, cr, uid, ids, context=None): message = _("Repair has been <b>cancelled</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_ready_send_note(self, cr, uid, ids, context=None): message = _("Repair Order is now <b>ready</b> to repair.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True def set_done_send_note(self, cr, uid, ids, context=None): message = _("Repair Order is <b>closed</b>.") - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) return True mrp_repair() diff --git a/addons/mrp_subproduct/i18n/nb.po b/addons/mrp_subproduct/i18n/nb.po new file mode 100644 index 00000000000..49292481c49 --- /dev/null +++ b/addons/mrp_subproduct/i18n/nb.po @@ -0,0 +1,132 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:49+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: mrp_subproduct +#: field:mrp.subproduct,product_id:0 +msgid "Product" +msgstr "Produkt" + +#. module: mrp_subproduct +#: selection:mrp.subproduct,subproduct_type:0 +msgid "Fixed" +msgstr "Fast" + +#. module: mrp_subproduct +#: sql_constraint:mrp.bom:0 +msgid "" +"All product quantities must be greater than 0.\n" +"You should install the mrp_subproduct module if you want to manage extra " +"products on BoMs !" +msgstr "" +"Alle produktantall må være større en 0.\n" +"Du bør installere modulen mrp:subproduct hvis du vil håndtere ekstra " +"produkter på Materiallister !" + +#. module: mrp_subproduct +#: view:mrp.bom:0 +msgid "sub products" +msgstr "Sub produkter" + +#. module: mrp_subproduct +#: model:ir.model,name:mrp_subproduct.model_mrp_production +msgid "Manufacturing Order" +msgstr "Produksjonsordre" + +#. module: mrp_subproduct +#: constraint:mrp.bom:0 +msgid "BoM line product should not be same as BoM product." +msgstr "Bom linje produktet bør ikke være det samme som BOM produkt." + +#. module: mrp_subproduct +#: view:mrp.bom:0 +msgid "Sub Products" +msgstr "Sub produkter." + +#. module: mrp_subproduct +#: field:mrp.subproduct,subproduct_type:0 +msgid "Quantity Type" +msgstr "Antall Type." + +#. module: mrp_subproduct +#: model:ir.model,name:mrp_subproduct.model_mrp_bom +msgid "Bill of Material" +msgstr "Materialliste" + +#. module: mrp_subproduct +#: field:mrp.subproduct,product_qty:0 +msgid "Product Qty" +msgstr "Produkt Ant" + +#. module: mrp_subproduct +#: field:mrp.subproduct,product_uom:0 +msgid "Product UOM" +msgstr "Produkt Enhet" + +#. module: mrp_subproduct +#: field:mrp.subproduct,bom_id:0 +msgid "BoM" +msgstr "Materialliste" + +#. module: mrp_subproduct +#: sql_constraint:mrp.production:0 +msgid "Reference must be unique per Company!" +msgstr "Referanse må være unik pr firma!" + +#. module: mrp_subproduct +#: field:mrp.bom,sub_products:0 +msgid "sub_products" +msgstr "Sub_produkter." + +#. module: mrp_subproduct +#: selection:mrp.subproduct,subproduct_type:0 +msgid "Variable" +msgstr "Variabel" + +#. module: mrp_subproduct +#: model:ir.model,name:mrp_subproduct.model_mrp_subproduct +msgid "Sub Product" +msgstr "Sub produkt." + +#. module: mrp_subproduct +#: constraint:mrp.production:0 +msgid "Order quantity cannot be negative or zero!" +msgstr "Ordrekvantum kan ikke være negativt eller null!" + +#. module: mrp_subproduct +#: help:mrp.subproduct,subproduct_type:0 +msgid "" +"Define how the quantity of subproducts will be set on the production orders " +"using this BoM. 'Fixed' depicts a situation where the quantity of created " +"subproduct is always equal to the quantity set on the BoM, regardless of how " +"many are created in the production order. By opposition, 'Variable' means " +"that the quantity will be computed as '(quantity of subproduct set on the " +"BoM / quantity of manufactured product set on the BoM * quantity of " +"manufactured product in the production order.)'" +msgstr "" +"Definere hvordan mengden av delkomponenter vil bli satt på produksjonsordrer " +"ved hjelp av denne BOM. 'Fast' beskriver en situasjon der mengden av laget " +"subproduct er alltid lik mengde sett på BOM, uavhengig av hvor mange som er " +"skapt i produksjonen rekkefølge. Av motstand, 'variabel' betyr at mengden " +"vil bli beregnet som '(mengde subproduct satt på BOM / mengde produsert " +"produkt satt på BOM * mengde produsert produkt i produksjonen rekkefølge.)'" + +#. module: mrp_subproduct +#: constraint:mrp.bom:0 +msgid "Error ! You cannot create recursive BoM." +msgstr "Feil! Du kan ikke lage en rekursive BoM." diff --git a/addons/multi_company/multi_company_demo.xml b/addons/multi_company/multi_company_demo.xml index 462a355e789..d120550e3b9 100644 --- a/addons/multi_company/multi_company_demo.xml +++ b/addons/multi_company/multi_company_demo.xml @@ -52,47 +52,12 @@ <!-- User --> - <record id="res_users_editor_demo" model="res.users"> - <field name="password">editor_demo</field> - <field name="name">OpenERP Editor</field> - <field name="login">editor_demo</field> - <field name="company_id" ref="res_company_oerp_editor"/> - <field name="company_ids" eval="[(4, ref('res_company_oerp_editor'))]"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'),ref('base.group_sale_manager'),ref('project.group_project_user'),ref('account.group_account_invoice')])]"/> - </record> - <record id="res_users_sales_demo_us" model="res.users"> - <field name="password">sales_demo_us</field> - <field name="name">OpenERP Salesman US</field> - <field name="login">sales_demo_us</field> - <field name="company_id" ref="res_company_oerp_us"/> - <field name="company_ids" eval="[(4, ref('res_company_oerp_us'))]"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'),ref('base.group_sale_manager'),ref('project.group_project_user'),ref('account.group_account_invoice')])]"/> - </record> - <record id="res_users_sales_demo_be" model="res.users"> - <field name="password">sales_demo_be</field> - <field name="name">OpenERP Salesman BE</field> - <field name="login">sales_demo_be</field> - <field name="company_id" ref="res_company_oerp_be"/> - <field name="company_ids" eval="[(4, ref('res_company_oerp_be'))]"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'),ref('base.group_sale_manager'),ref('project.group_project_user'),ref('account.group_account_invoice')])]"/> + <record id="base.user_root" model="res.users"> + <field name="company_ids" eval="[(4, ref('res_company_oerp_editor')),(4, ref('res_company_oerp_us')),(4, ref('res_company_oerp_be'))]"/> </record> - <record id="res_users_sales_demo_in" model="res.users"> - <field name="password">sales_demo_in</field> - <field name="name">OpenERP Salesman IN</field> - <field name="login">sales_demo_in</field> - <field name="company_id" ref="res_company_oerp_in"/> - <field name="company_ids" eval="[(4, ref('res_company_oerp_in'))]"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'),ref('base.group_sale_manager'),ref('project.group_project_user'),ref('account.group_account_invoice')])]"/> + <record id="base.user_demo" model="res.users"> + <field name="company_ids" eval="[(4, ref('res_company_oerp_us')),(4, ref('res_company_oerp_be')),(4, ref('res_company_odoo'))]"/> </record> - <record id="res_users_odoo_demo" model="res.users"> - <field name="password">odoo_demo</field> - <field name="name">Odoo</field> - <field name="login">odoo_demo</field> - <field name="company_id" ref="res_company_odoo"/> - <field name="company_ids" eval="[(4, ref('res_company_odoo'))]"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'),ref('base.group_sale_manager'),ref('project.group_project_user'),ref('account.group_account_invoice')])]"/> - </record> - <!-- Resource: product.product diff --git a/addons/note/__init__.py b/addons/note/__init__.py new file mode 100644 index 00000000000..9bf064877b5 --- /dev/null +++ b/addons/note/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +import note + diff --git a/addons/project_mailgate/__openerp__.py b/addons/note/__openerp__.py index 05eacb782cc..62d9962634a 100644 --- a/addons/project_mailgate/__openerp__.py +++ b/addons/note/__openerp__.py @@ -19,34 +19,45 @@ # ############################################################################## - { - 'name': 'Tasks-Mail Integration', - 'version': '1.1', - 'author': 'OpenERP SA', - 'website': 'http://www.openerp.com', - 'category': 'Project Management', - 'images': ['images/project_mailgate_task.jpeg'], - 'depends': ['project', 'mail'], + 'name': 'Notes', + 'version': '0.1', + 'category': 'Tools', 'description': """ -This module can automatically create Project Tasks based on incoming emails. -============================================================================ +This module allows users to create their own notes inside OpenERP +================================================================= -Allows creating tasks based on new emails arriving at a given mailbox, -similarly to what the CRM application has for Leads/Opportunities. +Use notes to write meeting minutes, organize ideas, organize personnal todo +lists, etc. Each user manages his own personnal notes. Notes are available to +their authors only, but they can share notes to others users so that several +people can work on the same note in real time. It's very efficient to share +meeting minutes. -There are two common alternatives to configure the mailbox integration: ------------------------------------------------------------------------ - * Install the ``fetchmail`` module and configure a new mailbox, then select - ``Project Tasks`` as the target for incoming emails. - * Set it up manually on your mail server based on the 'mail gateway' script - provided in the ``mail`` module - and connect it to the `project.task` model. - """, - 'data': [], - 'demo': [], +Notes can be found in the 'Home' menu. +""", + 'author': 'OpenERP SA', + 'website': 'http://openerp.com', + 'summary': 'Sticky Notes, Collaborative', + 'depends': [ + 'base_tools', + 'mail', + 'pad', + ], + 'data': [ + 'security/note_security.xml', + 'security/ir.model.access.csv', + 'note_data.xml', + 'note_view.xml', + ], + 'demo': [ + 'note_demo.xml', + ], + 'css': [ + 'static/src/css/note.css', + ], 'installable': True, + 'application': True, 'auto_install': False, - 'certificate': '001075048780413258261', } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/note/note.py b/addons/note/note.py new file mode 100644 index 00000000000..abc7dca297e --- /dev/null +++ b/addons/note/note.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +from openerp.osv import osv, fields +from tools.translate import _ + +class note_stage(osv.osv): + """ Category of Note """ + _name = "note.stage" + _description = "Note Stage" + _columns = { + 'name': fields.char('Category Name', size=64, required=True), + 'sequence': fields.integer('Sequence', help="Used to order the note stages"), + 'user_id': fields.many2one('res.users', 'Owner', help="Owner of the note stage.", required=True, readonly=True), + 'fold': fields.boolean('Folded'), + } + _order = 'sequence asc' + _defaults = { + 'fold': 0, + 'user_id': lambda self, cr, uid, ctx: uid, + 'sequence' : 1, + } + +class note_note(osv.osv): + """ Note """ + _name = 'note.note' + _inherit = ['mail.thread','pad.common'] + _pad_fields = ['note_pad'] + _description = "Note" + + def _set_note_first_line(self, cr, uid, id, name, value, args, context=None): + return self.write(cr, uid, [id], {'note': value}, context=context) + + def _get_note_first_line(self, cr, uid, ids, name, args, context=None): + res = {} + for note in self.browse(cr, uid, ids, context=context): + res[note.id] = (note.note or '').strip().split('\n')[0] + return res + + def _get_default_stage_id(self,cr,uid,context=None): + ids = self.pool.get('note.stage').search(cr,uid,[('user_id','=',uid)]) + return ids and ids[0] or False + + def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): + access_rights_uid = access_rights_uid or uid + stage_obj = self.pool.get('note.stage') + + # only show stage groups not folded and owned by user + search_domain = [('user_id', '=', uid)] + + stage_ids = stage_obj._search(cr, uid, search_domain, order=self._order, access_rights_uid=access_rights_uid, context=context) + result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + return result, fold + + _columns = { + 'name': fields.function(_get_note_first_line, fnct_inv=_set_note_first_line, string='Note Summary', type="text"), + 'note': fields.text('Pad Content'), + 'note_pad_url': fields.char('Pad Url', size=250), + 'sequence': fields.integer('Sequence'), + 'stage_id': fields.many2one('note.stage', 'Stage'), + 'active': fields.boolean('Active'), + 'color': fields.integer('Color Index'), + # to be replaced by message_follower_ids + 'follower_ids': fields.many2many('res.users', 'mail_subscription', 'res_id', 'user_id', 'Followers') + } + + _defaults = { + 'active' : 1, + 'stage_id' : _get_default_stage_id, + 'note_pad_url': lambda self, cr, uid, context: self.pad_generate_url(cr, uid, context), + } + _order = 'sequence asc' + _group_by_full = { + 'stage_id' : _read_group_stage_ids, + } diff --git a/addons/note/note_data.xml b/addons/note/note_data.xml new file mode 100644 index 00000000000..670b4c89269 --- /dev/null +++ b/addons/note/note_data.xml @@ -0,0 +1,24 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <record model="note.stage" id="note_stage_01"> + <field name="name">Todo today</field> + <field name="sequence">1</field> + <field name="user_id" eval="ref('base.user_root')"/> + </record> + + <record model="note.stage" id="note_stage_02"> + <field name="name">Todo later</field> + <field name="sequence">2</field> + <field name="user_id" eval="ref('base.user_root')"/> + </record> + + <record model="note.stage" id="note_stage_03"> + <field name="name">For Info</field> + <field name="sequence">3</field> + <field name="user_id" eval="ref('base.user_root')"/> + </record> + + </data> +</openerp> diff --git a/addons/note/note_demo.xml b/addons/note/note_demo.xml new file mode 100644 index 00000000000..e6ab25198a6 --- /dev/null +++ b/addons/note/note_demo.xml @@ -0,0 +1,152 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <record id="note_1" model="note.note"> + <field name="note"><![CDATA[Buying list + * coffee + * fruits + * ice cream + * towels + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_01"/> + <field name="user_id" ref="base.user_root"/> + <field name="color">2</field> + </record> + <record id="note_2" model="note.note"> + <field name="note"><![CDATA[Buying list + * more coffee + * tee + * sugar + * towels + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_01"/> + <field name="user_id" ref="base.user_root"/> + </record> + + <record id="note_3" model="note.note"> + <field name="note"><![CDATA[Idea to develop + * Phasellus vel eros lorem, vel tristique risus. + * Cras elementum risus ut massa bibendum ac sollicitudin quam feugiat. + * Phasellus ullamcorper mauris quis lectus volutpat non dignissim elit lacinia. + + Nulla vitae metus risus, nec placerat diam. + Proin quis nunc sodales dolor tempus eleifend. + Praesent iaculis libero ut orci egestas a dapibus libero ornare. + + * Proin at nisl sit amet ante feugiat euismod nec at orci. + * Nam aliquam massa id neque tincidunt eu pellentesque neque euismod. + * Proin eget mi sit amet erat tempus pellentesque. + + * Phasellus congue est lectus, vitae semper arcu. + * Suspendisse sodales tortor ut odio volutpat fringilla. + * Vestibulum mattis est quis risus dignissim sit amet faucibus nisl mattis. + * Pellentesque ullamcorper neque neque, vitae rhoncus massa. + + Ut id magna massa, quis scelerisque sapien. + + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_01"/> + <field name="user_id" ref="base.user_root"/> + </record> + + <record id="note_4" model="note.note"> + <field name="note"><![CDATA[Read some documentation about OpenERP before diving into the code + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_02"/> + <field name="user_id" ref="base.user_root"/> + </record> + + <record id="note_5" model="note.note"> + <field name="note"><![CDATA[New computer specs + * Motherboard + according to processor + * Processor + need to decide + * Graphic card + with great performance for games ! + * Hard drive + big, for lot of internet backups + * Tower + silent, better when watching films + * Blueray drive ? + is it interesting yet ? + * Screen + a big one, full of pixels, of course ! + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_02"/> + <field name="user_id" ref="base.user_root"/> + <field name="color">3</field> + </record> + + <record id="note_6" model="note.note"> + <field name="note"><![CDATA[Read those books + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_02"/> + <field name="user_id" ref="base.user_root"/> + </record> + + <record id="note_7" model="note.note"> + <field name="note"><![CDATA[Read some documentation about OpenERP before diving into the code + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_03"/> + <field name="user_id" ref="base.user_root"/> + </record> + + <record id="note_8" model="note.note"> + <field name="note"><![CDATA[Read those books + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_03"/> + <field name="user_id" ref="base.user_root"/> + <field name="color">5</field> + </record> + + <record id="note_9" model="note.note"> + <field name="note"><![CDATA[Read some documentation about OpenERP before diving into the code + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_03"/> + <field name="user_id" ref="base.user_root"/> + <field name="color">7</field> + </record> + + <record id="note_10" model="note.note"> + <field name="note"><![CDATA[Read those books + * Open ERP: a modern approach to integrated business management + * Open ERP for Retail and Industrial Management + ]]> + </field> + <field name="note_pad"></field> + <field name="stage_id" ref="note_stage_03"/> + <field name="user_id" ref="base.user_root"/> + </record> + + </data> +</openerp> diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml new file mode 100644 index 00000000000..73636ce1a9a --- /dev/null +++ b/addons/note/note_view.xml @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<openerp> + <data> + + <menuitem name="Organizer" id="note_my_stuff" parent="mail.mail_feeds_main"/> + + <!-- Note Stage Form View --> + <record model="ir.ui.view" id="view_note_stage_form"> + <field name="name">note.stage.form</field> + <field name="model">note.stage</field> + <field name="arch" type="xml"> + <form string="Stage of Notes" version="7.0"> + <group> + <field name="name"/> + </group> + </form> + </field> + </record> + + <!-- Note Stage Tree View --> + <record model="ir.ui.view" id="view_note_stage_tree"> + <field name="name">note.stage.tree</field> + <field name="model">note.stage</field> + <field name="field_parent"></field> + <field name="arch" type="xml"> + <tree string="Stages of notes" editable="bottom"> + <field name="sequence" widget="handle"/> + <field name="name"/> + <field name="fold"/> + </tree> + </field> + </record> + + <!-- Note Stage Action --> + <record model="ir.actions.act_window" id="action_note_stage"> + <field name="name">Stages</field> + <field name="res_model">note.stage</field> + <field name="view_mode">tree,form</field> + <field name="domain">[('user_id','=',uid)]</field> + </record> + + <menuitem name="Categories" + id="menu_notes_stage" parent="note_my_stuff" action="action_note_stage" sequence="40" groups="base.group_no_one"/> + + <!-- New Note Kanban View --> + <record model="ir.ui.view" id="view_note_note_kanban"> + <field name="name">note.note.kanban</field> + <field name="model">note.note</field> + <field name="arch" type="xml"> + <kanban default_group_by="stage_id" class="oe_notes" version="7.0"> + <field name="color"/> + <field name="sequence"/> + <field name="name"/> + <field name="stage_id"/> + <field name="active"/> + <field name="note"/> + <field name="follower_ids"/> + <templates> + <t t-name="kanban-box"> + <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_semantic_html_override"> + <!-- dropdown menu --> + <div class="oe_dropdown_toggle oe_dropdown_kanban"> + <span class="oe_e">í</span> + <ul class="oe_dropdown_menu"> + <li><a type="edit">Edit...</a></li> + <li><a type="delete">Delete</a></li> + <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> + </ul> + </div> + + <!-- kanban note --> + <div class="oe_kanban_content"> + <!-- title --> + <field name="name"/> + </div> + + <div class="oe_kanban_project_avatars"> + <t t-foreach="record.follower_ids.raw_value" t-as="follower"> + <img t-att-src="kanban_image('res.users', 'image_small', follower)" t-att-data-member_id="follower"/> + </t> + </div> + </div> + <div class="oe_clear"></div> + </t> + </templates> + </kanban> + </field> + </record> + + <!-- New Note Form View --> + <record model="ir.ui.view" id="view_note_note_form"> + <field name="name">note.note.form</field> + <field name="model">note.note</field> + <field name="arch" type="xml"> + <form string="Note" version="7.0"> + <header> + <field name="stage_id" domain="[('user_id','=',uid)]" widget="statusbar" clickable="1"/> + </header> + <field name="note"/> + <field name="note_pad_url" invisible="1"/> + <div class="oe_chatter"> + <field name="message_ids" widget="mail_thread"/> + </div> + </form> + </field> + </record> + + <!-- Search Note --> + <record model="ir.ui.view" id="view_note_note_filter"> + <field name="name">note.note.search</field> + <field name="model">note.note</field> + <field name="arch" type="xml"> + <search string="Notes"> + <field name="note" string="Note"/> + <field name="stage_id"/> + <group expand="0" string="Group By..."> + <filter icon="terp-personal" string="Creator" help="By Creators" context="{'group_by':'create_uid'}"/> + <filter icon="terp-stock_symbol-selection" string="Stage" help="By Note Category" context="{'group_by':'stage_id'}"/> + </group> + </search> + </field> + </record> + + <!-- Note Action --> + <record model="ir.actions.act_window" id="action_note_note"> + <field name="name">Notes</field> + <field name="res_model">note.note</field> + <field name="view_type">form</field> + <field name="view_mode">kanban,tree,form</field> + <field name="search_view_id" ref="view_note_note_filter"/> + </record> + + <menuitem name="Notes" id="note_notes" parent="note_my_stuff" sequence="20" action="action_note_note"/> + + </data> +</openerp> diff --git a/addons/note/security/ir.model.access.csv b/addons/note/security/ir.model.access.csv new file mode 100644 index 00000000000..fdd6030ca51 --- /dev/null +++ b/addons/note/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_note_stage_user,note.stage user,model_note_stage,base.group_user,1,1,1,1
+access_note_note_user,note.note user,model_note_note,base.group_user,1,1,1,1
diff --git a/addons/note/security/note_security.xml b/addons/note/security/note_security.xml new file mode 100644 index 00000000000..46eebee629c --- /dev/null +++ b/addons/note/security/note_security.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<openerp> +<data> + <record id="note_note_global" model="ir.rule"> + <field name="name">My notes</field> + <field ref="model_note_note" name="model_id"/> + <field eval="1" name="global"/> + <field name="domain_force">[('follower_ids','=',user.id)]</field> + </record> +</data> +</openerp> + diff --git a/addons/note/static/src/css/Makefile b/addons/note/static/src/css/Makefile new file mode 100644 index 00000000000..d6019f4ed4a --- /dev/null +++ b/addons/note/static/src/css/Makefile @@ -0,0 +1,3 @@ +note.css: note.sass + sass -t expanded note.sass note.css + diff --git a/addons/note/static/src/css/note.css b/addons/note/static/src/css/note.css new file mode 100644 index 00000000000..7e79438b46a --- /dev/null +++ b/addons/note/static/src/css/note.css @@ -0,0 +1,78 @@ +.openerp .oe_notes .oe_fold_column .oe_kanban_card { + text-decoration:none; + color:#000; + display:block; + padding:1em; + margin-right: 1em; + margin-bottom: 1em; + + -moz-box-shadow:5px 5px 7px rgba(33,33,33,1); + -webkit-box-shadow: 5px 5px 7px rgba(33,33,33,.7); + box-shadow: 5px 5px 7px rgba(33,33,33,.7); +} + +.openerp .oe_notes .oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(-2deg); + -o-transform: rotate(-2deg); + -moz-transform:rotate(-2deg); +} + +.openerp .oe_notes .oe_kanban_record:nth-of-type(even) .oe_kanban_card { + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); + -moz-transform:rotate(1deg); +} + +.openerp .oe_notes .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(4deg); + -o-transform: rotate(4deg); + -moz-transform:rotate(4deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(even) .oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(2deg); + -o-transform: rotate(2deg); + -moz-transform:rotate(2deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(even) .oe_kanban_record:nth-of-type(even) .oe_kanban_card { + -webkit-transform: rotate(-3deg); + -o-transform: rotate(-3deg); + -moz-transform:rotate(-3deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(even) .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); + -moz-transform:rotate(1deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(3n) .oe_kanban_record .oe_kanban_card { + -webkit-transform: rotate(-2deg); + -o-transform: rotate(-2deg); + -moz-transform:rotate(-2deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(3n) .oe_kanban_record:nth-of-type(even) .oe_kanban_card { + -webkit-transform: rotate(1deg); + -o-transform: rotate(1deg); + -moz-transform:rotate(1deg); +} + +.openerp .oe_notes .oe_kanban_column:nth-of-type(3n) .oe_kanban_record:nth-of-type(3n) .oe_kanban_card { + -webkit-transform: rotate(-1deg); + -o-transform: rotate(-1deg); + -moz-transform:rotate(-1deg); +} + +.openerp .oe_notes .oe_kanban_column .oe_fold_column .oe_kanban_card:hover, +.openerp .oe_notes .oe_kanban_column .oe_fold_column .oe_kanban_card:focus { + box-shadow:10px 10px 7px rgba(0,0,0,.7); + -moz-box-shadow:10px 10px 7px rgba(0,0,0,.7); + -webkit-box-shadow: 10px 10px 7px rgba(0,0,0,.7); + -webkit-transform: rotate(0); + -moz-transform: rotate(0); + -o-transform: rotate(0); + position:relative; + z-index:5; +} diff --git a/addons/note/static/src/css/note.sass b/addons/note/static/src/css/note.sass new file mode 100644 index 00000000000..cd04015dcbb --- /dev/null +++ b/addons/note/static/src/css/note.sass @@ -0,0 +1,137 @@ +@charset "utf-8" + +// Variables {{{ +$section-title-color: #8786b7 +$tag-bg-light: #f0f0fa +$tag-bg-dark: #8786b7 +$tag-border: #afafb6 +$tag-border-selected: #a6a6fe +$hover-background: #f0f0fa +$link-color: #8a89ba +$sheet-max-width: 860px +// }}} +// Mixins {{{ +@font-face + font-family: 'mnmliconsRegular' + src: url('/web/static/src/font/mnmliconsv21-webfont.eot') format('eot') + src: url('/web/static/src/font/mnmliconsv21-webfont.woff') format('woff') + src: url('/web/static/src/font/mnmliconsv21-webfont.ttf') format('truetype') + src: url('/web/static/src/font/mnmliconsv21-webfont.svg') format('svg') active + font-weight: normal + font-style: normal + +@font-face + font-family: 'EntypoRegular' + src: url('/web/static/src/font/entypo-webfont.eot') format('eot') + src: url('/web/static/src/font/entypo-webfont.eot?#iefix') format('embedded-opentype') + src: url('/web/static/src/font/entypo-webfont.woff') format('woff') + src: url('/web/static/src/font/entypo-webfont.ttf') format('truetype') + src: url('/web/static/src/font/entypo-webfont.svg') format('svg') active + font-weight: normal + font-style: normal + +@mixin reset() + border: none + padding: 0 + margin: 0 + background: none + @include radius(none) + @include box-shadow(none) + +@mixin vertical-gradient($startColor: #555, $endColor: #333) + background-color: $startColor + background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, $startColor, $endColor) /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, $startColor, $endColor) /* FF3.6 */ + background-image: -ms-linear-gradient(top, $startColor, $endColor) /* IE10 */ + background-image: -o-linear-gradient(top, $startColor, $endColor) /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, $startColor, $endColor) + +@mixin radial-gradient($gradient) + background-position: center center + background-image: -webkit-radial-gradient(circle, $gradient) + background-image: -moz-radial-gradient($gradient) + background-image: -ms-radial-gradient($gradient) + background-image: radial-gradient($gradient) + +@mixin radius($radius: 5px) + -moz-border-radius: $radius + -webkit-border-radius: $radius + border-radius: $radius + +@mixin box-shadow($bsval: 0px 1px 4px #777) + -moz-box-shadow: $bsval + -webkit-box-shadow: $bsval + box-shadow: $bsval + +@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s)) + -webkit-transition: $transval + -moz-transition: $transval + -ms-transition: $transval + -o-transition: $transval + transition: $transval + +@mixin opacity($opacity: .5) + filter: alpha(opacity=$opacity * 100) + opacity: $opacity + +@mixin background-clip($clip: padding-box) + -webkit-background-clip: $clip + -moz-background-clip: $clip + background-clip: $clip + +@mixin box-sizing($type: content) + // type = border || content || padding + -webkit-box-sizing: #{$type}-box + -moz-box-sizing: #{$type}-box + -ms-box-sizing: #{$type}-box + box-sizing: #{$type}-box + +// Transforms the (readable) text of an inline element into an mmlicons icon, +// allows for actual readable text in-code (and in readers?) with iconic looks +@mixin text-to-icon($icon-name, $color: #404040) + font-size: 1px + letter-spacing: -1px + color: transparent + &:before + font: 21px "mnmliconsRegular" + content: $icon-name + color: $color + +// }}} +// CSS animation bounces {{{ +@-moz-keyframes bounce + 0% + -moz-transform: scale(0) + opacity: 0 + 50% + -moz-transform: scale(1.3) + opacity: 0.4 + 75% + -moz-transform: scale(0.9) + opacity: 0.7 + 100% + -moz-transform: scale(1) + opacity: 1 + +@-webkit-keyframes bounce + 0% + -webkit-transform: scale(0) + opacity: 0 + 50% + -webkit-transform: scale(1.3) + opacity: 0.4 + 75% + -webkit-transform: scale(0.9) + opacity: 0.7 + 100% + -webkit-transform: scale(1) + opacity: 1 +// }}} + + +.oe_kanban_color_2 + background-color:red + +// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css" + diff --git a/addons/note/static/src/img/icon.png b/addons/note/static/src/img/icon.png Binary files differnew file mode 100644 index 00000000000..b6ad0548a40 --- /dev/null +++ b/addons/note/static/src/img/icon.png diff --git a/addons/pad/__openerp__.py b/addons/pad/__openerp__.py index 620d677a5bf..50b280d2c32 100644 --- a/addons/pad/__openerp__.py +++ b/addons/pad/__openerp__.py @@ -14,6 +14,7 @@ pads (by default, http://ietherpad.com/). 'website': 'http://openerp.com', 'depends': ['base'], 'data': ['res_company.xml'], + 'demo': ['pad_demo.xml'], 'installable': True, 'auto_install': False, 'web': True, diff --git a/addons/pad/i18n/nb.po b/addons/pad/i18n/nb.po new file mode 100644 index 00000000000..48977b7d1f8 --- /dev/null +++ b/addons/pad/i18n/nb.po @@ -0,0 +1,58 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 01:37+0100\n" +"PO-Revision-Date: 2012-09-10 13:38+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: pad +#: sql_constraint:res.company:0 +msgid "The company name must be unique !" +msgstr "Firmanavn må være unikt !" + +#. module: pad +#: help:res.company,pad_url_template:0 +msgid "Template used to generate pad URL." +msgstr "Mal som brukes til å generere pad URL." + +#. module: pad +#: model:ir.model,name:pad.model_res_company +msgid "Companies" +msgstr "Firmaer" + +#. module: pad +#: constraint:res.company:0 +msgid "Error! You can not create recursive companies." +msgstr "Feil ! Du kan ikke lage rekursive firmaer." + +#. module: pad +#: model:ir.model,name:pad.model_ir_attachment +msgid "ir.attachment" +msgstr "ir.vedlegg" + +#. module: pad +#: view:res.company:0 +msgid "Pad" +msgstr "Pad" + +#. module: pad +#: field:res.company,pad_url_template:0 +msgid "Pad URL Template" +msgstr "Pad URL Mal." + +#. openerp-web +#: /home/odo/repositories/addons/trunk/pad/static/src/xml/pad.xml:9 +msgid "Add Pad" +msgstr "Legg til Pad." diff --git a/addons/pad/pad_demo.xml b/addons/pad/pad_demo.xml new file mode 100644 index 00000000000..407f90e8242 --- /dev/null +++ b/addons/pad/pad_demo.xml @@ -0,0 +1,9 @@ +<openerp> + <data noupdate="1"> + + <record id="base.main_company" model="res.company"> + <field name="pad_server">pad.openerp.com</field> + </record> + + </data> +</openerp> diff --git a/addons/pad/res_company.xml b/addons/pad/res_company.xml index 5a9164db2f4..1e2184c61a3 100644 --- a/addons/pad/res_company.xml +++ b/addons/pad/res_company.xml @@ -7,11 +7,12 @@ <field name="arch" type="xml"> <xpath expr="//group[@name='account_grp']" position="after"> <group string="Pads"> - <field name="pad_server" placeholder="e.g. beta.primarypad.org"/> + <field name="pad_server" placeholder="e.g. beta.primarypad.com"/> <field name="pad_key"/> </group> </xpath> </field> </record> + </data> </openerp> diff --git a/addons/pad/static/src/xml/pad.xml b/addons/pad/static/src/xml/pad.xml index e84414dd4e8..ce79df52684 100644 --- a/addons/pad/static/src/xml/pad.xml +++ b/addons/pad/static/src/xml/pad.xml @@ -11,7 +11,6 @@ </div> </t> <t t-name="FieldPad.unconfigured"> - Please configure your etherpad server.<br/> - OpenERP Entreprise customers may safely use pad.openerp.com<br/> + You must configure the etherpad through the menu Setting > Companies > Companies, in the configuration tab of your company.<br/> </t> </templates> diff --git a/addons/pad_project/i18n/fr.po b/addons/pad_project/i18n/fr.po index ad5998d968e..9e549424e26 100644 --- a/addons/pad_project/i18n/fr.po +++ b/addons/pad_project/i18n/fr.po @@ -8,31 +8,32 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 00:36+0000\n" -"PO-Revision-Date: 2012-06-22 08:03+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2012-09-03 22:22+0000\n" +"Last-Translator: Nicolas JEUDY <njeudy@simplee.fr>\n" "Language-Team: French <fr@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:43+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: pad_project #: constraint:project.task:0 msgid "Error ! Task end-date must be greater then task start-date" msgstr "" +"Erreur ! la date de fin de la tâche doit être supérieure a celle de début" #. module: pad_project #: model:ir.model,name:pad_project.model_project_task msgid "Task" -msgstr "" +msgstr "Tâche" #. module: pad_project #: view:project.task:0 msgid "Pad" -msgstr "" +msgstr "Pad" #. module: pad_project #: constraint:project.task:0 msgid "Error ! You cannot create recursive tasks." -msgstr "" +msgstr "Erreur ! Vous ne pouvez pas créer de tâches récursives." diff --git a/addons/pad_project/i18n/nb.po b/addons/pad_project/i18n/nb.po new file mode 100644 index 00000000000..baa35cb2392 --- /dev/null +++ b/addons/pad_project/i18n/nb.po @@ -0,0 +1,38 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:38+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! Task end-date must be greater then task start-date" +msgstr "Feil! Oppgave sluttdato må være større enn oppgave startdato" + +#. module: pad_project +#: model:ir.model,name:pad_project.model_project_task +msgid "Task" +msgstr "Oppgave" + +#. module: pad_project +#: view:project.task:0 +msgid "Pad" +msgstr "Pad" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! You cannot create recursive tasks." +msgstr "Feil! Du kan ikke lage en rekursive oppgaver." diff --git a/addons/pad_project/i18n/tr.po b/addons/pad_project/i18n/tr.po new file mode 100644 index 00000000000..a19889de838 --- /dev/null +++ b/addons/pad_project/i18n/tr.po @@ -0,0 +1,38 @@ +# Turkish translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-07 20:06+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Turkish <tr@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-08 04:54+0000\n" +"X-Generator: Launchpad (build 15914)\n" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! Task end-date must be greater then task start-date" +msgstr "Hata! görev bitiş tarihi başlangıç tarihinden sonra olmalı" + +#. module: pad_project +#: model:ir.model,name:pad_project.model_project_task +msgid "Task" +msgstr "Görev" + +#. module: pad_project +#: view:project.task:0 +msgid "Pad" +msgstr "" + +#. module: pad_project +#: constraint:project.task:0 +msgid "Error ! You cannot create recursive tasks." +msgstr "Hata ! Yinelenen görevler oluşturamazsınız." diff --git a/addons/plugin/i18n/nb.po b/addons/plugin/i18n/nb.po new file mode 100644 index 00000000000..0ef79314ec0 --- /dev/null +++ b/addons/plugin/i18n/nb.po @@ -0,0 +1,23 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:40+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: plugin +#: model:ir.model,name:plugin.model_plugin_handler +msgid "plugin.handler" +msgstr "Plugg.behandleren" diff --git a/addons/plugin/plugin_handler.py b/addons/plugin/plugin_handler.py index e497f902dc2..9c582a8e3bf 100644 --- a/addons/plugin/plugin_handler.py +++ b/addons/plugin/plugin_handler.py @@ -44,7 +44,7 @@ class plugin_handler(osv.osv_memory): res_id = 0 url = "" name = "" - msg = mail_message_obj.parse_message(email) + msg = self.pool.get('mail.thread').parse_message(cr, uid, email) references = [msg.get('message-id')] refs = msg.get('references',False) if refs: @@ -91,7 +91,7 @@ class plugin_handler(osv.osv_memory): """ mail_message = self.pool.get('mail.message') model_obj = self.pool.get(model) - msg = mail_message.parse_message(email) + msg = self.pool.get('mail.thread').parse_message(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)]) @@ -101,16 +101,15 @@ class plugin_handler(osv.osv_memory): notify = "Email already pushed" elif res_id == 0: if model == 'res.partner': - notify = 'User the button Partner to create a new partner' + notify = 'User the Partner button to create a new partner' else: res_id = model_obj.message_new(cr, uid, msg) - notify = "Mail succefully pushed, a new %s has been created " % model + notify = "Mail succesfully pushed, a new %s has been created " % model else: if model == 'res.partner': model_obj = self.pool.get('mail.thread') - res = self.pool.get(model).browse(cr, uid, [res_id]) - model_obj.message_append_dict(cr, uid, res, msg) - notify = "Mail succefully pushed" + model_obj.message_post(cr, uid, [res_id], body=msg) + notify = "Mail succesfully pushed" url = self._make_url(cr, uid, res_id, model) return (model, res_id, url, notify) @@ -133,7 +132,7 @@ class plugin_handler(osv.osv_memory): return ('res.partner', partner_id, url) # Specific to outlook rfc822 is not available so we split in arguments headerd,body,attachemnts - def push_message_outlook(self, cr, uid, model, headers,res_id=0 ,body_text=False, body_html=False, attachments=False): + def push_message_outlook(self, cr, uid, model, headers,res_id=0 ,body=False, body_html=False, attachments=False): # ---------------------------------------- # solution 1 # construct a fake rfc822 from the separated arguement @@ -147,7 +146,7 @@ class plugin_handler(osv.osv_memory): mail_message = self.pool.get('mail.message') ir_attachment_obj = self.pool.get('ir.attachment') attach_ids = [] - msg = mail_message.parse_message(headers) + msg = self.pool.get('mail.thread').parse_message(cr, uid, headers) message_id = msg.get('message-id') push_mail = self.push_message(cr, uid, model, headers, res_id) res_id = push_mail[1] @@ -161,6 +160,6 @@ class plugin_handler(osv.osv_memory): attach_ids.append(ir_attachment_obj.create(cr, uid, vals)) mail_ids = mail_message.search(cr, uid, [('message_id','=',message_id),('res_id','=',res_id),('model','=',model)]) if mail_ids: - ids = mail_message.write(cr, uid,mail_ids[0],{ 'attachment_ids': [(6, 0, attach_ids)],'body_text':body_text,'body_html':body_html}) + ids = mail_message.write(cr, uid,mail_ids[0],{ 'attachment_ids': [(6, 0, attach_ids)],'body':body,'body_html':body_html}) url = self._make_url(cr, uid, res_id, model) return (model, res_id, url) diff --git a/addons/plugin_outlook/i18n/nb.po b/addons/plugin_outlook/i18n/nb.po new file mode 100644 index 00000000000..0bb6f6bd40f --- /dev/null +++ b/addons/plugin_outlook/i18n/nb.po @@ -0,0 +1,78 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-09 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:42+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: plugin_outlook +#: view:outlook.installer:0 +msgid "" +"This plug-in allows you to create and link outlook emails with openerp " +"objects." +msgstr "" +"Denne plug-in tillater deg å opprette og koble Outlook e-post med openerp " +"objekter." + +#. module: plugin_outlook +#: field:outlook.installer,name:0 +msgid "Outlook Plug-in" +msgstr "Outlook Plugginn" + +#. module: plugin_outlook +#: model:ir.actions.act_window,name:plugin_outlook.action_outlook_installer +#: model:ir.actions.act_window,name:plugin_outlook.action_outlook_wizard +#: model:ir.ui.menu,name:plugin_outlook.menu_base_config_plugins_outlook +#: view:outlook.installer:0 +msgid "Install Outlook Plug-In" +msgstr "Installere Outlook Plugg-Inn." + +#. module: plugin_outlook +#: field:outlook.installer,config_logo:0 +msgid "Image" +msgstr "Bilde" + +#. module: plugin_outlook +#: view:outlook.installer:0 +msgid "title" +msgstr "tittel" + +#. module: plugin_outlook +#: model:ir.model,name:plugin_outlook.model_outlook_installer +msgid "outlook.installer" +msgstr "outlook.installer" + +#. module: plugin_outlook +#: view:outlook.installer:0 +msgid "_Close" +msgstr "_Lukk" + +#. module: plugin_outlook +#: view:outlook.installer:0 +msgid "Installation and Configuration Steps" +msgstr "Installasjon og Konfigurasjon trinn" + +#. module: plugin_outlook +#: help:outlook.installer,name:0 +msgid "" +"outlook plug-in file. Save as this file and install this plug-in in outlook." +msgstr "" +"Outlook pluginn-filen. Lagre som denne filen og installere denne plugin-" +"modulen i outlook." + +#. module: plugin_outlook +#: field:outlook.installer,description:0 +msgid "Description" +msgstr "Beskrivelse:" diff --git a/addons/plugin_thunderbird/i18n/nb.po b/addons/plugin_thunderbird/i18n/nb.po new file mode 100644 index 00000000000..c8dcc889c48 --- /dev/null +++ b/addons/plugin_thunderbird/i18n/nb.po @@ -0,0 +1,108 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-09 00:36+0000\n" +"PO-Revision-Date: 2012-09-10 13:45+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: plugin_thunderbird +#: field:plugin_thunderbird.installer,pdf_file:0 +msgid "Installation Manual" +msgstr "Installasjons håndbok." + +#. module: plugin_thunderbird +#: field:plugin_thunderbird.installer,description:0 +msgid "Description" +msgstr "Beskrivelse:" + +#. module: plugin_thunderbird +#: view:plugin_thunderbird.installer:0 +msgid "title" +msgstr "tittel" + +#. module: plugin_thunderbird +#: model:ir.model,name:plugin_thunderbird.model_plugin_thunderbird_installer +msgid "plugin_thunderbird.installer" +msgstr "plugin_thunderbird.installer" + +#. module: plugin_thunderbird +#: field:plugin_thunderbird.installer,config_logo:0 +msgid "Image" +msgstr "Bilde" + +#. module: plugin_thunderbird +#: field:plugin_thunderbird.installer,plugin_file:0 +#: field:plugin_thunderbird.installer,thunderbird:0 +msgid "Thunderbird Plug-in" +msgstr "Thunderbird Plug-in" + +#. module: plugin_thunderbird +#: view:plugin_thunderbird.installer:0 +msgid "" +"This plug-in allows you to link your email to OpenERP's documents. You can " +"attach it to any existing one in OpenERP or create a new one." +msgstr "" + +#. module: plugin_thunderbird +#: help:plugin_thunderbird.installer,thunderbird:0 +msgid "" +"Allows you to select an object that you would like to add to your email and " +"its attachments." +msgstr "" +"Lar deg velge et objekt som du ønsker å legge til din e-post og vedlegg." + +#. module: plugin_thunderbird +#: help:plugin_thunderbird.installer,pdf_file:0 +msgid "The documentation file :- how to install Thunderbird Plug-in." +msgstr "Dokumentasjonen fil: - hvordan du installerer Thunderbird Plug-in." + +#. module: plugin_thunderbird +#: view:plugin_thunderbird.installer:0 +msgid "_Close" +msgstr "_Lukk" + +#. module: plugin_thunderbird +#: view:plugin_thunderbird.installer:0 +msgid "Installation and Configuration Steps" +msgstr "Installasjon og Konfigurasjon trinn" + +#. module: plugin_thunderbird +#: field:plugin_thunderbird.installer,name:0 +#: field:plugin_thunderbird.installer,pdf_name:0 +msgid "File name" +msgstr "Filnavn" + +#. module: plugin_thunderbird +#: help:plugin_thunderbird.installer,plugin_file:0 +msgid "" +"Thunderbird plug-in file. Save as this file and install this plug-in in " +"thunderbird." +msgstr "" +"Thunderbird plug-in-filen. Lagre som denne filen og installere denne plugin-" +"modulen i thunderbird." + +#. module: plugin_thunderbird +#: model:ir.actions.act_window,name:plugin_thunderbird.action_thunderbird_installer +#: model:ir.ui.menu,name:plugin_thunderbird.menu_base_config_plugins_thunderbird +#: view:plugin_thunderbird.installer:0 +msgid "Install Thunderbird Plug-In" +msgstr "Installer Thunderbird Plug-in." + +#~ msgid "" +#~ "This plug-in allows you to link your e-mail to OpenERP's documents. You can " +#~ "attach it to any existing one in OpenERP or create a new one." +#~ msgstr "" +#~ "Denne plug-in tillater deg å koble din e-post til OpenERP er dokumenter. Du " +#~ "kan feste den til enhver eksisterende i OpenERP eller opprette en ny." diff --git a/addons/point_of_sale/__openerp__.py b/addons/point_of_sale/__openerp__.py index bcc1eeda001..ec5add595a7 100644 --- a/addons/point_of_sale/__openerp__.py +++ b/addons/point_of_sale/__openerp__.py @@ -92,7 +92,8 @@ Main features: 'static/src/js/main.js', ], 'css': [ - 'static/src/css/pos.css', + 'static/src/css/pos.css', # this is the default css with hover effects + #'static/src/css/pos_nohover.css', # this css has no hover effects (for resistive touchscreens) 'static/src/css/keyboard.css' ], 'qweb': ['static/src/xml/pos.xml'], diff --git a/addons/point_of_sale/i18n/nb.po b/addons/point_of_sale/i18n/nb.po new file mode 100644 index 00000000000..42e5832b237 --- /dev/null +++ b/addons/point_of_sale/i18n/nb.po @@ -0,0 +1,2720 @@ +# Norwegian Bokmal translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 01:37+0100\n" +"PO-Revision-Date: 2012-08-31 07:48+0000\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: Norwegian Bokmal <nb@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-01 04:55+0000\n" +"X-Generator: Launchpad (build 15890)\n" + +#. module: point_of_sale +#: field:report.transaction.pos,product_nb:0 +msgid "Product Nb." +msgstr "Produkt Nb" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_trans_pos_tree_today +msgid "Sales by day" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_open_statement.py:49 +#, python-format +msgid "" +"You must define which payment method must be available through the point of " +"sale by reusing existing bank and cash through \"Accounting > Configuration " +"> Financial Accounting > Journals\". Select a journal and check the field " +"\"PoS Payment Method\" from the \"Point of Sale\" tab. You can also create " +"new payment methods directly from menu \"PoS Backend > Configuration > " +"Payment Methods\"." +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 view:report.pos.order:0 +msgid "Today" +msgstr "I dag" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.plain_water +msgid "Plain Water" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Year from Creation date of cash register" +msgstr "" + +#. module: point_of_sale +#: view:pos.confirm:0 +msgid "Post All Orders" +msgstr "Poster Alle Ordre" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_2l_product_template +msgid "Spa Reine 2L" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_box_out +msgid "Pos Box Out" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_cash_register_all +#: model:ir.ui.menu,name:point_of_sale.menu_report_cash_register_all +msgid "Register Analysis" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_lines_detail +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Details of Sales" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 field:report.cash.register,day:0 +#: view:report.pos.order:0 field:report.pos.order,day:0 +msgid "Day" +msgstr "Dag" + +#. module: point_of_sale +#: view:pos.make.payment:0 +msgid "Add payment :" +msgstr "" + +#. module: point_of_sale +#: field:pos.box.out,name:0 +msgid "Description / Reason" +msgstr "Beskrivelse / Årsak" + +#. module: point_of_sale +#: field:report.sales.by.margin.pos,product_name:0 +#: field:report.sales.by.margin.pos.month,product_name:0 +msgid "Product Name" +msgstr "Produktnavn" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Month from Creation date of cash register" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 field:pos.box.entries,amount:0 +#: field:pos.box.out,amount:0 report:pos.invoice:0 +#: field:pos.make.payment,amount:0 report:pos.user.product:0 +#: field:report.transaction.pos,amount:0 +msgid "Amount" +msgstr "" + +#. module: point_of_sale +#: constraint:account.journal:0 +msgid "" +"Configuration error! The currency chosen should be shared by the default " +"accounts too." +msgstr "Konfigurasjonsfeil ! Valgt valuta må settes opp i kontoplan" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.pos_category_action +#: model:ir.ui.menu,name:point_of_sale.menu_pos_category view:pos.category:0 +msgid "PoS Categories" +msgstr "PoS Kategorier" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_box_out +#: model:ir.ui.menu,name:point_of_sale.menu_wizard_enter_jrnl2 +msgid "Take Money Out" +msgstr "" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "VAT" +msgstr "MVA" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Origin" +msgstr "Opphav" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Tax" +msgstr "MVA" + +#. module: point_of_sale +#: report:pos.user.product:0 +msgid "Starting Date" +msgstr "Startdato" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_box_entries.py:46 +#, python-format +msgid "" +"You do not have any open cash register. You must create a payment method or " +"open a cash register." +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 field:report.pos.order,partner_id:0 +msgid "Partner" +msgstr "Partner" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Total of the day" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 field:report.pos.order,average_price:0 +msgid "Average Price" +msgstr "Gjennomsnittspris" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "Disc. (%)" +msgstr "Rab. (%)" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_point_open_config +msgid "Cash Register Management" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_naturel_45g_product_template +msgid "Lays Naturel 45g" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Accounting Information" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_regular_2l_product_template +msgid "Coca-Cola Regular 2L" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_trans_pos_tree_month +msgid "Sales by month" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.soda_orange +msgid "Orange" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_user_pos_today +#: view:report.sales.by.user.pos:0 view:report.sales.by.user.pos.month:0 +msgid "Sales by User" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Disc.(%)" +msgstr "Rab.(%)" + +#. module: point_of_sale +#: field:pos.box.entries,ref:0 +msgid "Ref" +msgstr "Ref" + +#. module: point_of_sale +#: view:report.pos.order:0 field:report.pos.order,price_total:0 +msgid "Total Price" +msgstr "Totalpris" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.leffe_brune_33cl_product_template +msgid "Leffe Brune 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_sales_user +#: report:pos.sales.user:0 +msgid "Sales Report" +msgstr "Salgsrapport" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.beverage +msgid "Beverages" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.stella_50cl_product_template +msgid "Stella Artois 50cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.details:0 +msgid "Dates" +msgstr "Datoer" + +#. module: point_of_sale +#: field:pos.category,parent_id:0 +msgid "Parent Category" +msgstr "Overordnet kategori" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.lines:0 report:pos.payment.report.user:0 +#: report:pos.user.product:0 +msgid "[" +msgstr "" + +#. module: point_of_sale +#: field:report.sales.by.margin.pos,total:0 +#: field:report.sales.by.margin.pos.month,total:0 +msgid "Margin" +msgstr "Margin" + +#. module: point_of_sale +#: field:pos.discount,discount:0 field:pos.order.line,discount:0 +msgid "Discount (%)" +msgstr "Rabatt (%)" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_speciale_product_template +msgid "Dr. Oetker Ristorante Speciale" +msgstr "" + +#. module: point_of_sale +#: view:pos.order.line:0 +msgid "Total qty" +msgstr "Total ant" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_orange_33cl_product_template +msgid "Fanta Orange 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_user_pos_month +msgid "Sales by User Monthly" +msgstr "" + +#. module: point_of_sale +#: report:all.closed.cashbox.of.the.day:0 +msgid "Today's Closed Cashbox" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Day from Creation date of cash register" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_point_of_sale +msgid "Daily Operations" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:285 +#, python-format +msgid "Configuration Error !" +msgstr "Konfigurasjonsfeil!" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.sparkling_water +msgid "Sparkling Water" +msgstr "" + +#. module: point_of_sale +#: view:pos.box.entries:0 +msgid "Fill in this form if you put money in the cash register:" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 +msgid "Search Cash Statements" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "August" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_max_lemon_33cl_product_template +msgid "Pepsi Max Cool Lemon 33cl" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "June" +msgstr "" + +#. module: point_of_sale +#: view:pos.order.line:0 +msgid "POS Order line" +msgstr "POS Ordre linje" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_orange_50cl_product_template +msgid "Fanta Orange 50cl" +msgstr "" + +#. module: point_of_sale +#: field:pos.category,child_id:0 +msgid "Children Categories" +msgstr "Underordnede Kategorier" + +#. module: point_of_sale +#: field:pos.make.payment,payment_date:0 +msgid "Payment Date" +msgstr "Betalingsdato" + +#. module: point_of_sale +#: help:product.product,pos_categ_id:0 +msgid "" +"If you want to sell this product through the point of sale, select the " +"category it belongs to." +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +msgid "Closing Date" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "October" +msgstr "Oktober" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_1l_product_template +msgid "Coca-Cola Light 1L" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Summary" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_50cl_product_template +msgid "Chaudfontaine 50cl" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 report:pos.lines:0 field:pos.order.line,qty:0 +#: field:report.sales.by.user.pos,qty:0 +#: field:report.sales.by.user.pos.month,qty:0 +msgid "Quantity" +msgstr "Antall" + +#. module: point_of_sale +#: field:pos.order.line,name:0 +msgid "Line No" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 +msgid "Period" +msgstr "Periode" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_open_statement.py:49 +#, python-format +msgid "No Cash Register Defined !" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Net Total:" +msgstr "Netto total:" + +#. module: point_of_sale +#: field:pos.make.payment,payment_name:0 +msgid "Payment Reference" +msgstr "Betalingsreferanse" + +#. module: point_of_sale +#: report:pos.details_summary:0 +msgid "Mode of Payment" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_confirm +msgid "Post POS Journal Entries" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:408 +#, python-format +msgid "Customer Invoice" +msgstr "Kundefaktura" + +#. module: point_of_sale +#: view:pos.box.out:0 +msgid "Output Operation" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 field:report.pos.order,total_discount:0 +msgid "Total Discount" +msgstr "Rabatt totalt" + +#. module: point_of_sale +#: view:pos.details:0 view:pos.payment.report:0 view:pos.payment.report.user:0 +#: view:pos.sale.user:0 +msgid "Print Report" +msgstr "Skriv ut rapport" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_bolognese_product_template +msgid "Dr. Oetker Ristorante Bolognese" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_box_entries.py:105 +#, python-format +msgid "Please check that income account is set to %s" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.pizza +msgid "Pizza" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_return.py:85 +#: code:addons/point_of_sale/wizard/pos_return.py:240 +#, python-format +msgid "Add Product" +msgstr "Legg til produkt" + +#. module: point_of_sale +#: field:report.transaction.pos,invoice_am:0 +msgid "Invoice Amount" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.coke +msgid "Coke" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Tel. :" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_confirm +#: model:ir.ui.menu,name:point_of_sale.menu_wizard_pos_confirm +msgid "Create Sale Entries" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: model:ir.actions.act_window,name:point_of_sale.action_pos_payment +#: report:pos.details:0 view:pos.order:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:87 +msgid "Payment" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Cash Analysis created in current month" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +msgid "Ending Balance" +msgstr "Sluttbalanse" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_max_50cl_product_template +msgid "Pepsi Max 50cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.san_pellegrino_1l_product_template +msgid "San Pellegrino 1L" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Post Entries" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_1l_product_template +msgid "Spa Reine 1L" +msgstr "" + +#. module: point_of_sale +#: report:pos.details_summary:0 +msgid "Mode of Taxes" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_close_statement.py:50 +#, python-format +msgid "Cash registers are already closed." +msgstr "" + +#. module: point_of_sale +#: constraint:product.product:0 +msgid "Error: Invalid ean code" +msgstr "Feil: Ugyldig ean kode" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_open_statement +#: view:pos.open.statement:0 +msgid "Open Statements" +msgstr "" + +#. module: point_of_sale +#: field:pos.details,date_end:0 field:pos.sale.user,date_end:0 +msgid "Date End" +msgstr "Sluttdato" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "POS ordered created during current year" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_pos_form +#: model:ir.ui.menu,name:point_of_sale.menu_point_ofsale view:pos.order:0 +msgid "PoS Orders" +msgstr "POS Ordre" + +#. module: point_of_sale +#: report:pos.details:0 +msgid "Sales total(Revenue)" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Total paid" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_all_menu_all_register +msgid "List of Cash Registers" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.maes_50cl_product_template +msgid "Maes 50cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_transaction_pos +msgid "transaction for the pos" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "Not Invoiced" +msgstr "Ikke fakturert" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "March" +msgstr "Mars" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_users_product_re +#: report:pos.user.product:0 +msgid "User's Product" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:641 +#, python-format +msgid "" +"You have to select a pricelist in the sale form !\n" +"Please set one before choosing a product." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_orange_2l_product_template +msgid "Fanta Orange 2L" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_33cl_product_template +msgid "Spa Reine 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_discount +msgid "Add a Global Discount" +msgstr "" + +#. module: point_of_sale +#: field:pos.order.line,price_subtotal_incl:0 +msgid "Subtotal" +msgstr "Subtotal" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_prosciutto_product_template +msgid "Dr. Oetker Ristorante Prosciutto" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_light_paprika_170g_product_template +#: model:product.template,name:point_of_sale.lays_paprika_170g_product_template +msgid "Lays Light Paprika 170g" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_lemon_50cl_product_template +msgid "Coca-Cola Light Lemon 50cl" +msgstr "" + +#. module: point_of_sale +#: field:report.cash.register,balance_end_real:0 +msgid "Closing Balance" +msgstr "Sluttsaldo" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.act_pos_open_statement +#: model:ir.model,name:point_of_sale.model_pos_close_statement +msgid "Close Statements" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.croky_naturel_45g_product_template +msgid "Croky Naturel 45g" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.product_product_putmoneyforchange1_product_template +msgid "Cash Out" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +msgid "Starting Balance" +msgstr "Inngående saldo" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_category +#: field:product.product,pos_categ_id:0 +msgid "PoS Category" +msgstr "PoS Kategori" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chimay_bleu_75cl_product_template +msgid "Chimay Bleu 75cl" +msgstr "" + +#. module: point_of_sale +#: report:pos.payment.report.user:0 +msgid "Payment By User" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 field:report.pos.order,nbr:0 +msgid "# of Lines" +msgstr "# linjer" + +#. module: point_of_sale +#: help:account.journal,check_dtls:0 +msgid "" +"This field authorize Validation of Cashbox without controlling the closing " +"balance." +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_details +#: model:ir.ui.menu,name:point_of_sale.menu_pos_details +msgid "Sale Details" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.pils +msgid "Pils" +msgstr "" + +#. module: point_of_sale +#: report:all.closed.cashbox.of.the.day:0 +msgid "St.Name" +msgstr "" + +#. module: point_of_sale +#: report:pos.details_summary:0 +msgid "Sales total" +msgstr "" + +#. module: point_of_sale +#: view:pos.order.line:0 +msgid "Sum of subtotals" +msgstr "Sum av subtotaler" + +#. module: point_of_sale +#: field:pos.order,lines:0 +msgid "Order Lines" +msgstr "Ordrelinjer" + +#. module: point_of_sale +#: view:report.transaction.pos:0 +msgid "Total Transaction" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_petillante_50cl_product_template +msgid "Chaudfontaine Petillante 50cl" +msgstr "" + +#. module: point_of_sale +#: field:pos.order.line,create_date:0 +msgid "Creation Date" +msgstr "Opprettet, dato:" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_sales_user_today +msgid "Today's Sales" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_naturel_300g_product_template +msgid "Lays Naturel XXL 300g" +msgstr "" + +#. module: point_of_sale +#: view:report.sales.by.margin.pos:0 view:report.sales.by.margin.pos.month:0 +#: view:report.sales.by.user.pos:0 view:report.sales.by.user.pos.month:0 +#: view:report.transaction.pos:0 +msgid "POS " +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:pos.user.product:0 +msgid "Total :" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 view:report.pos.order:0 +msgid "My Sales" +msgstr "Mine salg" + +#. module: point_of_sale +#: field:pos.order,pricelist_id:0 +msgid "Pricelist" +msgstr "Prisliste" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Total invoiced" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 field:report.pos.order,product_qty:0 +msgid "# of Qty" +msgstr "# av Ant" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_sales_by_margin_pos_month +msgid "Sales by margin monthly" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 field:report.sales.by.margin.pos,date_order:0 +#: field:report.sales.by.margin.pos.month,date_order:0 +#: field:report.sales.by.user.pos,date_order:0 +#: field:report.sales.by.user.pos.month,date_order:0 +msgid "Order Date" +msgstr "Ordredato" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.stella_33cl_product_template +msgid "Stella Artois 33cl" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Cash Analysis created during this year" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Draft Invoice" +msgstr "Fakturakladd" + +#. module: point_of_sale +#: constraint:account.bank.statement.line:0 +msgid "" +"The amount of the voucher must be the same amount as the one on the " +"statement line" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Fiscal Position Remark :" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.evian_2l_product_template +msgid "Evian 2L" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "September" +msgstr "September" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +msgid "Opening Date" +msgstr "" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "Taxes :" +msgstr "Mva" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_2l_product_template +msgid "Coca-Cola Light 2L" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_funghi_product_template +msgid "Dr. Oetker Ristorante Funghi" +msgstr "" + +#. module: point_of_sale +#: field:report.transaction.pos,disc:0 +msgid "Disc." +msgstr "Rab." + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lindemans_kriek_37,5cl_product_template +msgid "Lindemans Kriek 37.5cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_zero_33cl_product_template +msgid "Coca-Cola Zero 33cl" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "VAT :" +msgstr "MVA:" + +#. module: point_of_sale +#: view:pos.order.line:0 +msgid "POS Order lines" +msgstr "POS Ordre linjer" + +#. module: point_of_sale +#: view:pos.receipt:0 +msgid "Receipt :" +msgstr "Kvittering" + +#. module: point_of_sale +#: field:account.bank.statement.line,pos_statement_id:0 +#: field:pos.order,amount_return:0 +msgid "unknown" +msgstr "ukjent" + +#. module: point_of_sale +#: report:pos.details:0 field:report.transaction.pos,date_create:0 +msgid "Date" +msgstr "Dato" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_discount +#: view:pos.discount:0 +msgid "Apply Discount" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +#: view:pos.details:0 report:pos.sales.user:0 report:pos.sales.user.today:0 +#: report:pos.user.product:0 view:report.cash.register:0 +#: field:report.cash.register,user_id:0 +#: field:report.sales.by.margin.pos,user_id:0 +#: field:report.sales.by.margin.pos.month,user_id:0 +#: field:report.sales.by.user.pos,user_id:0 +#: field:report.sales.by.user.pos.month,user_id:0 +#: field:report.transaction.pos,user_id:0 +#: model:res.groups,name:point_of_sale.group_pos_user +msgid "User" +msgstr "Bruker" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_33cl_product_template +msgid "Coca-Cola Light 33cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.perrier_1l_product_template +msgid "Perrier 1L" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Total discount" +msgstr "Total rabatt" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_box_entries +msgid "Pos Box Entries" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.boon_framboise_37,5cl_product_template +msgid "Boon Framboise 37.5cl" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "November" +msgstr "November" + +#. module: point_of_sale +#: field:pos.config.journal,code:0 +msgid "Code" +msgstr "Engelsk" + +#. module: point_of_sale +#: view:account.bank.statement:0 report:account.statement:0 +#: report:all.closed.cashbox.of.the.day:0 +#: model:ir.model,name:point_of_sale.model_account_journal +#: field:pos.config.journal,journal_id:0 field:pos.order,sale_journal:0 +#: view:report.cash.register:0 field:report.cash.register,journal_id:0 +#: field:report.pos.order,journal_id:0 +msgid "Journal" +msgstr "Journal" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.timmermans_faro_37,5cl_product_template +msgid "Timmermans Faro 37.5cl" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 view:pos.order:0 field:pos.order,state:0 +#: report:pos.sales.user:0 report:pos.sales.user.today:0 +#: field:report.cash.register,state:0 field:report.pos.order,state:0 +msgid "State" +msgstr "" + +#. module: point_of_sale +#: field:report.transaction.pos,no_trans:0 +msgid "Number of Transaction" +msgstr "Antall transaksjoner" + +#. module: point_of_sale +#: view:pos.box.entries:0 view:pos.box.out:0 view:pos.confirm:0 +#: view:pos.make.payment:0 view:pos.open.statement:0 view:pos.receipt:0 +msgid "Cancel" +msgstr "Avbryt" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_details_summary +msgid "Sales (summary)" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.timmermans_kriek_37,5cl_product_template +msgid "Timmermans Kriek 37.5cl" +msgstr "" + +#. module: point_of_sale +#: field:account.journal,check_dtls:0 +msgid "Control Balance Before Closing" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_order_line +msgid "Lines of Point of Sale" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 view:report.transaction.pos:0 +msgid "Amount total" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_new_bank_statement_all_tree +msgid "Cash Registers" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 +msgid "Users" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.food +msgid "Food" +msgstr "Mat" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: report:pos.details:0 report:pos.invoice:0 report:pos.lines:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:30 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:69 +msgid "Price" +msgstr "Pris" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.ijsboerke_stracciatella_2,5l_product_template +msgid "ijsboerke Stracciatella 2.5L" +msgstr "" + +#. module: point_of_sale +#: field:account.journal,journal_user:0 +msgid "PoS Payment Method" +msgstr "" + +#. module: point_of_sale +#: help:product.product,expense_pdt:0 +msgid "" +"This is a product you can use to take cash from a statement for the point of " +"sale backend, exemple: money lost, transfer to bank, etc." +msgstr "" + +#. module: point_of_sale +#: field:report.cash.register,date:0 +msgid "Create Date" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_regular_33cl_product_template +msgid "Coca-Cola Regular 33cl" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:65 +#, python-format +msgid "Unable to Delete !" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_paprika_oven_150g_product_template +msgid "Lays Paprika Oven Baked 150g" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Start Period" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 field:pos.category,complete_name:0 +#: field:pos.category,name:0 report:pos.sales.user:0 +#: report:pos.sales.user.today:0 +msgid "Name" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_gazeuse_33cl_product_template +msgid "Spa Barisart 33cl" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:284 +#, python-format +msgid "" +"There is no receivable account defined to make payment for the partner: " +"\"%s\" (id:%d)" +msgstr "" + +#. module: point_of_sale +#: view:pos.confirm:0 +msgid "" +"Generate all sale journal entries for non invoiced orders linked to a closed " +"cash register or statement." +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:293 +#: code:addons/point_of_sale/point_of_sale.py:472 +#: code:addons/point_of_sale/report/pos_invoice.py:46 +#: code:addons/point_of_sale/wizard/pos_box_entries.py:46 +#: code:addons/point_of_sale/wizard/pos_box_entries.py:100 +#: code:addons/point_of_sale/wizard/pos_box_entries.py:105 +#: code:addons/point_of_sale/wizard/pos_box_out.py:86 +#: code:addons/point_of_sale/wizard/pos_box_out.py:88 +#, python-format +msgid "Error !" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.lines:0 report:pos.payment.report.user:0 +#: report:pos.user.product:0 +msgid "]" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_account_bank_statement +msgid "Bank Statement" +msgstr "" + +#. module: point_of_sale +#: sql_constraint:account.journal:0 +msgid "The name of the journal must be unique per company !" +msgstr "" + +#. module: point_of_sale +#: 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 Light Naturel 170g" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 report:pos.lines:0 +#: report:pos.payment.report.user:0 report:pos.sales.user:0 +#: report:pos.sales.user.today:0 report:pos.user.product:0 +msgid "Print Date" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:282 +#, python-format +msgid "There is no receivable account defined to make payment" +msgstr "" + +#. module: point_of_sale +#: view:pos.open.statement:0 +msgid "Do you want to open cash registers?" +msgstr "" + +#. module: point_of_sale +#: help:pos.category,sequence:0 +msgid "" +"Gives the sequence order when displaying a list of product categories." +msgstr "" + +#. module: point_of_sale +#: field:product.product,expense_pdt:0 +msgid "PoS Cash Output" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.soda +msgid "Soda" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 view:pos.order:0 view:report.cash.register:0 +#: view:report.pos.order:0 +msgid "Group By..." +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "POS Orders" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.all_closed_cashbox_of_the_day +msgid "All Closed CashBox" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:640 +#, python-format +msgid "No Pricelist !" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Update" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Base" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.categ_others +msgid "Others" +msgstr "" + +#. module: point_of_sale +#: view:product.product:0 +msgid "Point-of-Sale" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.evian_50cl_product_template +msgid "Evian 50cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Notes" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Sale Order" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_lemon_2l_product_template +msgid "Coca-Cola Light Lemon 2L" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.invoice:0 field:pos.order,amount_tax:0 +msgid "Taxes" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_order_line +#: model:ir.actions.act_window,name:point_of_sale.action_pos_order_line_day +#: model:ir.actions.act_window,name:point_of_sale.action_pos_order_line_form +msgid "Sale line" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_naturel_oven_150g_product_template +msgid "Lays Naturel Oven Baked 150g" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_decaf_33cl_product_template +msgid "Coca-Cola Light Decaf 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.product_normal_action +#: model:ir.ui.menu,name:point_of_sale.menu_point_of_sale_product +#: model:ir.ui.menu,name:point_of_sale.menu_pos_products +msgid "Products" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_4formaggi_product_template +msgid "Dr. Oetker Ristorante Quattro Formaggi" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_zero_1l_product_template +msgid "Coca-Cola Zero 1L" +msgstr "" + +#. module: point_of_sale +#: report:pos.sales.user:0 report:pos.sales.user.today:0 +#: field:report.pos.order,date:0 +msgid "Date Order" +msgstr "" + +#. module: point_of_sale +#: view:report.sales.by.margin.pos:0 view:report.sales.by.margin.pos.month:0 +#: view:report.sales.by.user.pos:0 view:report.sales.by.user.pos.month:0 +#: view:report.transaction.pos:0 +msgid "POS" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_product_output +#: model:ir.ui.menu,name:point_of_sale.products_for_output_operations +msgid "Products 'Put Money In'" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.croky_bolognaise_250g_product_template +msgid "Croky Bolognaise 250g" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Extra Info" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Fax :" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,user_id:0 +msgid "Connected Salesman" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_sale_all +#: model:ir.ui.menu,name:point_of_sale.menu_point_ofsale_all +msgid "All Sales Orders" +msgstr "" + +#. module: point_of_sale +#: help:product.product,income_pdt:0 +msgid "" +"This is a product you can use to put cash into a statement for the point of " +"sale backend." +msgstr "" + +#. module: point_of_sale +#: view:pos.receipt:0 +msgid "Print the Receipt of the Sale" +msgstr "" + +#. module: point_of_sale +#: field:pos.make.payment,journal:0 +msgid "Payment Mode" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_paprika_45g_product_template +msgid "Lays Paprika 45g" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: report:pos.details:0 report:pos.payment.report.user:0 +#: report:pos.user.product:0 field:report.sales.by.margin.pos,qty:0 +#: field:report.sales.by.margin.pos.month,qty:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:32 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:59 +msgid "Qty" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 view:report.pos.order:0 +msgid "Month -1" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_sale_user +#: model:ir.model,name:point_of_sale.model_pos_sale_user +#: view:pos.payment.report.user:0 +msgid "Sale by User" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.evian_1l_product_template +msgid "Evian 1L" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_open_statement +msgid "Open Cash Registers" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.water +msgid "Water" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "state" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "July" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_poivre_sel_oven_150g_product_template +msgid "Lays Sel et Poivre Oven Baked 150g" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "Qty of product" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chimay_rouge_33cl_product_template +msgid "Chimay Rouge 33cl" +msgstr "" + +#. module: point_of_sale +#: field:report.pos.order,delay_validation:0 +msgid "Delay Validation" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,nb_print:0 +msgid "Number of Print" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_make_payment +msgid "Point of Sale Payment" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_50cl_product_template +msgid "Coca-Cola Light 50cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.jupiler_50cl_product_template +msgid "Jupiler 50cl" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.details_summary:0 +msgid "End Period" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_light_lemon_33cl_product_template +msgid "Coca-Cola Light Lemon 33cl" +msgstr "" + +#. module: point_of_sale +#: field:account.journal,auto_cash:0 +msgid "Automatic Opening" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_ketchup_250g_product_template +msgid "Lays Ketchup 250g" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.perrier_50cl_product_template +msgid "Perrier 50cl" +msgstr "" + +#. module: point_of_sale +#: selection:report.pos.order,state:0 +msgid "Synchronized" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 field:report.cash.register,month:0 +#: view:report.pos.order:0 field:report.pos.order,month:0 +msgid "Month" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 +msgid "Statement Name" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "Year of order date" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_new_bank_statement_tree +#: field:pos.box.entries,journal_id:0 field:pos.box.out,journal_id:0 +msgid "Cash Register" +msgstr "" + +#. module: point_of_sale +#: view:pos.close.statement:0 +msgid "Yes" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_receipt +msgid "Point of sale receipt" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_margin_pos_today +msgid "Sales by User Daily margin" +msgstr "" + +#. module: point_of_sale +#: view:pos.open.statement:0 +msgid "Open Registers" +msgstr "" + +#. module: point_of_sale +#: field:report.transaction.pos,journal_id:0 +msgid "Sales Journal" +msgstr "" + +#. module: point_of_sale +#: field:report.cash.register,balance_start:0 +msgid "Opening Balance" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 selection:report.pos.order,state:0 +msgid "Closed" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_vegetale_product_template +msgid "Dr. Oetker Ristorante Vegetale" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "POS ordered created by today" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_zero_2l_product_template +msgid "Coca-Cola Zero 2L" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,amount_paid:0 selection:pos.order,state:0 +msgid "Paid" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_all_sales_lines +msgid "All sales lines" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Discount" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Cash Analysis created in last month" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.beers +msgid "Beers" +msgstr "" + +#. module: point_of_sale +#: view:pos.close.statement:0 +msgid "" +"OpenERP will close all cash registers he can close automatically without " +"validation. He will also open all cash registers for which you have to " +"control the ending belance before closing manually." +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Cash Analysis created by today" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,state:0 +msgid "Quotation" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.product_product_putmoneyforchange0_product_template +msgid "Cash In" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: report:all.closed.cashbox.of.the.day:0 report:pos.invoice:0 +#: report:pos.lines:0 report:pos.payment.report.user:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:49 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:93 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:271 +msgid "Total:" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_sales_by_margin_pos +msgid "Sales by margin" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_config_journal +msgid "Journal Configuration" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Statement lines" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.croky_paprika_45g_product_template +msgid "Croky Paprika 45g" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Reprint" +msgstr "" + +#. module: point_of_sale +#: help:pos.order,user_id:0 +msgid "" +"Person who uses the the cash register. It could be a reliever, a student or " +"an interim employee." +msgstr "" + +#. module: point_of_sale +#: field:report.transaction.pos,invoice_id:0 +msgid "Nbr Invoice" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_receipt +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:231 +msgid "Receipt" +msgstr "" + +#. module: point_of_sale +#: view:pos.open.statement:0 +msgid "" +"The system will open all cash registers, so that you can start recording " +"payments. We suggest you to control the opening balance of each register, " +"using their CashBox tab." +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 report:pos.lines:0 field:pos.order.line,price_unit:0 +#: report:pos.payment.report.user:0 +msgid "Unit Price" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Done" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_invoice_report +#: report:pos.invoice:0 view:pos.order:0 field:pos.order,invoice_id:0 +msgid "Invoice" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.rochefort_8_33cl_product_template +msgid "Rochefort \"8\" 33cl" +msgstr "" + +#. module: point_of_sale +#: view:account.bank.statement:0 selection:report.cash.register,state:0 +msgid "Open" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,name:0 field:pos.order.line,order_id:0 +msgid "Order Ref" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.timmermans_geuze_37,5cl_product_template +msgid "Timmermans Geuze 37.5cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_pickles_250g_product_template +msgid "Lays Pickels 250g" +msgstr "" + +#. module: point_of_sale +#: field:report.sales.by.margin.pos,net_margin_per_qty:0 +#: field:report.sales.by.margin.pos.month,net_margin_per_qty:0 +msgid "Net margin per Qty" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_sales_by_user_pos_month +msgid "Sales by user monthly" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_et_fruit_50cl_product_template +msgid "Spa et Fruit Orange 50cl" +msgstr "" + +#. module: point_of_sale +#: view:report.sales.by.margin.pos:0 view:report.sales.by.margin.pos.month:0 +msgid "Sales by User Margin" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_payment.py:59 +#, python-format +msgid "Paiement" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Taxes:" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_max_33cl_product_template +msgid "Pepsi Max 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_pos_order +msgid "Point of Sale Orders Statistics" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: model:ir.model,name:point_of_sale.model_product_product +#: report:pos.details:0 field:pos.order.line,product_id:0 +#: report:pos.payment.report.user:0 report:pos.user.product:0 +#: view:report.pos.order:0 field:report.pos.order,product_id:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:29 +msgid "Product" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_pollo_product_template +msgid "Dr. Oetker Ristorante Pollo" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_lines_report +msgid "Pos Lines" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.grisette_cerise_25cl_product_template +msgid "Grisette Cerise 25cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_33cl_product_template +msgid "Chaudfontaine 33cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_margherita_product_template +msgid "Dr. Oetker La Margherita" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_point_root +msgid "PoS Backend" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 selection:pos.order,state:0 +msgid "Posted" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,date_order:0 +msgid "Date Ordered" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_payment_report_user +#: model:ir.model,name:point_of_sale.model_pos_payment_report_user +msgid "Sales lines by Users" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_2l_product_template +msgid "Pepsi 2L" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 +msgid "Order" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:472 +#, python-format +msgid "There is no income account defined for this product: \"%s\" (id:%d)" +msgstr "" + +#. module: point_of_sale +#: view:pos.details:0 +msgid "POS Details" +msgstr "" + +#. module: point_of_sale +#: field:pos.details,user_ids:0 +msgid "Salesmen" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_report_cash_register +#: view:report.cash.register:0 +msgid "Point of Sale Cash Register Analysis" +msgstr "" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "Net Total :" +msgstr "" + +#. module: point_of_sale +#: model:res.groups,name:point_of_sale.group_pos_manager +msgid "Manager" +msgstr "" + +#. module: point_of_sale +#: field:pos.details,date_start:0 field:pos.sale.user,date_start:0 +msgid "Date Start" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: field:pos.order,amount_total:0 report:pos.payment.report.user:0 +#: field:report.sales.by.user.pos,amount:0 +#: field:report.sales.by.user.pos.month,amount:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:33 +msgid "Total" +msgstr "" + +#. module: point_of_sale +#: view:pos.sale.user:0 +msgid "Sale By User" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_open_statement +msgid "Open Cash Register" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lindemans_pecheresse_37,5cl_product_template +msgid "Lindemans Pecheresse 37.5cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_33cl_product_template +msgid "Pepsi 33cl" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 +#: model:ir.actions.report.xml,name:point_of_sale.account_statement +msgid "Statement" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_payment_report +#: model:ir.model,name:point_of_sale.model_pos_payment_report +#: view:pos.payment.report:0 +msgid "Payment Report" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_orange_25cl_product_template +msgid "Fanta Orange 25cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.confirm:0 +msgid "Generate Journal Entries" +msgstr "" + +#. module: point_of_sale +#: report:account.statement:0 report:all.closed.cashbox.of.the.day:0 +#: report:pos.details:0 report:pos.details_summary:0 report:pos.lines:0 +#: field:pos.order,company_id:0 field:pos.order.line,company_id:0 +#: report:pos.payment.report.user:0 report:pos.sales.user:0 +#: report:pos.sales.user.today:0 report:pos.user.product:0 +#: field:report.pos.order,company_id:0 +msgid "Company" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Invoice Date" +msgstr "" + +#. module: point_of_sale +#: field:pos.box.entries,name:0 +msgid "Reason" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.orangina_33cl_product_template +msgid "Orangina 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_product_input +#: model:ir.ui.menu,name:point_of_sale.products_for_input_operations +msgid "Products 'Take Money Out'" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chimay_bleu_33cl_product_template +msgid "Chimay Bleu 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_account_journal_form +#: model:ir.ui.menu,name:point_of_sale.menu_action_account_journal_form_open +msgid "Payment Methods" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.chips +msgid "Chips" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_spinaci_product_template +msgid "Dr. Oetker Ristorante Spinaci" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:293 +#: code:addons/point_of_sale/wizard/pos_box_entries.py:100 +#: code:addons/point_of_sale/wizard/pos_box_out.py:88 +#, python-format +msgid "You have to open at least one cashbox" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.report.xml,name:point_of_sale.pos_payment_report_user +msgid "Today's Payment By User" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_box_entries +#: model:ir.ui.menu,name:point_of_sale.menu_wizard_enter_jrnl +msgid "Put Money In" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:238 +#: code:addons/point_of_sale/point_of_sale.py:253 +#, python-format +msgid "Error!" +msgstr "" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "No. Of Articles" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.ijsboerke_vanille_2,5l_product_template +msgid "ijsboerke Vanille 2.5L" +msgstr "" + +#. module: point_of_sale +#: selection:pos.order,state:0 selection:report.pos.order,state:0 +msgid "Cancelled" +msgstr "" + +#. module: point_of_sale +#: report:pos.user.product:0 +msgid "Ending Date" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.orangina_1,5l_product_template +msgid "Orangina 1.5L" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,picking_id:0 +msgid "Picking" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.ice_cream +msgid "Ice Cream" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,shop_id:0 field:report.pos.order,shop_id:0 +msgid "Shop" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.belle_vue_kriek_25cl_product_template +msgid "Belle-Vue Kriek 25cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_petillante_1,5l_product_template +msgid "Chaudfontaine Petillante 1.5l" +msgstr "" + +#. module: point_of_sale +#: view:report.sales.by.user.pos:0 view:report.sales.by.user.pos.month:0 +#: view:report.transaction.pos:0 +msgid "POS Report" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_1,5l_product_template +msgid "Chaudfontaine 1.5l" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_close_statement +msgid "Close Cash Register" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:65 +#, python-format +msgid "In order to delete a sale, it must be new or cancelled." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_regular_50cl_product_template +msgid "Coca-Cola Regular 50cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.confirm:0 +msgid "Generate Entries" +msgstr "" + +#. module: point_of_sale +#: field:pos.box.entries,product_id:0 field:pos.box.out,product_id:0 +msgid "Operation" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,account_move:0 +msgid "Journal Entry" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,state:0 +msgid "Confirmed" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Cancelled Invoice" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Confirm" +msgstr "" + +#. module: point_of_sale +#: help:account.journal,auto_cash:0 +msgid "" +"This field authorize the automatic creation of the cashbox, without control " +"of the initial balance." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_zero_50cl_product_template +msgid "Coca-Cola Zero 50cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_tonno_product_template +msgid "Dr. Oetker Ristorante Tonno" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Supplier Invoice" +msgstr "" + +#. module: point_of_sale +#: constraint:account.bank.statement:0 +msgid "The journal and period chosen have to belong to the same company." +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 selection:pos.order,state:0 +#: selection:report.pos.order,state:0 +msgid "New" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:238 +#, python-format +msgid "In order to set to draft a sale, it must be cancelled." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_zero_orange_33cl_product_template +msgid "Fanta Zero Orange 33cl" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "Day of order date" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_point_rep +msgid "Reporting" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.maes_33cl_product_template +msgid "Maes 33cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.box.entries:0 +msgid "Put Money" +msgstr "" + +#. module: point_of_sale +#: sql_constraint:account.journal:0 +msgid "The code of the journal must be unique per company !" +msgstr "" + +#. module: point_of_sale +#: model:ir.ui.menu,name:point_of_sale.menu_point_config_product +msgid "Configuration" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.orval_33cl_product_template +msgid "Orval 33cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_regular_1l_product_template +msgid "Coca-Cola Regular 1L" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:253 +#, python-format +msgid "Unable to cancel the picking." +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "POS ordered created during current month" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "POS ordered created last month" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_pos_invoice +msgid "Invoices" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "December" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_gazeuse_1,5l_product_template +msgid "Spa Barisart 1.5l" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:329 view:pos.order:0 +#, python-format +msgid "Return Products" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.jupiler_33cl_product_template +msgid "Jupiler 33cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.ijsboerke_chocolat_2,5l_product_template +msgid "ijsboerke Chocolat 2.5L" +msgstr "" + +#. module: point_of_sale +#: view:pos.box.out:0 +msgid "Take Money" +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_details +msgid "Sales Details" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_close_statement.py:50 +#, python-format +msgid "Message" +msgstr "" + +#. module: point_of_sale +#: view:account.journal:0 model:ir.model,name:point_of_sale.model_pos_order +#: model:ir.ui.menu,name:point_of_sale.menu_point_root_touchscreen +#: view:product.product:0 +msgid "Point of Sale" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 field:pos.payment.report.user,user_id:0 +#: field:pos.sale.user,user_id:0 field:pos.sales.user.today,user_id:0 +#: view:report.pos.order:0 field:report.pos.order,user_id:0 +msgid "Salesman" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 view:pos.order:0 selection:pos.order,state:0 +#: view:report.pos.order:0 selection:report.pos.order,state:0 +msgid "Invoiced" +msgstr "" + +#. module: point_of_sale +#: view:pos.close.statement:0 +msgid "No" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_gazeuse_50cl_product_template +msgid "Spa Barisart 50cl" +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.special_beers +msgid "Special Beers" +msgstr "" + +#. module: point_of_sale +#: field:pos.order.line,notice:0 +msgid "Discount Notice" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Re-Print" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid "Draft" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.fanta_zero_orange_1,5l_product_template +msgid "Fanta Zero Orange 1.5L" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "PRO-FORMA" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:358 +#, python-format +msgid "Please provide a partner for the sale." +msgstr "" + +#. module: point_of_sale +#: model:pos.category,name:point_of_sale.fruity_beers +msgid "Fruity Beers" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.ijsboerke_dame_blanche_2,5l_product_template +msgid "ijsboerke Dame Blanche 2.5L" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.ijsboerke_moka_2,5l_product_template +msgid "ijsboerke Moka 2.5L" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Search Sales Order" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,help:point_of_sale.action_account_journal_form +msgid "" +"Payment methods are defined by accounting journals having the field Payment " +"Method checked." +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_trans_pos_tree +#: model:ir.model,name:point_of_sale.model_report_sales_by_user_pos +msgid "Sales by user" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_hawaii_product_template +msgid "Dr. Oetker Ristorante Hawaii" +msgstr "" + +#. module: point_of_sale +#: view:pos.receipt:0 +msgid "Print Receipt" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "January" +msgstr "" + +#. module: point_of_sale +#: view:pos.order.line:0 +msgid "POS Orders lines" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.chaudfontaine_petillante_33cl_product_template +msgid "Chaudfontaine Petillante 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,help:point_of_sale.product_normal_action +msgid "" +"You must define a Product for everything you buy or sell. Products can be " +"raw materials, stockable products, consumables or services. The Product form " +"contains detailed information about your products related to procurement " +"logistics, sales price, product category, suppliers and so on." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.pepsi_max_2l_product_template +msgid "Pepsi Max 2L" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/point_of_sale.py:358 +#, python-format +msgid "Error" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.lays_paprika_300g_product_template +msgid "Lays Paprika XXL 300g" +msgstr "" + +#. module: point_of_sale +#: view:pos.close.statement:0 +msgid "Do you want to close your cash registers?" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Refund" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/report/pos_invoice.py:46 +#, python-format +msgid "Please create an invoice for this sale." +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.oetker_mozzarella_product_template +msgid "Dr. Oetker Ristorante Mozzarella" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_close_statement.py:66 +#: model:ir.ui.menu,name:point_of_sale.menu_close_statement +#: view:pos.close.statement:0 +#, python-format +msgid "Close Cash Registers" +msgstr "" + +#. module: point_of_sale +#: report:pos.details:0 report:pos.payment.report.user:0 +msgid "Disc(%)" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "General Information" +msgstr "" + +#. #-#-#-#-# point_of_sale.pot (OpenERP Server 6.1rc1) #-#-#-#-# +#. module: point_of_sale +#. #-#-#-#-# point_of_sale.pot.web (PROJECT VERSION) #-#-#-#-# +#. openerp-web +#: view:pos.details:0 view:pos.discount:0 view:pos.payment.report:0 +#: view:pos.payment.report.user:0 view:pos.sale.user:0 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:13 +msgid "Close" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Order lines" +msgstr "" + +#. module: point_of_sale +#: view:account.journal:0 +msgid "Extended Configuration" +msgstr "" + +#. module: point_of_sale +#: field:pos.order.line,price_subtotal:0 +msgid "Subtotal w/o Tax" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.spa_50cl_product_template +msgid "Spa Reine 50cl" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.leffe_blonde_33cl_product_template +msgid "Leffe Blonde 33cl" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_payment +msgid "Pyament Report" +msgstr "" + +#. module: point_of_sale +#: field:report.transaction.pos,jl_id:0 +msgid "Cash Journals" +msgstr "" + +#. module: point_of_sale +#: report:pos.sales.user.today:0 +msgid "Today's Sales By User" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Customer Code" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_box_out.py:86 +#, python-format +msgid "please check that account is set to %s" +msgstr "" + +#. module: point_of_sale +#: field:pos.config.journal,name:0 report:pos.invoice:0 report:pos.lines:0 +msgid "Description" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "May" +msgstr "" + +#. module: point_of_sale +#: report:pos.lines:0 +msgid "Sales lines" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 +msgid "Yesterday" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,note:0 +msgid "Internal Notes" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.coca_zero_decaf_33cl_product_template +msgid "Coca-Cola Zero Decaf 33cl" +msgstr "" + +#. module: point_of_sale +#: view:pos.box.out:0 +msgid "Describe why you take money from the cash register:" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_pos_order_all +#: model:ir.ui.menu,name:point_of_sale.menu_report_pos_order_all +#: view:report.pos.order:0 +msgid "Point of Sale Analysis" +msgstr "" + +#. module: point_of_sale +#: view:pos.order:0 field:pos.order,partner_id:0 view:report.pos.order:0 +msgid "Customer" +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "February" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 +msgid " Today " +msgstr "" + +#. module: point_of_sale +#: selection:report.cash.register,month:0 selection:report.pos.order,month:0 +msgid "April" +msgstr "" + +#. module: point_of_sale +#: field:pos.order,statement_ids:0 +msgid "Payments" +msgstr "" + +#. module: point_of_sale +#: report:pos.invoice:0 +msgid "Supplier Refund" +msgstr "" + +#. module: point_of_sale +#: model:ir.actions.act_window,name:point_of_sale.action_report_sales_by_margin_pos_month +msgid "Sales by User Monthly margin" +msgstr "" + +#. module: point_of_sale +#: model:product.template,name:point_of_sale.leffe_9_33cl_product_template +msgid "Leffe Brune \"9\" 33cl" +msgstr "" + +#. module: point_of_sale +#: help:account.journal,journal_user:0 +msgid "" +"Check this box if this journal define a payment method that can be used in " +"point of sales." +msgstr "" + +#. module: point_of_sale +#: field:pos.category,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: point_of_sale +#: code:addons/point_of_sale/wizard/pos_return.py:316 view:pos.make.payment:0 +#, python-format +msgid "Make Payment" +msgstr "" + +#. module: point_of_sale +#: constraint:pos.category:0 +msgid "Error ! You cannot create recursive categories." +msgstr "" + +#. module: point_of_sale +#: model:ir.model,name:point_of_sale.model_pos_sales_user_today +msgid "Sales User Today" +msgstr "" + +#. module: point_of_sale +#: view:report.pos.order:0 +msgid "Month of order date" +msgstr "" + +#. module: point_of_sale +#: field:product.product,income_pdt:0 +msgid "PoS Cash Input" +msgstr "" + +#. module: point_of_sale +#: view:report.cash.register:0 field:report.cash.register,year:0 +#: view:report.pos.order:0 field:report.pos.order,year:0 +msgid "Year" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:31 +msgid "Disc (%)" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:41 +msgid "Subtotal:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:45 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:268 +msgid "Tax:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:64 +msgid "Disc" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:71 +msgid "+/-" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:105 +msgid "Paid:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:113 +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:288 +msgid "Change:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:124 +msgid "Back to Products" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:127 +msgid "Validate" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:140 +msgid "pending orders" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:145 +msgid "" +"There are pending operations that could not be saved into the database, are " +"you sure you want to exit?" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:236 +msgid "Print" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:238 +msgid "Next Order" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:248 +msgid "Phone:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:249 +msgid "User:" +msgstr "" + +#. openerp-web +#: /home/odo/repositories/addons/trunk/point_of_sale/static/src/xml/pos.xml:250 +msgid "Shop:" +msgstr "" diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index b17e672cb9c..95b54b6baed 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -314,10 +314,9 @@ class pos_session(osv.osv): # define some cash journal if no payment method exists if not pos_config.journal_ids: - cashids = self.pool.get('account.journal').search(cr, uid, [('journal_user','=',True)], context=context) + cashids = self.pool.get('account.journal').search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context) if not cashids: - cashids = self.pool.get('account.journal').search(cr, uid, [('type','=','cash')], context=context) - self.pool.get('account.journal').write(cr, uid, cashids, {'journal_user': True}) + cashids = self.pool.get('account.journal').search(cr, uid, [('journal_user','=',True)], context=context) jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]}) @@ -422,7 +421,7 @@ class pos_session(osv.osv): self.pool.get('pos.order')._create_account_move_line(cr, uid, order_ids, session, move_id, context=context) for order in session.order_ids: - if order.state != 'paid': + if order.state not in ('paid', 'invoiced'): raise osv.except_osv( _('Error!'), _("You cannot confirm all orders of this session, because they have not the 'paid' status")) @@ -1211,29 +1210,27 @@ class pos_category(osv.osv): 'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."), - # NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail + # NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail # for at least one category, then we display a default image on the other, so that the buttons have consistent styling. - # In this case, the default image is set by the js code. - + # In this case, the default image is set by the js code. + # NOTE2: image: all image fields are base64 encoded and PIL-supported 'image': fields.binary("Image", - help="This field holds the image used for the category. "\ - "The image is base64 encoded, and PIL-supported. "\ - "It is limited to a 1024x1024 px image."), + help="This field holds the image used as image for the cateogry, limited to 1024x1024px."), 'image_medium': fields.function(_get_image, fnct_inv=_set_image, string="Medium-sized image", type="binary", multi="_get_image", - store = { + store={ 'pos.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Medium-sized image of the category. It is automatically "\ - "resized as a 180x180 px image, with aspect ratio preserved. "\ + "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="Smal-sized image", type="binary", multi="_get_image", - store = { + store={ 'pos.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Small-sized image of the category. It is automatically "\ - "resized as a 50x50 px image, with aspect ratio preserved. "\ + "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), } @@ -1242,7 +1239,7 @@ import io, StringIO class ean_wizard(osv.osv_memory): _name = 'pos.ean_wizard' _columns = { - 'ean13_pattern': fields.char('Ean13 Pattern', size=32, required=True, translate=True), + 'ean13_pattern': fields.char('Reference', size=32, required=True, translate=True), } def sanitize_ean13(self, cr, uid, ids, context): for r in self.browse(cr,uid,ids): @@ -1306,7 +1303,7 @@ class product_product(osv.osv): def edit_ean(self, cr, uid, ids, context): return { - 'name': "Edit Ean", + 'name': _("Assign a Custom EAN"), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', diff --git a/addons/point_of_sale/point_of_sale_data.xml b/addons/point_of_sale/point_of_sale_data.xml index dda0b78e952..35a2516da02 100644 --- a/addons/point_of_sale/point_of_sale_data.xml +++ b/addons/point_of_sale/point_of_sale_data.xml @@ -24,14 +24,15 @@ <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Point of Sale has been installed</value> - <value>Encode sale orders, register payments, compute money to return, create invoices, and manage refunds of former sales through a specific, web-based, touch-screen user interface. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Point of Sale application installed!</field> + <field name="body">Record sale orders, register payments, compute change to return, create invoices, and manage refunds through a specific web touch-screen interface. -If you install the PoS proxy, you will be able to interface OpenERP with retail materials; barcode scanners, printers, cash registers, weighing machine, credit card payments.</value> - </function> +If you install the PoS proxy you will be able to interface OpenERP with retail hardware: barcode scanners, printers, cash registers, weighing machines, credit card payment terminals.</field> + </record> <record id="unreferenced_product" model="product.product"> <field name="list_price">1.00</field> diff --git a/addons/point_of_sale/point_of_sale_demo.xml b/addons/point_of_sale/point_of_sale_demo.xml index 47256279c59..7d80dd493ad 100644 --- a/addons/point_of_sale/point_of_sale_demo.xml +++ b/addons/point_of_sale/point_of_sale_demo.xml @@ -11,30 +11,14 @@ <record id="account.cash_journal" model="account.journal"> <field eval="True" name="journal_user"/> </record> - <record id="base.user_jsmith" model="res.users"> - <field name="name">John Smith (Cashier)</field> - <field name="login">jsmith</field> + <record id="base.user_root" model="res.users"> <field name="ean13">0410300000004</field> <field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/> </record> - <record id="base.user_jdoe" model="res.users"> - <field name="name">John Doe (Cashier)</field> - <field name="login">jdoe</field> + <record id="base.user_demo" model="res.users"> <field name="ean13">0410400000003</field> <field name="groups_id" eval="[(4,ref('group_pos_manager'))]"/> </record> - <record id="base.user_jbloggs" model="res.users"> - <field name="name">Joe Bloggs (Client)</field> - <field name="login">jbloggs</field> - <field name="ean13">0420100000005</field> - <field name="groups_id" eval="[(4,ref('group_pos_user'))]"/> - </record> - <record id="base.user_bsoap" model="res.users"> - <field name="name">Bob Soap (Client)</field> - <field name="login">bsoap</field> - <field name="ean13">0420200000004</field> - <field name="groups_id" eval="[(4,ref('group_pos_user'))]"/> - </record> <record model="pos.config" id="pos_config_main"> <field name="journal_ids" eval="[(6, 0, [ref('account.cash_journal'), ref('account.bank_journal')])]" /> diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 0e906186285..6fef9bda282 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -608,35 +608,21 @@ <field name="domain">[('date_order','like',time.strftime('%Y-%m'))]</field> </record> - <record id="product_normal_form_pos_view" model="ir.ui.view"> - <field name="name">product.normal.pos.form.inherit</field> - <field name="model">product.product</field> - <field name="inherit_id" ref="product.product_normal_form_view"/> - <field name="arch" type="xml"> - <group name="status" position="after"> - <group name="pos" string="Point of Sale"> - <field name="pos_categ_id"/> - <field name="to_weight" /> - </group> - </group> - </field> - </record> - - <record id="product_normal_form_view" model="ir.ui.view"> <field name="name">product.normal.form.inherit</field> <field name="model">product.product</field> <field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/> <field name="arch" type="xml"> - <group name="misc" position="after"> - <group name="pos" colspan="2" col="2"> - <separator string="Point of Sale" colspan="2"/> + <group name="sale" position="inside"> + <group name="pos" string="Point of Sale"> + <field name="pos_categ_id"/> + <field name="to_weight" /> <field name="income_pdt"/> <field name="expense_pdt"/> </group> </group> <field name="ean13" position="after"> - <button name="edit_ean" type="object" string="Edit" /> + <button name="edit_ean" type="object" string="Set a Custom EAN" class="oe_link oe_edit_only"/> </field> </field> </record> @@ -911,7 +897,7 @@ </group> <newline/> - <group string="Opening Cash Control" attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', '=', 'closed')]}"> + <group string="Opening Cash Control" attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', 'in', ('opened', 'closing_control'))]}"> <field name="opening_details_ids" nolabel="1" colspan="2" attrs="{'readonly' : [('state', 'not in', ('opening_control',))]}"> <tree string="Opening Cashbox Lines" editable="bottom"> <field name="pieces" readonly="1" /> @@ -920,7 +906,7 @@ </tree> </field> </group> - <group string="Closing Cash Control" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', '!=', 'closing_control')]}"> + <group string="Closing Cash Control" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'in', ('opening_control', 'opened'))]}"> <field name="details_ids" nolabel="1" colspan="2"> <tree string="Cashbox Lines" editable="bottom"> <field name="pieces" readonly="1" /> @@ -930,14 +916,14 @@ </field> </group> - <div attrs="{'invisible': [('state', '=', 'closed')]}"> + <div attrs="{'invisible' : ['|', ('cash_control', '=', False),('state', 'in', ('opened', 'closing_control'))]}"> <group class="oe_subtotal_footer oe_right"> <field name="cash_register_balance_start" readonly="1" string="Opening Balance" class="oe_subtotal_footer_separator"/> <field name="cash_register_total_entry_encoding" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="+ Transactions"/> <field name="cash_register_balance_end" attrs="{'invisible' : [('state', '=', 'opening_control')]}" string="= Theorical Balance"/> </group> <div class="oe_clear"/> - <div attrs="{'invisible' : [('cash_journal_id', '=', False)]}" class="oe_view_nocontent" groups="point_of_sale.group_pos_manager"> + <div attrs="{'invisible' : ['|', ('cash_journal_id', '=', False), ('state', '!=', 'opening_control')]}" class="oe_view_nocontent" groups="point_of_sale.group_pos_manager"> <p class="oe_view_nocontent_create"> You can define another list of available currencies on the <i>Cash Registers</i> tab of the <b><field name="cash_journal_id" class="oe_inline"/></b> @@ -947,7 +933,7 @@ </div> - <group class="oe_subtotal_footer oe_right" attrs="{'invisible' : [('state', 'in', ('opening_control', 'closed'))]}"> + <group class="oe_subtotal_footer oe_right" attrs="{'invisible': ['|', ('cash_control', '=', False), ('state', 'in', ('opening_control', 'opened'))]}"> <field name="cash_register_balance_end_real" class="oe_subtotal_footer_separator"/> <field name="cash_register_difference" class="oe_subtotal_footer_separator"/> </group> @@ -962,7 +948,7 @@ <field name="total_entry_encoding" /> <field name="balance_end_real" /> <field name="difference" /> - <field name="currency" /> + <field name="currency" groups="base.group_multi_currency" /> <field name="state" /> </tree> </field> @@ -1070,9 +1056,17 @@ <field name="type">form</field> <field name="arch" type="xml"> <form string="Ean13 Generator" version="7.0"> - <field name="ean13_pattern"/> + <p class="oe_grey"> + Enter a reference for this product, it will be converted + automatically to a valid EAN number. + </p> + <group> + <field name="ean13_pattern" class="oe_inline"/> + </group> <footer> - <button name="sanitize_ean13" type="object" string="Apply"/> + <button name="sanitize_ean13" type="object" string="Apply" class="oe_highlight"/> or + <button icon="gtk-stop" special="cancel" + string="Cancel" class="oe_link"/> </footer> </form> </field> diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index a9baf041942..9ac7d4405d0 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -20,6 +20,12 @@ user-select: none; } +.point-of-sale ul, .point-of-sale li { + margin: 0; + padding: 0; + list-style-type: none; +} + .point-of-sale .shadow-top{ position: absolute; top:0; @@ -1072,12 +1078,14 @@ -webkit-transition-timing-function: ease-out; } -.point-of-sale .pos-actionbar .button.disabled{ - color:#AAA; +.point-of-sale .pos-actionbar .button.disabled *{ + opacity: 0.5; } .point-of-sale .pos-actionbar .button.disabled:hover{ border: 1px solid #cacaca; border-radius: 4px; + color: #555; + cursor: default; background: #e2e2e2; background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); diff --git a/addons/point_of_sale/static/src/css/pos_nohover.css b/addons/point_of_sale/static/src/css/pos_nohover.css new file mode 100644 index 00000000000..6f24abda53b --- /dev/null +++ b/addons/point_of_sale/static/src/css/pos_nohover.css @@ -0,0 +1,1168 @@ +@font-face{ + font-family: 'Inconsolata'; + src: url(../fonts/Inconsolata.otf); +} + +.point-of-sale { + padding: 0; + margin: 0; + background-color: #f0eeee; + font-family: "Lucida Grande", Helvetica, Verdana, Arial; + color: #555555; + font-size: 12px; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.point-of-sale .shadow-top{ + position: absolute; + top:0; + left:0; + right:0; + height:10px; + background: -webkit-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); + background: -moz-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); + background: -ms-linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); + background: linear-gradient(top,rgba(0,0,0,0.09),rgba(0,0,0,0)); +} + +.point-of-sale .darker-shadow-top{ + position: absolute; + top:0; + left:0; + right:0; + height:10px; + background: -webkit-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); + background: -moz-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); + background: -ms-linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); + background: linear-gradient(top,rgba(0,0,0,0.15),rgba(0,0,0,0)); +} + +/* ********* The black loading screen ********* */ + +.point-of-sale .loader{ + background-color: #222; + position:absolute; + left:0px; + top:0px; + width:100%; + height:100%; + z-index: 999; + text-align: center; +} +.point-of-sale .loader img{ + position:absolute; + top:50%; + left:50%; +} + +/* ********* Generic element styling ********* */ + +.point-of-sale a { + text-decoration: none; + color: #555555; +} +.point-of-sale button, .point-of-sale a.button { + display: inline-block; + cursor: pointer; + padding: 4px 10px; + font-size: 11px; + border: 1px solid #cacaca; + border-radius: 4px; + background: #e2e2e2; + background: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0), to(#e2e2e2)); + background: -moz-linear-gradient(#f0f0f0, #e2e2e2); + background: -ms-linear-gradient(#f0f0f0, #e2e2e2); + background: linear-gradient(#f0f0f0, #e2e2e2); +} +.point-of-sale ul, .point-of-sale ol { + padding: 0; + margin: 0; +} +.point-of-sale li { + list-style-type: none; +} +.point-of-sale .pos-right-align { + text-align: right; +} +.point-of-sale .pos-right-align input { + text-align: right; + border: 1px solid #cecbcb; + border-radius: 4px; +} + +/* ********* The black header bar ********* */ + + +.point-of-sale #topheader { + position:absolute; + left:0; + top:0; + width: 100%; + height: 33px; + margin:0; + padding:0; + color: gray; + border-top: solid 1px #d3d3d3; + border-bottom: solid 1px black; + background: #393939; + background: -moz-linear-gradient(#7b7979, #393939); + background: -webkit-gradient(linear, left top, left bottom, from(#7b7979), to(#393939)); +} + +/* a) The left part of the top-bar */ + +.point-of-sale #branding{ + position: absolute; + display: table-cell; + left:0; + top:0; + width:439px; + height:100%; + margin:0; + padding:0; + border-right: 1px solid #373737; + text-align:left; + line-height:100%; + vertical-align: middle; +} +.point-of-sale #branding img { + height: 32px; + width: 116px; + margin-left:5px; + vertical-align:middle; +} +.point-of-sale #branding .username{ + float:right; + color:#DDD; + font-size:16px; + margin-right:32px; + margin-top:10px; + font-style:italic; +} + +/* b) The right part of the top-bar */ + +.point-of-sale #rightheader { + position: absolute; + left:440px; + right:0; + top:0; + height:100%; +} + +.point-of-sale #rightheader button { + color: #273072; + height:27px; + margin:3px; + margin-right:0px; + border: 1px solid #353A7E; + background: #7f82ac; + background: -webkit-gradient(linear, left top, left bottom, from(#b2b3d7), to(#7f82ac)); + background: -moz-linear-gradient(#b2b3d7, #7f82ac); + background: -ms-linear-gradient(#b2b3d7, #7f82ac); + background: linear-gradient(#b2b3d7, #7f82ac); +} + +.point-of-sale #rightheader button.neworder-button { + width: 32px; + margin-left:4px; + margin-right:4px; +} + +.point-of-sale div#order-selector { + display: inline; +} +.point-of-sale ol#orders { + display: inline; +} +.point-of-sale li.order-selector-button { + display: inline; +} +.point-of-sale li.selected-order button { + font-weight: 900; +} + +/* c) The session buttons */ + +.point-of-sale #rightheader .header-button{ + float:right; + height:32px; + padding-left:10px; + padding-right:10px; + border-right: 1px solid #3a3a3a; + border-left: 1px solid #3a3a3a; + color:#DDD; + line-height:32px; + text-align:center; + cursor: pointer; + + -webkit-transition-property: background; + -webkit-transition-duration: 0.2s; + -webkit-transition-timing-function: ease-out; +} +.point-of-sale #rightheader .header-button:last-child{ + border-left: 1px solid #3a3a3a; +} + +/* c) The notifications indicator */ + +.point-of-sale .oe_pos_synch-notification{ + float:right; + color: rgba(255,255,255,0.4); + padding: 8px; + line-height:16px; + font-size:16px; + vertical-align:middle; + font-style: italic; + cursor:pointer; +} + +.point-of-sale .oe_pos_synch-notification .oe_status_red{ + display:inline-block; + cursor:pointer; + width:16px; height:16px; + background: url("../img/gtk-no.png") no-repeat ; +} + +.point-of-sale .oe_pos_synch-notification .oe_status_green{ + display:inline-block; + width:16px; height:16px; + background: url("../img/gtk-yes.png") no-repeat; +} + +/* ********* Contains everything below the bar ********* */ + +.point-of-sale #content { + width: 100%; + position: absolute; + top: 35px; + bottom: 0; + background: #F0EEEE; +} + +/* ********* The leftpane contains the order, numpad and paypad ********* */ + +.point-of-sale #leftpane { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + position:absolute; + left:0; + width:440px; + top:0px; + /*bottom:105px;*/ + bottom:0; + border-right: solid 1px #CECBCB; + background-color: white; +} +.point-of-sale #leftpane footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + background: #F0EEEE; + white-space: nowrap; +} + +/* ********* The paypad contains the payment buttons ********* */ + +.point-of-sale #paypad { + padding: 8px 4px 8px 8px; + display: inline-block; + text-align: center; + vertical-align: top; +} +.point-of-sale #paypad button { + height: 54px; + width: 208px; + margin: 0 -3px; + font-weight: bold; + vertical-align: middle; + color: #555555; + border-top: 1px solid #efefef; + font-size: 14px; +} + +/* ********* The Numpad ********* */ + +.point-of-sale #numpad { + padding: 8px 8px 8px 4px; + display: inline-block; + text-align: center; +} +.point-of-sale #numpad button { + height: 54px; + width: 54px; + margin: 0 -3px; + font-weight: bold; + vertical-align: middle; + color: #555555; + border-top: 1px solid #efefef; +} +.point-of-sale #numpad .selected-mode { + color: white; + background: #7f82ac; + background: -webkit-gradient(linear, left top, left bottom, from(#9d9fc5), to(#7f82ac)); + background: -moz-linear-gradient(#9d9fc5, #7f82ac); + background: -ms-linear-gradient(#9d9fc5, #7f82ac); + background: linear-gradient(#9d9fc5, #7f82ac); +} +.point-of-sale .input-button { + font-size: 24px; +} +.point-of-sale .mode-button, .point-of-sale #numpad-delete, .point-of-sale #numpad-minus { + font-size: 14px; +} + +/* ********* The right pane contains the screens and headers ********* */ + +.point-of-sale #rightpane { + position: absolute; + top: 0; + /*bottom: 105px;*/ + bottom:0; + left: 440px; + right: 0; + vertical-align: top; +} + +.point-of-sale #rightpane header { + padding: 0; + height: 32px; + border-bottom: 1px solid #cecbcb; + background: #d3d3d3; + background: -moz-linear-gradient(white, #d3d3d3); + background: -webkit-gradient(linear, left top, left bottom, from(white), to(#d3d3d3)); +} + +/* ********* The product list ********* */ + +.point-of-sale .product-list { + padding:10px; +} + +.point-of-sale .product-list-scroller{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + width:100%; + height:100%; + overflow: hidden; +} +.point-of-sale .product-list-container { + position:absolute; + top:0px; + bottom:0px; + left:0px; + right:0px; + background: #eeedff; +} + +/* a) the product list navigation bar */ + +.point-of-sale .breadcrumb li { + float: left; + line-height: 32px; + height: 32px; +} +.point-of-sale .breadcrumb li:last-child { + padding-right: 3px; + border-right: 1px solid #c5c5c5; +} +.point-of-sale .breadcrumb a { + display: inline-block; + padding: 0 9px; + vertical-align: top; + text-shadow: #f7f7f7 0 1px 1px; + color: #555555; + font-weight: bold; +} +.point-of-sale .bc-arrow { + height: 33px; +} +.point-of-sale .homeimg { + width: 19px; + height: 19px; + margin: 6px 0; +} + +/* b) the search box */ + +.point-of-sale .searchbox { + position: absolute; + right: 2px; +} +.point-of-sale .searchbox input { + width: 130px; + border-radius: 11px; + border: 1px solid #cecbcb; + padding: 3px 19px; + margin: 6px; + background: url("../img/search.png") no-repeat 5px; + background-color: white; +} +.point-of-sale .search-clear { + position: absolute; + top: 11px; + right: 11px; + cursor: pointer; + display: none; +} + +/* c) the categories list */ + +.point-of-sale #categories { + /*background:#f0f0f0;*/ + position: relative; + background-image: url('../img/bg.png'); + border-bottom: 1px solid #cecbcb; +} +.point-of-sale #categories .white-gradient{ + position: absolute; + top:50%; + left:0; + right:0; + bottom:0; + background: -webkit-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0)); + background: -moz-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0)); + background: -ms-linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0)); + background: linear-gradient(bottom,rgba(255,255,255,0.5),rgba(255,255,255,0)); +} +.point-of-sale #categories h4 { + display: inline-block; + margin: 9px 5px; +} + +.point-of-sale .category-list{ + padding:10px; +} +/* d) the category button */ + +.point-of-sale .category-button { + position: relative; + vertical-align: top; + display: inline-block; + font-size: 11px; + margin: 5px; + width: 120px; + height:120px; + background:#fff; + border: 1px solid #fff; + border-radius: 3px; + -webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); + -moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); + box-shadow: 0px 1px 8px rgba(0,0,0,0.2); + cursor: pointer; +} + +.point-of-sale .category-simple-button{ + position: relative; + display: inline-block; + font-size: 14px; + margin-right:10px; + padding:5px; + + cursor: pointer; + border: 1px solid #cacaca; + border-radius: 4px; + + background: #e2e2e2; + background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); + background: -moz-linear-gradient(#f0f0f0, #e2e2e2); + background: -ms-linear-gradient(#f0f0f0, #e2e2e2); + background: linear-gradient(#f0f0f0, #e2e2e2); + -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); + -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); + box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); +} + +.point-of-sale .category-button .category-img { + position: relative; + width: 120px; + height: 100px; + text-align: center; + cursor: pointer; +} + +.point-of-sale .category-button .category-img img { + max-height: 100px; + max-width: 120px; +} + +.point-of-sale .category-button .category-name { + position: absolute; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + top:auto; + left: 2px; + right: 2px; + bottom: 2px; + background: #8a89ba; /*-webkit-linear-gradient(-90deg,rgba(138,137,186,0),rgba(138,137,186,1), rgba(138,137,186,1));*/ + padding: 3px; + /*color:#8a89ba;*/ + color: #FFF; + cursor: pointer; + border-radius: 3px; +} + +/* e) the product */ + +.point-of-sale .product { + position:relative; + vertical-align: top; + display: inline-block; + line-height: 100px; + font-size: 11px; + margin: 5px; + width: 120px; + height:120px; + background:#fff; + border: 1px solid #fff; + border-radius: 3px; + -webkit-box-shadow: 0px 1px 8px rgba(127,130,172,0.4); + -moz-box-shadow: 0px 1px 8px rgba(127,130,172,0.4); + box-shadow: 0px 1px 8px rgba(127,130,172,0.4); +} + +.point-of-sale .product .product-img { + position: relative; + width: 120px; + height: 100px; + background: white; + text-align: center; +} + +.point-of-sale .product .product-img img { + max-height: 100px; + max-width: 120px; +} + +.point-of-sale .product .price-tag { + position: absolute; + top: 2px; + right: 2px; + vertical-align: top; + color: white; + line-height: 14px; + background: #7f82ac; + padding: 2px 5px; + border-radius: 3px; +} + +.point-of-sale .product .product-name { + position: absolute; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + bottom:0; + top:auto; + line-height: 14px; + width:100%; + background: -webkit-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); + background: -moz-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); + background: -ms-linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); + background: linear-gradient(-90deg,rgba(255,255,255,0),rgba(255,255,255,1), rgba(255,255,255,1)); + /*background:#FFF;*/ + padding: 3px; + padding-top:15px; +} + + +/* ********* The Screens ********* */ + +.point-of-sale .screen { + position:absolute; + text-align: center; + top:0px; + bottom:0px; + width:100%; +} +.point-of-sale .screen header h2 { + margin-top: 0px; + padding-top: 7px; +} +.point-of-sale .screen p{ + font-size: 18px; +} + +/* a) Layout for the Product Screen */ + +.point-of-sale .screen .layout-table { + border:none; + width:100%; + height:100%; +} + +.point-of-sale .screen .header-row { + border:none; + width:100%; + height:0px; +} + +.point-of-sale .screen .header-cell{ + border:none; + width:100%; + height:0px; +} +.point-of-sale .screen .content-row { + width:100%; + height:100%; +} +.point-of-sale .screen .content-cell{ + width:100%; +} +.point-of-sale .screen .content-cell .content-container{ + height:100%; + position:relative; +} + +/* b) The payment screen */ + +.point-of-sale .pos-step-container { + display: inline-block; + font-size: 1.5em; +} +.point-of-sale .greyed-out{ + color: #AAA; +} +.point-of-sale .pos-step-container input{ + font-size: 1em; +} + +.point-of-sale .pos-payment-container { + text-align: left; + min-width: 500px; +} +.point-of-sale .pos-payment-container .left-block{ + display: inline-block; + width:49%; + margin:0; + padding:0; + text-align:left; +} +.point-of-sale .pos-payment-container .header{ + margin-top: 50px; + margin-bottom:20px; + font-weight: bold; +} +.point-of-sale .pos-payment-container .infoline{ + margin-top:5px; + margin-bottom:5px; +} +.point-of-sale .pos-payment-container .right-block{ + display: inline-block; + width:49%; + margin:0; + padding:0; + text-align:right; +} +.point-of-sale .pos-payment-container table { + width: 100%; + margin-bottom: 20px; +} +.point-of-sale .pos-payment-container td { + vertical-align: middle; +} +.point-of-sale .pos-payment-container .paymentline-type { + font-size: 1em; + font-weight: bold; + margin-right:10px; +} + +/* c) The receipt screen */ + +.point-of-sale .pos-receipt-container { + font-size: 0.75em; +} + +.point-of-sale .pos-sale-ticket { + text-align: left; + width: 300px; + background-color: white; + margin: 20px; + padding: 15px; + padding-bottom:30px; + display: inline-block; + font-family: "Inconsolata"; + -webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); + box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); +} +.point-of-sale .pos-sale-ticket .emph{ + font-size: 20px; + margin:5px; +} +.point-of-sale .pos-sale-ticket table { + width: 100%; + border: 0; +} +.point-of-sale .pos-sale-ticket table td { + border: 0; +} + +@media print { + .point-of-sale #topheader, .point-of-sale #leftpane { + display: none !important; + } + .point-of-sale #content { + top: 0px !important; + } + .point-of-sale #rightpane { + left: 0px !important; + background-color: white; + } + #receipt-screen header { + display: none !important; + } + #receipt-screen { + text-align: left; + } + .pos-sale-ticket { + margin: 0; + } +} + +/* d) The Scale screen */ + +.point-of-sale .scale-screen .display{ + position:relative; + width:600px; + height:190px; + margin-top: 100px; + margin-left: auto; + margin-right: auto; +} + +.point-of-sale .scale-screen .product-picture { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + display: float; + float: right; + margin: 5px; + width: 180px; + height:180px; + line-height:180px; + cursor:pointer; + + background:#fff; + border: 1px solid #fff; + border-radius: 3px; + -webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); + -moz-box-shadow: 0px 1px 8px rgba(0,0,0,0.2); + box-shadow: 0px 1px 8px rgba(0,0,0,0.2); +} + +.point-of-sale .scale-screen .product-picture img{ + vertical-align: middle; + cursor:pointer; +} + +.point-of-sale .scale-screen .product-picture .product-price{ + position: absolute; + top:8px; + right:8px; + width:auto; + height:auto; + line-height:1; + color:white; + background: #7f82ac; + padding: 2px 5px; + border-radius: 3px; + cursor:pointer; +} + +.point-of-sale .scale-screen .product-name { + position: absolute; + left:0; + top:20px; + height:50px; + font-size:40px; + line-height:50px; + text-align:right; + right:225px; +} +.point-of-sale .scale-screen .weight{ + position: absolute; + left:0; + height:90px; + bottom:15px; + right:220px; + padding:5px; +} +.point-of-sale .scale-screen .weight p{ + display: inline-block; + text-align:right; + line-height: 90px; + font-size: 80px; + width:100%; + height:100%; + margin:0; + font-family: "Inconsolata"; +} + +/* e) The Welcome Screen */ +.point-of-sale .goodbye-message{ + position: absolute; + left:50%; + top:30%; + width:500px; + height:400px; + margin-left: -250px; + margin-top: -200px; + padding:10px; + padding-top:20px; + text-align:center; + font-size:20px; + font-weight:bold; + background-color: #F0EEEE; + border: 1px solid #E0DDDD; + -webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + z-index:1150; +} + +/* ********* The OrderWidget ********* */ + +.point-of-sale .order-container{ + position: absolute; + top: 0px; + bottom: 232px; + width:100%; + background: #F0EEEE; +} + +.point-of-sale .order-scroller{ + width:100%; + height:100%; + overflow:hidden; +} + +.point-of-sale .order{ + background: #F00; + background: -webkit-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1)); + background: -moz-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1)); + background: -ms-linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1)); + background: linear-gradient(0deg,rgba(245,245,245,1),rgba(255,255,255,1), rgba(245,245,245,1)); + padding-bottom:15px; + padding-top:15px; + margin-left:16px; + margin-right:16px; + margin-top:16px; + margin-bottom:16px; + font-size:16px; + -webkit-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); + box-shadow: 0px 5px 16px rgba(0,0,0, 0.3); + +} + +.point-of-sale .order .empty{ + text-align:center; + margin-top: 15px; + margin-bottom: 5px; + color:#999; + font-weight: normal; +} + +.point-of-sale .order .summary{ + width:100%; + text-align:right; + font-weight: bold; + margin-top:20px; + margin-bottom:10px; +} +.point-of-sale .order .summary .line{ + margin-right:15px; + padding-top:5px; + border-top: solid 2px; + border-color:#777; +} +.point-of-sale .order .summary .line.empty{ + border-color:#BBB; + color:#999; +} + +/* ********* The OrderLineWidget ********* */ + +.point-of-sale .order .orderline{ + width:100%; + margin:0px; + padding-top:3px; + padding-bottom:10px; + padding-left:15px; + padding-right:15px; + cursor: pointer; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: background 250ms ease-in-out; + -moz-transition: background 250ms ease-in-out; + transition: background 250ms ease-in-out; +} + +.point-of-sale .order .orderline.selected{ + background: rgba(140,143,183,0.2); + -webkit-transition: background 250ms ease-in-out; + -moz-transition: background 250ms ease-in-out; + transition: background 250ms ease-in-out; + cursor: default; +} +.point-of-sale .order .orderline .product-name{ + padding:0; + display:inline-block; + font-weight: bold; + width:80%; + overflow:hidden; +} +.point-of-sale .order .orderline .price{ + padding:0; + font-weight: bold; + float:right; +} +.point-of-sale .order .orderline .info-list{ + color: #888; + margin-left:10px; +} +.point-of-sale .order .orderline .info-list em{ + color: #777; + font-weight: bold; + font-style:normal; +} + +/* ********* The ActionBarWidget ********* */ + +.point-of-sale .pos-actionbar{ + position:absolute; + left: 0; + bottom: 0px; + height: 105px; + width: 100%; + margin: 0; + background: #f5f5f5; /*#ebebeb;*/ + border-top: solid 1px #afafb6; + z-index:900; +} + +.point-of-sale .pos-actionbar ul{ + list-style: none; +} + +.point-of-sale .pos-actionbar-left-pane{ + height: 100%; + width: 434px; + margin: 0px; + padding-left:3px; + padding-right:3px; + border-right: solid 1px #dfdfdf; + float: left; +} + +.point-of-sale .pos-actionbar-button-list{ + height: 100%; + margin: 0px; + padding-left:3px; + padding-right:3px; + overflow:hidden; +} + +.point-of-sale .pos-actionbar .button{ + width: 90px; + height: 90px; + text-align:center; + margin:3px; + margin-top:6px; + float:left; + + font-size: 14px; + font-weight: bold; + + cursor: pointer; + + border: 1px solid #cacaca; + border-radius: 4px; + + background: #e2e2e2; + background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); + background: -moz-linear-gradient(#f0f0f0, #e2e2e2); + background: -ms-linear-gradient(#f0f0f0, #e2e2e2); + background: linear-gradient(#f0f0f0, #e2e2e2); + -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); + -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); + box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); +} +.point-of-sale .pos-actionbar .button .label{ + margin-top: 37px; +} +.point-of-sale .pos-actionbar .button .icon{ + margin-top: 10px; +} + +.point-of-sale .pos-actionbar .button.disabled *{ + opacity: 0.5; +} + +.point-of-sale .pos-actionbar .button.rightalign{ + float:right; +} + +/* ********* The PopupWidgets ********* */ + +.point-of-sale .modal-dialog{ + position: absolute; + left: 0; + top: 0; + width: 100%; + height:100%; + background-color: rgba(0,0,0,0.5); + z-index:1000; +} +.point-of-sale .modal-dialog .popup{ + position: absolute; + left:50%; + top:50%; + width:500px; + height:400px; + margin-left: -250px; + margin-top: -200px; + padding:10px; + padding-top:20px; + text-align:center; + font-size:20px; + font-weight:bold; + background-color: #F0EEEE; + border: 1px solid #E0DDDD; + -webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + z-index:1200; +} +.point-of-sale .popup .footer{ + position:absolute; + bottom:0; + left:0; + width:100%; + height:60px; + border-top: 1px solid #E0DDDD; +} +.point-of-sale .popup .button{ + float:right; + width: 110px; + height: 40px; + line-height:40px; + text-align:center; + margin:3px; + margin-top:10px; + margin-right:10px; + + font-size: 14px; + font-weight: bold; + + cursor: pointer; + + border: 1px solid #cacaca; + border-radius: 4px; + + background: #e2e2e2; + background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); + background: -moz-linear-gradient(#f0f0f0, #e2e2e2); + background: -ms-linear-gradient(#f0f0f0, #e2e2e2); + background: linear-gradient(#f0f0f0, #e2e2e2); + -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); + box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); +} + +.point-of-sale .popup .button.big-left{ + position:absolute; + top: 120px; + left:40px; + width: 180px; + height: 180px; + line-height:180px; +} + +.point-of-sale .popup .button.big-right{ + position:absolute; + top: 120px; + right:40px; + width: 180px; + height: 180px; + line-height:180px; +} + +/* ********* The ScrollBarWidget ********* */ + +.point-of-sale .scrollbar{ + position:absolute; + top:7px; + right:7px; + width:48px; + bottom:7px; + background: rgba(0,0,0,0.1); + +} +.point-of-sale .scrollbar .button{ + width:100%; + height: 48px; + line-height: 38px; + text-align: center; + font-size:48px; + border-radius: 4px; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all 250ms ease-in-out; +} +.point-of-sale .scrollbar .button{ + color:white; + background: rgba(0,0,0,0.6); + -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,0.01); + -moz-box-shadow: 0px 1px 4px rgba(0,0,0,0.01); + box-shadow: 0px 1px 4px rgba(0,0,0,0.01); + text-shadow: rgba(255,255,255,0.5) 0px 0px 10px; + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} +.point-of-sale .scrollbar .button.disabled{ + background: rgba(0,0,0,0.3); + color:rgba(255,255,255,0.5); + -webkit-transition: all 250ms ease-in-out; + -moz-transition: all 250ms ease-in-out; + transition: all 250ms ease-in-out; +} +.point-of-sale .scrollbar .down-button{ + position:absolute; + bottom:0px; +} +.point-of-sale .scrollbar .up-button{ + position:absolute; + top:0px; +} +.point-of-sale .scrollbar .scroller{ + position:absolute; + top:33%; + bottom:50%; + width:100%; + background: rgba(0,0,0,0.1); + border-radius: 4px; +} + diff --git a/addons/point_of_sale/static/src/js/TODO.txt b/addons/point_of_sale/static/src/js/TODO.txt index edadbe188ea..1696f0b117d 100644 --- a/addons/point_of_sale/static/src/js/TODO.txt +++ b/addons/point_of_sale/static/src/js/TODO.txt @@ -47,11 +47,12 @@ TODO AUG 20 v supprimer l'écran v bloquer sur l'impression v L'impression est foireuse + - description_sales * CLIENT - - create a new branch + v create a new branch - Self-checkout welcome screen - - removal of products for the root category + ~ removal of products for the root category - Terminal de payement - Code à barres - Vidanges diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index a6be16b3835..e36070414b6 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -184,6 +184,8 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal * price_with_tax: (number) the price paid for this orderline, tax included * price_without_tax: (number) the price paid for this orderline, without taxes * tax: (number) the price paid in taxes on this orderline + * product_description: (string) generic description of the product + * product_description_sale: (string) sales related information of the product * } * - paymentlines : list of paymentlines : * { diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index a1f6def6bf3..93860697492 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -58,6 +58,21 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal // We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched, // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. + // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. + $.when(this.load_server_data()) + .then(function(){ + //self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging + self.ready.resolve(); + },function(){ + //we failed to load some backend data, or the backend was badly configured. + //the error messages will be displayed in PosWidget + self.ready.reject(); + }); + }, + + // loads all the needed data on the sever. returns a deferred indicating when all the data has loaded. + load_server_data: function(){ + var self = this; var loaded = fetch('res.users',['name','company_id'],[['id','=',this.session.uid]]) .pipe(function(users){ @@ -142,8 +157,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal return fetch( 'product.product', - ['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type'], - [['pos_categ_id','!=', false]], + ['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', + 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'], + [['pos_categ_id','!=', false],['sale_ok','=',true]], {pricelist: self.get('shop').pricelist_id[0]} // context for price ); }).pipe(function(products){ @@ -173,17 +189,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal } self.set({'cashRegisters' : new module.CashRegisterCollection(self.get('bank_statements'))}); }); - - // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. - $.when(loaded) - .then(function(){ - //self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging - self.ready.resolve(); - },function(){ - //we failed to load some backend data, or the backend was badly configured. - //the error messages will be displayed in PosWidget - self.ready.reject(); - }); + + return loaded; }, // logs the usefull posmodel data to the console for debug purposes @@ -417,6 +424,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal price_with_tax : this.get_price_with_tax(), price_without_tax: this.get_price_without_tax(), tax: this.get_tax(), + product_description: this.get_product().get('description'), + product_description_sale: this.get_product().get('description_sale'), }; }, get_price_without_tax: function(){ diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 4d4e09575ea..9a2071c6335 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -735,11 +735,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa this.validate_button = this.add_action_button({ label: 'Validate', + name: 'validation', icon: '/point_of_sale/static/src/img/icons/png48/validate.png', click: function(){ self.validateCurrentOrder(); }, }); + + this.updatePaymentSummary(); }, close: function(){ this._super(); @@ -806,6 +809,9 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa this.$('#payment-paid-total').html(paidTotal.toFixed(2)); this.$('#payment-remaining').html(remaining.toFixed(2)); this.$('#payment-change').html(change.toFixed(2)); + if(this.pos_widget.action_bar){ + this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0); + } }, set_numpad_state: function(numpadState) { if (this.numpadState) { diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index 5b98865a971..6de31c88dfd 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -370,14 +370,21 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.label = options.label || 'button'; this.rightalign = options.rightalign || false; this.click_action = options.click; + this.disabled = options.disabled || false; if(options.icon){ this.icon = options.icon; this.template = this.icon_template; } }, + set_disabled: function(disabled){ + if(this.disabled != disabled){ + this.disabled = !!disabled; + this.renderElement(); + } + }, renderElement: function(){ this._super(); - if(this.click_action){ + if(this.click_action && !this.disabled){ this.$el.click(_.bind(this.click_action, this)); } }, @@ -388,12 +395,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa init: function(parent, options){ this._super(parent,options); this.button_list = []; - this.fake_buttons = {}; + this.buttons = {}; this.visibility = {}; }, set_element_visible: function(element, visible, action){ if(visible != this.visibility[element]){ - this.visibility[element] = visible; + this.visibility[element] = !!visible; if(visible){ this.$('.'+element).show(); }else{ @@ -401,14 +408,22 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa } } if(visible && action){ + this.action[element] = action; this.$('.'+element).off('click').click(action); } }, + set_button_disabled: function(name, disabled){ + var b = this.buttons[name]; + if(b){ + b.set_disabled(disabled); + } + }, destroy_buttons:function(){ for(var i = 0; i < this.button_list.length; i++){ this.button_list[i].destroy(); } this.button_list = []; + this.buttons = {}; return this; }, get_button_count: function(){ @@ -417,6 +432,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa add_new_button: function(button_options){ var button = new module.ActionButtonWidget(this,button_options); this.button_list.push(button); + if(button_options.name){ + this.buttons[button_options.name] = button; + } button.appendTo(this.$('.pos-actionbar-button-list')); return button; }, diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 81f929d50e2..bbe278758d5 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -540,7 +540,7 @@ </t> <t t-name="ActionButtonWidget"> - <li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '')"> + <li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '') + (widget.disabled ? 'disabled ' : '')"> <div class='label'> <t t-esc="widget.label" /> </div> @@ -548,7 +548,7 @@ </t> <t t-name="ActionButtonWidgetWithIcon"> - <li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '')"> + <li t-att-class=" 'button '+ (widget.rightalign ? 'rightalign ' : '') + (widget.disabled ? 'disabled ' : '')"> <div class='icon'> <img t-att-src="widget.icon" /> <div class='iconlabel'><t t-esc="widget.label" /></div> diff --git a/addons/point_of_sale/wizard/pos_box.py b/addons/point_of_sale/wizard/pos_box.py index 8c3b0aa4607..2d1c1a8e0cf 100644 --- a/addons/point_of_sale/wizard/pos_box.py +++ b/addons/point_of_sale/wizard/pos_box.py @@ -15,8 +15,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, context.get('active_ids', []) or [], context=context) - + records = self.pool.get(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: @@ -30,6 +29,30 @@ class PosBox(CashBox): class PosBoxIn(PosBox): _inherit = 'cash.box.in' + def _compute_values_for_statement_line(self, cr, uid, box, record, context=None): + values = super(PosBoxIn, self)._compute_values_for_statement_line(cr, uid, box, record, context=context) + + active_model = context.get('active_model', False) or False + 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] + values['ref'] = session.name + + return values + + class PosBoxOut(PosBox): _inherit = 'cash.box.out' + def _compute_values_for_statement_line(self, cr, uid, box, record, context=None): + values = super(PosBoxOut, self)._compute_values_for_statement_line(cr, uid, box, record, context=context) + + active_model = context.get('active_model', False) or False + 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] + values['ref'] = session.name + + return values diff --git a/addons/point_of_sale/wizard/pos_session_opening.py b/addons/point_of_sale/wizard/pos_session_opening.py index 4364998b6a4..d6f95cbb7fc 100644 --- a/addons/point_of_sale/wizard/pos_session_opening.py +++ b/addons/point_of_sale/wizard/pos_session_opening.py @@ -6,14 +6,18 @@ import netsvc from openerp.addons.point_of_sale.point_of_sale import pos_session + class pos_session_opening(osv.osv_memory): _name = 'pos.session.opening' _columns = { 'pos_config_id' : fields.many2one('pos.config', 'Point of Sale', required=True), 'pos_session_id' : fields.many2one('pos.session', 'PoS Session'), - 'pos_state' : fields.selection(pos_session.POS_SESSION_STATE, - 'Session State', readonly=True), + 'pos_state' : fields.related('pos_session_id', 'state', + type='selection', + selection=pos_session.POS_SESSION_STATE, + string='Session State', readonly=True), + 'pos_state_str' : fields.char('State', 32, readonly=True), 'show_config' : fields.boolean('Show Config', readonly=True), 'pos_session_name' : fields.related('pos_session_id', 'name', type='char', size=64, readonly=True), @@ -71,22 +75,25 @@ class pos_session_opening(osv.osv_memory): result = { 'pos_session_id': False, 'pos_state': False, + 'pos_state_str' : '', 'pos_session_username' : False, 'pos_session_name' : False, } if not config_id: - return {'value': result} + return {'value' : result} proxy = self.pool.get('pos.session') session_ids = proxy.search(cr, uid, [ - ('state', '<>', 'closed'), + ('state', '!=', 'closed'), ('config_id', '=', config_id), ], context=context) if session_ids: session = proxy.browse(cr, uid, session_ids[0], context=context) - result['pos_state'] = session.state + result['pos_state'] = str(session.state) + result['pos_state_str'] = dict(pos_session.POS_SESSION_STATE).get(session.state, '') result['pos_session_id'] = session.id result['pos_session_name'] = session.name result['pos_session_username'] = session.user_id.name + return {'value' : result} def default_get(self, cr, uid, fieldnames, context=None): diff --git a/addons/point_of_sale/wizard/pos_session_opening.xml b/addons/point_of_sale/wizard/pos_session_opening.xml index 9279d5fce45..6e44030e582 100644 --- a/addons/point_of_sale/wizard/pos_session_opening.xml +++ b/addons/point_of_sale/wizard/pos_session_opening.xml @@ -12,7 +12,7 @@ <field name="pos_config_id" on_change="on_change_config(pos_config_id)" widget="selection" domain="[('state','=','active')]" class="oe_inline"/> - <field name="pos_state" class="oe_inline" attrs="{'invisible' : [('pos_state', '=', False)]}" /> + <field name="pos_state" invisible="1" /> </group> <field name="pos_session_id" invisible="1"/> <button name="open_ui" type="object" string="Start Selling" @@ -49,8 +49,8 @@ Click to continue the session. </p> <p> - The session <field name="pos_session_name" class="oe_inline" /> (<field name="pos_session_username" class="oe_inline" />) is "<b><field name="pos_state" class="oe_inline" /></b>"<br/> - You can continue sales from the touchscreen interface by clicking on "<b>Settings</b>" or close the cash register session ? + The session <field name="pos_session_name" class="oe_inline" /> (<field name="pos_session_username" class="oe_inline" />) is "<b><field name="pos_state_str" class="oe_inline" /></b>"<br/> + You can continue sales from the touchscreen interface by clicking on "<b>Sellings</b>" or close the cash register session ? </p> </div> diff --git a/addons/portal/portal.py b/addons/portal/portal.py index 2c8b2db587d..3672d87cf30 100644 --- a/addons/portal/portal.py +++ b/addons/portal/portal.py @@ -37,7 +37,7 @@ class portal(osv.osv): 'group_id': fields.many2one('res.groups', required=True, ondelete='cascade', string='Group', help='The group corresponding to this portal'), - 'url': fields.char('URL', size=64, + 'url': fields.char('URL', help="The url where portal users can connect to the server"), 'home_action_id': fields.many2one('ir.actions.actions', string='Home Action', diff --git a/addons/portal/portal_demo.xml b/addons/portal/portal_demo.xml index a07709cd0d5..1ace02f3d7d 100644 --- a/addons/portal/portal_demo.xml +++ b/addons/portal/portal_demo.xml @@ -22,8 +22,7 @@ <field name="subject">Our first company's blogpost !</field> <field name="model">mail.group</field> <field name="res_id" ref="company_news_feed"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Hello, and welcome to our company's portal ! + <field name="body"><![CDATA[Hello, and welcome to our company's portal ! Lorem ipsum <b>sit amet</b>, consectetur <em>adipiscing elit</em>. Pellentesque et quam sapien, in sagittis tellus. Praesent vel massa sed massa consequat egestas in tristique orci. Praesent iaculis libero et neque vehicula iaculis. Vivamus placerat tincidunt orci ac ornare. Proin ut dolor fringilla velit ultricies consequat. Maecenas sit amet ipsum non leo interdum imperdiet. Donec sapien mi. @@ -32,27 +31,25 @@ Fusce tempus elit volutpat mi auctor adipiscing. Nam congue luctus suscipit. Sed Nulla turpis leo, rhoncus ut egestas sit amet, consectetur vitae urna. Mauris in dolor in sapien tempus vehicula.]]></field> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_root"/> </record> <record id="message_company_news0_comment0" model="mail.message"> <field name="model">mail.group</field> <field name="res_id" ref="company_news_feed"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Great first blogpost ! (first comment)]]></field> + <field name="body"><![CDATA[Great first blogpost ! (first comment)]]></field> <field name="parent_id" ref="message_company_news0"/> <field name="type">comment</field> - <field name="user_id" ref="base.user_demo"/> + <field name="author_id" ref="base.res_partner_1"/> </record> <record id="message_company_news0_comment1" model="mail.message"> <field name="model">mail.group</field> <field name="res_id" ref="company_news_feed"/> - <field name="content_subtype">html</field> - <field name="body_html"><![CDATA[Thanks ! (second comment)]]></field> + <field name="body"><![CDATA[Thanks ! (second comment)]]></field> <field name="parent_id" ref="message_company_news0"/> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_demo"/> </record> </data> diff --git a/addons/portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py index 0260ce338fc..85f6566eba6 100644 --- a/addons/portal/wizard/portal_wizard.py +++ b/addons/portal/wizard/portal_wizard.py @@ -171,7 +171,7 @@ class wizard(osv.osv_memory): 'url': wiz.portal_id.url or _("(missing url)"), 'db': cr.dbname, } - mail_message_obj = self.pool.get('mail.message') + mail_mail_obj = self.pool.get('mail.mail') dest_uids = user_obj.search(cr, ROOT_UID, login_cond) dest_users = user_obj.browse(cr, ROOT_UID, dest_uids) for dest_user in dest_users: @@ -184,10 +184,12 @@ class wizard(osv.osv_memory): email_to = dest_user.email subject = _(WELCOME_EMAIL_SUBJECT) % data body = _(WELCOME_EMAIL_BODY) % data - res = mail_message_obj.schedule_with_attach(cr, uid, email_from , [email_to], subject, body, context=context) - if not res: - _logger.warning( - 'Failed to send email from %s to %s', email_from, email_to) + mail_id = mail_mail_obj.create(cr, uid, { + 'email_from': email_from , + 'email_to': email_to, + 'subject': subject, + 'state': 'outgoing', + 'body_html': '<pre>%s</pre>' % body}, context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/portal_event/event_view.xml b/addons/portal_event/event_view.xml index 1dd9fee7181..675bddbc42e 100644 --- a/addons/portal_event/event_view.xml +++ b/addons/portal_event/event_view.xml @@ -3,7 +3,7 @@ <data> <!-- add visibility field to the event form view --> - <record id="view_employee_form" model="ir.ui.view"> + <record id="view_event_form_portal" model="ir.ui.view"> <field name="name">portal.event.form</field> <field name="model">event.event</field> <field name="inherit_id" ref="event.view_event_form"/> @@ -17,6 +17,5 @@ </xpath> </field> </record> - </data> </openerp> diff --git a/addons/portal_hr_employees/hr_employee_view.xml b/addons/portal_hr_employees/hr_employee_view.xml index dc1b1bae56c..4e133061da6 100644 --- a/addons/portal_hr_employees/hr_employee_view.xml +++ b/addons/portal_hr_employees/hr_employee_view.xml @@ -8,12 +8,8 @@ <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> - <xpath expr="//page[last()]" position="after"> - <page string="Portal Settings"> - <group> - <field name="visibility"/> - </group> - </page> + <xpath expr="//field[@name='active']" position="before"> + <field name="visibility"/> </xpath> </field> </record> diff --git a/addons/portal_hr_employees/static/src/css/portal_hr_employees.css b/addons/portal_hr_employees/static/src/css/portal_hr_employees.css index 351fd442f20..5fa658de8c1 100644 --- a/addons/portal_hr_employees/static/src/css/portal_hr_employees.css +++ b/addons/portal_hr_employees/static/src/css/portal_hr_employees.css @@ -12,3 +12,9 @@ width: 65px; height: 65px; } + +.openerp .oe_employee_vignette ul, .openerp .oe_employee_vignette li { + margin: 0; + padding: 0; + list-style: none; +} diff --git a/addons/process/__openerp__.py b/addons/process/__openerp__.py index ef554fe7226..f0ece3704dc 100644 --- a/addons/process/__openerp__.py +++ b/addons/process/__openerp__.py @@ -45,5 +45,15 @@ This module shows the basic processes involved in the selected modules and in th 'auto_install': False, 'certificate': '0055447636669', 'images': ['images/process_nodes.jpeg','images/process_transitions.jpeg', 'images/processes.jpeg'], + 'js': [ + 'static/src/js/process.js' + ], + 'css': [ + 'static/src/css/process.css' + ], + 'qweb': [ + 'static/src/xml/*.xml' + ], + 'auto_install': True } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/process/process.py b/addons/process/process.py index f9744608c50..fa7596287a3 100644 --- a/addons/process/process.py +++ b/addons/process/process.py @@ -195,8 +195,8 @@ class process_process(osv.osv): if nodes[nid].get('directory_id', False): resource['directory'] = self.pool.get('document.directory').get_resource_path(cr, uid, nodes[nid]['directory_id'], ref_model, ref_id) - resource['name'] = refobj.name_get(context)[0][1] - resource['perm'] = pool.get(ref_model).perm_read(cr, uid, [ref_id], context)[0] + resource['name'] = pool.get(ref_model).name_get(cr, uid, [ref_id], context=context)[0][1] + resource['perm'] = pool.get(ref_model).perm_read(cr, uid, [ref_id], context=context)[0] for r in relatives: node = nodes[r] diff --git a/addons/process/static/src/css/process.css b/addons/process/static/src/css/process.css new file mode 100644 index 00000000000..7442db9bc4a --- /dev/null +++ b/addons/process/static/src/css/process.css @@ -0,0 +1,70 @@ + +a.cta-a { + float: left; + padding: 5px 10px; + border: 1px solid #ccc; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + background: #eeeded url(/process/static/src/img/cta-a.gif) repeat-x; + box-shadow: 0 1px 0 #fff; + -moz-box-shadow: 0 1px 0 #fff; + -webkit-box-shadow: 0 1px 0 #fff; + color: #8c8c8c; + font-size: 0.9em; + text-transform: uppercase; + font-weight: bold; + text-shadow: #fff 0 1px 0; + margin: 2px; +} + +a.cta-a span { + float: left; + padding: 7px 0 5px 5px; + background-position: 0 50%; + background-repeat: no-repeat; + cursor: pointer; +} + +a.cta-a strong { + display: block; + color: #393939; +} +.process-links { + padding: 5px 10px; + text-align: center; + display: table; + margin: auto; +} +.process-links a.cta-a { + display: table-cell; +} +.process_canvas{ + border: 1px solid #aaa; + background-color:#FFF; +} +.process_canvas svg{ + height:500px;!important; + padding:15px; +} +.oe_process { + height: 20px; + margin-top:3px; + padding: 0; + width: 24px; + cursor: pointer; + display: block; + background: url(/web/static/src/img/iconset-a-help.png) no-repeat center center; + float: left; +} + +.process-help-text { + float: left; + padding:5px 10px; + min-height:56px; + font-size: 120%; +} + +td.process_fields,button.toggle_fields span:last-child { + display: none; +} diff --git a/addons/process/static/src/img/button-a-a.png b/addons/process/static/src/img/button-a-a.png Binary files differnew file mode 100644 index 00000000000..bd046bbbef7 --- /dev/null +++ b/addons/process/static/src/img/button-a-a.png diff --git a/addons/process/static/src/img/cta-a.gif b/addons/process/static/src/img/cta-a.gif Binary files differnew file mode 100644 index 00000000000..daa561e29f5 --- /dev/null +++ b/addons/process/static/src/img/cta-a.gif diff --git a/addons/process/static/src/img/iconset-a-help.gif b/addons/process/static/src/img/iconset-a-help.gif Binary files differnew file mode 100644 index 00000000000..5400acec57b --- /dev/null +++ b/addons/process/static/src/img/iconset-a-help.gif diff --git a/addons/process/static/src/img/node-current.png b/addons/process/static/src/img/node-current.png Binary files differnew file mode 100644 index 00000000000..51f86b04c78 --- /dev/null +++ b/addons/process/static/src/img/node-current.png diff --git a/addons/process/static/src/img/node-gray.png b/addons/process/static/src/img/node-gray.png Binary files differnew file mode 100644 index 00000000000..972013540cf --- /dev/null +++ b/addons/process/static/src/img/node-gray.png diff --git a/addons/process/static/src/img/node-subflow-gray.png b/addons/process/static/src/img/node-subflow-gray.png Binary files differnew file mode 100644 index 00000000000..ffafe1bd681 --- /dev/null +++ b/addons/process/static/src/img/node-subflow-gray.png diff --git a/addons/process/static/src/img/node-subflow.png b/addons/process/static/src/img/node-subflow.png Binary files differnew file mode 100644 index 00000000000..fd2a480204d --- /dev/null +++ b/addons/process/static/src/img/node-subflow.png diff --git a/addons/process/static/src/img/node.png b/addons/process/static/src/img/node.png Binary files differnew file mode 100644 index 00000000000..853ac85cd56 --- /dev/null +++ b/addons/process/static/src/img/node.png diff --git a/addons/process/static/src/js/process.js b/addons/process/static/src/js/process.js new file mode 100644 index 00000000000..bbf9080de95 --- /dev/null +++ b/addons/process/static/src/js/process.js @@ -0,0 +1,249 @@ +openerp.process = function (instance) { +var QWeb = instance.web.qweb, + _t = instance.web._t; +instance.web.ViewManager.include({ + start: function() { + var self = this; + var _super = this._super(); + this.process_help = this.action ? this.action.help : ''; + self.process_help = $(this.process_help).text(); + if(this.action) { + this.process_model = this.action.res_model; + } else { + this.process_model = this.dataset.model; + } + this.$el.on('click', '.oe_process', function(ev) { self.initialize_process_view(ev);}); + return _super; + }, + initialize_process_view: function(ev) { + var self = this; + this.record_id = false; + if(this.active_view == 'form') { + this.record_id = this.views[this.active_view].controller.datarecord.id; + } + this.process_get_object().pipe(function(process) { + if(process && process.length) { + if(process.length > 1) { + self.process_selection = process; + } else { + self.process_id = process[0][0], + self.process_title = process[0][1]; + } + } + return $.Deferred().resolve(); + }).pipe(function() { + var def = $.Deferred(); + if(self.process_id) { + $.when(self.process_graph_get()).done(function(res) { + self.process_notes = res.notes; + self.process_title = res.name; + self.process_subflows = _(res.nodes).chain() + .filter(function (node) { return node['subflow'] !== false; }) + .uniq(false, function (node) { return node['subflow'][0]; }).value(); + self.process_related = res.related; + def.resolve(res); + }); + } else def.resolve(); + return def.promise(); + }).done(function(res) { + $.when(self.process_render_view()).done(function() { + if(res) { + self.process_draw_graph(res); + } + }); + }); + }, + process_graph_get: function() { + var self = this; + var def = $.Deferred(); + this.process_id = parseInt(this.process_id, 10); + this.process_dataset + .call("graph_get",[self.process_id, self.model || self.dataset.model, self.record_id, [80,80,150,100], self.session.user_context]) + .done(function(res) { + self.process_dataset + .call("search_by_model",[self.model || self.dataset.model, self.session.user_context]) + .done( + function(r) { + res['related'] = r; + def.resolve(res); + }); + }); + return def.promise(); + }, + process_get_object : function() { + var self = this, + def = $.Deferred(); + if(this.process_id) + return def.resolve().promise(); + this.process_dataset = new instance.web.DataSet(self, "process.process", self.session.user_context); + this.process_dataset + .call("search_by_model", [self.process_model,self.session.user_context]) + .done(function(res) { + if (!res.length) { + self.process_model = false; + self.process_get_object().done(def.resolve); + } + else { + def.resolve(res); + } + }) + .fail(def.reject); + return def.promise(); + }, + process_render_view : function() { + var self = this; + this.$el.html(QWeb.render("process.ProcessView", this)); + this.$el.addClass('oe_view_process').css({'background-color':'#F0EEEE'}); + this.$el.find('#edit_process').click(function() { + self.process_edit_view(); + }); + var $parent = this.getParent().$el; + $parent.find('#change_process').click(function() { + self.process_selection = false, + self.process_id = $parent.find('#select_process').val(), + self.process_title = $.trim($parent.find('#select_process option:selected').text()); + self.initialize_process_view(); + }); + this.$el.find(".process_subflow").click(function() { + self.process_id = this.id; + self.initialize_process_view(); + }); + }, + process_draw_graph : function(result) { + var self = this; + var res_nodes = result['nodes']; + var res_edges = result['transitions']; + var id_to_node = {}; + var canvas = $('div.process_canvas').empty().get(0); + var style = { + edge_color: "#A0A0A0", + edge_label_font_size: 10, + edge_width: 2, + edge_spacing: 40, + edge_loop_radius: 200, + + node_label_color: "#333", + node_label_font_size: 12, + node_outline_color: "#333", + node_outline_width: 1, + node_outline_color: "#F5F5F5", + node_outline_width: 0, + node_selected_width: 0, + node_size_x: 150, + node_size_y: 100, + + gray: "#DCDCDC", + white: "#FFF", + viewport_margin: 50 + }; + var r = new Raphael(canvas,'100%','100%'); + var graph = new CuteGraph(r,style,canvas.parentNode); + var render_process = function(r,nodes){ + //For Image + var image_node = nodes.kind == "subflow" ? "node-subflow" : "node"; + image_node = nodes.gray ? image_node + "-gray" : image_node; + image_node = nodes.active ? 'node-current': image_node; + var img_src = '/web_process/static/src/img/'+ image_node + '.png'; + var image = r['image'](img_src, nodes.x-25, nodes.y,150, 100).attr({"cursor": "default"}) .mousedown(function() { return false; }); + //For Node + var process_node = r['rect'](nodes.x, nodes.y, 150, 150).attr({stroke: "none"}); + // Node text + if(nodes.name.length > 18){ + var text = nodes.name.substr(0,16) + '...' + } + var node_text = r.text(nodes.x+60, nodes.y+10,(text || nodes.name.substr(0,18))).attr({"fill": "#fff","font-weight": "bold", "cursor": "default","title":nodes.name}); + //Node Description + var new_notes = nodes.notes; + if(nodes.notes.length > 25) { + var to; + var temp_str = new_notes = ''; + var from = to = 0; + while (1) { + from = 25; + temp_str = nodes.notes.substr(to, 25); + if (temp_str.lastIndexOf(" ") < 25 && temp_str.length >= 25) { + from = temp_str.lastIndexOf(" "); + } + new_notes += "\n" + nodes.notes.substr(to, from); + if (new_notes.length > 80){ + break; + } + to += from; + } + } + if(nodes.res) + new_notes = nodes.res.name + '\n' + new_notes; + if(nodes.notes.length > 60){ + var notes = new_notes.substring(0,60) +'..'; + } + r.text(nodes.x+60, nodes.y+30, (notes || new_notes)).attr({"title":nodes.notes,"cursor": "default"}); + if(nodes.menu) { + r['image']('/web/static/src/img/icons/gtk-jump-to.png', nodes.x+100, nodes.y+75, 16, 16) + .attr({"cursor": "pointer", "title": nodes.menu.name}) + .click(function() { + self.process_jump_to_view(nodes.res_model, nodes.menu.id); + }); + } + var process_set =r.set().push(process_node); + process_set.mousedown(function() { + return false; + }); + return process_set; + } + _.each(res_nodes, function(node,id) { + node['res_model'] = self.model, + node['res_id'] = false, + node['color'] = 'gray' + var n = new CuteNode( + graph, + node.x + 50, //FIXME the +50 should be in the layout algorithm + node.y + 50, + CuteGraph.wordwrap("", 14)); + n.id = id; + id_to_node[id] = n; + return render_process(r,node); + }); + _.each(res_edges, function(edge) { + var e = new CuteEdge( + graph, + CuteGraph.wordwrap(" ",0), + id_to_node[edge.source], + id_to_node[edge.target] || id_to_node[edge.source]); + e.id = edge.id; + }); + }, + process_jump_to_view: function(model,id) { + var self = this; + var dataset = new instance.web.DataSet(this, 'ir.values', this.session.user_context); + var action_manager = new instance.web.ActionManager(self); + dataset.call('get', + ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context], + function(res) { + var action = res[0][res[0].length - 1]; + self.rpc("/web/action/load", { + action_id: action.id, + context: dataset.context + }, function(result) { + action_manager.replace(self.$el); + action_manager.do_action(result.result); + }) + }); + }, + process_edit_view: function() { + var self = this; + var pop = new instance.web.form.FormOpenPopup(self); + pop.show_element( + self.process_dataset.model, + self.process_id, + self.context || self.dataset.context, + { + title: _t('Process') + }); + var form_controller = pop.view_form; + pop.on_write_completed.add_last(function() { + self.initialize_process_view(); + }); + } +}); +}; +// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/process/static/src/xml/process.xml b/addons/process/static/src/xml/process.xml new file mode 100644 index 00000000000..b1a39aa6ce8 --- /dev/null +++ b/addons/process/static/src/xml/process.xml @@ -0,0 +1,93 @@ +<template> +<!-- vim:fdl=1: +--> +<t t-extend="ViewManagerAction"> + <t t-jquery="h2.oe_view_title" t-operation="after"> + <t t-if="widget.session.debug && widget.flags.display_title !== false"> + <a class="oe_process" title="Process View" href="javascript: void(0)"></a> + </t> + </t> +</t> +<t t-name="process.ProcessView"> + <table class="view" border="0" width="auto" height="auto" cellpadding="0" cellspacing="0"> + <tr> + <td width="70%" valign="top" class="fields collapsed"> + <div class="oe_view_manager oe_view_manager_current"> + <div class="oe_view_manager_header" style="padding: 8px;"> + <div class="oe_header_row"> + <h2 class="oe_view_title"> + <span class="oe_view_title_text oe_breadcrumb_title"><t t-esc="action.name"/> (<t t-esc="action.res_model"/>)</span> + </h2> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td style="padding-left:10px;"> + <p><t t-esc="process_help"/></p> + </td> + </tr> + <tr> + <td style="padding-left:10px;"> + <h2> <t t-esc="process_title"/> Process </h2> + <t t-if="process_notes"> + <p> + <strong>Notes:</strong> <t t-esc="process_notes"/> + </p> + <p> + <strong>Last modified by:</strong> N/A + </p> + <t t-if="process_subflows.length"> + <strong>Subflows:</strong> + <t t-foreach="process_subflows" t-as="subflow"> + <t t-if="subflow.subflow[0] != process_id"> + <p> + <a class="oe_bold process_subflow" t-att-id="subflow.subflow[0]" href="javascript: void(0)"> + <t t-esc="subflow.subflow[1]"/> + </a> + </p> + </t> + </t> + </t> + <t t-if="process_related.length and process_related[0][0] != process_id"> + <strong>Related:</strong> + <p> + <a class="oe_bold process_subflow" t-att-id="process_related[0][0]" href="javascript: void(0)"> + <t t-esc="process_related[0][1]"/> + </a> + </p> + </t> + </t> + </td> + </tr> + <tr t-if="process_selection"> + <td style="padding-left:10px;"> + <fieldset> + <legend> + Select Process + </legend> + <select id="select_process"> + <t t-foreach="process_selection" t-as="prc"> + <option t-att-value="prc[0]"> + <t t-esc="prc[1]"/> + </option> + </t> + </select> + <button class="oe_button oe_highlight" id="change_process">Select</button> + </fieldset> + </td> + </tr> + <tr t-if="process_id"> + <td> + <div class="process_canvas"></div> + </td> + </tr> + <tr t-if="process_id"> + <td style="padding-left:10px;"> + <button class="oe_button oe_highlight" id="edit_process">Edit Process</button> + </td> + </tr> + </table> +</t> +</template> diff --git a/addons/procurement/board_mrp_procurement_view.xml b/addons/procurement/board_mrp_procurement_view.xml index a01ec168efa..f30ea629d5e 100644 --- a/addons/procurement/board_mrp_procurement_view.xml +++ b/addons/procurement/board_mrp_procurement_view.xml @@ -15,9 +15,9 @@ <field name="model">board.board</field> <field name="inherit_id" ref="stock.board_warehouse_form"/> <field name="arch" type="xml"> - <xpath expr="/form/board/column/action[@string='Incoming Shipments']" position="before"> + <xpath expr="//column" position="inside"> <action name="%(procurement_action_board)d" string="Procurements in Exception"/> - </xpath> + </xpath> </field> </record> </data> diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index d1fb3f8a395..ec1dc4b26fb 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -80,7 +80,7 @@ class procurement_order(osv.osv): """ _name = "procurement.order" _description = "Procurement" - _order = 'priority,date_planned desc' + _order = 'priority desc,date_planned' _inherit = ['mail.thread'] _log_create = False _columns = { @@ -289,14 +289,14 @@ class procurement_order(osv.osv): return False if not procurement.product_id.seller_ids: message = _('No supplier defined for this product !') - self.message_append_note(cr, uid, [procurement.id], body=message) + self.message_post(cr, uid, [procurement.id], body=message) cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id)) return False partner = procurement.product_id.seller_id #Taken Main Supplier of Product of Procurement. if not partner: message = _('No default supplier defined for this product') - self.message_append_note(cr, uid, [procurement.id], body=message) + self.message_post(cr, uid, [procurement.id], body=message) cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id)) return False if user.company_id and user.company_id.partner_id: @@ -306,7 +306,7 @@ class procurement_order(osv.osv): address_id = partner_obj.address_get(cr, uid, [partner.id], ['delivery'])['delivery'] if not address_id: message = _('No address defined for the supplier') - self.message_append_note(cr, uid, [procurement.id], body=message) + self.message_post(cr, uid, [procurement.id], body=message) cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id)) return False return True @@ -319,13 +319,13 @@ class procurement_order(osv.osv): if record.move_id and record.move_id.state == 'cancel': return True return False - + #Initialize get_phantom_bom_id method as it is raising an error from yml of mrp_jit #when one install first mrp and after that, mrp_jit. get_phantom_bom_id defined in mrp module #which is not dependent for mrp_jit. def get_phantom_bom_id(self, cr, uid, ids, context=None): return False - + def action_confirm(self, cr, uid, ids, context=None): """ Confirms procurement and writes exception message if any. @return: True @@ -365,7 +365,7 @@ class procurement_order(osv.osv): message = _('From stock: products assigned.') self.write(cr, uid, ids, {'state': 'running', 'message': message}, context=context) - self.message_append_note(cr, uid, ids, body=message, context=context) + self.message_post(cr, uid, ids, body=message, context=context) self.running_send_note(cr, uid, ids, context=context) return True @@ -389,7 +389,7 @@ class procurement_order(osv.osv): ok = ok and self.pool.get('stock.move').action_assign(cr, uid, [id]) order_point_id = self.pool.get('stock.warehouse.orderpoint').search(cr, uid, [('product_id', '=', procurement.product_id.id)], context=context) if not order_point_id and not ok: - message = _("Not enough stock and no minimum orderpoint rule defined.") + message = _("Not enough stock and no minimum orderpoint rule defined.") elif not order_point_id: message = _("No minimum orderpoint rule defined.") elif not ok: @@ -398,7 +398,7 @@ class procurement_order(osv.osv): if message: message = _("Procurement '%s' is in exception: ") % (procurement.name) + message cr.execute('update procurement_order set message=%s where id=%s', (message, procurement.id)) - self.message_append_note(cr, uid, [procurement.id], body=message, context=context) + self.message_post(cr, uid, [procurement.id], body=message, context=context) return ok def action_produce_assign_service(self, cr, uid, ids, context=None): @@ -496,22 +496,22 @@ class procurement_order(osv.osv): return obj_id def create_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been <b>created</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been <b>created</b>."), context=context) def confirm_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been <b>confirmed</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been <b>confirmed</b>."), context=context) def running_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been set to <b>running</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been set to <b>running</b>."), context=context) def ready_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been set to <b>ready</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been set to <b>ready</b>."), context=context) def cancel_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been <b>cancelled</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been <b>cancelled</b>."), context=context) def done_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Procurement has been <b>done</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Procurement has been <b>done</b>."), context=context) procurement_order() diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index 7a87697f5c8..1c8ac5934fc 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -54,7 +54,6 @@ class procurement_order(osv.osv): ''' if context is None: context = {} - try: if use_new_cursor: cr = pooler.get_db(use_new_cursor).cursor() @@ -119,19 +118,6 @@ class procurement_order(osv.osv): offset += len(ids) if not ids: break end_date = fields.datetime.now() - if uid: - # Chatter: old res.request is now a chatter on res.users, id=uid - summary = _("""Here is the procurement scheduling report. - - Start Time: %s - End Time: %s - Total Procurements processed: %d - Procurements with exceptions: %d - Skipped Procurements (scheduled date outside of scheduler range) %d - - Exceptions:\n""") % (start_date, end_date, report_total, report_except, report_later) - summary += '\n'.join(report) - procurement_obj.message_append_note(cr, uid, ids, body=summary, context=context) if use_new_cursor: cr.commit() diff --git a/addons/procurement/wizard/make_procurement_view.xml b/addons/procurement/wizard/make_procurement_view.xml index 4d2e7553371..9228fcc323b 100644 --- a/addons/procurement/wizard/make_procurement_view.xml +++ b/addons/procurement/wizard/make_procurement_view.xml @@ -9,13 +9,21 @@ <field name="model">make.procurement</field> <field name="arch" type="xml"> <form string="Procurement Request" version="7.0"> - <group col="4" string="This wizard will plan the procurement for this product. This procurement may generate task, production orders or purchase orders."/> - <group> + <p class="oe_gray"> + Fill is this for to launch a procurement request for this + product. According to the product configuration, this may + trigger a draft purchase order, a manufacturing order or + a new task. + </p> + <group> + <field name="warehouse_id" groups="stock.group_locations"/> <field name="product_id" on_change="onchange_product_id(product_id)"/> - <field name="qty"/> - <field name="uom_id" groups="product.group_uom"/> + <label for="qty"/> + <div> + <field name="qty" class="oe_inline"/> + <field name="uom_id" groups="product.group_uom"/> + </div> <field name="date_planned"/> - <field name="warehouse_id" groups="stock.group_locations"/> </group> <footer> <button name="make_procurement" string="Ask New Products" type="object" class="oe_highlight" /> @@ -26,13 +34,13 @@ </field> </record> - <act_window name="Procurement Request" - res_model="make.procurement" - src_model="product.product" - view_mode="form" - target="new" - key2="client_action_multi" - id="act_make_procurement"/> + <record model="ir.actions.act_window" id="act_make_procurement"> + <field name="name">Procurement Request</field> + <field name="res_model">make.procurement</field> + <field name="src_model">product.product</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> </data> </openerp> diff --git a/addons/product/pricelist_view.xml b/addons/product/pricelist_view.xml index adefd9e5d3c..dc120660c43 100644 --- a/addons/product/pricelist_view.xml +++ b/addons/product/pricelist_view.xml @@ -119,7 +119,7 @@ <field name="name" string="Products Price"/> <field name="type"/> <field name="active" /> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </search> </field> </record> @@ -132,7 +132,7 @@ <tree string="Products Price List"> <field name="name"/> <field name="type"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="active" /> </tree> </field> @@ -149,7 +149,7 @@ <group col="4"> <field name="active"/> <field name="type"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="company_id" groups="base.group_multi_company" widget="selection"/> </group> <field name="version_id"> @@ -230,7 +230,7 @@ <field name="name"/> <field name="active"/> <field name="field"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> </group> </form> </field> diff --git a/addons/product/product.py b/addons/product/product.py index 214445204bb..c9345a22103 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -288,13 +288,13 @@ class product_template(osv.osv): 'rental': fields.boolean('Can be Rent'), 'categ_id': fields.many2one('product.category','Category', required=True, change_default=True, domain="[('type','=','normal')]" ,help="Select category for the current product"), 'list_price': fields.float('Sale Price', digits_compute=dp.get_precision('Product Price'), help="Base price for computing the customer price. Sometimes called the catalog price."), - 'standard_price': fields.float('Cost Price', required=True, digits_compute=dp.get_precision('Product Price'), help="Product's cost for accounting stock valuation. It is the base price for the supplier price.", groups="base.group_user"), + 'standard_price': fields.float('Cost', digits_compute=dp.get_precision('Product Price'), help="Product's cost for accounting stock valuation. It is the base price for the supplier price.", groups="base.group_user"), 'volume': fields.float('Volume', help="The volume in m3."), 'weight': fields.float('Gross Weight', digits_compute=dp.get_precision('Stock Weight'), help="The gross weight in Kg."), 'weight_net': fields.float('Net Weight', digits_compute=dp.get_precision('Stock Weight'), help="The net weight in Kg."), 'cost_method': fields.selection([('standard','Standard Price'), ('average','Average Price')], 'Costing Method', required=True, help="Standard Price: the cost price is fixed and recomputed periodically (usually at the end of the year), Average Price: the cost price is recomputed at each reception of products."), - 'warranty': fields.float('Warranty (months)'), + 'warranty': fields.float('Warranty'), 'sale_ok': fields.boolean('Can be Sold', help="Determines if the product can be visible in the list of product within a selection from a sale order line."), 'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."), 'state': fields.selection([('',''), @@ -302,7 +302,7 @@ class product_template(osv.osv): ('sellable','Normal'), ('end','End of Lifecycle'), ('obsolete','Obsolete')], 'Status', help="Tells the user if he can use the product or not."), - 'uom_id': fields.many2one('product.uom', 'Default Unit of Measure', required=True, help="Default Unit of Measure used for all stock operation."), + 'uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True, help="Default Unit of Measure used for all stock operation."), 'uom_po_id': fields.many2one('product.uom', 'Purchase Unit of Measure', required=True, help="Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure."), 'uos_id' : fields.many2one('product.uom', 'Unit of Sale', help='Used by companies that manage two units of measure: invoicing and inventory management. For example, in food industries, you will manage a stock of ham but invoice in Kg. Keep empty to use the default Unit of Measure.'), @@ -354,7 +354,7 @@ class product_template(osv.osv): 'list_price': lambda *a: 1, 'cost_method': lambda *a: 'standard', 'supply_method': lambda *a: 'buy', - 'standard_price': lambda *a: 1, + 'standard_price': lambda *a: 0.0, 'sale_ok': lambda *a: 1, 'sale_delay': lambda *a: 7, 'produce_delay': lambda *a: 1, @@ -509,7 +509,7 @@ class product_product(osv.osv): 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) + result[obj.id] = tools.image_get_resized_images(obj.image, avoid_resize_medium=True) return result def _set_image(self, cr, uid, id, name, value, args, context=None): @@ -535,9 +535,9 @@ class product_product(osv.osv): 'outgoing_qty': fields.function(_product_outgoing_qty, type='float', string='Outgoing'), 'price': fields.function(_product_price, type='float', string='Pricelist', digits_compute=dp.get_precision('Product Price')), 'lst_price' : fields.function(_product_lst_price, type='float', string='Public Price', digits_compute=dp.get_precision('Product Price')), - 'code': fields.function(_product_code, type='char', string='Reference'), + 'code': fields.function(_product_code, type='char', string='Internal Reference'), 'partner_ref' : fields.function(_product_partner_ref, type='char', string='Customer ref'), - 'default_code' : fields.char('Reference', size=64, select=True), + 'default_code' : fields.char('Internal Reference', size=64, select=True), 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the product without removing it."), 'variants': fields.char('Variants', size=64), 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade"), @@ -548,25 +548,24 @@ class product_product(osv.osv): 'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'), 'name_template': fields.related('product_tmpl_id', 'name', string="Name", type='char', size=128, store=True, select=True), 'color': fields.integer('Color Index'), + # image: all image fields are base64 encoded and PIL-supported 'image': fields.binary("Image", - help="This field holds the image used for the product. "\ - "The image is base64 encoded, and PIL-supported. "\ - "It is limited to a 1024x1024 px image."), + help="This field holds the image used as image for the product, limited to 1024x1024px."), 'image_medium': fields.function(_get_image, fnct_inv=_set_image, string="Medium-sized image", type="binary", multi="_get_image", - store = { + store={ 'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Medium-sized image of the product. It is automatically "\ - "resized as a 180x180 px image, with aspect ratio preserved. "\ - "Use this field in form views or some kanban views."), + "resized as a 128x128px image, with aspect ratio preserved, "\ + "only when the image exceeds one of those sizes. Use this field in form views or some kanban views."), 'image_small': fields.function(_get_image, fnct_inv=_set_image, string="Small-sized image", type="binary", multi="_get_image", - store = { + store={ 'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10), }, help="Small-sized image of the product. It is automatically "\ - "resized as a 50x50 px image, with aspect ratio preserved. "\ + "resized as a 64x64px image, with aspect ratio preserved. "\ "Use this field anywhere a small image is required."), 'seller_info_id': fields.function(_calc_seller, type='many2one', relation="product.supplierinfo", multi="seller_info"), 'seller_delay': fields.function(_calc_seller, type='integer', string='Supplier Lead Time', multi="seller_info", help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."), @@ -580,7 +579,7 @@ class product_product(osv.osv): return obj_id def create_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Product has been <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Product has been <b>created</b>."), context=context) def unlink(self, cr, uid, ids, context=None): unlink_ids = [] diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index d341b5883d6..ff9172d3917 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -12,8 +12,6 @@ <filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/> <filter string="Products" icon="terp-accessories-archiver" domain="['|',('type','=','product'),('type','=','consu')]" help="Both stockable and consumable products"/> <separator/> - <filter string="To Sell" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/> - <separator/> <filter name="filter_to_purchase" string="To Purchase" icon="terp-accessories-archiver+" domain="[('purchase_ok', '=', 1)]"/> <filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/> <filter name="filter_to_purchase" string="Can be Purchased" icon="terp-accessories-archiver+" domain="[('purchase_ok', '=', 1)]"/> @@ -41,6 +39,7 @@ <field name="default_code"/> <field name="name"/> <field name="categ_id" invisible="1"/> + <field name="type" invisible="1"/> <field name="variants" groups="product.group_product_variant"/> <field name="uom_id" string="Unit of Measure" groups="product.group_uom"/> <field name="qty_available"/> @@ -71,6 +70,12 @@ </h1> <label for="categ_id" class="oe_edit_only"/> <h2><field name="categ_id"/></h2> + <div name="options" groups="base.group_user"> + <field name="sale_ok"/> + <label for="sale_ok"/> + <field name="purchase_ok"/> + <label for="purchase_ok"/> + </div> </div> <div class="oe_right oe_button_box" name="buttons"> </div> @@ -78,57 +83,77 @@ <page string="Information"> <group> <group> + <field name="type"/> + <field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/> + <field name="list_price"/> + </group> + <group> <field name="default_code"/> - <field name="ean13" placeholder="e.g. 5901234123457"/> - <field groups="product.group_product_variant" name="variants"/> + <label for="ean13"/> + <div name="ean"> + <field name="ean13" placeholder="e.g. 5901234123457"/> + </div> </group> - <group groups="base.group_user"> - <field name="sale_ok"/> - <field name="purchase_ok"/> + <group> + <field groups="product.group_product_variant" name="variants"/> + <field name="price_margin" groups="product.group_product_variant"/> + <field name="price_extra" groups="product.group_product_variant"/> </group> - <group string="Procurement"> - <field name="type"/> + </group> + <field name="description" placeholder="describe the product's characteristics for internal use..."/> + </page> + <page string="Procurements" groups="base.group_user"> + <group name="procurement"> + <group> <field name="procure_method" groups="base.group_user" attrs="{'invisible':[('type','=', 'service')]}"/> - </group> - <group string="Prices"> - <field name="list_price"/> <field name="cost_method" groups="product.group_costing_method"/> <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/> - <field name="price_margin" groups="product.group_product_variant"/> - <field name="price_extra" groups="product.group_product_variant"/> </group> - + <group> + <field name="uom_po_id" groups="product.group_uom"/> + </group> + </group> + <separator string="Notes for Suppliers"/> + <field name="description_purchase" placeholder="This note will be displayed on requests for quotation..."/> + </page> + <page string="Inventory" groups="base.group_user"> + <group name="inventory"> + <group name="status" string="Status"> + <field name="state"/> + <field name="product_manager"/> + </group> + <group name="store" string="Storage Localisation"> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + <field name="loc_rack" attrs="{'invisible':[('type','=','service')]}"/> + <field name="loc_row" attrs="{'invisible':[('type','=','service')]}"/> + <field name="loc_case" attrs="{'invisible':[('type','=','service')]}"/> + </group> <group groups="product.group_stock_packaging" string="Weights"> <field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/> <field name="weight" attrs="{'readonly':[('type','=','service')]}"/> <field name="weight_net" attrs="{'readonly':[('type','=','service')]}"/> </group> - - <group name="uom" groups="product.group_uom" string="Unit of Measure"> - <field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)"/> - <field name="uom_po_id"/> + </group> + </page> + <page string="Sales" attrs="{'readonly':[('sale_ok','=',0)]}"> + <group name="sale"> + <group string="Sale Conditions"> + <label for="sale_delay"/> + <div> + <field name="sale_delay" class="oe_inline"/> days + </div> + <label for="warranty"/> + <div> + <field name="warranty" class="oe_inline"/> months + </div> + </group> + <group groups="product.group_uos" string="Unit of Measure"> <field name="uos_id"/> <field name="uos_coeff"/> <field name="mes_type"/> </group> - - <group name="status" string="Status"> - <field name="state"/> - <field name="product_manager"/> - </group> - </group> - </page> - <page string="Descriptions" groups="base.group_user"> - <separator string="Description"/> - <field name="description"/> - <separator string="Sale Description"/> - <field name="description_sale" placeholder="This note will be displayed on quotations..."/> - <separator string="Purchase Description"/> - <field name="description_purchase" placeholder="This note will be displayed on requests for quotation..."/> - </page> - <page groups="product.group_stock_packaging" string="Packaging"> - <field name="packaging"> + <field name="packaging" groups="product.group_stock_packaging"> <form string="Packaging" version="7.0"> <group col="4"> <field name="ean"/> @@ -150,6 +175,8 @@ <field name="name"/> </form> </field> + <separator string="Notes on Quotations"/> + <field name="description_sale" placeholder="note to be displayed on quotations..."/> </page> </notebook> </sheet> @@ -162,42 +189,30 @@ </record> <!-- Product Kanban View --> - <record model="ir.ui.view" id="product_kanban_view"> + + <record model="ir.ui.view" id="product.product_kanban_view"> <field name="name">Product Kanban</field> <field name="model">product.product</field> <field name="arch" type="xml"> <kanban> <field name="color"/> <field name="type"/> - <field name="image_medium"/> + <field name="image_small"/> <field name="list_price"/> <templates> <t t-name="kanban-box"> - <t t-if="record.type.raw_value!='service' and record.qty_available.raw_value lte 0" t-set="border">oe_kanban_color_red</t> - <div t-attf-class="#{kanban_color(record.color.raw_value)} #{border || ''}"> - <div class="oe_kanban_box oe_kanban_color_border"> - <div class="oe_kanban_box_header oe_kanban_color_bgdark oe_kanban_color_border oe_kanban_draghandle oe_kanban_title"> - <field name="name"/> - </div> - <div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger"> - <img t-att-src="kanban_image('product.product', 'image_medium', record.id.value)" width="48" style="float: left; padding: 0 4px 4px 0"/> - Code: <field name="code"/> - <div t-if="record.type.raw_value == 'service'">No Stock</div> - <t t-if="record.type.raw_value != 'service'"> - <div>Stock: <field name="qty_available"/> on hand, <field name="virtual_available"/> available</div> - <div t-if="record.list_price.raw_value != 0">Public Price: <field name="lst_price"/></div> - <!-- TODO would deserve to be hidden at the field level --> - <div groups="base.group_user">Cost : <field name="standard_price"/></div> - </t> - <div class="oe_kanban_clear"/> - </div> - <div class="oe_kanban_buttons_set oe_kanban_color_border oe_kanban_color_bglight oe_kanban_box_show_onclick"> - <div class="oe_kanban_left"> - <a string="Edit" icon="gtk-edit" type="edit"/> - <a string="Change Color" icon="color-picker" type="color" name="color"/> - </div> - <div class="oe_kanban_clear"/> - </div> + <div class="oe_kanban_vignette oe_semantic_html_override"> + <a type="open"><img t-att-src="kanban_image('product.product', 'image_small', record.id.value)" class="oe_kanban_image"/></a> + <div class="oe_kanban_details"> + <h4> + <a type="open"> + <t t-if="record.code.raw_value">[<field name="code"/>]</t> <field name="name"/> + </a> + </h4> + <div name="tags"/> + <ul> + <li>Price: <field name="lst_price"></field></li> + </ul> </div> </div> </t> @@ -227,7 +242,7 @@ <field name="name">Products</field> <field name="type">ir.actions.act_window</field> <field name="res_model">product.product</field> - <field name="view_mode">tree,form,kanban</field> + <field name="view_mode">kanban,tree,form</field> <field name="view_type">form</field> <field name="context">{"search_default_filter_to_sell":1}</field> <field name="view_id" ref="product_product_tree_view"/> @@ -250,7 +265,7 @@ <record id="open_view_product_tree1" model="ir.actions.act_window.view"> <field name="sequence" eval="2"/> <field name="view_mode">tree</field> - <field name="view_id" ref="product_product_tree_view"/> + <field name="view_id" ref="product_product_tree_view"/> <field name="act_window_id" ref="product_normal_action_sell"/> </record> @@ -264,8 +279,8 @@ <record id="open_view_product_kanban1" model="ir.actions.act_window.view"> <field name="sequence" eval="1"/> <field name="view_mode">kanban</field> - <field name="view_id" ref="product_kanban_view"/> - <field name="act_window_id" ref="product_normal_action"/> + <field name="view_id" ref="product_kanban_view"/> + <field name="act_window_id" ref="product_normal_action_sell"/> </record> <menuitem id="base.menu_product" name="Products" parent="base.menu_base_partner" sequence="9"/> @@ -534,7 +549,7 @@ <field name="model">product.packaging</field> <field name="arch" type="xml"> <tree string="Packaging"> - <field name="sequence" invisible="1"/> + <field name="sequence" widget="handle"/> <field name="ean"/> <field name="qty"/> <field name="ul"/> @@ -606,7 +621,7 @@ <field name="model">product.supplierinfo</field> <field name="arch" type="xml"> <tree string="Supplier Information"> - <field name="sequence" string="Seq"/> + <field name="sequence" widget="handle"/> <field name="name"/> <field name="delay"/> <field name="min_qty"/> @@ -716,7 +731,10 @@ <group> <group name="delay" string="Delays"> <field name="sale_delay" attrs="{'readonly':[('sale_ok','=',0)]}"/> - <field name="produce_delay"/> + <label for="produce_delay"/> + <div> + <field name="produce_delay" class="oe_inline"/> days + </div> <field name="warranty"/> </group> <group name="store" string="Storage Localisation"> diff --git a/addons/product_expiry/product_expiry_view.xml b/addons/product_expiry/product_expiry_view.xml index f883ca55fe0..88ea6f365e8 100644 --- a/addons/product_expiry/product_expiry_view.xml +++ b/addons/product_expiry/product_expiry_view.xml @@ -21,14 +21,14 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view" /> <field name="arch" type="xml"> - <notebook position="inside"> - <page string="Dates"> + <group name="inventory"> + <group string="Dates"> <field name="life_time" /> <field name="use_time" /> <field name="removal_time" /> <field name="alert_time" /> - </page> - </notebook> + </group> + </group> </field> </record> </data> diff --git a/addons/product_manufacturer/product_manufacturer_view.xml b/addons/product_manufacturer/product_manufacturer_view.xml index 5f620747d64..88f528693df 100644 --- a/addons/product_manufacturer/product_manufacturer_view.xml +++ b/addons/product_manufacturer/product_manufacturer_view.xml @@ -7,23 +7,21 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view" /> <field name="arch" type="xml"> - <notebook position="inside"> - <page string="Manufacturer"> - <group string="Manufacturer"> - <field name="manufacturer" /> - <field name="manufacturer_pname"/> - <field name="manufacturer_pref"/> - </group> - <group string="Attributes"> - <field name="attribute_ids" colspan="4" nolabel="1"> - <tree string="Product Attributes" editable="bottom"> - <field name="name"/> - <field name="value"/> - </tree> - </field> - </group> - </page> - </notebook> + <group name="procurement" position="inside"> + <group string="Manufacturer"> + <field name="manufacturer" /> + <field name="manufacturer_pname"/> + <field name="manufacturer_pref"/> + </group> + <group string="Attributes"> + <field name="attribute_ids" colspan="4" nolabel="1"> + <tree string="Product Attributes" editable="bottom"> + <field name="name"/> + <field name="value"/> + </tree> + </field> + </group> + </group> </field> </record> diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml index 2023ba5a0b9..931c661d211 100644 --- a/addons/project/board_project_view.xml +++ b/addons/project/board_project_view.xml @@ -16,7 +16,7 @@ <field name="effective_hours" widget="float_time"/> <field name="progress" widget="progressbar"/> <field name="stage_id" invisible="context.get('set_visible',False)"/> - <field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/> + <field name="state" invisible="1"/> </tree> </field> </record> diff --git a/addons/project/project.py b/addons/project/project.py index 904e19671cb..9bd939dd4fe 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -26,6 +26,7 @@ from osv import fields, osv from openerp.addons.resource.faces import task as Task import time from tools.translate import _ +from openerp import SUPERUSER_ID _TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')] @@ -66,7 +67,7 @@ class project(osv.osv): _description = "Project" _inherits = {'account.analytic.account': "analytic_account_id", "mail.alias": "alias_id"} - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): if user == 1: @@ -186,23 +187,6 @@ class project(osv.osv): """Overriden in project_issue to offer more options""" return [('project.task', "Tasks")] - def _get_followers(self, cr, uid, ids, name, arg, context=None): - ''' - Functional field that computes the users that are 'following' a thread. - ''' - res = {} - for project in self.browse(cr, uid, ids, context=context): - l = set() - for message in project.message_ids: - l.add(message.user_id and message.user_id.id or False) - res[project.id] = list(filter(None, l)) - return res - - def _search_followers(self, cr, uid, obj, name, args, context=None): - project_obj = self.pool.get('project.project') - project_ids = project_obj.search(cr, uid, [('message_ids.user_id.id', 'in', args[0][2])], context=context) - return [('id', 'in', project_ids)] - # 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) @@ -246,8 +230,6 @@ class project(osv.osv): help="The kind of document created when an email is received on this project's email alias"), 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True), 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,), - 'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers, - type='many2many', relation='res.users', string='Followers'), } def _get_type_common(self, cr, uid, context): @@ -511,11 +493,6 @@ def Project(): # OpenChatter methods and notifications # ------------------------------------------------ - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(project, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def create(self, cr, uid, vals, context=None): if context is None: context = {} # Prevent double project creation when 'use_tasks' is checked! @@ -536,23 +513,23 @@ def Project(): return project_id def create_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Project has been <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Project has been <b>created</b>."), context=context) def set_open_send_note(self, cr, uid, ids, context=None): message = _("Project has been <b>opened</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def set_pending_send_note(self, cr, uid, ids, context=None): message = _("Project is now <b>pending</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def set_cancel_send_note(self, cr, uid, ids, context=None): message = _("Project has been <b>cancelled</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def set_close_send_note(self, cr, uid, ids, context=None): message = _("Project has been <b>closed</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_post(cr, uid, ids, body=message, context=context) def write(self, cr, uid, ids, vals, context=None): # if alias_model has been changed, update alias_model_id accordingly @@ -565,7 +542,7 @@ class task(base_stage, osv.osv): _name = "project.task" _description = "Task" _date_name = "date_start" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_project_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ @@ -605,13 +582,17 @@ class task(base_stage, osv.osv): search_domain = [] project_id = self._resolve_project_id_from_context(cr, uid, context=context) if project_id: - search_domain += ['|', '&', ('project_ids', '=', project_id), ('fold', '=', False)] - search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)] + search_domain += ['|', ('project_ids', '=', project_id)] + search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) - return result + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + return result, fold def _read_group_user_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): res_users = self.pool.get('res.users') @@ -628,7 +609,7 @@ class task(base_stage, osv.osv): result = res_users.name_get(cr, access_rights_uid, ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(ids.index(x[0]), ids.index(y[0]))) - return result + return result, {} _group_by_full = { 'stage_id': _read_group_stage_ids, @@ -738,7 +719,7 @@ class task(base_stage, osv.osv): 'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True), 'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."), 'stage_id': fields.many2one('project.task.type', 'Stage', - domain="['|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), + domain="['&', ('fold', '=', False), '|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), 'state': fields.related('stage_id', 'state', type="selection", store=True, selection=_TASK_STATE, string="State", readonly=True, help='The state is set to \'Draft\', when a case is created.\ @@ -877,7 +858,7 @@ class task(base_stage, osv.osv): if context is None: context = {} # read uom as admin to avoid access rights issues, e.g. for portal/share users, # this should be safe (no context passed to avoid side-effects) - obj_tm = users_obj.browse(cr, 1, uid, context=context).company_id.project_time_mode_id + obj_tm = users_obj.browse(cr, SUPERUSER_ID, uid, context=context).company_id.project_time_mode_id tm = obj_tm and obj_tm.name or 'Hours' res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu) @@ -1179,6 +1160,45 @@ class task(base_stage, osv.osv): return result # --------------------------------------------------- + # mail gateway + # --------------------------------------------------- + + def message_new(self, cr, uid, msg, custom_values=None, context=None): + """ Override to updates the document according to the email. """ + if custom_values is None: custom_values = {} + custom_values.update({ + 'name': subject, + 'planned_hours': 0.0, + 'subject': msg.get('subject'), + }) + return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + + def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): + """ Override to update the task according to the email. """ + if update_vals is None: update_vals = {} + act = False + maps = { + 'cost':'planned_hours', + } + for line in msg['body'].split('\n'): + line = line.strip() + res = tools.misc.command_re.match(line) + if res: + match = res.group(1).lower() + field = maps.get(match) + if field: + try: + update_vals[field] = float(res.group(2).lower()) + except (ValueError, TypeError): + pass + elif match.lower() == 'state' \ + and res.group(2).lower() in ['cancel','close','draft','open','pending']: + act = 'do_%s' % res.group(2).lower() + if act: + getattr(self,act)(cr, uid, ids, context=context) + return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) + + # --------------------------------------------------- # OpenChatter methods and notifications # --------------------------------------------------- @@ -1206,19 +1226,19 @@ class task(base_stage, osv.osv): def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) + return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) def create_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context) def case_draft_send_note(self, cr, uid, ids, context=None): msg = _('Task has been set as <b>draft</b>.') - return self.message_append_note(cr, uid, ids, body=msg, context=context) + return self.message_post(cr, uid, ids, body=msg, context=context) def do_delegation_send_note(self, cr, uid, ids, context=None): for task in self.browse(cr, uid, ids, context=context): msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name) - self.message_append_note(cr, uid, [task.id], body=msg, context=context) + self.message_post(cr, uid, [task.id], body=msg, context=context) return True diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index c2d37f7943d..5cc4c99df1b 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -85,13 +85,15 @@ </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Project Management has been installed</value> - <value>Manage multi-level projects and tasks. You can delegate tasks, track the work done on tasks, and review your planning based on the entered data. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Project Management application installed!</field> + <field name="body">Manage multi-level projects and tasks. You can delegate tasks, track task work, and review your planning. -You can manage todo lists on tasks by installing the module "Todo Lists", which supports the methodology Getting Things Done (GTD). You can also manage issues/bugs in projects by installing the module "Issues Tracker."</value> - </function> +You can manage todo lists on tasks by installing the "Todo Lists" application, supporting the Getting Things Done (GTD) methodology. +You can also manage issues/bugs in projects by installing the "Issue Tracker" application.</field> + </record> </data> </openerp> diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml index aaad6b167a0..cd22bad67d4 100644 --- a/addons/project/project_demo.xml +++ b/addons/project/project_demo.xml @@ -7,49 +7,6 @@ <field name="groups_id" eval="[(4, ref('base.group_sale_salesman')),(4, ref('group_project_user'))]"/> </record> - <record id="base.user_niv" model="res.users"> - <field name="name">Nicolas</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_manager')])]"/> - <field name="login">niv</field> - <field name="password">niv</field> - </record> - <record id="base.user_al" model="res.users"> - <field name="name">Antony</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_manager')])]"/> - <field name="login">al</field> - <field name="password">al</field> - </record> - <record id="base.user_qdp" model="res.users"> - <field name="name">Quentin</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_user')])]"/> - <field name="login">qdp</field> - <field name="password">qdp</field> - </record> - <record id="base.user_fpi" model="res.users"> - <field name="name">Francois</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_user')])]"/> - <field name="login">fpi</field> - <field name="password">fpi</field> - </record> - <record id="base.user_jth" model="res.users"> - <field name="name">Julien</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_user')])]"/> - <field name="login">jth</field> - <field name="password">jth</field> - </record> - <record id="base.user_mit" model="res.users"> - <field name="name">Minh</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user'), ref('project.group_project_user')])]"/> - <field name="login">mit</field> - <field name="password">mit</field> - </record> - <record id="base.user_vad" model="res.users"> - <field name="name">Valérie</field> - <field name="groups_id" eval="[(6, 0, [ref('base.group_user')])]"/> - <field name="login">vad</field> - <field name="password">vad</field> - </record> - <!-- Categories --> <record id="project_category_01" model="project.category"> <field name="name">Contact's suggestion</field> @@ -90,10 +47,9 @@ <field name="parent_id" ref="all_projects_account"/> <field name="privacy_visibility">followers</field> <field name="members" eval="[(6, 0, [ - ref('base.user_niv'), - ref('base.user_al'), - ref('base.user_qdp')])]"/> - <field name="user_id" ref="base.user_al"/> + ref('base.user_root'), + ref('base.user_demo')])]"/> + <field name="user_id" ref="base.user_demo"/> <field name="description">Implement new website for Sale and Warehouse Management for a customer.</field> <field name="alias_model">project.task</field> <field name="type_ids" eval="[(6, 0, [ref('project_tt_analysis'),ref('project_tt_specification'),ref('project_tt_negotiation'),ref('project_tt_development'),ref('project_tt_testing'),ref('project_tt_deployment')])]"/> @@ -102,7 +58,7 @@ <record id="project_project_2" model="project.project"> <field name="name">Research & Development</field> <field name="parent_id" ref="all_projects_account"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_demo"/> <field name="alias_model">project.task</field> <field name="type_ids" eval="[(6, 0, [ref('project_tt_specification'),ref('project_tt_development'),ref('project_tt_testing'),ref('project_tt_merge'),ref('project_tt_deployment')])]"/> </record> @@ -114,9 +70,8 @@ <field name="user_id" ref="base.user_demo"/> <field name="alias_model">project.task</field> <field name="members" eval="[(6, 0, [ - ref('base.user_fpi'), - ref('base.user_jth'), - ref('base.user_mit')])]"/> + ref('base.user_root'), + ref('base.user_demo')])]"/> <field name="type_ids" eval="[(6, 0, [ref('project_tt_specification'),ref('project_tt_development'),ref('project_tt_testing'),ref('project_tt_merge'),ref('project_tt_deployment')])]"/> </record> @@ -128,8 +83,8 @@ <field name="alias_model">project.task</field> <field name="type_ids" eval="[(6, 0, [ref('project_tt_design'),ref('project_tt_development'),ref('project_tt_testing')])]"/> <field name="members" eval="[(6, 0, [ - ref('base.user_niv'), - ref('base.user_mit')])]"/> + ref('base.user_root'), + ref('base.user_demo')])]"/> </record> <record id="project_project_5" model="project.project"> @@ -141,7 +96,7 @@ <field name="type_ids" eval="[(6, 0, [ref('project_tt_development')])]"/> <field name="members" eval="[(6, 0, [ ref('base.user_root'), - ref('base.user_qdp')])]"/> + ref('base.user_demo')])]"/> </record> @@ -149,7 +104,7 @@ <record id="project_task_1" model="project.task"> <field name="planned_hours" eval="40.0"/> <field name="remaining_hours" eval="40.0"/> - <field name="user_id" ref="base.user_niv"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">4</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Prepare Requirements Document</field> @@ -159,7 +114,7 @@ <record id="project_task_2" model="project.task"> <field name="planned_hours" eval="32.0"/> <field name="remaining_hours" eval="32.0"/> - <field name="user_id" ref="base.user_niv"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">4</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Make SRS</field> @@ -168,7 +123,7 @@ <record id="project_task_3" model="project.task"> <field name="planned_hours" eval="10.0"/> <field name="remaining_hours" eval="10.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">4</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Budget Planning</field> @@ -179,7 +134,7 @@ <record id="project_task_4" model="project.task"> <field name="planned_hours" eval="60.0"/> <field name="remaining_hours" eval="60.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">4</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Develop module for Sale Management</field> @@ -193,7 +148,7 @@ <record id="project_task_5" model="project.task"> <field name="planned_hours" eval="76.0"/> <field name="remaining_hours" eval="76.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Develop module for Warehouse</field> @@ -207,7 +162,7 @@ <record id="project_task_6" model="project.task"> <field name="planned_hours" eval="24.0"/> <field name="remaining_hours" eval="24.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Integrate Modules</field> @@ -216,7 +171,7 @@ <record id="project_task_7" model="project.task"> <field name="planned_hours" eval="15.0"/> <field name="remaining_hours" eval="5.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_1"/> <field name="name">Internal testing + Software Install</field> @@ -225,7 +180,7 @@ <record id="project_task_8" model="project.task"> <field name="planned_hours" eval="22.0"/> <field name="remaining_hours" eval="22.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_2"/> <field name="name">New portal system</field> @@ -237,7 +192,7 @@ <record id="project_task_9" model="project.task"> <field name="planned_hours" eval="18.0"/> <field name="remaining_hours" eval="18.0"/> - <field name="user_id" ref="base.user_al"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_2"/> <field name="name">Document history management</field> @@ -246,7 +201,7 @@ <record id="project_task_10" model="project.task"> <field name="planned_hours" eval="38.0"/> <field name="remaining_hours" eval="38.0"/> - <field name="user_id" ref="base.user_fpi"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_2"/> <field name="name">Social network integration</field> @@ -256,7 +211,7 @@ <record id="project_task_11" model="project.task"> <field name="planned_hours" eval="16.0"/> <field name="remaining_hours" eval="16.0"/> - <field name="user_id" ref="base.user_fpi"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_2"/> <field name="name">User interface improvements</field> @@ -270,7 +225,7 @@ <record id="project_task_12" model="project.task"> <field name="planned_hours" eval="40.0"/> <field name="remaining_hours" eval="40.0"/> - <field name="user_id" ref="base.user_fpi"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_2"/> <field name="name">Improve+clean code and functionality</field> @@ -282,7 +237,7 @@ <record id="project_task_13" model="project.task"> <field name="planned_hours" eval="12.0"/> <field name="remaining_hours" eval="12.0"/> - <field name="user_id" ref="base.user_mit"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">Design Use Cases</field> @@ -293,7 +248,7 @@ <record id="project_task_14" model="project.task"> <field name="planned_hours" eval="12.0"/> <field name="remaining_hours" eval="12.0"/> - <field name="user_id" ref="base.user_mit"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">Dataflow Design</field> @@ -302,7 +257,7 @@ <record id="project_task_15" model="project.task"> <field name="planned_hours" eval="55.0"/> <field name="remaining_hours" eval="55.0"/> - <field name="user_id" ref="base.user_fpi"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">User Interface design</field> @@ -316,7 +271,7 @@ <field name="sequence" eval="10"/> <field name="planned_hours" eval="80.0"/> <field name="remaining_hours" eval="80.0"/> - <field name="user_id" ref="base.user_mit"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">Set target for all deparments</field> @@ -327,7 +282,7 @@ <record id="project_task_17" model="project.task"> <field name="planned_hours" eval="34.0"/> <field name="remaining_hours" eval="34.0"/> - <field name="user_id" ref="base.user_niv"/> + <field name="user_id" ref="base.user_demo"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">Integration of core components</field> @@ -338,7 +293,7 @@ <record id="project_task_18" model="project.task"> <field name="planned_hours" eval="16.0"/> <field name="remaining_hours" eval="16.0"/> - <field name="user_id" ref="base.user_niv"/> + <field name="user_id" ref="base.user_root"/> <field name="priority">2</field> <field name="project_id" ref="project.project_project_3"/> <field name="name">Deploy and review on live system</field> @@ -441,36 +396,30 @@ <record id="message_task_1" model="mail.message"> <field name="model">project.task</field> <field name="res_id" ref="project_task_22"/> - <field name="content_subtype">plain</field> - <field name="body_text">Hello Quentin, + <field name="body">Hello Demo, There is a change in customer requirement. Can you check the document from customer again. Thanks,</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_root"/> </record> - <record id="message_task_2" model="mail.message"> <field name="model">project.task</field> <field name="res_id" ref="project_task_22"/> <field name="parent_id" ref="message_task_1"/> - <field name="content_subtype">plain</field> - <field name="body_text">Ok, I have checked the mail, + <field name="body">Ok, I have checked the mail, I will update the document and let you know.</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_qdp"/> + <field name="author_id" ref="base.partner_demo"/> </record> - - <record id="message_task_3" model="mail.message"> <field name="model">project.task</field> <field name="res_id" ref="project_task_22"/> <field name="parent_id" ref="message_task_2"/> - <field name="content_subtype">plain</field> - <field name="body_text">Fine! + <field name="body">Fine! Send it ASAP, its urgent.</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_root"/> </record> </data> diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index f864e207141..4f2fe7d54f1 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -139,8 +139,10 @@ <field name="parent_id" domain="[('id','!=',analytic_account_id)]" context="{'current_model': 'project.project'}"/> <field name="priority"/> <field name="active" attrs="{'invisible':[('state','in',['open', 'pending', 'template'])]}"/> - <field name="currency_id" groups="base.group_multi_company" required="1"/> - <field name="company_id" groups="base.group_multi_company" widget="selection" required="1"/> + + <field name="currency_id" groups="base.group_multi_currency" required="1"/> + <field name="parent_id" domain="[('id','!=',analytic_account_id)]" context="{'current_model': 'project.project'}"/> + </group> </group> </page> @@ -163,7 +165,7 @@ <field name="arch" type="xml"> <search string="Search Project"> <field name="complete_name" string="Project Name"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-check" string="Open" name="Current" domain="[('state', '=','open')]" help="Open Projects"/> <filter icon="gtk-media-pause" string="Pending" name="Pending" domain="[('state', '=','pending')]" help="Pending Projects"/> @@ -188,9 +190,9 @@ <field name="model">project.project</field> <field name="field_parent">child_ids</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="red:date and (date<current_date) and (state == 'open');blue:state in ('draft','pending');grey: state in ('close','cancelled')" string="Projects"> + <tree fonts="bold:message_unread==True" colors="red:date and (date<current_date) and (state == 'open');blue:state in ('draft','pending');grey: state in ('close','cancelled')" string="Projects"> <field name="sequence" invisible="1"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> <field name="date" invisible="1"/> <field name="name" string="Project Name"/> <field name="user_id" string="Project Manager"/> @@ -218,7 +220,7 @@ <field name="name">project.project.kanban</field> <field name="model">project.project</field> <field name="arch" type="xml"> - <kanban> + <kanban version="7.0" class="oe_background_grey"> <field name="effective_hours"/> <field name="planned_hours"/> <field name="name"/> @@ -236,8 +238,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">í</span> <ul class="oe_dropdown_menu"> - <li><a type="edit">Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -424,7 +426,7 @@ <button name="%(action_project_task_reevaluate)d" string="Reactivate" type="action" states="cancelled,done" context="{'button_reactivate':True}"/> <button name="action_close" string="Done" type="object" - states="draft,open,pending" class="oe_highlight"/> + states="draft,open,pending"/> <button name="%(action_project_task_delegate)d" string="Delegate" type="action" states="pending,open,draft" groups="project.group_delegate_task"/> <button name="do_cancel" string="Cancel" type="object" @@ -445,12 +447,10 @@ </group> <group> <field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/> + <field name="categ_ids" widget="many2many_tags"/> <field name="progress" widget="progressbar" groups="project.group_time_work_estimation_tasks"/> </group> - <group> - <field name="categ_ids" widget="many2many_tags"/> - </group> </group> <notebook> <page string="Description"> @@ -484,7 +484,7 @@ <field name="name"/> <field name="user_id"/> <field name="stage_id"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="effective_hours" widget="float_time"/> <field name="progress" widget="progressbar"/> <field name="remaining_hours" widget="float_time"/> @@ -500,7 +500,7 @@ <field name="planned_hours" widget="float_time" attrs="{'readonly':[('state','!=','draft')]}" groups="project.group_time_work_estimation_tasks" on_change="onchange_planned(planned_hours, effective_hours)"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> </group> </page> </notebook> @@ -531,7 +531,7 @@ <field name="remaining_hours" sum="Remaining Time" groups="project.group_time_work_estimation_tasks"/> <field name="date_deadline"/> <field name="message_summary"/> - <field name="needaction_pending"/> + <field name="message_unread"/> <field name="categ_ids"/> <templates> <t t-name="kanban-box"> @@ -539,8 +539,8 @@ <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">í</span> <ul class="oe_dropdown_menu"> - <li><a type="edit" >Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li> <ul class="oe_kanban_project_times"> <li><a name="set_remaining_time_1" type="object" class="oe_kanban_button">1</a></li> @@ -596,8 +596,8 @@ <field name="model">project.task</field> <field eval="2" name="priority"/> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="grey:state in ('cancelled','done');blue:state == 'pending';red:date_deadline and (date_deadline<current_date) and (state in ('draft','pending','open'))" string="Tasks"> - <field name="needaction_pending" invisible="1"/> + <tree fonts="bold:message_unread==True" colors="grey:state in ('cancelled','done');blue:state == 'pending';red:date_deadline and (date_deadline<current_date) and (state in ('draft','pending','open'))" string="Tasks"> + <field name="message_unread" invisible="1"/> <field name="sequence" invisible="not context.get('seq_visible', False)"/> <field name="name"/> <field name="project_id" icon="gtk-indent" invisible="context.get('user_invisible', False)"/> @@ -609,7 +609,7 @@ <field name="remaining_hours" widget="float_time" sum="Remaining Hours" on_change="onchange_remaining(remaining_hours,planned_hours)" invisible="context.get('set_visible',False)" groups="project.group_time_work_estimation_tasks"/> <field name="date_deadline" invisible="context.get('deadline_visible',True)"/> <field name="stage_id" invisible="context.get('set_visible',False)"/> - <field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="date_start" invisible="1" groups="base.group_no_one"/> <field name="date_end" invisible="1" groups="base.group_no_one"/> <field name="progress" widget="progressbar" invisible="context.get('set_visible',False)"/> @@ -657,7 +657,7 @@ <field name="arch" type="xml"> <search string="Tasks"> <field name="name" string="Tasks"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/> <filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/> @@ -676,7 +676,6 @@ <filter string="Users" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/> <filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/> <filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> - <filter string="Status" name="group_state" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/> <filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}" groups="base.group_no_one"/> <filter string="End Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}" groups="base.group_no_one"/> diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml index 5d6132b1b8f..1d60479f65c 100644 --- a/addons/project/report/project_report_view.xml +++ b/addons/project/report/project_report_view.xml @@ -79,7 +79,6 @@ <filter string="Task" icon="terp-stock_align_left_24" context="{'group_by':'name'}" /> <filter string="Contact" icon="terp-partner" context="{'group_by':'partner_id'}" /> <filter string="Assigned to" name="User" icon="terp-personal" context="{'group_by':'user_id'}" /> - <filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/> <filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> <filter string="Day" icon="terp-go-today" context="{'group_by':'day'}" help="Creation Date"/> <filter string="Month" icon="terp-go-month" context="{'group_by':'month'}" help="Creation Date"/> diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml index f867285138a..b499e682669 100644 --- a/addons/project/security/project_security.xml +++ b/addons/project/security/project_security.xml @@ -45,7 +45,7 @@ <field name="name">public Members</field> <field name="model_id" ref="model_project_project"/> <field name="global" eval="True"/> - <field name="domain_force">['|','|',('privacy_visibility','in',[False,'public']),('members','in',[user.id]),('followers','in',[user.id])]</field> + <field name="domain_force">['|','|',('privacy_visibility','in',[False,'public']),('members','in',[user.id]),('message_follower_ids','in',[user.partner_id.id])]</field> </record> <record model="ir.rule" id="task_comp_rule"> diff --git a/addons/project/static/src/css/project.css b/addons/project/static/src/css/project.css index 51bb8f45ee9..f90162fcc76 100644 --- a/addons/project/static/src/css/project.css +++ b/addons/project/static/src/css/project.css @@ -6,6 +6,7 @@ width: 250px; min-height: 160px; } + .oe_kanban_project_list { margin: 4px 0; } diff --git a/addons/project/test/task_process.yml b/addons/project/test/task_process.yml index ad4bbe53bb3..e37b2116714 100644 --- a/addons/project/test/task_process.yml +++ b/addons/project/test/task_process.yml @@ -11,7 +11,7 @@ - state == "pending" - !record {model: project.task.delegate, id: delegate_id}: - user_id: base.user_niv + user_id: base.user_demo planned_hours: 12.0 planned_hours_me: 2.0 - diff --git a/addons/project_gtd/project_gtd_data.xml b/addons/project_gtd/project_gtd_data.xml index dc1df93dc46..61b2468329c 100644 --- a/addons/project_gtd/project_gtd_data.xml +++ b/addons/project_gtd/project_gtd_data.xml @@ -26,11 +26,14 @@ <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Todo Lists has been installed</value> - <value>Add todo items on project tasks, to help you organize your work. This module supports the methodology Getting Things Done (GTD), created by David Allen, and described in the book of the same name.</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Todo Lists application installed!</field> + <field name="body">Add todo items on project tasks, to help you organize your work. +This application supports the Getting Things Done (GTD) methodology, based on David Allen's book.</field> + </record> + </data> </openerp> diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py index 07e453af885..3d005f3fad6 100644 --- a/addons/project_issue/__openerp__.py +++ b/addons/project_issue/__openerp__.py @@ -21,11 +21,11 @@ ############################################################################## { - 'name': 'Issues Tracker', + 'name': 'Issue Tracker', 'version': '1.0', 'category': 'Project Management', 'sequence': 22, - 'summary': 'Support, Bug Traker, Helpdesk', + 'summary': 'Support, Bug Tracker, Helpdesk', 'description': """ This module provides Issues/Bugs Management in Project. ======================================================= diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml index 3b4fa57fc58..d7574042c37 100644 --- a/addons/project_issue/board_project_issue_view.xml +++ b/addons/project_issue/board_project_issue_view.xml @@ -16,7 +16,7 @@ <field name="version_id" widget="selection"/> <field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/> <field name="stage_id" widget="selection" readonly="1"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="categ_ids" invisible="1"/> <field name="task_id" invisible="1"/> </tree> diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index c2ff76d05a6..6121acbd607 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -46,8 +46,7 @@ class project_issue(base_stage, osv.osv): _name = "project.issue" _description = "Project Issue" _order = "priority, create_date desc" - _inherit = ['ir.needaction_mixin', 'mail.thread'] - _mail_compose_message = True + _inherit = ['mail.thread', 'ir.needaction_mixin'] def _get_default_project_id(self, cr, uid, context=None): """ Gives default project by checking if present in the context """ @@ -88,14 +87,18 @@ class project_issue(base_stage, osv.osv): search_domain = [] project_id = self._resolve_project_id_from_context(cr, uid, context=context) if project_id: - search_domain += ['|', '&', ('project_ids', '=', project_id), ('fold', '=', False)] - search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)] + search_domain += ['|', ('project_ids', '=', project_id)] + search_domain += ['|', ('id', 'in', ids), ('case_default', '=', True)] # perform search stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context) result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context) # restore order of the search result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0]))) - return result + + fold = {} + for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context): + fold[stage.id] = stage.fold or False + return result, fold def _compute_day(self, cr, uid, ids, fields, args, context=None): """ @@ -240,7 +243,7 @@ class project_issue(base_stage, osv.osv): 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'version_id': fields.many2one('project.issue.version', 'Version'), 'stage_id': fields.many2one ('project.task.type', 'Stage', - domain="['|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), + domain="['&', ('fold', '=', False), '|', ('project_ids', '=', project_id), ('case_default', '=', True)]"), 'project_id':fields.many2one('project.project', 'Project'), 'duration': fields.float('Duration'), 'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"), @@ -364,7 +367,6 @@ class project_issue(base_stage, osv.osv): return super(project_issue, self).write(cr, uid, ids, vals, context) def onchange_task_id(self, cr, uid, ids, task_id, context=None): - result = {} if not task_id: return {'value':{}} task = self.pool.get('project.task').browse(cr, uid, task_id, context=context) @@ -454,14 +456,13 @@ class project_issue(base_stage, osv.osv): custom_values.update({ 'name': msg.get('subject') or _("No Subject"), - 'description': msg.get('body_text'), + 'description': msg.get('body'), 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, }) if msg.get('priority'): custom_values['priority'] = msg.get('priority') - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from'), context=context)) res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=custom_values, context=context) # self.convert_to_bug(cr, uid, [res_id], context=context) @@ -477,16 +478,16 @@ class project_issue(base_stage, osv.osv): if update_vals is None: update_vals = {} # Update doc values according to the message - update_vals['description'] = msg.get('body_text', '') + update_vals['description'] = msg.get('body', '') if msg.get('priority'): update_vals['priority'] = msg.get('priority') - # Parse 'body_text' to find values to update + # Parse 'body' to find values to update maps = { 'cost': 'planned_cost', 'revenue': 'planned_revenue', 'probability': 'probability', } - for line in msg.get('body_text', '').split('\n'): + for line in msg.get('body', '').split('\n'): line = line.strip() res = tools.misc.command_re.match(line) if res and maps.get(res.group(1).lower(), False): @@ -499,36 +500,31 @@ class project_issue(base_stage, osv.osv): # OpenChatter methods and notifications # ------------------------------------------------------- - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'user_id' to the monitored fields """ - res = super(project_issue, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['user_id'] - def stage_set_send_note(self, cr, uid, ids, stage_id, context=None): """ Override of the (void) default notification method. """ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) + return self.message_post(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context) def case_get_note_msg_prefix(self, cr, uid, id, context=None): """ Override of default prefix for notifications. """ return 'Project issue' def convert_to_task_send_note(self, cr, uid, ids, context=None): - message = _("Project issue has been <b>converted</b> into task.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + message = _("Project issue <b>converted</b> to task.") + return self.message_post(cr, uid, ids, body=message, context=context) def create_send_note(self, cr, uid, ids, context=None): - message = _("Project issue has been <b>created</b>.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + message = _("Project issue <b>created</b>.") + return self.message_post(cr, uid, ids, body=message, context=context) def case_escalate_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): if obj.project_id: - message = _("has been <b>escalated</b> to <em>'%s'</em>.") % (obj.project_id.name) - obj.message_append_note(body=message, context=context) + message = _("<b>escalated</b> to <em>'%s'</em>.") % (obj.project_id.name) + obj.message_post(body=message) else: - message = _("has been <b>escalated</b>.") - obj.message_append_note(body=message, context=context) + message = _("<b>escalated</b>.") + obj.message_post(body=message) return True project_issue() diff --git a/addons/project_issue/project_issue_data.xml b/addons/project_issue/project_issue_data.xml index a191cb4454f..ddb0d575e63 100644 --- a/addons/project_issue/project_issue_data.xml +++ b/addons/project_issue/project_issue_data.xml @@ -32,13 +32,15 @@ </record> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Issues Tracker has been installed.</value> - <value>Manage the issues you might face in a project, like bugs in a system, client complaints or material breakdowns. You can record issues, assign them to some responsible person, and keep track of their status as they evolve over time. - -You can access issues from the top menu Project, and access the issues of a specific project from the projects gallery view.</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Issue Tracker application installed!</field> + <field name="body">Manage the issues you might face in a project, such as bugs in a system, client complaints or material breakdowns. +You can record issues, assign them to a responsible person, and keep track of their status as they evolve over time. +Access all issues from the top Project menu, and access the issues of a specific project via the projects gallery view.</field> + </record> + </data> </openerp> diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 75bf50b23a4..fea22c519a8 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -151,7 +151,7 @@ <field name="id"/> <field name="active"/> </group> - <group colspan="4" col="4"> + <group colspan="4" col="4" invisible="1"> <separator string="Status" colspan="4"/> <field name="state" groups="base.group_no_one" string="Status"/> </group> @@ -170,8 +170,8 @@ <field name="name">Project Issue Tracker Tree</field> <field name="model">project.issue</field> <field name="arch" type="xml"> - <tree string="Issue Tracker Tree" fonts="bold:needaction_pending==True" colors="black:state=='open';blue:state=='pending';grey:state in ('cancel', 'done')"> - <field name="needaction_pending" invisible="1"/> + <tree string="Issue Tracker Tree" fonts="bold:message_unread==True" colors="black:state=='open';blue:state=='pending';grey:state in ('cancel', 'done')"> + <field name="message_unread" invisible="1"/> <field name="id"/> <field name="create_date" groups="base.group_no_one"/> <field name="name"/> @@ -182,7 +182,7 @@ <field name="user_id"/> <field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/> <field name="stage_id" widget="selection" readonly="1"/> - <field name="state" groups="base.group_no_one"/> + <field name="state" invisible="1"/> <field name="categ_ids" invisible="1"/> <field name="task_id" invisible="1"/> </tree> @@ -196,7 +196,7 @@ <search string="Issue Tracker Search"> <field name="name" string="Issue" filter_domain="['|', '|',('partner_id','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/> <field name="id"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter string="New" icon="terp-document-new" domain="[('state','=','draft')]" help="New Issues"/> <filter string="To Do" domain="[('state','=','open')]" help="To Do Issues" icon="terp-check"/> @@ -212,7 +212,6 @@ <filter string="Category" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'categ_id'}"/> <filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}"/> <filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> <filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}" help="Creation Month"/> </group> </search> @@ -253,12 +252,12 @@ </ul> </t> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click"> + <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click oe_semantic_html_override"> <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">i</span> <ul class="oe_dropdown_menu"> - <li><a type="edit" >Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> @@ -311,9 +310,9 @@ <field name="name">Project Issue- Feature Tracker Tree</field> <field name="model">project.issue</field> <field name="arch" type="xml"> - <tree string="Feature Tracker Tree" fonts="bold:needaction_pending==True" colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')"> + <tree string="Feature Tracker Tree" fonts="bold:message_unread==True" colors="red:state=='open';black:state in ('draft', 'cancel','done','pending')"> <field name="id"/> - <field name="needaction_pending" invisible="1"/> + <field name="message_unread" invisible="1"/> <field name="name" string="Feature description"/> <field name="partner_id"/> <field name="priority" string="Priority"/> diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml index 625e6e64374..f90f349ea3b 100644 --- a/addons/project_issue/report/project_issue_report_view.xml +++ b/addons/project_issue/report/project_issue_report_view.xml @@ -67,7 +67,6 @@ <filter string="Version" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'version_id'}"/> <filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" /> <filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}" /> <filter string="Company" icon="terp-go-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> <filter string="Day" icon="terp-go-today" domain="[]" context="{'group_by':'day'}" help="Creation Date"/> <filter string="Month" icon="terp-go-month" domain="[]" context="{'group_by':'month'}" help="Creation Date"/> diff --git a/addons/project_mailgate/i18n/ar.po b/addons/project_mailgate/i18n/ar.po deleted file mode 100644 index 42b0945e3a8..00000000000 --- a/addons/project_mailgate/i18n/ar.po +++ /dev/null @@ -1,102 +0,0 @@ -# Arabic translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-12 21:12+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Arabic <ar@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "معلومات المحفوظات" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "مهمة" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "خطأ ! لا يمكنك انشاء مهام رجعية." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "الرسائل" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "مسودة" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "خطأ! يجب ان يكون تاريخ انتهاء المهمة اكبر من تاريخ البداية" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "إلغاء" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "تم" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "فتح" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "معلّق" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "محفوظات" - -#~ msgid "Attachments" -#~ msgstr "مرفقات" - -#~ msgid "Details" -#~ msgstr "تفاصيل" - -#~ msgid "Project MailGateWay" -#~ msgstr "مدخل بريد المشروع" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "هذه الوحدة هي تفاعل التي تتزامن البريد مع مهام مشروع OpenERP .\n" -#~ "وهي تسمح بإنشاء المهام في اقرب وقت كما يصل البريد الجديد في خادم البريد الذي " -#~ "تم ضبطه.\n" -#~ "علاوة على ذلك, تستمر في تعقب المزيد من كل الاتصالات وحالات المهمة.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/ca.po b/addons/project_mailgate/i18n/ca.po deleted file mode 100644 index 622d0a55ac1..00000000000 --- a/addons/project_mailgate/i18n/ca.po +++ /dev/null @@ -1,106 +0,0 @@ -# Catalan translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-02-06 21:57+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Catalan <ca@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informació històrica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tasca" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Error! No podeu crear tasques recursives." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Missatges" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Esborrany" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Error ! La data final de la tasca ha de ser major que la data d'inici" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Canceŀla" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realitzat" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Obre" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendent" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Històric" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta d'enllaç del projecte" - -#~ msgid "Attachments" -#~ msgstr "Adjunts" - -#~ msgid "Details" -#~ msgstr "Detalls" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Aquest mòdul proporciona una interfície per sincronitzar correus amb les " -#~ "tasques de projectes d'OpenERP.\n" -#~ "\n" -#~ "Permet crear tasques tan aviat com arriba un nou correu en el nostre " -#~ "servidor de correu prèviament configurat.\n" -#~ "A més realitza un seguiment de totes les comunicacions addicionals i estats " -#~ "de la tasca.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/cs.po b/addons/project_mailgate/i18n/cs.po deleted file mode 100644 index b65ae7910f5..00000000000 --- a/addons/project_mailgate/i18n/cs.po +++ /dev/null @@ -1,78 +0,0 @@ -# Czech translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-04-06 06:41+0000\n" -"Last-Translator: Jiří Hajda <robie@centrum.cz>\n" -"Language-Team: Czech <cs@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informace historie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Úkol" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Chyba ! Nemůžete vytvořit rekurzivní úkoly." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Zprávy" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Koncept" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Chyba ! Datum ukončení úkolu musí být větší než počáteční datum" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Zrušit" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Dokončené" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otevřené" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Čekající" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historie" diff --git a/addons/project_mailgate/i18n/da.po b/addons/project_mailgate/i18n/da.po deleted file mode 100644 index 43aeba4c665..00000000000 --- a/addons/project_mailgate/i18n/da.po +++ /dev/null @@ -1,78 +0,0 @@ -# Danish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-27 06:33+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Danish <da@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" diff --git a/addons/project_mailgate/i18n/de.po b/addons/project_mailgate/i18n/de.po deleted file mode 100644 index a8f8bfa17c6..00000000000 --- a/addons/project_mailgate/i18n/de.po +++ /dev/null @@ -1,106 +0,0 @@ -# German translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-31 10:37+0000\n" -"Last-Translator: Thorsten Vocks (OpenBig.org) <thorsten.vocks@big-" -"consulting.net>\n" -"Language-Team: German <de@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Information Historie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Aufgabe" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fehler ! Sie können keine rekursiven Aufgaben definieren." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Nachrichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Entwurf" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fehler! Aufgaben End-Datum muss größer als Aufgaben-Beginn sein" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Abbrechen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Erledigt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Offen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Im Wartezustand" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historie" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projekte Mailgateway" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Dieses Modul ermöglicht die Synchronisation von Email mit openERP " -#~ "Projektaufgaben.\n" -#~ "\n" -#~ "Es ermöglicht die Erstellung einer neuen Aufgabe sobald eine neue EMail von " -#~ "Ihrem Mailserver empfangen wird.\n" -#~ "Ausserdem können Sie dann alle weitere Korrespondenz und Kommunikation " -#~ "rückverfolgen sowie den Status der Aufgabe überwachen.\n" -#~ " " - -#~ msgid "Details" -#~ msgstr "Details" - -#~ msgid "Attachments" -#~ msgstr "Anhänge" diff --git a/addons/project_mailgate/i18n/es.po b/addons/project_mailgate/i18n/es.po deleted file mode 100644 index e5aab3c52b1..00000000000 --- a/addons/project_mailgate/i18n/es.po +++ /dev/null @@ -1,106 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"<jesteve@zikzakmedia.com>\n" -"Language-Team: Spanish <es@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"¡ Error ! La fecha final de la tarea debe ser mayor que la fecha de inicio" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace del proyecto" - -#~ msgid "Details" -#~ msgstr "Detalles" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona una interfaz para sincronizar correos con las tareas " -#~ "de proyectos de OpenERP.\n" -#~ "Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -#~ "servidor de correo previamente configurado.\n" -#~ "Además realiza un seguimiento de todas las comunicaciones adicionales y " -#~ "estados de la tarea.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Adjuntos" diff --git a/addons/project_mailgate/i18n/es_CR.po b/addons/project_mailgate/i18n/es_CR.po deleted file mode 100644 index 4e420fa49fa..00000000000 --- a/addons/project_mailgate/i18n/es_CR.po +++ /dev/null @@ -1,107 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-02-17 00:30+0000\n" -"Last-Translator: Carlos Vásquez (CLEARCORP) " -"<carlos.vasquez@clearcorp.co.cr>\n" -"Language-Team: Spanish <es@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" -"Language: es\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"¡ Error ! La fecha final de la tarea debe ser mayor que la fecha de inicio" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace del proyecto" - -#~ msgid "Details" -#~ msgstr "Detalles" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona una interfaz para sincronizar correos con las tareas " -#~ "de proyectos de OpenERP.\n" -#~ "Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -#~ "servidor de correo previamente configurado.\n" -#~ "Además realiza un seguimiento de todas las comunicaciones adicionales y " -#~ "estados de la tarea.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Adjuntos" diff --git a/addons/project_mailgate/i18n/es_MX.po b/addons/project_mailgate/i18n/es_MX.po deleted file mode 100644 index 2736d2572ac..00000000000 --- a/addons/project_mailgate/i18n/es_MX.po +++ /dev/null @@ -1,108 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"<jesteve@zikzakmedia.com>\n" -"Language-Team: Spanish <es@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:56+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: model:ir.module.module,description:project_mailgate.module_meta_information -msgid "" -"This module is an interface that synchronises mails with OpenERP Project " -"Task.\n" -"\n" -"It allows creating tasks as soon as a new mail arrives in our configured " -"mail server.\n" -"Moreover, it keeps track of all further communications and task states.\n" -" " -msgstr "" -"Este módulo proporciona una interfaz para sincronizar correos con las tareas " -"de proyectos de OpenERP.\n" -"Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -"servidor de correo previamente configurado.\n" -"Además realiza un seguimiento de todas las comunicaciones adicionales y " -"estados de la tarea.\n" -" " - -#. module: project_mailgate -#: view:project.task:0 -msgid "Attachments" -msgstr "Adjuntos" - -#. module: project_mailgate -#: model:ir.module.module,shortdesc:project_mailgate.module_meta_information -msgid "Project MailGateWay" -msgstr "Ruta de enlace del proyecto" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:123 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: view:project.task:0 -msgid "Details" -msgstr "Detalles" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:149 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:143 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:129 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:135 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" diff --git a/addons/project_mailgate/i18n/es_VE.po b/addons/project_mailgate/i18n/es_VE.po deleted file mode 100644 index 2736d2572ac..00000000000 --- a/addons/project_mailgate/i18n/es_VE.po +++ /dev/null @@ -1,108 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2010-12-28 08:35+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"<jesteve@zikzakmedia.com>\n" -"Language-Team: Spanish <es@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:56+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarea" - -#. module: project_mailgate -#: model:ir.module.module,description:project_mailgate.module_meta_information -msgid "" -"This module is an interface that synchronises mails with OpenERP Project " -"Task.\n" -"\n" -"It allows creating tasks as soon as a new mail arrives in our configured " -"mail server.\n" -"Moreover, it keeps track of all further communications and task states.\n" -" " -msgstr "" -"Este módulo proporciona una interfaz para sincronizar correos con las tareas " -"de proyectos de OpenERP.\n" -"Permite crear tareas tan pronto como llega un nuevo correo en nuestro " -"servidor de correo previamente configurado.\n" -"Además realiza un seguimiento de todas las comunicaciones adicionales y " -"estados de la tarea.\n" -" " - -#. module: project_mailgate -#: view:project.task:0 -msgid "Attachments" -msgstr "Adjuntos" - -#. module: project_mailgate -#: model:ir.module.module,shortdesc:project_mailgate.module_meta_information -msgid "Project MailGateWay" -msgstr "Ruta de enlace del proyecto" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "¡Error! No puede crear tareas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensajes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:123 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: view:project.task:0 -msgid "Details" -msgstr "Detalles" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:149 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:143 -#, python-format -msgid "Done" -msgstr "Realizado" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:129 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:135 -#, python-format -msgid "Pending" -msgstr "Pendiente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" diff --git a/addons/project_mailgate/i18n/fi.po b/addons/project_mailgate/i18n/fi.po deleted file mode 100644 index 76677ae237a..00000000000 --- a/addons/project_mailgate/i18n/fi.po +++ /dev/null @@ -1,88 +0,0 @@ -# Finnish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-06-22 10:01+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Finnish <fi@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historiatiedot" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tehtävä" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Virhe ! Et voi luoda rekursiivisiä tehtäviä." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Viestit" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Luonnos" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Virhe! Tehtävän lopetuspäivän tulee olla myöhäisempi kuin aloituspäivä" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Peruuta" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Valmis" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Auki" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Odottava" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historia" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projektin sähköpostivälittäjä" - -#~ msgid "Attachments" -#~ msgstr "Liitteet" - -#~ msgid "Details" -#~ msgstr "Yksityiskohdat" diff --git a/addons/project_mailgate/i18n/fr.po b/addons/project_mailgate/i18n/fr.po deleted file mode 100644 index e763eca65a3..00000000000 --- a/addons/project_mailgate/i18n/fr.po +++ /dev/null @@ -1,106 +0,0 @@ -# French translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-13 23:14+0000\n" -"Last-Translator: lholivier <olivier.lenoir@free.fr>\n" -"Language-Team: French <fr@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historique" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tâche" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erreur ! Vous ne pouvez pas créer de tâches récursives." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Messages" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Brouillon" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Erreur ! La date de fin de la tâche doit être postérieure à la date de " -"démarrage" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuler" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Terminé" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Ouvert" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "En attente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historique" - -#~ msgid "Attachments" -#~ msgstr "Pièces jointes" - -#~ msgid "Details" -#~ msgstr "Détails" - -#~ msgid "Project MailGateWay" -#~ msgstr "Messagerie de Projet" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ce module est un interface de synchronisation des emails avec la gestion de " -#~ "projet d'OpenERP.\n" -#~ "\n" -#~ "Il permet la création de tâches sur l'arrivée de nouveaux emails dans le " -#~ "serveur de messagerie configuré.\n" -#~ "De plus, il conserve les échanges ultérieurs et l'état des tâches.\n" -#~ " " diff --git a/addons/project_mailgate/i18n/gl.po b/addons/project_mailgate/i18n/gl.po deleted file mode 100644 index 0e7334b1116..00000000000 --- a/addons/project_mailgate/i18n/gl.po +++ /dev/null @@ -1,105 +0,0 @@ -# Galician translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-05-10 08:39+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Galician <gl@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Información histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro! Non pode crear tarefas recorrentes." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensaxes" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Borrador" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Erro! A data de remate da tarefa debe ser posterior á data de inicio da " -"tarefa" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anular" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Feito" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historial" - -#~ msgid "Project MailGateWay" -#~ msgstr "Ruta de enlace do proxecto" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo proporciona unha interface para sincronizar correos coas tarefas " -#~ "de proxectos de OpenERP. Permite crear tarefas tan pronto como chegue un " -#~ "novo correo no noso servidor de correo previamente configurado. Ademais " -#~ "realiza un seguimento de tódalas comunicacións adicionais e dos estados da " -#~ "tarefa.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Anexos" - -#~ msgid "Details" -#~ msgstr "Detalles" diff --git a/addons/project_mailgate/i18n/hr.po b/addons/project_mailgate/i18n/hr.po deleted file mode 100644 index 15952d948a1..00000000000 --- a/addons/project_mailgate/i18n/hr.po +++ /dev/null @@ -1,104 +0,0 @@ -# Croatian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-12-12 08:56+0000\n" -"Last-Translator: Tomislav Bosnjakovic <Unknown>\n" -"Language-Team: Croatian <hr@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informacije o povjesti" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Zadatak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Greška! Ne možete kreirati rekurzivne zadatke." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Poruke" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Nacrt" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Odustani" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Izvršeno" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otvoreno" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Na čekanju" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Povijest" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay projekta" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ovaj modul je sučelje za sinhronizaciju poruka sa OpenERP projektnim " -#~ "zadacima.\n" -#~ "\n" -#~ "Omogućava kreiranje zadataka čim stigne novi e-mail na naš konfigurirani " -#~ "mail poslužitelj.\n" -#~ "Štoviše, čuva zapise o svim kasnijim komunikacijama i statusima zadataka.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Privici" - -#~ msgid "Details" -#~ msgstr "Pojedinosti" diff --git a/addons/project_mailgate/i18n/hu.po b/addons/project_mailgate/i18n/hu.po deleted file mode 100644 index b1d551f476e..00000000000 --- a/addons/project_mailgate/i18n/hu.po +++ /dev/null @@ -1,84 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * project_mailgate -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 6.0dev\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-25 13:21+0000\n" -"Last-Translator: NOVOTRADE RENDSZERHÁZ ( novotrade.hu ) " -"<openerp@novotrade.hu>\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: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Előzmény" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Feladat" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Hiba! Nem hozhat létre rekurzív feladatokat." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Üzenetek" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Tervezet" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Mégsem" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Kész" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Nyitott" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Függőben lévő" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Előzmény" - -#~ msgid "Attachments" -#~ msgstr "Mellékletek" - -#~ msgid "Details" -#~ msgstr "Részletek" diff --git a/addons/project_mailgate/i18n/it.po b/addons/project_mailgate/i18n/it.po deleted file mode 100644 index a6d56020918..00000000000 --- a/addons/project_mailgate/i18n/it.po +++ /dev/null @@ -1,106 +0,0 @@ -# Italian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-04-22 09:54+0000\n" -"Last-Translator: simone.sandri <lexluxsox@hotmail.it>\n" -"Language-Team: Italian <it@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Storico informazioni" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Attività" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Errore ! Non è possibile creare attività ricorsive." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Messaggi" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Bozza" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Errore ! La data finale della mansione deve essere più vecchia di quella " -"iniziale" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annulla" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Completato" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Apri" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "In sospeso" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Storico" - -#~ msgid "Attachments" -#~ msgstr "Allegati" - -#~ msgid "Details" -#~ msgstr "Dettagli" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Questo modulo è una infertaccia che sincronizza mail con le attività di " -#~ "progetto di OpenERP.\n" -#~ "\n" -#~ "Esso permette di creare attività non appena una nuova mail arriva nel mail " -#~ "server configurato.\n" -#~ "Inoltre, tiene traccia di tutte le comunicazioni e lo stato delle attività.\n" -#~ " " - -#~ msgid "Project MailGateWay" -#~ msgstr "Ingresso Mail di Progetto" diff --git a/addons/project_mailgate/i18n/ja.po b/addons/project_mailgate/i18n/ja.po deleted file mode 100644 index 2370edbec6d..00000000000 --- a/addons/project_mailgate/i18n/ja.po +++ /dev/null @@ -1,78 +0,0 @@ -# Japanese translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-06-10 03:13+0000\n" -"Last-Translator: Akira Hiyama <Unknown>\n" -"Language-Team: Japanese <ja@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "履歴情報" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "タスク" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "エラー。再帰的なタスクを作成することはできません。" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "メッセージ" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "ドラフト" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "エラー。タスクの終了日は開始日以降の日付に設定しなければなりません。" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "キャンセル" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "完了" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "開く" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "保留中" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "履歴" diff --git a/addons/project_mailgate/i18n/lv.po b/addons/project_mailgate/i18n/lv.po deleted file mode 100644 index 4bb1683a2f2..00000000000 --- a/addons/project_mailgate/i18n/lv.po +++ /dev/null @@ -1,87 +0,0 @@ -# Latvian translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-31 10:38+0000\n" -"Last-Translator: OpenERP Administrators <Unknown>\n" -"Language-Team: Latvian <lv@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Vēstures informācija" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Uzdevums" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Kļūda! Nedrīkst veidot rekursīvus uzdevumus." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Ziņojumi" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Melnraksts" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Atcelt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Pabeigts" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Atvērt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Neizlemts" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Vēsture" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projekta MailGateWay" - -#~ msgid "Attachments" -#~ msgstr "Piesaistnes" - -#~ msgid "Details" -#~ msgstr "Sīkāka informācija" diff --git a/addons/project_mailgate/i18n/mn.po b/addons/project_mailgate/i18n/mn.po deleted file mode 100644 index 157b726d3bd..00000000000 --- a/addons/project_mailgate/i18n/mn.po +++ /dev/null @@ -1,85 +0,0 @@ -# Mongolian translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-21 13:53+0000\n" -"Last-Translator: OpenERP Administrators <Unknown>\n" -"Language-Team: Mongolian <mn@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Түүх" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Ажил" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Алдаа ! Та рекурсив цэс үүсгэж болохгүй!" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Мессежүүд" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Ноорог" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Алдаа ! Даалгаврын дуусах хугацаа нь эхлэх хугацаанаасаа хойно байх ёстой" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Болих" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Хийсэн" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Нээх" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Хүлээгдэж буй" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Түүх" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay Төсөл" - -#~ msgid "Details" -#~ msgstr "Нарийвчлал" diff --git a/addons/project_mailgate/i18n/nl.po b/addons/project_mailgate/i18n/nl.po deleted file mode 100644 index 1fffc910a74..00000000000 --- a/addons/project_mailgate/i18n/nl.po +++ /dev/null @@ -1,104 +0,0 @@ -# Dutch translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-18 20:55+0000\n" -"Last-Translator: Douwe Wullink (Dypalio) <Unknown>\n" -"Language-Team: Dutch <nl@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historie informatie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Taak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fout! U kunt geen recursieve taken aanmaken." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Berichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Concept" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fout! Einddatum taak moet groter zijn dat begindatum taak" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuleren" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Gereed" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Open" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Wachtend" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Geschiedenis" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Deze module is een interface dat mail synchroniseert met OpenERP Project " -#~ "taak.\n" -#~ "\n" -#~ "Het maakt nieuwe taken aan zodra mail binnenkomt op de ingestelde " -#~ "mailserver.\n" -#~ "Daarbij houdt het alle verdere communicaties en taak statussen bij.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Bijlagen" - -#~ msgid "Details" -#~ msgstr "Details" - -#~ msgid "Project MailGateWay" -#~ msgstr "Project MailGateWay" diff --git a/addons/project_mailgate/i18n/nl_BE.po b/addons/project_mailgate/i18n/nl_BE.po deleted file mode 100644 index 40124ec1928..00000000000 --- a/addons/project_mailgate/i18n/nl_BE.po +++ /dev/null @@ -1,105 +0,0 @@ -# Dutch (Belgium) translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-03-01 15:19+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Dutch (Belgium) <nl_BE@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historiekinformatie" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Taak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "U kunt niet dezelfde taken maken." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Berichten" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Voorlopig" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Let op: de einddatum van de taak moet na de begindatum van de taak liggen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Annuleren" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Gereed" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Open" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Wachtend" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historiek" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Deze module is een interface waarmee mails met OpenERP-projecttaken worden " -#~ "gesynchroniseerd.\n" -#~ "\n" -#~ "Hiermee worden taken gemaakt zodra een nieuwe mail binnenkomt via de " -#~ "ingestelde mailserver.\n" -#~ "Bovendien worden alle communicatie en taakstatussen bijgehouden.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Bijlagen" - -#~ msgid "Project MailGateWay" -#~ msgstr "Projectmailgateway" - -#~ msgid "Details" -#~ msgstr "Details" diff --git a/addons/project_mailgate/i18n/pl.po b/addons/project_mailgate/i18n/pl.po deleted file mode 100644 index 448e4b44ad9..00000000000 --- a/addons/project_mailgate/i18n/pl.po +++ /dev/null @@ -1,102 +0,0 @@ -# Polish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-11-28 12:29+0000\n" -"Last-Translator: OpenERP Administrators <Unknown>\n" -"Language-Team: Polish <pl@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informacja o historii" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Zadanie" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Wiadomosći" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Projekt" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anuluj" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Wykonano" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Otwórz" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Oczekujące" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historia" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGateWay dla projektu" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Ten moduł jest interfejsem do synchronizacji poczty z zadaniami w projektach " -#~ "OpenERP.\n" -#~ "\n" -#~ "Pozwala tworzyć projekty po otrzymaniu wiadomości przez odpowiednio " -#~ "skonfigurowany\n" -#~ "server pocztowy. Co więcej, utrzymuje on w jednym miejscu dalszą komunikację " -#~ "i stany zadań.\n" -#~ " " - -#~ msgid "Details" -#~ msgstr "Szczegóły" diff --git a/addons/project_mailgate/i18n/project_mailgate.pot b/addons/project_mailgate/i18n/project_mailgate.pot deleted file mode 100644 index acb1e4e981c..00000000000 --- a/addons/project_mailgate/i18n/project_mailgate.pot +++ /dev/null @@ -1,77 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * project_mailgate -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 6.1rc1\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-02-08 00:37+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" - diff --git a/addons/project_mailgate/i18n/pt.po b/addons/project_mailgate/i18n/pt.po deleted file mode 100644 index 8a907a4d013..00000000000 --- a/addons/project_mailgate/i18n/pt.po +++ /dev/null @@ -1,84 +0,0 @@ -# Portuguese translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2010-12-12 10:12+0000\n" -"Last-Translator: OpenERP Administrators <Unknown>\n" -"Language-Team: Portuguese <pt@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informação do Histórico" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro ! Não se pode criar tarefas recursivas" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensagens" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Rascunho" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Erro !Data final da tarefa dever ser posterior à data inicial" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Concluído" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Histórico" - -#~ msgid "Details" -#~ msgstr "Detalhes" - -#~ msgid "Attachments" -#~ msgstr "Anexos" diff --git a/addons/project_mailgate/i18n/pt_BR.po b/addons/project_mailgate/i18n/pt_BR.po deleted file mode 100644 index cd084a2e1e2..00000000000 --- a/addons/project_mailgate/i18n/pt_BR.po +++ /dev/null @@ -1,105 +0,0 @@ -# Brazilian Portuguese translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-01-17 11:53+0000\n" -"Last-Translator: Emerson <Unknown>\n" -"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Informação Histórica" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Tarefa" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Erro! Você não pode criar tarefas recursivas." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mensagens" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Provisório" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Erro ! A data final deve ser maior do que a data inicial" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Cancelar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Concluído" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Abrir" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Pendente" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Histórico" - -#~ msgid "Details" -#~ msgstr "Detalhes" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo é uma interface que sincroniza emails com as Tarefas de Projetos " -#~ "OpenERP.\n" -#~ "\n" -#~ "Permite a criação de tarefas tão logo um novo email chegue no nosso servidor " -#~ "de email configurado.\n" -#~ "Além disso, mantém o controle de todas as demais comunicações e estados das " -#~ "tarefas.\n" -#~ " " - -#~ msgid "Project MailGateWay" -#~ msgstr "Projeto Gateway de Email" - -#~ msgid "Attachments" -#~ msgstr "Anexos" diff --git a/addons/project_mailgate/i18n/ro.po b/addons/project_mailgate/i18n/ro.po deleted file mode 100644 index dacd3b477f8..00000000000 --- a/addons/project_mailgate/i18n/ro.po +++ /dev/null @@ -1,107 +0,0 @@ -# Romanian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-13 11:48+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Romanian <ro@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Istoric Informatii" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Sarcina" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Eroare ! Nu puteti crea sarcini recursive." - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mesaje" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Ciorna" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" -"Eroare ! Data de sfarsit a sarcinii trebuie sa fie mai mare decat data de " -"inceput" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Anulati" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Efectuat" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Deschideti" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "In asteptare" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Istoric" - -#~ msgid "Project MailGateWay" -#~ msgstr "MailGAteWay Proiect" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "Acest modul este o interfată care sincronizează email-urile cu Activitatea " -#~ "de Proiect OpenERP.\n" -#~ "\n" -#~ "Permite crearea de sarcini de indată ce un nou email soseste in serverul de " -#~ "mail configurat.\n" -#~ "In plus, tine evidenta tuturor comunicatiilor viitoare si a stărilor " -#~ "activitătilor.\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "Atașamente" - -#~ msgid "Details" -#~ msgstr "Detalii" diff --git a/addons/project_mailgate/i18n/sv.po b/addons/project_mailgate/i18n/sv.po deleted file mode 100644 index 02d5c55be00..00000000000 --- a/addons/project_mailgate/i18n/sv.po +++ /dev/null @@ -1,78 +0,0 @@ -# Swedish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-06-18 23:27+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Swedish <sv@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "Historikinformation" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Aktivitet" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "Fel ! Du kan inte skapa rekursiva aktiviteter" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Meddelanden" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Preliminär" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "Fel ! Aktivitetens slutdatum måste komma efter startdatumet" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "Avbryt" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Färdig" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Öppen" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Väntande" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "Historik" diff --git a/addons/project_mailgate/i18n/tr.po b/addons/project_mailgate/i18n/tr.po deleted file mode 100644 index 1f8a351bcbc..00000000000 --- a/addons/project_mailgate/i18n/tr.po +++ /dev/null @@ -1,78 +0,0 @@ -# Turkish translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-01-25 17:33+0000\n" -"Last-Translator: Ahmet Altınışık <Unknown>\n" -"Language-Team: Turkish <tr@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "Görev" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "Mesajlar" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "Taslak" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "İptal" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "Tamamlandı" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "Açık" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "Bekleyen" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "" diff --git a/addons/project_mailgate/i18n/zh_CN.po b/addons/project_mailgate/i18n/zh_CN.po deleted file mode 100644 index c1b8ea02d5a..00000000000 --- a/addons/project_mailgate/i18n/zh_CN.po +++ /dev/null @@ -1,101 +0,0 @@ -# Chinese (Simplified) translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR <EMAIL@ADDRESS>, 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" -"POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-06-28 16:25+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" -"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:40+0000\n" -"X-Generator: Launchpad (build 15864)\n" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History Information" -msgstr "日志信息" - -#. module: project_mailgate -#: model:ir.model,name:project_mailgate.model_project_task -msgid "Task" -msgstr "任务" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! You cannot create recursive tasks." -msgstr "错误!不能创建循环引用的任务" - -#. module: project_mailgate -#: field:project.task,message_ids:0 -msgid "Messages" -msgstr "消息" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:90 -#, python-format -msgid "Draft" -msgstr "草稿" - -#. module: project_mailgate -#: constraint:project.task:0 -msgid "Error ! Task end-date must be greater then task start-date" -msgstr "错误!任务结束日期必须大于任务开始日期" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:116 -#, python-format -msgid "Cancel" -msgstr "取消" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:110 -#, python-format -msgid "Done" -msgstr "完成" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:96 -#, python-format -msgid "Open" -msgstr "未结" - -#. module: project_mailgate -#: code:addons/project_mailgate/project_mailgate.py:102 -#, python-format -msgid "Pending" -msgstr "等待中" - -#. module: project_mailgate -#: view:project.task:0 -msgid "History" -msgstr "日志" - -#~ msgid "Project MailGateWay" -#~ msgstr "Project MailGateWay" - -#~ msgid "" -#~ "This module is an interface that synchronises mails with OpenERP Project " -#~ "Task.\n" -#~ "\n" -#~ "It allows creating tasks as soon as a new mail arrives in our configured " -#~ "mail server.\n" -#~ "Moreover, it keeps track of all further communications and task states.\n" -#~ " " -#~ msgstr "" -#~ "此模块是邮件和任务的接口。\n" -#~ "允许在特定邮件服务器收到邮件后马上创建任务。\n" -#~ "还能跟踪进一步的沟通和任务阶段\n" -#~ " " - -#~ msgid "Attachments" -#~ msgstr "附件" - -#~ msgid "Details" -#~ msgstr "详细信息" diff --git a/addons/project_mailgate/project_mailgate.py b/addons/project_mailgate/project_mailgate.py deleted file mode 100644 index ba3cd702760..00000000000 --- a/addons/project_mailgate/project_mailgate.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## - -import binascii -from osv import fields, osv -from tools.translate import _ -import tools - -class project_tasks(osv.osv): - _inherit = 'project.task' - - def message_new(self, cr, uid, msg, custom_values=None, context=None): - """ Overrides mail_thread message_new that is called by the mailgateway - through message_process. - This override updates the document according to the email. - """ - if custom_values is None: custom_values = {} - custom_values.update({ - 'name': subject, - 'planned_hours': 0.0, - 'subject': msg.get('subject'), - }) - custom_values.update(self.message_partner_by_email(cr, uid, msg.get('from', False), context=context)) - return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the task according to the email. - """ - if update_vals is None: update_vals = {} - act = False - maps = { - 'cost':'planned_hours', - } - for line in msg['body_text'].split('\n'): - line = line.strip() - res = tools.misc.command_re.match(line) - if res: - match = res.group(1).lower() - field = maps.get(match) - if field: - try: - update_vals[field] = float(res.group(2).lower()) - except (ValueError, TypeError): - pass - elif match.lower() == 'state' \ - and res.group(2).lower() in ['cancel','close','draft','open','pending']: - act = 'do_%s' % res.group(2).lower() - if act: - getattr(self,act)(cr, uid, ids, context=context) - return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) - - def message_thread_followers(self, cr, uid, ids, context=None): - followers = super(project_tasks,self).message_thread_followers(cr, uid, ids, context=context) - for task in self.browse(cr, uid, followers.keys(), context=context): - task_followers = set(followers[task.id]) - task_followers.add(task.user_id.email) - followers[task.id] = filter(None, task_followers) - return followers - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project_mrp/project_mrp_view.xml b/addons/project_mrp/project_mrp_view.xml index 841a61e6c31..f9e761117b6 100644 --- a/addons/project_mrp/project_mrp_view.xml +++ b/addons/project_mrp/project_mrp_view.xml @@ -16,8 +16,8 @@ <field name="model">product.product</field> <field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/> <field name="arch" type="xml"> - <field name="active" position="after"> - <field name="project_id" attrs="{'readonly':[('type','!=','service')]}" /> + <field name="type" position="after"> + <field name="project_id" attrs="{'invisible':[('type','!=','service')]}" /> </field> </field> </record> diff --git a/addons/project_timesheet/project_timesheet_demo.xml b/addons/project_timesheet/project_timesheet_demo.xml index 6d3dfb23767..90873d2a418 100644 --- a/addons/project_timesheet/project_timesheet_demo.xml +++ b/addons/project_timesheet/project_timesheet_demo.xml @@ -3,13 +3,11 @@ <data noupdate="1"> <!-- Example employee --> <record id="hr.employee_qdp" model="hr.employee"> - <field name="user_id" ref="base.user_qdp"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> </record> <record id="hr.employee_al" model="hr.employee"> - <field name="user_id" ref="base.user_al"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> </record> @@ -17,7 +15,6 @@ <record id="hr.employee_phu" model="hr.employee"> <field name="name">Phuong</field> <field name="calendar_id" ref="resource.timesheet_group1"/> - <field name="user_id" ref="base.user_phu"/> <field name="category_ids" eval="[(6, 0, [ref('hr.employee_category_4')])]"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> @@ -26,7 +23,6 @@ <record id="hr.employee_fpi" model="hr.employee"> <field name="name">Francois</field> <field name="calendar_id" ref="resource.timesheet_group1"/> - <field name="user_id" ref="base.user_fpi"/> <field name="category_ids" eval="[(6, 0, [ref('hr.employee_category_4')])]"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> @@ -34,7 +30,6 @@ <record id="hr.employee_jth" model="hr.employee"> <field name="name">Julien</field> <field name="calendar_id" ref="resource.timesheet_group1"/> - <field name="user_id" ref="base.user_jth"/> <field name="category_ids" eval="[(6, 0, [ref('hr.employee_category_4')])]"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> @@ -42,7 +37,6 @@ <record id="hr.employee7" model="hr.employee"> <field name="name">Aline</field> <field name="calendar_id" ref="resource.timesheet_group1"/> - <field name="user_id" ref="base.user_apr"/> <field name="category_ids" eval="[(6, 0, [ref('hr.employee_category_4')])]"/> <field name="product_id" ref="product.product_product_consultant"/> <field name="journal_id" ref="hr_timesheet.analytic_journal"/> diff --git a/addons/project_timesheet/test/work_timesheet.yml b/addons/project_timesheet/test/work_timesheet.yml index 2f75f908735..4867f92b4b1 100644 --- a/addons/project_timesheet/test/work_timesheet.yml +++ b/addons/project_timesheet/test/work_timesheet.yml @@ -6,7 +6,7 @@ name: Test Timesheet records task_id: project.project_task_10 hours: 20.0 - user_id: base.user_fpi + user_id: base.user_demo company_id: base.main_company - I check Timesheet line for employee in current Timesheet @@ -16,6 +16,7 @@ start = (datetime.date.today().strftime('%Y-%m-%d')) task_work = self.search(cr, uid, [("name","=","Social network integration: Test Timesheet records")],context)[0] task_ids = self.browse(cr, uid, task_work, context) - assert task_ids.user_id.id == ref("base.user_fpi"), 'Error, The User in Timesheet is not Correct' + assert task_ids.user_id.id == ref("base.user_demo"), 'Error, The User in Timesheet is not Correct' assert task_ids.date == start, 'Error, The Date in Timesheet is not ok' assert task_ids.product_uom_id.name == "Hour", 'Error, The Hour in Timesheet is not ok' + diff --git a/addons/purchase/board_purchase_view.xml b/addons/purchase/board_purchase_view.xml index 67c3981f383..894271e46ce 100644 --- a/addons/purchase/board_purchase_view.xml +++ b/addons/purchase/board_purchase_view.xml @@ -62,7 +62,7 @@ <form string="Purchase Dashboard" version="7.0"> <board style="2-1"> <column> - <action name="%(purchase_draft)d" string="Request for Quotations" creatable="true"/> + <action name="%(purchase_draft)d" string="Request for Quotations"/> </column> <column> <action name="%(action_purchase_order_monthly_categ_graph)d" string="Monthly Purchases by Category"/> diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index b97da91fa1d..daad4467929 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -108,51 +108,6 @@ </div> </div> ]]></field> - <field name="body_text"><![CDATA[ -Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''}, - -Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}: - | Order number: *${object.name}* - | Order total: *${object.amount_total} ${object.pricelist_id.currency_id.name}* - | Order date: ${object.date_order} - % if object.origin: - | Order reference: ${object.origin} - % endif - % if object.partner_ref: - | Your reference: ${object.partner_ref}<br /> - % endif - | Your contact: ${object.validator.name} ${object.validator.email and '<%s>'%(object.validator.email) or ''} - -You can view the ${object.state in ('draft', 'sent') and 'request for quotation' or 'order confirmation'} and download it using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -If you have any question, do not hesitate to contact us. - -Thank you! - - --- -${object.validator.name} ${object.validator.email and '<%s>'%(object.validator.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]></field> </record> </data> </openerp> diff --git a/addons/purchase/i18n/ar.po b/addons/purchase/i18n/ar.po index 87ef655e38e..378d405db66 100644 --- a/addons/purchase/i18n/ar.po +++ b/addons/purchase/i18n/ar.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1806,7 +1806,7 @@ msgstr "" "الرجاء تعيين واحداً قبل اختيار المنتج." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/bg.po b/addons/purchase/i18n/bg.po index d9356e97ee2..5bc0064dd0c 100644 --- a/addons/purchase/i18n/bg.po +++ b/addons/purchase/i18n/bg.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1768,7 +1768,7 @@ msgstr "" " Моля, изберете преди избора на продукт." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/bs.po b/addons/purchase/i18n/bs.po index c335ab087ce..e17c64da401 100644 --- a/addons/purchase/i18n/bs.po +++ b/addons/purchase/i18n/bs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1763,7 +1763,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/ca.po b/addons/purchase/i18n/ca.po index 556b2e7932f..71da9bfdc15 100644 --- a/addons/purchase/i18n/ca.po +++ b/addons/purchase/i18n/ca.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1837,7 +1837,7 @@ msgstr "" "Indiqueu un abans de seleccionar un producte." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/cs.po b/addons/purchase/i18n/cs.po index fd4ac39416a..8c08429673d 100644 --- a/addons/purchase/i18n/cs.po +++ b/addons/purchase/i18n/cs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" "X-Poedit-Language: Czech\n" #. module: purchase @@ -1777,7 +1777,7 @@ msgstr "" "Před výběrem výrobku prosíme nastavte jednoho." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/da.po b/addons/purchase/i18n/da.po index 21a70f01728..32f4d820acf 100644 --- a/addons/purchase/i18n/da.po +++ b/addons/purchase/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/de.po b/addons/purchase/i18n/de.po index b7857d453b0..be27e50799e 100644 --- a/addons/purchase/i18n/de.po +++ b/addons/purchase/i18n/de.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1880,7 +1880,7 @@ msgstr "" "Auftragspositionen wählen." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/el.po b/addons/purchase/i18n/el.po index a63e402cc8f..6153fbf083d 100644 --- a/addons/purchase/i18n/el.po +++ b/addons/purchase/i18n/el.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" "X-Poedit-Country: GREECE\n" "X-Poedit-Language: Greek\n" "X-Poedit-SourceCharset: utf-8\n" @@ -1770,7 +1770,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/en_GB.po b/addons/purchase/i18n/en_GB.po index 93b488ebca1..d444a651b00 100644 --- a/addons/purchase/i18n/en_GB.po +++ b/addons/purchase/i18n/en_GB.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1818,7 +1818,7 @@ msgstr "" "Please set one before choosing a product." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/es.po b/addons/purchase/i18n/es.po index 15848dfcf07..35589f062dc 100644 --- a/addons/purchase/i18n/es.po +++ b/addons/purchase/i18n/es.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1838,7 +1838,7 @@ msgstr "" "Indique uno antes de seleccionar un producto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/es_AR.po b/addons/purchase/i18n/es_AR.po index a6d64bb920e..93d07c576c1 100644 --- a/addons/purchase/i18n/es_AR.po +++ b/addons/purchase/i18n/es_AR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1766,7 +1766,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/es_CL.po b/addons/purchase/i18n/es_CL.po index a9798b07a2b..bd2b69e5b91 100644 --- a/addons/purchase/i18n/es_CL.po +++ b/addons/purchase/i18n/es_CL.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1838,7 +1838,7 @@ msgstr "" "Indique uno antes de seleccionar un producto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/es_CR.po b/addons/purchase/i18n/es_CR.po index a98b3da9cc3..877e382ad81 100644 --- a/addons/purchase/i18n/es_CR.po +++ b/addons/purchase/i18n/es_CR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" "Language: \n" #. module: purchase @@ -1863,7 +1863,7 @@ msgstr "" "Indique uno antes de seleccionar un producto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/es_EC.po b/addons/purchase/i18n/es_EC.po index df1d28b21f1..3c3a694a896 100644 --- a/addons/purchase/i18n/es_EC.po +++ b/addons/purchase/i18n/es_EC.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1865,7 +1865,7 @@ msgstr "" "Indique uno antes de seleccionar un producto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/et.po b/addons/purchase/i18n/et.po index 157cb59a8a2..37b03923ce9 100644 --- a/addons/purchase/i18n/et.po +++ b/addons/purchase/i18n/et.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: code:addons/purchase/purchase.py:735 @@ -1770,7 +1770,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/fi.po b/addons/purchase/i18n/fi.po index feda226d820..aff6368643a 100644 --- a/addons/purchase/i18n/fi.po +++ b/addons/purchase/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1818,7 +1818,7 @@ msgstr "" "Ole hyvä ja valitse yksi ennenkuin valitset tuotteen." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/fr.po b/addons/purchase/i18n/fr.po index dbc6411e03e..0fd3f5fb99c 100644 --- a/addons/purchase/i18n/fr.po +++ b/addons/purchase/i18n/fr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1870,7 +1870,7 @@ msgstr "" "Veuillez en sélectionner un avant de choisir un produit." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/gl.po b/addons/purchase/i18n/gl.po index 60647d89d7a..65475c658d9 100644 --- a/addons/purchase/i18n/gl.po +++ b/addons/purchase/i18n/gl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/hr.po b/addons/purchase/i18n/hr.po index b7c89ba215b..ab84932d9b4 100644 --- a/addons/purchase/i18n/hr.po +++ b/addons/purchase/i18n/hr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1820,7 +1820,7 @@ msgstr "" "naloga za nabavu." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/hu.po b/addons/purchase/i18n/hu.po index b2d22d2c062..32fc1a68540 100644 --- a/addons/purchase/i18n/hu.po +++ b/addons/purchase/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1842,7 +1842,7 @@ msgstr "" "Kérem, állítsa be, mielőtt kiválasztaná a terméket!" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/id.po b/addons/purchase/i18n/id.po index 9560ba073ea..23dd0de81e9 100644 --- a/addons/purchase/i18n/id.po +++ b/addons/purchase/i18n/id.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1808,7 +1808,7 @@ msgstr "" "Harap menetapkan satu sebelum memilih produk." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/it.po b/addons/purchase/i18n/it.po index dda4fa14d22..8e0d8bd90db 100644 --- a/addons/purchase/i18n/it.po +++ b/addons/purchase/i18n/it.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1825,7 +1825,7 @@ msgstr "" "Inserire quindi una di queste voci prima di scegliere il prodotto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/ja.po b/addons/purchase/i18n/ja.po index 64fdc7d561f..3c3503dda31 100644 --- a/addons/purchase/i18n/ja.po +++ b/addons/purchase/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1789,7 +1789,7 @@ msgstr "" "製品を選択する前にどちらかをセットして下さい。" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/ko.po b/addons/purchase/i18n/ko.po index 3c2e61b6282..fdebdb6a145 100644 --- a/addons/purchase/i18n/ko.po +++ b/addons/purchase/i18n/ko.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1752,7 +1752,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/lt.po b/addons/purchase/i18n/lt.po index de97b68ed9e..e4ea511a9b6 100644 --- a/addons/purchase/i18n/lt.po +++ b/addons/purchase/i18n/lt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1754,7 +1754,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/lv.po b/addons/purchase/i18n/lv.po index 2ef362be035..2407428bfb1 100644 --- a/addons/purchase/i18n/lv.po +++ b/addons/purchase/i18n/lv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/mn.po b/addons/purchase/i18n/mn.po index 62c274f6ceb..578cee625d4 100644 --- a/addons/purchase/i18n/mn.po +++ b/addons/purchase/i18n/mn.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1855,7 +1855,7 @@ msgstr "" "Бараа сонгохоосоо өмнө аль нэгийг нь сонго." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/nb.po b/addons/purchase/i18n/nb.po index ffbc6422e80..c9da9748bf7 100644 --- a/addons/purchase/i18n/nb.po +++ b/addons/purchase/i18n/nb.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1845,7 +1845,7 @@ msgstr "" "Vennligst velg en før du velger prosukt." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/nl.po b/addons/purchase/i18n/nl.po index cc50ab56d40..94375e08522 100644 --- a/addons/purchase/i18n/nl.po +++ b/addons/purchase/i18n/nl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1868,7 +1868,7 @@ msgstr "" "Stel er een in voordat u een product kiest." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/nl_BE.po b/addons/purchase/i18n/nl_BE.po index 1675d90fc6d..c86766b63de 100644 --- a/addons/purchase/i18n/nl_BE.po +++ b/addons/purchase/i18n/nl_BE.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1849,7 +1849,7 @@ msgstr "" "Gelieve dit te doen voordat u een product kiest." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/pl.po b/addons/purchase/i18n/pl.po index 827add12e12..28c8bebad0a 100644 --- a/addons/purchase/i18n/pl.po +++ b/addons/purchase/i18n/pl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1838,7 +1838,7 @@ msgstr "" "Wybierz jedno z powyższych przed wybraniem produktu." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/pt.po b/addons/purchase/i18n/pt.po index 959815d37c6..325f63f78ef 100644 --- a/addons/purchase/i18n/pt.po +++ b/addons/purchase/i18n/pt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1858,7 +1858,7 @@ msgstr "" "Por favor, defina um antes de escolher um artigo." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/pt_BR.po b/addons/purchase/i18n/pt_BR.po index d96d1d28320..8f2cccb110b 100644 --- a/addons/purchase/i18n/pt_BR.po +++ b/addons/purchase/i18n/pt_BR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1865,7 +1865,7 @@ msgstr "" "Por favor, selecione um antes de escolher um produto." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/purchase.pot b/addons/purchase/i18n/purchase.pot index 44355e26cea..a7fd4cfaab8 100644 --- a/addons/purchase/i18n/purchase.pot +++ b/addons/purchase/i18n/purchase.pot @@ -1767,7 +1767,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or ''}${object." diff --git a/addons/purchase/i18n/ro.po b/addons/purchase/i18n/ro.po index 23a41b61d22..0a8c4858c18 100644 --- a/addons/purchase/i18n/ro.po +++ b/addons/purchase/i18n/ro.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1875,7 +1875,7 @@ msgstr "" "Va rugam sa setati una inainte de a alege un produs." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/ru.po b/addons/purchase/i18n/ru.po index 73c82213e6a..9fa7d0c5d39 100644 --- a/addons/purchase/i18n/ru.po +++ b/addons/purchase/i18n/ru.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1796,7 +1796,7 @@ msgstr "" "Пожалуйста выберите, перед выбором ТМЦ." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sk.po b/addons/purchase/i18n/sk.po index e49bbd2b301..739d72747c9 100644 --- a/addons/purchase/i18n/sk.po +++ b/addons/purchase/i18n/sk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sl.po b/addons/purchase/i18n/sl.po index cba54e28834..5bcbd3ddd5c 100644 --- a/addons/purchase/i18n/sl.po +++ b/addons/purchase/i18n/sl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1800,7 +1800,7 @@ msgstr "" "Prosim izberi en sklop, preden se izbere izdelek." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sq.po b/addons/purchase/i18n/sq.po index ba537c1e000..0f73670c8a8 100644 --- a/addons/purchase/i18n/sq.po +++ b/addons/purchase/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:00+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sr.po b/addons/purchase/i18n/sr.po index 5a7166b458e..ee8e7b3811b 100644 --- a/addons/purchase/i18n/sr.po +++ b/addons/purchase/i18n/sr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1764,7 +1764,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sr@latin.po b/addons/purchase/i18n/sr@latin.po index cafdb7c9d97..dc37c25f445 100644 --- a/addons/purchase/i18n/sr@latin.po +++ b/addons/purchase/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1764,7 +1764,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/sv.po b/addons/purchase/i18n/sv.po index 14caf767e28..53a3f72dec7 100644 --- a/addons/purchase/i18n/sv.po +++ b/addons/purchase/i18n/sv.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -22,6 +22,8 @@ msgid "" "The buyer has to approve the RFQ before being sent to the supplier. The RFQ " "becomes a confirmed Purchase Order." msgstr "" +"Köparen måste godkänna offertförfrågan innan den skickas till leverantören. " +"Inköpsförfrågan blir då en bekräftad Inköpsorder." #. module: purchase #: model:process.node,note:purchase.process_node_productrecept0 @@ -43,7 +45,7 @@ msgstr "Destination" #: code:addons/purchase/purchase.py:236 #, python-format msgid "In order to delete a purchase order, it must be cancelled first!" -msgstr "" +msgstr "För att ta bort en Inköpsorder, måste den avbrytas först!" #. module: purchase #: help:purchase.report,date:0 @@ -118,6 +120,9 @@ msgid "" "products, etc. For each purchase order, you can track the products received, " "and control the supplier invoices." msgstr "" +"Använd den här menyn för att söka i dina Inköpsordrar genom referenser, " +"leverantör, produkter etc. Du kan spåra produkter som mottagits, och styra " +"leverantörsfakturor, för varje Inköpsorder." #. module: purchase #: code:addons/purchase/wizard/purchase_line_invoice.py:145 @@ -166,7 +171,7 @@ msgstr "Företag" #. module: purchase #: help:res.company,po_lead:0 msgid "This is the leads/security time for each purchase order." -msgstr "" +msgstr "Detta är ledtiden för varje beställning." #. module: purchase #: model:ir.actions.act_window,name:purchase.action_purchase_order_monthly_categ_graph @@ -323,7 +328,7 @@ msgstr "Produkter" #: model:ir.actions.act_window,name:purchase.action_purchase_order_report_graph #: view:purchase.report:0 msgid "Total Qty and Amount by month" -msgstr "" +msgstr "Totalt Antal och belopp per månad" #. module: purchase #: model:process.transition,note:purchase.process_transition_packinginvoice0 @@ -331,6 +336,8 @@ msgid "" "A Pick list generates an invoice. Depending on the Invoicing control of the " "sale order, the invoice is based on delivered or on ordered quantities." msgstr "" +"En plocklista genererar en faktura. Beroende på faktureringskontrollen av " +"försäljningen blir fakturan baserad på levererad eller beställda kvantiteter." #. module: purchase #: selection:purchase.order,state:0 selection:purchase.order.line,state:0 @@ -394,7 +401,7 @@ msgstr "" #. module: purchase #: field:purchase.report,dest_address_id:0 msgid "Dest. Address Contact Name" -msgstr "" +msgstr "Dest. Adress Kontakt Namn" #. module: purchase #: report:purchase.order:0 @@ -440,6 +447,7 @@ msgstr "Ordrar under senaste månaden" #, python-format msgid "You must first cancel all receptions related to this purchase order." msgstr "" +"Du måste först avbryta alla inleveranser relaterade till denna Inköpsorder." #. module: purchase #: selection:purchase.order.line,state:0 @@ -464,7 +472,7 @@ msgstr "Anger att plockningen är klar." #. module: purchase #: view:purchase.order:0 msgid "Purchase orders which are in exception state" -msgstr "" +msgstr "Beställningar som befinner sig i undantag" #. module: purchase #: report:purchase.order:0 field:purchase.report,validator:0 @@ -485,7 +493,7 @@ msgstr "Snittpris" #. module: purchase #: view:stock.picking:0 msgid "Incoming Shipments already processed" -msgstr "" +msgstr "Inkommande leveranser som redan behandlats" #. module: purchase #: report:purchase.order:0 @@ -522,6 +530,9 @@ msgid "" "of the purchase order, the invoice is based on received or on ordered " "quantities." msgstr "" +"En plocklista genererar en leverantörsfaktura. Beroende på " +"faktureringskontroll över Inköpsordern blir fakturan baserad på mottagna " +"eller beställda kvantiteter." #. module: purchase #: model:ir.actions.act_window,help:purchase.purchase_line_form_action2 @@ -532,11 +543,16 @@ msgid "" "supplier invoice, you can generate a draft supplier invoice based on the " "lines from this menu." msgstr "" +"Om du ställer in Faktureringskontrollen på en Inköpsorder som \"Baserat på " +"rader i inköpsorder\", kan du spåra alla rader på Inköpsorder som du ännu " +"inte har fått Leverantörsfaktura. När du är redo att ta emot en " +"Leverantörsfaktura kan du då skapa ett utkast på leverantörsfaktura baserad " +"på raderna från den här menyn." #. module: purchase #: view:purchase.order:0 msgid "Purchase order which are in the exception state" -msgstr "" +msgstr "Inköpsorder som befinner sig i undantag" #. module: purchase #: model:ir.actions.act_window,help:purchase.action_stock_move_report_po @@ -544,6 +560,8 @@ msgid "" "Reception Analysis allows you to easily check and analyse your company order " "receptions and the performance of your supplier's deliveries." msgstr "" +"Mottagning Analys gör att du enkelt kan kontrollera och analysera företagets " +"ordermottagningar och resultatet för dina leverantörens leveranser." #. module: purchase #: report:purchase.quotation:0 @@ -651,6 +669,9 @@ msgid "" "order is 'On picking'. The invoice can also be generated manually by the " "accountant (Invoice control = Manual)." msgstr "" +"Fakturan skapas automatiskt om fakturakontroll av beställningen är \"Mot " +"plockning\". Fakturan kan också genereras manuellt av den som bokför " +"(Faktura kontroll = Manuell)." #. module: purchase #: report:purchase.order:0 @@ -664,6 +685,9 @@ msgid "" "purchase history and performance. From this menu you can track your " "negotiation performance, the delivery performance of your suppliers, etc." msgstr "" +"Inköpsanalys gör att du kan analysera och enkelt kontrollera din köphistorik " +"och företagets resultat. Från denna meny kan du spåra förhandlingsprestanda, " +"leveranssäkerhet av dina leverantörer, osv." #. module: purchase #: model:ir.ui.menu,name:purchase.menu_configuration_misc @@ -674,7 +698,7 @@ msgstr "Allmänt" #: code:addons/purchase/purchase.py:769 #, python-format msgid "The selected supplier only sells this product by %s" -msgstr "" +msgstr "Den valda leverantören säljer endast denna produkt med %s" #. module: purchase #: view:purchase.report:0 @@ -739,7 +763,7 @@ msgstr "Inkommande leverans" #: code:addons/purchase/purchase.py:285 #, python-format msgid "You cannot confirm a purchase order without any lines." -msgstr "" +msgstr "Du kan inte bekräfta en Inköpsorder utan några rader." #. module: purchase #: model:ir.actions.act_window,help:purchase.action_invoice_pending @@ -749,12 +773,16 @@ msgid "" "according to your settings. Once you receive a supplier invoice, you can " "match it with the draft invoice and validate it." msgstr "" +"Använd den här menyn för att kontrollera inkommande leverantörsfakturor. " +"OpenERP kan tillverka utkast till fakturor baserat dina beställningar eller " +"mottagningar, beroende på dina inställningar. När du får en " +"leverantörsfaktura kan du matcha den mot fakturautkastet och godkänna den." #. module: purchase #: model:process.node,name:purchase.process_node_draftpurchaseorder0 #: model:process.node,name:purchase.process_node_draftpurchaseorder1 msgid "RFQ" -msgstr "RFQ" +msgstr "Offertförfrågan" #. module: purchase #: code:addons/purchase/edi/purchase_order.py:139 @@ -869,7 +897,7 @@ msgstr "Bild" #. module: purchase #: view:purchase.report:0 msgid "Total Orders Lines by User per month" -msgstr "" +msgstr "Totalt orderrader per användare per månad" #. module: purchase #: view:purchase.order:0 @@ -978,7 +1006,7 @@ msgstr "Produktkategorier" #. module: purchase #: selection:purchase.config.wizard,default_method:0 msgid "Pre-Generate Draft Invoices based on Purchase Orders" -msgstr "" +msgstr "Tillverka utkast på fakturor baserade på inköpsorder" #. module: purchase #: model:ir.actions.act_window,name:purchase.action_view_purchase_line_invoice @@ -1004,7 +1032,7 @@ msgstr "Calendar View" #. module: purchase #: selection:purchase.config.wizard,default_method:0 msgid "Based on Purchase Order Lines" -msgstr "" +msgstr "Baserat på inköpsorderrader" #. module: purchase #: help:purchase.order,amount_untaxed:0 @@ -1015,7 +1043,7 @@ msgstr "Summa utan moms." #: code:addons/purchase/purchase.py:754 #, python-format msgid "Selected UOM does not belong to the same category as the product UOM" -msgstr "" +msgstr "Vald enhet tillhör inte samma kategori som produktens enhet" #. module: purchase #: code:addons/purchase/purchase.py:907 @@ -1030,6 +1058,9 @@ msgid "" "the buyer. Depending on the Invoicing control of the purchase order, the " "invoice is based on received or on ordered quantities." msgstr "" +"En inköpsorder genererar en leverantörsfaktura, så snart den bekräftas av " +"köparen. Beroende på faktureringskontroll på beställningen blir fakturan " +"baserad på mottagna eller beställda kvantiteter." #. module: purchase #: field:purchase.order,amount_untaxed:0 @@ -1078,6 +1109,9 @@ msgid "" "order is 'On order'. The invoice can also be generated manually by the " "accountant (Invoice control = Manual)." msgstr "" +"Fakturan skapas automatiskt om Fakturakontrollen på beställningen är \"Mot " +"order\". Fakturan kan också genereras manuellt av den som bokför (Faktura " +"kontroll = Manuell)." #. module: purchase #: model:ir.actions.act_window,name:purchase.action_email_templates @@ -1119,13 +1153,13 @@ msgstr "Utökade filter..." #. module: purchase #: view:purchase.config.wizard:0 msgid "Invoicing Control on Purchases" -msgstr "" +msgstr "FaktureringsKontroll mot inköp" #. module: purchase #: code:addons/purchase/wizard/purchase_order_group.py:48 #, python-format msgid "Please select multiple order to merge in the list view." -msgstr "" +msgstr "Välj flera för att sammanfoga i listvyn." #. module: purchase #: model:ir.actions.act_window,help:purchase.action_import_create_supplier_installer @@ -1182,12 +1216,12 @@ msgstr "Cancel Purchase Order" #: code:addons/purchase/purchase.py:411 code:addons/purchase/purchase.py:418 #, python-format msgid "Unable to cancel this purchase order!" -msgstr "" +msgstr "Kan inte avbryta inköpsorder!" #. module: purchase #: model:process.transition,note:purchase.process_transition_createpackinglist0 msgid "A pick list is generated to track the incoming products." -msgstr "" +msgstr "En plocklista genereras för att spåra inkommande produkter." #. module: purchase #: help:purchase.order,pricelist_id:0 @@ -1211,7 +1245,7 @@ msgstr "Referensen måste vara unik per bolag!" #. module: purchase #: view:purchase.report:0 field:purchase.report,price_standard:0 msgid "Products Value" -msgstr "" +msgstr "Varuvärde" #. module: purchase #: model:ir.ui.menu,name:purchase.menu_partner_categories_in_form @@ -1236,6 +1270,12 @@ msgid "" "Based on generated invoice: create a draft invoice you can validate later.\n" "Based on receptions: let you create an invoice when receptions are validated." msgstr "" +"Baserat på rader i inköpsorder: placera enskilda rader i 'FakturaKontroll > " +"Baserat på inköpsorderrader' från där du kan välja att skapa en faktura.\n" +"Baserat på genererad faktura: skapar ett fakturautkast som du kan godkänna " +"senare.\n" +"Baserat på mottagningar: låter dig skapa en faktura när mottagningen " +"godkänts." #. module: purchase #: model:ir.actions.act_window,name:purchase.action_supplier_address_form @@ -1256,7 +1296,7 @@ msgstr "Produkter per kategori" #. module: purchase #: view:purchase.report:0 field:purchase.report,delay:0 msgid "Days to Validate" -msgstr "" +msgstr "Dagar för att godkänna" #. module: purchase #: help:purchase.order,origin:0 @@ -1267,7 +1307,7 @@ msgstr "" #. module: purchase #: view:purchase.order:0 msgid "Purchase orders which are not approved yet." -msgstr "" +msgstr "Inköpsordrar som inte är godkända ännu." #. module: purchase #: help:purchase.order,state:0 @@ -1279,12 +1319,12 @@ msgid "" "received, the state becomes 'Done'. If a cancel action occurs in the invoice " "or in the reception of goods, the state becomes in exception." msgstr "" -"The state of the purchase order or the quotation request. A quotation is a " -"purchase order in a 'Draft' state. Then the order has to be confirmed by the " -"user, the state switch to 'Confirmed'. Then the supplier must confirm the " -"order to change the state to 'Approved'. When the purchase order is paid and " -"received, the state becomes 'Done'. If a cancel action occurs in the invoice " -"or in the reception of goods, the state becomes in exception." +"Tillstånd för Inköpsorder eller Offertförfråga. En offert är en Inköpsorder " +"i \"utkast\"-läge. Ordern måste därefter bekräftas av användaren och växlar " +"då över till \"Bekräftat\"-läge. Sedan måste leverantören bekräfta för att " +"ändra tillståndet till \"Godkänd\". När beställningen är betald och " +"mottagen, blir statusen \"Klar\". Om man Avbryter fakturan eller " +"inleveransen blir statusen Undantag." #. module: purchase #: field:purchase.order.line,price_subtotal:0 @@ -1306,7 +1346,7 @@ msgstr "Inköpsorder '%s' är bekräftad." #. module: purchase #: help:purchase.order,date_approve:0 msgid "Date on which purchase order has been approved" -msgstr "" +msgstr "Datum då inköpsorder har godkänts" #. module: purchase #: view:purchase.order:0 field:purchase.order,state:0 @@ -1376,7 +1416,7 @@ msgstr "Purchase Order Lines" #. module: purchase #: model:process.transition,note:purchase.process_transition_approvingpurchaseorder0 msgid "The supplier approves the Purchase Order." -msgstr "" +msgstr "Leverantören godkänner beställningen." #. module: purchase #: code:addons/purchase/wizard/purchase_order_group.py:80 @@ -1406,7 +1446,7 @@ msgstr "Merge orders" #. module: purchase #: model:ir.model,name:purchase.model_purchase_order_line_invoice msgid "Purchase Order Line Make Invoice" -msgstr "" +msgstr "Inköpsorderrader Tillverka Faktura" #. module: purchase #: model:ir.ui.menu,name:purchase.menu_action_picking_tree4 @@ -1416,7 +1456,7 @@ msgstr "Inkommande leveranser" #. module: purchase #: model:ir.actions.act_window,name:purchase.action_purchase_order_by_user_all msgid "Total Orders by User per month" -msgstr "" +msgstr "Totalt order per användare per månad" #. module: purchase #: model:ir.actions.report.xml,name:purchase.report_purchase_quotation @@ -1442,12 +1482,12 @@ msgstr "Our Order Reference" #. module: purchase #: view:purchase.order:0 view:purchase.order.line:0 msgid "Search Purchase Order" -msgstr "" +msgstr "Sök Inköpsorder" #. module: purchase #: model:ir.actions.act_window,name:purchase.action_purchase_config msgid "Set the Default Invoicing Control Method" -msgstr "" +msgstr "Sätt Standardmetod för FaktureringsKontroll" #. module: purchase #: model:process.node,note:purchase.process_node_draftpurchaseorder0 @@ -1468,12 +1508,12 @@ msgstr "Datum godkänd" #. module: purchase #: selection:purchase.report,state:0 msgid "Waiting Supplier Ack" -msgstr "" +msgstr "Väntar på Leverantörsbekräftelse" #. module: purchase #: model:ir.ui.menu,name:purchase.menu_procurement_management_pending_invoice msgid "Based on draft invoices" -msgstr "" +msgstr "Baserat på fakturautkast" #. module: purchase #: view:purchase.order:0 @@ -1487,6 +1527,8 @@ msgid "" "The selected supplier has a minimal quantity set to %s %s, you should not " "purchase less." msgstr "" +"Den valda leverantören har en minimal mängd satt till %s %s, ska bör inte " +"köpa mindre." #. module: purchase #: field:purchase.order.line,date_planned:0 @@ -1545,6 +1587,9 @@ msgid "" "suppliers. You can track all your interactions with them through the History " "tab: emails, orders, meetings, etc." msgstr "" +"Visar din leverantör och upprätthåller en god relation med din leverantör. " +"Du kan spåra alla dina leverantörsaktiviteter genom fliken Historik: e-post, " +"ordrar, möten mm" #. module: purchase #: view:purchase.order:0 @@ -1589,7 +1634,7 @@ msgstr "Reservation" #. module: purchase #: view:purchase.order:0 msgid "Purchase orders that include lines not invoiced." -msgstr "" +msgstr "Beställningar som inkluderar rader som inte är fakturerade." #. module: purchase #: view:purchase.order:0 @@ -1607,6 +1652,8 @@ msgid "" "This tool will help you to select the method you want to use to control " "supplier invoices." msgstr "" +"Detta verktyg hjälper dig att välja den metod du vill använda för att " +"kontrollera leverantörsfakturor." #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder1 @@ -1614,6 +1661,9 @@ msgid "" "In case there is no supplier for this product, the buyer can fill the form " "manually and confirm it. The RFQ becomes a confirmed Purchase Order." msgstr "" +"Om det inte finns någon leverantör för denna produkt, kan köparen fylla i " +"formuläret manuellt och bekräfta det. Offertförfrågan blir en bekräftad " +"Inköpsorder." #. module: purchase #: selection:purchase.report,month:0 @@ -1650,6 +1700,9 @@ msgid "" "receptions\", you can track here all the product receptions and create " "invoices for those receptions." msgstr "" +"Om du ställer in Faktureringskontroll på en beställning som \"Baserat på " +"mottagningar\", kan du spåra alla mottagna produkter och skapa fakturor för " +"dessa mottagningar." #. module: purchase #: view:purchase.order:0 @@ -1681,16 +1734,26 @@ msgid "" "\n" " " msgstr "" +" Notera att:\n" +" \n" +" Beställningar kommer endast att slås samman om:\n" +" * Inköpsorder är utkast\n" +" * Inköpsorder tillhör samma leverantör\n" +" * Inköpsorder är har samma lagerplats, samma prislista\n" +" \n" +" Rader kommer endast att slås samman om:\n" +" * Order rader är exakt samma utom produkter, kvantitet och enhet \n" +" " #. module: purchase #: field:purchase.report,negociation:0 msgid "Purchase-Standard Price" -msgstr "" +msgstr "Standardpris Inköp" #. module: purchase #: field:purchase.config.wizard,default_method:0 msgid "Default Invoicing Control Method" -msgstr "" +msgstr "Standard Faktureringskontrollmetod" #. module: purchase #: model:product.pricelist.type,name:purchase.pricelist_type_purchase @@ -1734,6 +1797,13 @@ msgid "" " \n" "* The 'Cancelled' state is set automatically when user cancel purchase order." msgstr "" +" * Tillståndet \"Utkast\" ställs in automatiskt när inköpsorder är i utkast " +"tillstånd.\n" +"* Tillståndet \"Bekräftad\" ställs in automatiskt när bekräfta när " +"inköpsorder är i bekräfta tillstånd.\n" +"* Tillståndet \"Klar\" ställs in automatiskt när inköpsorder är klar.\n" +"* Tillståndet \"Inställd\" ställs in automatiskt när användaren avbryter ett " +"inköp." #. module: purchase #: code:addons/purchase/purchase.py:426 @@ -1771,7 +1841,7 @@ msgstr "" #. module: purchase #: model:process.transition,name:purchase.process_transition_purchaseinvoice0 msgid "From a purchase order" -msgstr "" +msgstr "Från en inköpsorder" #. module: purchase #: code:addons/purchase/purchase.py:735 @@ -1780,9 +1850,11 @@ msgid "" "You have to select a pricelist or a supplier in the purchase form !\n" "Please set one before choosing a product." msgstr "" +"Du måste välja en prislista eller en leverantör i inköpsformuläret!\n" +"Välj en innan du väljer produkt." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " @@ -1837,11 +1909,62 @@ msgid "" "% endif\n" " " msgstr "" +"\n" +"Hej ${object.partner_address_id.name and ' ' or " +"''}${object.partner_address_id.name or ''},\n" +"\n" +"Här är en inköpsorder bekräftelse från ${object.company_id.name}:\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.partner_ref:\n" +" | Your reference: ${object.partner_ref}<br />\n" +" % endif\n" +" | Your contact: ${object.validator.name} " +"${object.validator.user_email and '<%s>'%(object.validator.user_email) or " +"''}\n" +"\n" +"Du kan visa orderbekräftelsen och ladda ner det via följande länk:\n" +" ${ctx.get('edi_web_url_view') or 'n/a'}\n" +"\n" +"Om du har några frågor, tveka inte att kontakta oss.\n" +"\n" +"Tack!\n" +"\n" +"\n" +"--\n" +"${object.validator.name} ${object.validator.user_email and " +"'<%s>'%(object.validator.user_email) or ''}\n" +"${object.company_id.name}\n" +"% if object.company_id.street:\n" +"${object.company_id.street or ''}\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 or ''} ${object.company_id.city or ''}\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" +"% if object.company_id.phone:\n" +"Phone: ${object.company_id.phone}\n" +"% endif\n" +"% if object.company_id.website:\n" +"${object.company_id.website or ''}\n" +"% endif\n" +" " #. module: purchase #: view:purchase.order:0 msgid "Purchase orders which are in draft state" -msgstr "" +msgstr "Beställningar som är Utkast" #. module: purchase #: selection:purchase.report,month:0 @@ -1878,7 +2001,7 @@ msgstr "År" #: model:ir.ui.menu,name:purchase.menu_purchase_line_order_draft #: selection:purchase.order,invoice_method:0 msgid "Based on Purchase Order lines" -msgstr "" +msgstr "Baserat på inköpsorderrader" #. module: purchase #: model:ir.actions.todo.category,name:purchase.category_purchase_config @@ -1906,7 +2029,7 @@ msgstr "Ordrar" msgid "" "unique number of the purchase order,computed automatically when the purchase " "order is created" -msgstr "" +msgstr "Unikt inköpsordernummer, sätts automatiskt när inköpsordern skapas" #. module: purchase #: view:board.board:0 diff --git a/addons/purchase/i18n/th.po b/addons/purchase/i18n/th.po index c61648bfa28..cb219f6d201 100644 --- a/addons/purchase/i18n/th.po +++ b/addons/purchase/i18n/th.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1777,7 +1777,7 @@ msgstr "" "กรุณาเลือกก่อนที่จะเลือกสินค้า" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/tlh.po b/addons/purchase/i18n/tlh.po index 24a8e6af888..9993644f106 100644 --- a/addons/purchase/i18n/tlh.po +++ b/addons/purchase/i18n/tlh.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1748,7 +1748,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/tr.po b/addons/purchase/i18n/tr.po index e8180e1e7ef..fcc6a30e190 100644 --- a/addons/purchase/i18n/tr.po +++ b/addons/purchase/i18n/tr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -542,6 +542,11 @@ msgid "" "supplier invoice, you can generate a draft supplier invoice based on the " "lines from this menu." msgstr "" +"Bir satınalma siparişinin Fatura Kontrolünü \"Satınalma Siparişi Öğelerine " +"Göre\" ayarlarsanız, henüz almadığınız tedarikçi faturalarının satınalma " +"öğelerinintümünü buradan izleyebilirsiniz. Bir tedarikçi faturası alma " +"durumuna gelmişseniz, bu menü öğelerine göre bir taslak tedarikçi faturası " +"oluşturabilirsiniz." #. module: purchase #: view:purchase.order:0 @@ -1161,6 +1166,9 @@ msgid "" "can import your existing partners by CSV spreadsheet from \"Import Data\" " "wizard" msgstr "" +"Bu formdan elle Tedarikçileri ve kişilerini oluşturabilir ya da " +"içeaktarabilirsiniz veya \"Veri İçeaktarma\" sihirbazından CSV çalışma " +"sayfası olarak varolan paydaşlarınızı içeaktarabilirsiniz." #. module: purchase #: model:process.transition,name:purchase.process_transition_createpackinglist0 @@ -1261,6 +1269,12 @@ msgid "" "Based on generated invoice: create a draft invoice you can validate later.\n" "Based on receptions: let you create an invoice when receptions are validated." msgstr "" +"Satınalma Sipariş satırlarına dayalı: seçerek fatura oluşturacağınız 'Fatura " +"Kontrolü > S.S. satırlarına dayalı'nın içine tek satırları yerleştirin.\n" +"Oluşturulan faturaya dayalı: daha sonra doğrulayabileceğiniz bir taslak " +"fatura oluşturun.\n" +"Kabullere dayanarak: kabuller doğrulandığında fatura oluşturmanıza izin " +"verir." #. module: purchase #: model:ir.actions.act_window,name:purchase.action_supplier_address_form @@ -1684,6 +1698,9 @@ msgid "" "receptions\", you can track here all the product receptions and create " "invoices for those receptions." msgstr "" +"Satınalma siparişlerinin Fatura Kontrolünü \"Kabullere Dayanarak\" " +"ayarlarsanız, burada tüm ürün kabullerini izleyebilir ve bu kabuller için " +"faturalar oluşturabilirsiniz." #. module: purchase #: view:purchase.order:0 @@ -1838,7 +1855,7 @@ msgstr "" "Lütfen bir ürün seçmeden önce birini belirtin." #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/uk.po b/addons/purchase/i18n/uk.po index 7691c36b3d8..eb0da389943 100644 --- a/addons/purchase/i18n/uk.po +++ b/addons/purchase/i18n/uk.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1756,7 +1756,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/vi.po b/addons/purchase/i18n/vi.po index b26c44842d5..1f9ee993aad 100644 --- a/addons/purchase/i18n/vi.po +++ b/addons/purchase/i18n/vi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:56+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1749,7 +1749,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/zh_CN.po b/addons/purchase/i18n/zh_CN.po index d950f429261..e010694db60 100644 --- a/addons/purchase/i18n/zh_CN.po +++ b/addons/purchase/i18n/zh_CN.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1772,7 +1772,7 @@ msgid "" msgstr "请在采购界面选择价格表或供应商!然后再选择产品。" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/i18n/zh_TW.po b/addons/purchase/i18n/zh_TW.po index 148ee88a7b2..dae81c1827c 100644 --- a/addons/purchase/i18n/zh_TW.po +++ b/addons/purchase/i18n/zh_TW.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:01+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: purchase #: model:process.transition,note:purchase.process_transition_confirmingpurchaseorder0 @@ -1748,7 +1748,7 @@ msgid "" msgstr "" #. module: purchase -#: model:email.template,body_text:purchase.email_template_edi_purchase +#: model:email.template,body:purchase.email_template_edi_purchase msgid "" "\n" "Hello${object.partner_address_id.name and ' ' or " diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 756016d39e2..592bf783a64 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -220,7 +220,7 @@ class purchase_order(osv.osv): ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'), ] _name = "purchase.order" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Purchase Order" _order = "name desc" @@ -731,24 +731,15 @@ class purchase_order(osv.osv): # OpenChatter methods and notifications # -------------------------------------- - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(purchase_order, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if obj.state == 'approved': - result[obj.id].append(obj.validator.id) - return result - - def message_get_monitored_follower_fields(self, cr, uid, ids, context=None): - """ Add 'validator' to the monitored fields """ - res = super(purchase_order, self).message_get_monitored_follower_fields(cr, uid, ids, context=context) - return res + ['validator'] + def needaction_domain_get(self, cr, uid, ids, context=None): + return [('state', '=', 'draft')] def create_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Request for quotation <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Request for quotation <b>created</b>."), context=context) def confirm_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) + self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to a Purchase Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) def shipment_send_note(self, cr, uid, ids, picking_id, context=None): for order in self.browse(cr, uid, ids, context=context): @@ -757,25 +748,25 @@ class purchase_order(osv.osv): # convert it to the user TZ and re-render it with %Z to add the timezone picking_datetime = fields.DT.datetime.strptime(picking.min_date, DEFAULT_SERVER_DATETIME_FORMAT) picking_date_str = fields.datetime.context_timestamp(cr, uid, picking_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)") - self.message_append_note(cr, uid, [order.id], body=_("Shipment <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context) + self.message_post(cr, uid, [order.id], body=_("Shipment <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context) def invoice_send_note(self, cr, uid, ids, invoice_id, context=None): for order in self.browse(cr, uid, ids, context=context): for invoice in (inv for inv in order.invoice_ids if inv.id == invoice_id): - self.message_append_note(cr, uid, [order.id], body=_("Draft Invoice of %s %s is <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context) + self.message_post(cr, uid, [order.id], body=_("Draft Invoice of %s %s is <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context) def shipment_done_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("""Shipment <b>received</b>."""), context=context) + self.message_post(cr, uid, ids, body=_("""Shipment <b>received</b>."""), context=context) def invoice_done_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context) def draft_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Purchase Order has been set to <b>draft</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Purchase Order has been set to <b>draft</b>."), context=context) def cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Purchase Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context) + self.message_post(cr, uid, [obj.id], body=_("Purchase Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context) purchase_order() diff --git a/addons/purchase/purchase_data.xml b/addons/purchase/purchase_data.xml index 75074d4cedf..693d18eb1ba 100644 --- a/addons/purchase/purchase_data.xml +++ b/addons/purchase/purchase_data.xml @@ -3,14 +3,15 @@ <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Purchase Management has been installed.</value> - <value>From the top menu Purchases, create purchase orders to buy products from your suppliers, encode supplier invoices and manage your payments. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Purchase Management application installed!</field> + <field name="body">From the top menu Purchases, create purchase orders to buy products from your suppliers, enter supplier invoices and manage payments. -You can also manage purchase requisitions, see the Purchase Settings.</value> - </function> +You can also manage purchase requisitions, see also the Purchase Settings.</field> + </record> <record id="req_link_purchase_order" model="res.request.link"> <field name="name">Purchase Order</field> diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index f513e2a60e8..5d6f6c34b3b 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -73,7 +73,7 @@ parent="base.menu_purchase_root" sequence="6"/> <record id="action_invoice_pending" model="ir.actions.act_window"> - <field name="name">Based on Draft Invoices</field> + <field name="name">On Draft Invoices</field> <field name="res_model">account.invoice</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,graph</field> @@ -95,7 +95,7 @@ </field> </record> - <menuitem name="Based on Draft Invoices" + <menuitem id="menu_procurement_management_pending_invoice" action="action_invoice_pending" parent="menu_procurement_management_invoice" @@ -177,7 +177,7 @@ <field name="arch" type="xml"> <form string="Purchase Order" version="7.0"> <header> - <button name="purchase_confirm" states="draft,sent" string="Confirm Quotation" class="oe_highlight"/> + <button name="purchase_confirm" states="draft,sent" string="Confirm Order" class="oe_highlight"/> <button name="wkf_send_rfq" states="draft" string="Send RFQ" type="object" context="{'send_rfq':True}"/> <button name="wkf_send_rfq" states="confirmed" string="Resend Purchase Order" type="object" class="oe_highlight"/> <button name="action_cancel" states="except_picking,except_invoice" string="Cancel" type="object" /> @@ -214,12 +214,16 @@ <notebook> <page string="Purchase Order"> <field name="order_line"> - <tree string="Purchase Order Lines"> - <field name="date_planned"/> + <tree string="Purchase Order Lines" editable="bottom"> + <field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/> <field name="name"/> - <field name="product_qty"/> - <field name="product_uom" groups="product.group_uom"/> + <field name="date_planned"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + <field name="account_analytic_id" groups="purchase.group_analytic_accounting" domain="[('parent_id','!=',False)]"/> + <field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/> + <field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/> <field name="price_unit"/> + <field name="taxes_id" widget="many2many_tags" domain="[('parent_id','=',False),('type_tax_use','!=','sale')]"/> <field name="price_subtotal"/> </tree> </field> @@ -298,7 +302,7 @@ <field name="arch" type="xml"> <search string="Search Purchase Order"> <field name="name" string="Reference"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-document-new" name="draft" string="Quotations" domain="[('state','=','draft')]" help="Purchase orders which are in draft state"/> <filter icon="terp-check" name="approved" string="Purchase Orders" domain="[('state','not in',('draft','cancel'))]" help="Approved purchase orders"/> @@ -323,8 +327,8 @@ <field name="name">purchase.order.tree</field> <field name="model">purchase.order</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="grey:state=='cancel';blue:state in ('confirmed');red:state in ('except_invoice','except_picking')" string="Purchase Order"> - <field name="needaction_pending" invisible="1"/> + <tree fonts="bold:message_unread==True" colors="grey:state=='cancel';blue:state in ('wait','confirmed');red:state in ('except_invoice','except_picking')" string="Purchase Order"> + <field name="message_unread" invisible="1"/> <field name="name" string="Reference"/> <field name="date_order" /> <field name="partner_id"/> @@ -343,6 +347,7 @@ <field name="type">ir.actions.act_window</field> <field name="res_model">purchase.order</field> <field name="context">{'search_default_draft': 1}</field> + <field name="domain">[('state','in',('draft','sent','confirmed'))]</field> <field name="view_mode">tree,form,graph,calendar</field> <field name="search_view_id" ref="view_purchase_order_filter"/> <field name="help" type="html"> @@ -367,7 +372,8 @@ <field name="type">ir.actions.act_window</field> <field name="res_model">purchase.order</field> <field name="view_mode">tree,form,graph,calendar</field> - <field name="context">{'search_default_approved': 1}</field> + <field name="context">{}</field> + <field name="domain">[('state','not in',('draft','sent','confirmed'))]</field> <field name="search_view_id" ref="view_purchase_order_filter"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> @@ -508,7 +514,7 @@ <record id="purchase_line_form_action2" model="ir.actions.act_window"> - <field name="name">Based on Purchase Order Lines</field> + <field name="name">On Purchase Order Lines</field> <field name="type">ir.actions.act_window</field> <field name="res_model">purchase.order.line</field> <field name="domain">[('order_id.invoice_method','<>','picking'), ('state','in',('confirmed','done')), ('invoiced', '=', False)]</field> @@ -562,11 +568,10 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> - <xpath expr="//page[@string='Descriptions']" position="before"> - <page string="Suppliers" groups="base.group_user"> - <field name="seller_ids" context="{'uom_id': uom_id}"/> - </page> - </xpath> + <group name="procurement" position="after"> + <separator string="Suppliers"/> + <field name="seller_ids" context="{'uom_id': uom_id}"/> + </group> </field> </record> </data> diff --git a/addons/purchase/stock_view.xml b/addons/purchase/stock_view.xml index ad1c26a84c1..b522618ed2e 100644 --- a/addons/purchase/stock_view.xml +++ b/addons/purchase/stock_view.xml @@ -109,7 +109,7 @@ </record> <record id="action_picking_tree4_picking_to_invoice" model="ir.actions.act_window"> - <field name="name">Based on Incoming Shipments</field> + <field name="name">On Incoming Shipments</field> <field name="res_model">stock.picking</field> <field name="type">ir.actions.act_window</field> <field name="view_type">form</field> diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index cf0294730a7..90a5244e66f 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -31,7 +31,7 @@ import decimal_precision as dp class purchase_requisition(osv.osv): _name = "purchase.requisition" _description="Purchase Requisition" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _columns = { 'name': fields.char('Requisition Reference', size=32,required=True), 'origin': fields.char('Source', size=32), @@ -91,16 +91,16 @@ class purchase_requisition(osv.osv): return True def in_progress_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Draft Requisition has been <b>sent to suppliers</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Draft Requisition has been <b>sent to suppliers</b>."), context=context) def reset_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Purchase Requisition has been set to <b>draft</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Purchase Requisition has been set to <b>draft</b>."), context=context) def done_to_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Purchase Requisition has been <b>done</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Purchase Requisition has been <b>done</b>."), context=context) def cancel_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Purchase Requisition has been <b>cancelled</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Purchase Requisition has been <b>cancelled</b>."), context=context) def _planned_date(self, requisition, delay=0.0): company = requisition.company_id @@ -184,7 +184,7 @@ class purchase_requisition(osv.osv): return res def create_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body=_("Purchase Requisition has been <b>created</b>."), context=context) + return self.message_post(cr, uid, ids, body=_("Purchase Requisition has been <b>created</b>."), context=context) def create(self, cr, uid, vals, context=None): requisition = super(purchase_requisition, self).create(cr, uid, vals, context=context) @@ -194,27 +194,6 @@ class purchase_requisition(osv.osv): purchase_requisition() -class mail_message(osv.osv): - _inherit = 'mail.message' - - def schedule_with_attach(self, cr, uid, email_from, email_to, subject, body, model=False, email_cc=None, - email_bcc=None, reply_to=False, attachments=None, message_id=False, references=False, - res_id=False, content_subtype='plain', headers=None, mail_server_id=False, auto_delete=False, - context=None): - result = super(mail_message, self).schedule_with_attach(cr, uid, email_from, email_to, subject, body, model=model, email_cc=email_cc, - email_bcc=email_bcc, reply_to=reply_to, attachments=attachments, message_id=message_id, references=references, - res_id=res_id, content_subtype=content_subtype, headers=headers, mail_server_id=mail_server_id, auto_delete=auto_delete, - context=context) - # check model is purchase.order - if model and model == 'purchase.order' and res_id: - requisition_id = self.pool.get('purchase.order').browse(cr, uid, res_id, context=context).requisition_id - if requisition_id: - result = self.schedule_with_attach(cr, uid, email_from, email_to, subject, body, model='purchase.requisition', email_cc=email_cc, - email_bcc=email_bcc, reply_to=reply_to, attachments=attachments, message_id=message_id, references=references, - res_id=requisition_id.id, content_subtype=content_subtype, headers=headers, mail_server_id=mail_server_id, auto_delete=auto_delete, - context=context) - return result - class purchase_requisition_line(osv.osv): _name = "purchase.requisition.line" diff --git a/addons/purchase_requisition/purchase_requisition_view.xml b/addons/purchase_requisition/purchase_requisition_view.xml index a1b270934e2..1fafec92bc7 100644 --- a/addons/purchase_requisition/purchase_requisition_view.xml +++ b/addons/purchase_requisition/purchase_requisition_view.xml @@ -112,8 +112,8 @@ <field name="name">purchase.requisition.tree</field> <field name="model">purchase.requisition</field> <field name="arch" type="xml"> - <tree fonts="bold:needaction_pending==True" colors="grey:state == 'cancel';red:date_end and date_end<current_date;black:date_end>current_date;" string="Purchase Requisition"> - <field name="needaction_pending" invisible="1"/> + <tree fonts="bold:message_unread==True" colors="grey:state == 'cancel';red:date_end and date_end<current_date;black:date_end>current_date;" string="Purchase Requisition"> + <field name="message_unread" invisible="1"/> <field name="name"/> <field name="date_start"/> <field name="user_id"/> @@ -183,9 +183,7 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> <field name="procure_method" position="before"> - <group colspan="2" col="2" attrs="{'invisible': [('supply_method','<>','buy')]}"> <field name="purchase_requisition"/> - </group> </field> </field> </record> diff --git a/addons/report_intrastat/report_intrastat_view.xml b/addons/report_intrastat/report_intrastat_view.xml index 4f02c8f8c75..8d8da4a078f 100644 --- a/addons/report_intrastat/report_intrastat_view.xml +++ b/addons/report_intrastat/report_intrastat_view.xml @@ -84,7 +84,7 @@ <field name="weight"/> <field name="value"/> <field name="supply_units"/> - <field name="currency_id"/> + <field name="currency_id" groups="base.group_multi_currency"/> <field name="type"/> </tree> </field> diff --git a/addons/report_webkit_sample/i18n/ru.po b/addons/report_webkit_sample/i18n/ru.po new file mode 100644 index 00000000000..82fde059f51 --- /dev/null +++ b/addons/report_webkit_sample/i18n/ru.po @@ -0,0 +1,123 @@ +# Russian translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR <EMAIL@ADDRESS>, 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" +"POT-Creation-Date: 2012-02-08 00:37+0000\n" +"PO-Revision-Date: 2012-09-10 14:37+0000\n" +"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n" +"Language-Team: Russian <ru@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:37 +msgid "Refund" +msgstr "Возврат" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:22 +msgid "Fax" +msgstr "Факс" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "Disc.(%)" +msgstr "Дисконт (%)" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:19 +msgid "Tel" +msgstr "Тел" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "Description" +msgstr "Описание" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:35 +msgid "Supplier Invoice" +msgstr "Счет поставщика" + +#. module: report_webkit_sample +#: model:ir.actions.report.xml,name:report_webkit_sample.report_webkit_html +msgid "WebKit invoice" +msgstr "Счет Webkit" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "Price" +msgstr "Цена" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:44 +msgid "Invoice Date" +msgstr "Дата счёта" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "Taxes" +msgstr "Налоги" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "QTY" +msgstr "Кол-во" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:25 +msgid "Email" +msgstr "Эл. почта" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:64 +msgid "Amount" +msgstr "Сумма" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:64 +msgid "Base" +msgstr "" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:33 +msgid "Invoice" +msgstr "Счет" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:39 +msgid "Supplier Refund" +msgstr "Возврат средств от поставщика" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:44 +msgid "Document" +msgstr "Документ" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:49 +msgid "Unit Price" +msgstr "Цена за ед." + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:44 +msgid "Partner Ref." +msgstr "Ссылка на контрагента" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:28 +msgid "VAT" +msgstr "НДС" + +#. module: report_webkit_sample +#: report:addons/report_webkit_sample/report/report_webkit_html.mako:76 +msgid "Total" +msgstr "Всего" diff --git a/addons/sale/board_sale_view.xml b/addons/sale/board_sale_view.xml index 702bc0c61cc..4839744c6d2 100644 --- a/addons/sale/board_sale_view.xml +++ b/addons/sale/board_sale_view.xml @@ -51,7 +51,7 @@ <form string="Sales Dashboard" version="7.0"> <board style="2-1"> <column> - <action name="%(action_quotation_for_sale)d" string="My Quotations" creatable="true"/> + <action name="%(action_quotation_for_sale)d" string="My Quotations"/> </column> <column> <action name="%(action_turnover_by_month)d" string="Monthly Turnover"/> diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index 181bd8183d7..c818a3a22ab 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -47,7 +47,7 @@ <p>Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''},</p> - <p>Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from {$object.company_id.name}: </p> + <p>Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}: </p> <p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> @@ -126,67 +126,6 @@ </div> </div> ]]></field> - <field name="body_text"><![CDATA[ -Hello${object.partner_id.name and ' ' or ''}${object.partner_id.name or ''}, - -Here is your ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'} from ${object.company_id.name}: - | Order number: *${object.name}* - | Order total: *${object.amount_total} ${object.pricelist_id.currency_id.name}* - | Order date: ${object.date_order} - % if object.origin: - | Order reference: ${object.origin} - % endif - % if object.client_order_ref: - | Your reference: ${object.client_order_ref}<br /> - % endif - | Your contact: ${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} - -You can view the ${object.state in ('draft', 'sent') and 'quotation' or 'order confirmation'}, download it and even pay online using the following link: - ${ctx.get('edi_web_url_view') or 'n/a'} - -% if object.order_policy in ('prepaid','manual') and object.company_id.paypal_account and object.state not in ('draft', 'sent'): -<% -comp_name = quote(object.company_id.name) -order_name = quote(object.name) -paypal_account = quote(object.company_id.paypal_account) -order_amount = quote(str(object.amount_total)) -cur_name = quote(object.pricelist_id.currency_id.name) -paypal_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s&invoice=%s&amount=%s" \ - "¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow_%s" % \ - (paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_name) -%> -It is also possible to directly pay with Paypal: - ${paypal_url} -% endif - -If you have any question, do not hesitate to contact us. - - -Thank you for choosing ${object.company_id.name}! - - --- -${object.user_id.name} ${object.user_id.email and '<%s>'%(object.user_id.email) or ''} -${object.company_id.name} -% if object.company_id.street: -${object.company_id.street or ''} -% endif -% if object.company_id.street2: -${object.company_id.street2} -% endif -% if object.company_id.city or object.company_id.zip: -${object.company_id.zip or ''} ${object.company_id.city or ''} -% endif -% if object.company_id.country_id: -${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''} -% endif -% if object.company_id.phone: -Phone: ${object.company_id.phone} -% endif -% if object.company_id.website: -${object.company_id.website or ''} -% endif - ]]></field> </record> </data> </openerp> diff --git a/addons/sale/i18n/ar.po b/addons/sale/i18n/ar.po index 684963d5bde..812f921b85f 100644 --- a/addons/sale/i18n/ar.po +++ b/addons/sale/i18n/ar.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1121,7 +1121,7 @@ msgid "Document of the move to the output or to the customer." msgstr "وثيقة الانتقال الى الخروج او اللى العميل." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/bg.po b/addons/sale/i18n/bg.po index 1441f8a5120..571dde6252f 100644 --- a/addons/sale/i18n/bg.po +++ b/addons/sale/i18n/bg.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1101,7 +1101,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/bs.po b/addons/sale/i18n/bs.po index 06cc830bf41..a41483e0a3d 100644 --- a/addons/sale/i18n/bs.po +++ b/addons/sale/i18n/bs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1095,7 +1095,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/ca.po b/addons/sale/i18n/ca.po index 14e3c3acd52..88360a3ac56 100644 --- a/addons/sale/i18n/ca.po +++ b/addons/sale/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1141,7 +1141,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Document del moviment a la sortida o al client." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/cs.po b/addons/sale/i18n/cs.po index 7e6739338f0..4164a564d77 100644 --- a/addons/sale/i18n/cs.po +++ b/addons/sale/i18n/cs.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" "X-Poedit-Language: Czech\n" #. module: sale @@ -1103,7 +1103,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokument přesunu na výstup nebo k zákazníkovi" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/da.po b/addons/sale/i18n/da.po index 158b9dcefe0..b368a2a0fa8 100644 --- a/addons/sale/i18n/da.po +++ b/addons/sale/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1102,7 +1102,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/de.po b/addons/sale/i18n/de.po index 74faa4a11ea..ccc728a1bb6 100644 --- a/addons/sale/i18n/de.po +++ b/addons/sale/i18n/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -338,6 +338,19 @@ msgid "" " - The 'Invoice based on deliveries' choice is used to create an invoice " "during the picking process." msgstr "" +"Die Regeln zur Rechnungserstellung stimmen Rechnungs- und " +"Lieferscheinerstellung ab.\n" +" - \"Zahlung vor Auslieferung\" erstellt zuerst die Rechnung. Der " +"Lieferschein, und damit die Kommissionierung, erfolgen erst nach " +"Zahlungseingang.\n" +" - \"Kommissionierung & Rechnung aus Abruf\" erstellt sofort einen " +"Lieferschein zur (Vor-)Kommissionierung. Danach wird auf den manuellen " +"Rechnungsentwurf gemäss Verkaufsauftrag oder Auftragspositionen gewartet.\n" +" - Bei \"Rechnungserstellung nach Auslieferung\" erfolgt die " +"Rechnungsstellung erst nach erfolgreicher Auslieferung aller " +"Teillieferungen.\n" +" - \"Rechnung bei Kommissionierung\" erstellt im Moment der " +"Kommissionierung eine zugehörige Rechnung." #. module: sale #: code:addons/sale/sale.py:1171 @@ -898,6 +911,14 @@ msgid "" "The 'Waiting Schedule' state is set when the invoice is confirmed but " "waiting for the scheduler to run on the order date." msgstr "" +"Liefert den Status der Angebotsanfrage oder der Verkaufsauftrags.\n" +"Der Ausnahme-Status wird bei Abbruch eines Vorgangs automatisch gesetzt. Bei " +"Rechnungsprüfung wird einem Rechnungsabbruch folgend und bei " +"Kommissionierungsfehlern oder Auslieferungsfehlern der Lieferungsabbruch zu " +"dieser Ausnahme führen.\n" +"Der Zustand \"Warte auf Terminierung\" wird erreicht, wenn eine Rechnung " +"zwar erstellt und bestätigt wurde, aber die Auftrags-Terminierung diesen " +"Auftrag noch nicht abgearbeitet hat." #. module: sale #: code:addons/sale/sale.py:1272 @@ -1163,7 +1184,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Beleg für Warenversand oder Auslieferung" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " @@ -1299,6 +1320,9 @@ msgid "" "create a task, purchase order or manufacturing order linked to this sale " "order line." msgstr "" +"Wenn \"Auf Abruf\", so wird eine Beschaffung ausgelöst sobald der " +"Verkaufsauftrag bestätigt wurde. Somit wird eine Anforderung zum Einkauf " +"oder eine Fertigung erzeugt." #. module: sale #: field:sale.advance.payment.inv,amount:0 @@ -1450,6 +1474,10 @@ msgid "" "Here is a proposition of quantities according to the packaging:\n" "EAN: %s Quantity: %s Type of ul: %s" msgstr "" +"Sie haben die Anzahl von %d Einheiten gewählt.\n" +"Dies ist mit der ausgewählten Verpackung nicht vereinbar.\n" +"Ein Vorschlag der Verpackung entsprechend wäre:\n" +"EAN: %s Anzahl: %s Art: %s" #. module: sale #: view:sale.order:0 diff --git a/addons/sale/i18n/el.po b/addons/sale/i18n/el.po index 4a0eedb91c0..54ce749d11e 100644 --- a/addons/sale/i18n/el.po +++ b/addons/sale/i18n/el.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1108,7 +1108,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/es.po b/addons/sale/i18n/es.po index ea9898a9759..969fff34188 100644 --- a/addons/sale/i18n/es.po +++ b/addons/sale/i18n/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1149,7 +1149,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento del movimiento a la salida o al cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/es_AR.po b/addons/sale/i18n/es_AR.po index 77d5671b509..79290addc6c 100644 --- a/addons/sale/i18n/es_AR.po +++ b/addons/sale/i18n/es_AR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1095,7 +1095,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/es_CL.po b/addons/sale/i18n/es_CL.po index ce1098e192e..ef73d4445ad 100644 --- a/addons/sale/i18n/es_CL.po +++ b/addons/sale/i18n/es_CL.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1144,7 +1144,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento del movimiento a la salida o al cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/es_CR.po b/addons/sale/i18n/es_CR.po index df804d6d213..c50f084e181 100644 --- a/addons/sale/i18n/es_CR.po +++ b/addons/sale/i18n/es_CR.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" "Language: \n" #. module: sale @@ -1176,7 +1176,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento del movimiento a la salida o al cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/es_EC.po b/addons/sale/i18n/es_EC.po index a1d8996de8c..836615b8649 100644 --- a/addons/sale/i18n/es_EC.po +++ b/addons/sale/i18n/es_EC.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1110,7 +1110,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento del movimiento de la salida o del cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/et.po b/addons/sale/i18n/et.po index a6b0cd9a6b3..f30ef9345e7 100644 --- a/addons/sale/i18n/et.po +++ b/addons/sale/i18n/et.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: model:process.node,name:sale.process_node_deliveryorder0 @@ -1135,7 +1135,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/fi.po b/addons/sale/i18n/fi.po index ceb1570a393..79e723a5861 100644 --- a/addons/sale/i18n/fi.po +++ b/addons/sale/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1137,7 +1137,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokumentti siirrosta kohdepaikkaan tai asiakkalle." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/fr.po b/addons/sale/i18n/fr.po index 1fe8249abe5..2b4f026b882 100644 --- a/addons/sale/i18n/fr.po +++ b/addons/sale/i18n/fr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: code:addons/sale/sale.py:655 @@ -1206,7 +1206,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Document du mouvement vers la sortie ou vers le client." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/gl.po b/addons/sale/i18n/gl.po index 060cad40618..112ba8ea10c 100644 --- a/addons/sale/i18n/gl.po +++ b/addons/sale/i18n/gl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1099,7 +1099,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/hr.po b/addons/sale/i18n/hr.po index ea5059ef463..d1f6b210386 100644 --- a/addons/sale/i18n/hr.po +++ b/addons/sale/i18n/hr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" "Language: hr\n" #. module: sale @@ -1127,7 +1127,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokument premještaja na izlaz ili kod kupca." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/hu.po b/addons/sale/i18n/hu.po index f9f62d36f15..6e5538893ba 100644 --- a/addons/sale/i18n/hu.po +++ b/addons/sale/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1145,7 +1145,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokumentum a gyártásnak vagy a vevőnek irányuló árumozgásról." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/id.po b/addons/sale/i18n/id.po index f75b1f5a764..221dc70c76a 100644 --- a/addons/sale/i18n/id.po +++ b/addons/sale/i18n/id.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1137,7 +1137,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokumen pindah ke output atau kepada pelanggan" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/is.po b/addons/sale/i18n/is.po index 2b28148be9c..c75b587321a 100644 --- a/addons/sale/i18n/is.po +++ b/addons/sale/i18n/is.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1109,7 +1109,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/it.po b/addons/sale/i18n/it.po index 675c45f3262..e45c3253c23 100644 --- a/addons/sale/i18n/it.po +++ b/addons/sale/i18n/it.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1142,7 +1142,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento del movimento verso area di uscita o verso area cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/ja.po b/addons/sale/i18n/ja.po index 94f46d419ea..c774975f3a7 100644 --- a/addons/sale/i18n/ja.po +++ b/addons/sale/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1116,7 +1116,7 @@ msgid "Document of the move to the output or to the customer." msgstr "製造品へのあるいは顧客への移動のドキュメント" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/ko.po b/addons/sale/i18n/ko.po index 0bfbbefbbf9..ca5a6f9641b 100644 --- a/addons/sale/i18n/ko.po +++ b/addons/sale/i18n/ko.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/lo.po b/addons/sale/i18n/lo.po index a02f0e850f3..ce13dc2f73b 100644 --- a/addons/sale/i18n/lo.po +++ b/addons/sale/i18n/lo.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/lt.po b/addons/sale/i18n/lt.po index e38dd23f746..b4a73722c78 100644 --- a/addons/sale/i18n/lt.po +++ b/addons/sale/i18n/lt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1116,7 +1116,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/lv.po b/addons/sale/i18n/lv.po index a7db8d69dd9..2fc27ce217e 100644 --- a/addons/sale/i18n/lv.po +++ b/addons/sale/i18n/lv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/mn.po b/addons/sale/i18n/mn.po index a16728c99b5..b1de7eb2e73 100644 --- a/addons/sale/i18n/mn.po +++ b/addons/sale/i18n/mn.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1156,7 +1156,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Гарах байрлал эсвэл захиалагч руу хөдөлгөөний баримт" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/nb.po b/addons/sale/i18n/nb.po index 66c4a397314..5ad4b51a646 100644 --- a/addons/sale/i18n/nb.po +++ b/addons/sale/i18n/nb.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -505,6 +505,8 @@ msgid "" "Select a product of type service which is called 'Advance Product'. You may " "have to create it and set it as a default value on this field." msgstr "" +"Velg et produkt av typen tjeneste som kalles 'Forhånsfakturert produkt'. Du " +"må kanskje opprette den og sette den som en standar verdi på dette feltet." #. module: sale #: report:sale.order:0 @@ -518,6 +520,8 @@ msgid "" "You cannot make an advance on a sales order " "that is defined as 'Automatic Invoice after delivery'." msgstr "" +"Du kan ikke lage et forskudd på en salgsordre som er definert som " +"'Automatisk faktura etter levering'." #. module: sale #: view:sale.order:0 field:sale.order,note:0 view:sale.order.line:0 @@ -856,6 +860,8 @@ msgid "" "It indicates that the sales order has been delivered. This field is updated " "only after the scheduler(s) have been launched." msgstr "" +"Den indikerer at salgsordren har blitt levert. Dette feltet blir oppdatert " +"kun etter at planleggeren(e) har blitt lansert." #. module: sale #: field:sale.order,date_order:0 @@ -887,6 +893,11 @@ msgid "" "The 'Waiting Schedule' state is set when the invoice is confirmed but " "waiting for the scheduler to run on the order date." msgstr "" +"Viser status for forespørsel eller salgsordre.\n" +"Statusen 'Unntak' settes automatisk når det skjer en avbrytelse ved " +"fakturering (faktureringsunntak) eller plukking (forsendelsesunntak).\n" +"Statusen 'Avventer planlegging' settes når faktura er bekreftet men ordren " +"venter på kjøring av planleggeren." #. module: sale #: code:addons/sale/sale.py:1272 @@ -1003,7 +1014,7 @@ msgstr "Åpne faktura" #. module: sale #: model:ir.actions.server,name:sale.ir_actions_server_edi_sale msgid "Auto-email confirmed sale orders" -msgstr "" +msgstr "Auto e-post bekreftede salgsordre" #. module: sale #: code:addons/sale/sale.py:413 @@ -1144,10 +1155,10 @@ msgstr "Sett til forslag" #. module: sale #: model:process.node,note:sale.process_node_packinglist0 msgid "Document of the move to the output or to the customer." -msgstr "" +msgstr "Dokumentering av bevegelse til utgående lokasjon eller kunde." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " @@ -1405,7 +1416,7 @@ msgstr "" #. module: sale #: model:ir.model,name:sale.model_sale_order_line_make_invoice msgid "Sale OrderLine Make_invoice" -msgstr "" +msgstr "Sale OrderLine Make_invoice" #. module: sale #: selection:sale.order,state:0 selection:sale.report,state:0 @@ -1445,7 +1456,7 @@ msgstr "" #. module: sale #: view:sale.order:0 msgid "Recreate Packing" -msgstr "" +msgstr "Gjenskap pakking" #. module: sale #: view:sale.order:0 field:sale.order.line,property_ids:0 @@ -1537,7 +1548,7 @@ msgstr "Regler for plukking" #. module: sale #: model:process.node,note:sale.process_node_deliveryorder0 msgid "Document of the move to the customer." -msgstr "" +msgstr "Dokument av forsendelsen til kunden." #. module: sale #: help:sale.order,amount_untaxed:0 @@ -1830,6 +1841,9 @@ msgid "" "parts to the sales order. There is 1 pick list by sales order line which " "evolves with the availability of parts." msgstr "" +"Plukklisten dannes umiddelbart når en salgsordre bekreftes, samtidig med " +"anskaffelsesordren. Den tilordner varer til salgsordren. Det dannes en " +"plukkliste til salgsordren som utvides løpende med tilgjengelige produkter." #. module: sale #: selection:sale.order.line,state:0 @@ -1909,7 +1923,7 @@ msgstr "" #. module: sale #: view:sale.order.line:0 msgid "Sale order lines done" -msgstr "" +msgstr "Salgsordrelinjer ferdig" #. module: sale #: field:sale.order.line,th_weight:0 @@ -1939,6 +1953,9 @@ msgid "" "production of products regarding to the rules and to the sales order's " "parameters. " msgstr "" +"En anskaffelsesordre dannes automatisk når en salgsordre bekreftes eller " +"faktura er betalt. Denne driver innkjøp og/eller produksjon av produktet i " +"henhold til anskaffelsesregler og parametere på salgsordren. " #. module: sale #: view:sale.order.line:0 @@ -2008,7 +2025,7 @@ msgstr "MVA beløp." #. module: sale #: view:sale.order:0 msgid "Packings" -msgstr "" +msgstr "Pakkinger" #. module: sale #: view:sale.order.line:0 @@ -2035,7 +2052,7 @@ msgstr "Salgsordre" #. module: sale #: model:ir.model,name:sale.model_sale_shop view:sale.shop:0 msgid "Sales Shop" -msgstr "" +msgstr "Butikk" #. module: sale #: selection:sale.report,month:0 @@ -2045,7 +2062,7 @@ msgstr "November" #. module: sale #: field:sale.advance.payment.inv,product_id:0 msgid "Advance Product" -msgstr "" +msgstr "Forhåndsfakturert produkt" #. module: sale #: view:sale.order:0 @@ -2073,16 +2090,17 @@ msgstr "Salgsordre i arbeid" #: help:sale.order,origin:0 msgid "Reference of the document that generated this sales order request." msgstr "" +"Referanse til dokumentet som genererte denne salgsordreforespørselen." #. module: sale #: view:sale.report:0 field:sale.report,delay:0 msgid "Commitment Delay" -msgstr "" +msgstr "Bekreftelsesforsinkelse" #. module: sale #: selection:sale.order,order_policy:0 msgid "Deliver & invoice on demand" -msgstr "" +msgstr "Lever og fakturer på etterspørsel" #. module: sale #: model:process.node,note:sale.process_node_saleprocurement0 @@ -2090,6 +2108,7 @@ msgid "" "One Procurement order for each sales order line and for each of the " "components." msgstr "" +"En anskaffelsesordre for hver salgsordrelinje og for hver av komponentene." #. module: sale #: model:process.transition.action,name:sale.process_transition_action_assign0 @@ -2228,6 +2247,11 @@ msgid "" "having invoiced yet. If you want to analyse your turnover, you should use " "the Invoice Analysis report in the Accounting application." msgstr "" +"Denne rapporten analyserer dine sitater og salgsordrer. Analysen sjekker " +"salgsinntekter og sorterer de ved annen gruppe kriterier (selger, partner, " +"produkt, etc.) Bruk denne rapporten til å utføre analyser på salg du ikke " +"har fakturert enda. Hvis du ønsker å analysere din omsetning, bør du bruke " +"fakturaen analyserapporten i Regnskap søknaden." #. module: sale #: report:sale.order:0 diff --git a/addons/sale/i18n/nl.po b/addons/sale/i18n/nl.po index 3c9dba18e1e..0a443827877 100644 --- a/addons/sale/i18n/nl.po +++ b/addons/sale/i18n/nl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1172,7 +1172,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Document van de mutatie naar de expeditie of naar de klant." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/nl_BE.po b/addons/sale/i18n/nl_BE.po index d20fb064414..8e13f94c004 100644 --- a/addons/sale/i18n/nl_BE.po +++ b/addons/sale/i18n/nl_BE.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1165,7 +1165,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Document van de verplaatsing naar de verzendzone of naar de klant." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/oc.po b/addons/sale/i18n/oc.po index f9a2e950bff..bf3da283f04 100644 --- a/addons/sale/i18n/oc.po +++ b/addons/sale/i18n/oc.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/pl.po b/addons/sale/i18n/pl.po index 0ca3e71eab5..fb7a4b602ff 100644 --- a/addons/sale/i18n/pl.po +++ b/addons/sale/i18n/pl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1157,7 +1157,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokument przesunięcia do klienta." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/pt.po b/addons/sale/i18n/pt.po index 0cc88911287..b01e36597d4 100644 --- a/addons/sale/i18n/pt.po +++ b/addons/sale/i18n/pt.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1165,7 +1165,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento do movimento para a saída ou para o cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/pt_BR.po b/addons/sale/i18n/pt_BR.po index ccb754d681f..eea6b03757e 100644 --- a/addons/sale/i18n/pt_BR.po +++ b/addons/sale/i18n/pt_BR.po @@ -15,8 +15,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1171,7 +1171,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documento de movimento para a saída ou para o cliente." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/ro.po b/addons/sale/i18n/ro.po index bacd919cb9c..b8aa0abdf93 100644 --- a/addons/sale/i18n/ro.po +++ b/addons/sale/i18n/ro.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1173,7 +1173,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Documentul miscarii catre iesire sau catre client." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/ru.po b/addons/sale/i18n/ru.po index 2070d48b0e5..2af399a3226 100644 --- a/addons/sale/i18n/ru.po +++ b/addons/sale/i18n/ru.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-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-05-10 17:51+0000\n" -"Last-Translator: Raphael Collet (OpenERP) <Unknown>\n" +"PO-Revision-Date: 2012-09-10 14:56+0000\n" +"Last-Translator: Chertykov Denis <chertykov@gmail.com>\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: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 msgid "Based on Timesheet" -msgstr "" +msgstr "Основано на затратах времени" #. module: sale #: view:sale.order.line:0 @@ -27,6 +27,8 @@ msgid "" "Sale Order Lines that are confirmed, done or in exception state and haven't " "yet been invoiced" msgstr "" +"Позиции заказа на продажу в состоянии \"Подтвержден\", \"Cделано\" или " +"\"Исключение\" и на которые не был выставлен счет" #. module: sale #: view:board.board:0 @@ -78,7 +80,7 @@ msgstr "Запрос цен '%s' был преобразован в заказ #. module: sale #: view:sale.order:0 msgid "Print Quotation" -msgstr "" +msgstr "Печать предложения цен" #. module: sale #: code:addons/sale/wizard/sale_make_invoice.py:42 @@ -89,7 +91,7 @@ msgstr "Внимание!" #. module: sale #: report:sale.order:0 msgid "Tax" -msgstr "" +msgstr "Налог" #. module: sale #: model:process.node,note:sale.process_node_saleorderprocurement0 @@ -123,6 +125,9 @@ msgid "" "cancel a sale order, you must first cancel related picking or delivery " "orders." msgstr "" +"Для удаления подтвержденного заказа на продажу надо сначала его отменить ! " +"Для отмены заказа на продажу надо отменить его комплектование или заказ на " +"доставку." #. module: sale #: model:process.node,name:sale.process_node_saleprocurement0 @@ -137,7 +142,7 @@ msgstr "Контрагент" #. module: sale #: selection:sale.order,order_policy:0 msgid "Invoice based on deliveries" -msgstr "" +msgstr "Счет основанный на отгрузке" #. module: sale #: view:sale.order:0 @@ -187,12 +192,12 @@ msgstr "Условия платежа по умолчанию" #. module: sale #: field:sale.config.picking_policy,deli_orders:0 msgid "Based on Delivery Orders" -msgstr "" +msgstr "Основанный на заказе на отгрузку" #. module: sale #: field:sale.config.picking_policy,time_unit:0 msgid "Main Working Time Unit" -msgstr "" +msgstr "Основная единица рабочего времени" #. module: sale #: view:sale.order:0 view:sale.order.line:0 field:sale.order.line,state:0 @@ -218,7 +223,7 @@ msgstr "Отметьте для группирования счетов по зÐ #. module: sale #: view:sale.order:0 msgid "My Sale Orders" -msgstr "" +msgstr "Мои заказы на продажу" #. module: sale #: selection:sale.order,invoice_quantity:0 @@ -267,7 +272,7 @@ msgstr "" #. module: sale #: model:ir.actions.act_window,name:sale.act_res_partner_2_sale_order msgid "Quotations and Sales" -msgstr "" +msgstr "Предложения цен и продажи" #. module: sale #: model:ir.model,name:sale.model_sale_make_invoice @@ -278,7 +283,7 @@ msgstr "Продажи - создать счет" #: code:addons/sale/sale.py:330 #, python-format msgid "Pricelist Warning!" -msgstr "" +msgstr "Предупреждение по прайс листу!" #. module: sale #: field:sale.order.line,discount:0 @@ -344,7 +349,7 @@ msgstr "Продажи в состоянии \"Исключение\"" #: code:addons/sale/wizard/sale_make_invoice_advance.py:70 #, python-format msgid "Configuration Error !" -msgstr "" +msgstr "Ошибка конфигурации !" #. module: sale #: view:sale.order:0 @@ -406,7 +411,7 @@ msgstr "Октябрь" #. module: sale #: sql_constraint:stock.picking:0 msgid "Reference must be unique per Company!" -msgstr "" +msgstr "Ссылка должна быть уникальна для каждой компании!" #. module: sale #: view:board.board:0 view:sale.order:0 view:sale.report:0 @@ -469,7 +474,7 @@ msgstr "" #: code:addons/sale/sale.py:1074 #, python-format msgid "You cannot cancel a sale order line that has already been invoiced!" -msgstr "" +msgstr "Нельзя отменить позицию заказа на продажу, которая уже в счете!" #. module: sale #: code:addons/sale/sale.py:1079 @@ -519,7 +524,7 @@ msgstr "Примечания" #. module: sale #: sql_constraint:res.company:0 msgid "The company name must be unique !" -msgstr "" +msgstr "Название компании должно быть уникальным!" #. module: sale #: help:sale.order,partner_invoice_id:0 @@ -529,12 +534,12 @@ msgstr "Адрес счета для текущего заказа на прод #. module: sale #: view:sale.report:0 msgid "Month-1" -msgstr "" +msgstr "Месяц-1" #. module: sale #: view:sale.report:0 msgid "Ordered month of the sales order" -msgstr "" +msgstr "Месяц заказа в заказе на продажу" #. module: sale #: code:addons/sale/sale.py:504 @@ -542,11 +547,12 @@ msgstr "" msgid "" "You cannot group sales having different currencies for the same partner." msgstr "" +"Нельзя сгруппировать продажи в разных валютах для одного контрагента." #. module: sale #: selection:sale.order,picking_policy:0 msgid "Deliver each product when available" -msgstr "" +msgstr "Отгрузка каждого товара по готовности." #. module: sale #: field:sale.order,invoiced_rate:0 field:sale.order.line,invoiced:0 @@ -581,12 +587,12 @@ msgstr "Март" #. module: sale #: constraint:stock.move:0 msgid "You can not move products from or to a location of the type view." -msgstr "" +msgstr "Нельзя переместить ТМЦ в/из место хранения типа вид." #. module: sale #: field:sale.config.picking_policy,sale_orders:0 msgid "Based on Sales Orders" -msgstr "" +msgstr "Основанный на заказах на продажу" #. module: sale #: help:sale.order,amount_total:0 @@ -606,7 +612,7 @@ msgstr "Адрес счета:" #. module: sale #: field:sale.order.line,sequence:0 msgid "Line Sequence" -msgstr "" +msgstr "Последовательность" #. module: sale #: model:process.transition,note:sale.process_transition_saleorderprocurement0 @@ -697,7 +703,7 @@ msgstr "Дата создания" #. module: sale #: model:ir.ui.menu,name:sale.menu_sales_configuration_misc msgid "Miscellaneous" -msgstr "" +msgstr "Прочее" #. module: sale #: model:ir.actions.act_window,name:sale.action_order_line_tree3 @@ -741,7 +747,7 @@ msgstr "Кол-во" #: code:addons/sale/sale.py:1327 #, python-format msgid "Hour" -msgstr "" +msgstr "Час" #. module: sale #: view:sale.order:0 @@ -762,7 +768,7 @@ msgstr "Все предложения" #. module: sale #: view:sale.config.picking_policy:0 msgid "Options" -msgstr "" +msgstr "Настройки" #. module: sale #: selection:sale.report,month:0 @@ -773,7 +779,7 @@ msgstr "Сентябрь" #: code:addons/sale/sale.py:632 #, python-format msgid "You cannot confirm a sale order which has no line." -msgstr "" +msgstr "Нельзя подтвердить заказ на продажу без товара" #. module: sale #: code:addons/sale/sale.py:1259 @@ -782,6 +788,8 @@ msgid "" "You have to select a pricelist or a customer in the sales form !\n" "Please set one before choosing a product." msgstr "" +"Перед выбором ТМЦ, \n" +"вы должны выбрать прайс-лист или заказчика в форме ввода продаж !" #. module: sale #: view:sale.report:0 field:sale.report,categ_id:0 @@ -851,7 +859,7 @@ msgstr "" #. module: sale #: field:sale.order,date_order:0 msgid "Date" -msgstr "" +msgstr "Дата" #. module: sale #: view:sale.report:0 @@ -883,7 +891,7 @@ msgstr "" #: code:addons/sale/sale.py:1272 #, python-format msgid "No valid pricelist line found ! :" -msgstr "" +msgstr "Нет верной позиции в прайс листе ! :" #. module: sale #: view:sale.order:0 @@ -893,7 +901,7 @@ msgstr "Журнал" #. module: sale #: selection:sale.order,order_policy:0 msgid "Invoice on order after delivery" -msgstr "" +msgstr "Счет на заказ после отгрузки" #. module: sale #: help:sale.order,invoice_ids:0 @@ -935,7 +943,7 @@ msgstr "Ссылки" #. module: sale #: view:sale.order.line:0 msgid "My Sales Order Lines" -msgstr "" +msgstr "Мои позиции заказов на продажу" #. module: sale #: model:process.transition.action,name:sale.process_transition_action_cancel0 @@ -949,7 +957,7 @@ msgstr "Отмена" #. module: sale #: sql_constraint:sale.order:0 msgid "Order Reference must be unique per Company!" -msgstr "" +msgstr "Ссылка на заказ должны быть уникальной для каждой компании!" #. module: sale #: model:process.transition,name:sale.process_transition_invoice0 @@ -967,7 +975,7 @@ msgstr "Всего без налогов" #. module: sale #: view:sale.order.line:0 msgid "Order reference" -msgstr "" +msgstr "Ссылка на заказ" #. module: sale #: view:sale.open.invoice:0 @@ -1019,7 +1027,7 @@ msgstr "На основании отгруженного или заказанн #. module: sale #: selection:sale.order,picking_policy:0 msgid "Deliver all products at once" -msgstr "" +msgstr "Отгрузка всех товаров за раз" #. module: sale #: field:sale.order,picking_ids:0 @@ -1056,7 +1064,7 @@ msgstr "Создать заказ на доставку" #: code:addons/sale/sale.py:1303 #, python-format msgid "Cannot delete a sales order line which is in state '%s'!" -msgstr "" +msgstr "Нельзя удалить позицию заказа на продажу со статусом '%s'!" #. module: sale #: view:sale.order:0 @@ -1076,7 +1084,7 @@ msgstr "Создать комплектовочный лист" #. module: sale #: view:sale.report:0 msgid "Ordered date of the sales order" -msgstr "" +msgstr "Дата заказа в заказе на продажу" #. module: sale #: view:sale.report:0 @@ -1136,7 +1144,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Документ перемещения на выход или заказчику." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " @@ -1238,7 +1246,7 @@ msgstr "Налоги" #. module: sale #: view:sale.order:0 msgid "Sales Order ready to be invoiced" -msgstr "" +msgstr "Заказ на продажу готов к выставлению счета" #. module: sale #: help:sale.order,create_date:0 @@ -1258,7 +1266,7 @@ msgstr "Создать счета" #. module: sale #: view:sale.report:0 msgid "Sales order created in current month" -msgstr "" +msgstr "Заказы на продажу, созданные в этом месяце" #. module: sale #: report:sale.order:0 @@ -1300,6 +1308,8 @@ msgid "" "If you change the pricelist of this order (and eventually the currency), " "prices of existing order lines will not be updated." msgstr "" +"Даже если вы измените прайс-лист этого заказа (и даже валюту), то цена " +"позиции заказа не обновиться." #. module: sale #: model:ir.model,name:sale.model_stock_picking @@ -1323,12 +1333,12 @@ msgstr "Нельзя отменить заказ на продажу !" #. module: sale #: view:sale.order:0 msgid "Qty(UoM)" -msgstr "" +msgstr "Кол-во(Ед.изм)" #. module: sale #: view:sale.report:0 msgid "Ordered Year of the sales order" -msgstr "" +msgstr "Год заказа в заказе на продажу" #. module: sale #: selection:sale.report,month:0 @@ -1349,7 +1359,7 @@ msgstr "Исключение при доставке" #: code:addons/sale/sale.py:1156 #, python-format msgid "Picking Information ! : " -msgstr "" +msgstr "Информация по комплектованию ! : " #. module: sale #: field:sale.make.invoice,grouped:0 @@ -1359,13 +1369,13 @@ msgstr "Сгруппировать счета" #. module: sale #: field:sale.order,order_policy:0 msgid "Invoice Policy" -msgstr "" +msgstr "Политика выставления счета" #. module: sale #: model:ir.actions.act_window,name:sale.action_config_picking_policy #: view:sale.config.picking_policy:0 msgid "Setup your Invoicing Method" -msgstr "" +msgstr "Настройка способа выставления счета" #. module: sale #: model:process.node,note:sale.process_node_invoice0 @@ -1383,6 +1393,9 @@ msgid "" "This tool will help you to install the right module and configure the system " "according to the method you use to invoice your customers." msgstr "" +"Этот инструмент поможет вам установить правильный модуль и настроить систему " +"в соответствии с методом который вы используете для выставления счетов своим " +"клиентам." #. module: sale #: model:ir.model,name:sale.model_sale_order_line_make_invoice @@ -1457,13 +1470,13 @@ msgstr "" #. module: sale #: view:sale.order.line:0 msgid "Confirmed sale order lines, not yet delivered" -msgstr "" +msgstr "Не доставленные подтвержденные позиции заказа на продажу" #. module: sale #: code:addons/sale/sale.py:473 #, python-format msgid "Customer Invoices" -msgstr "" +msgstr "Счета заказчику" #. module: sale #: model:process.process,name:sale.process_process_salesprocess0 @@ -1544,7 +1557,7 @@ msgstr "Месяц" #. module: sale #: model:email.template,subject:sale.email_template_edi_sale msgid "${object.company_id.name} Order (Ref ${object.name or 'n/a' })" -msgstr "" +msgstr "${object.company_id.name} Заказ (Ссылка ${object.name or 'n/a' })" #. module: sale #: view:sale.order.line:0 field:sale.order.line,product_id:0 @@ -1566,7 +1579,7 @@ msgstr "sale.config.picking_policy" #: view:account.invoice.report:0 view:board.board:0 #: model:ir.actions.act_window,name:sale.action_turnover_by_month msgid "Monthly Turnover" -msgstr "" +msgstr "Ежемесячный оборот" #. module: sale #: field:sale.order,invoice_quantity:0 @@ -1677,12 +1690,12 @@ msgstr "" #. module: sale #: selection:sale.order,order_policy:0 msgid "Pay before delivery" -msgstr "" +msgstr "Оплата перед отгрузкой" #. module: sale #: view:board.board:0 model:ir.actions.act_window,name:sale.open_board_sales msgid "Sales Dashboard" -msgstr "" +msgstr "Панель продаж" #. module: sale #: model:ir.actions.act_window,name:sale.action_sale_order_make_invoice @@ -1730,6 +1743,8 @@ msgid "" "Couldn't find a pricelist line matching this product and quantity.\n" "You have to change either the product, the quantity or the pricelist." msgstr "" +"Не удалось найти позицию в прайс-листе для этого товара и этого количества.\n" +"Вы должны изменить товар, количество или прайс-лист." #. module: sale #: help:sale.order,picking_ids:0 @@ -1756,7 +1771,7 @@ msgstr "Отменено" #. module: sale #: view:sale.order.line:0 msgid "Sales Order Lines related to a Sales Order of mine" -msgstr "" +msgstr "Позиции заказа на продажу относящиеся к моим заказам" #. module: sale #: model:ir.actions.act_window,name:sale.action_shop_form @@ -1799,7 +1814,7 @@ msgstr "Количество (UoS)" #. module: sale #: view:sale.order.line:0 msgid "Sale Order Lines that are in 'done' state" -msgstr "" +msgstr "Позиции заказа на продажу в состоянии «Сделано»" #. module: sale #: model:process.transition,note:sale.process_transition_packing0 @@ -1823,7 +1838,7 @@ msgstr "Подтвержден" #. module: sale #: field:sale.config.picking_policy,order_policy:0 msgid "Main Method Based On" -msgstr "" +msgstr "Основной метод основан на" #. module: sale #: model:process.transition.action,name:sale.process_transition_action_confirm0 @@ -1871,12 +1886,12 @@ msgstr "" #. module: sale #: view:sale.order:0 msgid "Print Order" -msgstr "" +msgstr "Печать заказа" #. module: sale #: view:sale.report:0 msgid "Sales order created in current year" -msgstr "" +msgstr "Заказы на продажу созданные в этом году" #. module: sale #: code:addons/sale/wizard/sale_line_invoice.py:113 @@ -1894,7 +1909,7 @@ msgstr "" #. module: sale #: view:sale.order.line:0 msgid "Sale order lines done" -msgstr "" +msgstr "Позиции заказа на продажу сделаны" #. module: sale #: field:sale.order.line,th_weight:0 @@ -2003,18 +2018,18 @@ msgstr "Упаковки" #. module: sale #: view:sale.order.line:0 msgid "Sale Order Lines ready to be invoiced" -msgstr "" +msgstr "Позиции заказа на продажу готовы к выставлению счета" #. module: sale #: view:sale.report:0 msgid "Sales order created in last month" -msgstr "" +msgstr "Заказы на продажу созданные в прошлом месяце" #. module: sale #: model:ir.actions.act_window,name:sale.action_email_templates #: model:ir.ui.menu,name:sale.menu_email_templates msgid "Email Templates" -msgstr "" +msgstr "Шаблоны писем" #. module: sale #: model:ir.actions.act_window,name:sale.action_order_form @@ -2072,7 +2087,7 @@ msgstr "Задержка по обязательствам" #. module: sale #: selection:sale.order,order_policy:0 msgid "Deliver & invoice on demand" -msgstr "" +msgstr "Доставка и счета по требованию" #. module: sale #: model:process.node,note:sale.process_node_saleprocurement0 @@ -2101,7 +2116,7 @@ msgstr "Подтвержденный заказ на продажу в счет" #. module: sale #: view:sale.order:0 msgid "Sales Order that haven't yet been confirmed" -msgstr "" +msgstr "Заказы на продажу, которые еще не были подтверждены" #. module: sale #: code:addons/sale/sale.py:322 @@ -2123,7 +2138,7 @@ msgstr "Закрыть" #: code:addons/sale/sale.py:1261 #, python-format msgid "No Pricelist ! : " -msgstr "" +msgstr "Нет прайс листа ! : " #. module: sale #: field:sale.order,shipped:0 @@ -2204,7 +2219,7 @@ msgstr "Запрос заказа на продажу" #: code:addons/sale/sale.py:1255 #, python-format msgid "Not enough stock ! : " -msgstr "" +msgstr "Не достаточно запасов ! : " #. module: sale #: report:sale.order:0 field:sale.order,payment_term:0 diff --git a/addons/sale/i18n/sale.pot b/addons/sale/i18n/sale.pot index 2c477d1d7a0..393b3e59db3 100644 --- a/addons/sale/i18n/sale.pot +++ b/addons/sale/i18n/sale.pot @@ -1112,7 +1112,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or ''}${object.partner_order_id." diff --git a/addons/sale/i18n/sk.po b/addons/sale/i18n/sk.po index 16f7fd7b29d..2f4b6c6c332 100644 --- a/addons/sale/i18n/sk.po +++ b/addons/sale/i18n/sk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1105,7 +1105,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/sl.po b/addons/sale/i18n/sl.po index db9e7b82d87..138e96fd061 100644 --- a/addons/sale/i18n/sl.po +++ b/addons/sale/i18n/sl.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1107,7 +1107,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokument pemika za izhod ali za stranko" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/sq.po b/addons/sale/i18n/sq.po index c5151572329..32ddc7f40d5 100644 --- a/addons/sale/i18n/sq.po +++ b/addons/sale/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:16+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:57+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/sr.po b/addons/sale/i18n/sr.po index c77248dae6d..bfb6ce674ce 100644 --- a/addons/sale/i18n/sr.po +++ b/addons/sale/i18n/sr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1108,7 +1108,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokumenat za pomeranje na izlaz ili za kupca." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/sr@latin.po b/addons/sale/i18n/sr@latin.po index e46bd400565..ea9e5039c37 100644 --- a/addons/sale/i18n/sr@latin.po +++ b/addons/sale/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1108,7 +1108,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Dokumenat za pomeranje na izlaz ili za kupca." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/sv.po b/addons/sale/i18n/sv.po index f8d8a9df3ce..1197192d3fe 100644 --- a/addons/sale/i18n/sv.po +++ b/addons/sale/i18n/sv.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1159,7 +1159,7 @@ msgstr "" "Dokumentation av bokföringstransaktionen till utleveransen eller till kunden" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/th.po b/addons/sale/i18n/th.po index 8bf634b9aab..dcc9781fa1e 100644 --- a/addons/sale/i18n/th.po +++ b/addons/sale/i18n/th.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1094,7 +1094,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/tlh.po b/addons/sale/i18n/tlh.po index 0d19af4e8bd..dc69b3b07c6 100644 --- a/addons/sale/i18n/tlh.po +++ b/addons/sale/i18n/tlh.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1093,7 +1093,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/tr.po b/addons/sale/i18n/tr.po index 9ced9b50ae6..b7578a31a2a 100644 --- a/addons/sale/i18n/tr.po +++ b/addons/sale/i18n/tr.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -328,6 +328,17 @@ msgid "" " - The 'Invoice based on deliveries' choice is used to create an invoice " "during the picking process." msgstr "" +"Fatura Politikası fatura ve teslimat işlemlerini senkronize etmek için " +"kullanılır.\n" +" - 'Teslimattan önce öde' seçimi önce faturayı oluşturur ve sonra faturanın " +"ödenmesi üzerine toplama emrini oluşturur.\n" +" - 'Teslim et & istenirse Fatura kes' toplama emrini doğrudan oluşturacak " +"ve kullanıcının elle 'Fatura' düğmesini tıklayarak satış siparişine ve satış " +"siparişi satırlarına göre fatura oluşturmasını bekleyecek.\n" +" - 'Sipariş tesliminde sonra Fatura' seçimi bütün toplama listeleri " +"tamamlandıktan sonra satış siparişine göre taslak fatura oluşturacaktır.\n" +" - 'Teslimata dayalı fatura' seçimi toplama işlemi sırasında fatura " +"oluşturmak için kullanılır." #. module: sale #: code:addons/sale/sale.py:1171 @@ -882,6 +893,12 @@ msgid "" "The 'Waiting Schedule' state is set when the invoice is confirmed but " "waiting for the scheduler to run on the order date." msgstr "" +"Teklifin ya da satış siparişinin durumunu verir. \n" +"İstisna durumu fatura doğrulamasında (Fatura İstisnası) iptal işlemi " +"oluşursa otomatikman ayarlanır ya da toplama listesi işleminde (Naklie " +"İstisnası). \n" +"'Bekleyen Program) durumu fatura onaylandığında ama programcının sipariş " +"tarihini çalıştırmasının beklendiğinde ayarlanır." #. module: sale #: code:addons/sale/sale.py:1272 @@ -1142,7 +1159,7 @@ msgid "Document of the move to the output or to the customer." msgstr "Çıkışa ya da müşteriyeyapılan hareket belgesi." #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " @@ -1219,6 +1236,80 @@ msgid "" "% endif\n" " " msgstr "" +"\n" +"Merhaba${object.partner_order_id.name and ' ' or " +"''}${object.partner_order_id.name or ''},\n" +"\n" +"Bu siparişiniz için onay ${object.partner_id.name}:\n" +" | Sipariş numarası: *${object.name}*\n" +" | Sipariş toplamı: *${object.amount_total} " +"${object.pricelist_id.currency_id.name}*\n" +" | Sipariş tarihi: ${object.date_order}\n" +" % if object.origin:\n" +" | Sipariş referansı: ${object.origin}\n" +" % endif\n" +" % if object.client_order_ref:\n" +" | Referansınız: ${object.client_order_ref}<br />\n" +" % endif\n" +" | İlgili Kişiniz: ${object.user_id.name} ${object.user_id.user_email " +"and '<%s>'%(object.user_id.user_email) or ''}\n" +"\n" +"Aşağıdaki linkten sipariş onayınızı izleyebilir, indirebilir ve hatta " +"çevrimiçi ödeme yapabilirsiniz:\n" +" ${ctx.get('edi_web_url_view') or 'n/a'}\n" +"\n" +"% if object.order_policy in ('prepaid','manual') and " +"object.company_id.paypal_account:\n" +"<% \n" +"comp_name = quote(object.company_id.name)\n" +"order_name = quote(object.name)\n" +"paypal_account = quote(object.company_id.paypal_account)\n" +"order_amount = quote(str(object.amount_total))\n" +"cur_name = quote(object.pricelist_id.currency_id.name)\n" +"paypal_url = \"https://www.paypal.com/cgi-" +"bin/webscr?cmd=_xclick&business=%s&item_name=%s%%20Order%%20%s&invoice=%s&amo" +"unt=%s\" \\\n" +" " +"\"¤cy_code=%s&button_subtype=services&no_note=1&bn=OpenERP_Order_PayNow" +"_%s\" % \\\n" +" " +"(paypal_account,comp_name,order_name,order_name,order_amount,cur_name,cur_nam" +"e)\n" +"%>\n" +"Paypal ile doğrudan ödemeniz de olasıdır:\n" +" ${paypal_url}\n" +"% endif\n" +"\n" +"Eğer bir sorunuz olursa bize danışmakta tereddüt etmeyin.\n" +"\n" +"\n" +"Firmamız ${object.company_id.name} seçtiğiniz için teşekkür ederiz!\n" +"\n" +"\n" +"--\n" +"${object.user_id.name} ${object.user_id.user_email and " +"'<%s>'%(object.user_id.user_email) or ''}\n" +"${object.company_id.name}\n" +"% if object.company_id.street:\n" +"${object.company_id.street or ''}\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 or ''} ${object.company_id.city or ''}\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" +"% if object.company_id.phone:\n" +"Telefon: ${object.company_id.phone}\n" +"% endif\n" +"% if object.company_id.website:\n" +"${object.company_id.website or ''}\n" +"% endif\n" +" " #. module: sale #: model:process.transition.action,name:sale.process_transition_action_validate0 @@ -1278,6 +1369,9 @@ msgid "" "create a task, purchase order or manufacturing order linked to this sale " "order line." msgstr "" +"Eğer 'siparişte' ise, satış siparişi bir görev oluşturmak üzere onaylanırsa " +"bir satınalmayı başlatır, satınalma siparişi ya da üretim emri bu satış " +"siparişi öğesine bağlantılıdır." #. module: sale #: field:sale.advance.payment.inv,amount:0 @@ -1685,6 +1779,8 @@ msgid "" "output area and the customer is done through the Delivery Order manually or " "automatically." msgstr "" +"Çıkış konumunun yapılandırmasına bağlı olarak, müşteri ile çıkış konumu " +"arasındaki hareket Teslimat Emri yoluyla elle ya da otomatik olarak yapılır." #. module: sale #: selection:sale.order,order_policy:0 @@ -1742,6 +1838,8 @@ msgid "" "Couldn't find a pricelist line matching this product and quantity.\n" "You have to change either the product, the quantity or the pricelist." msgstr "" +"Bu ürün ve miktarla eşleşen bir fiyat listesi bulunamadı.\n" +"Hem ürünü hem de miktarı ya da fiyat listesini değiştirmelisiniz." #. module: sale #: help:sale.order,picking_ids:0 @@ -2096,7 +2194,7 @@ msgstr "İstek üzerine teslimat & fatura" msgid "" "One Procurement order for each sales order line and for each of the " "components." -msgstr "" +msgstr "Her satış siparişi öğesi ve her bileşen için bir Satınalma İşlemi." #. module: sale #: model:process.transition.action,name:sale.process_transition_action_assign0 @@ -2217,7 +2315,7 @@ msgstr "Satış Siparişi İsteği" #: code:addons/sale/sale.py:1255 #, python-format msgid "Not enough stock ! : " -msgstr "" +msgstr "Yeterli stok yok !: " #. module: sale #: report:sale.order:0 field:sale.order,payment_term:0 diff --git a/addons/sale/i18n/uk.po b/addons/sale/i18n/uk.po index e0e7bbc086a..886bab6fdab 100644 --- a/addons/sale/i18n/uk.po +++ b/addons/sale/i18n/uk.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1093,7 +1093,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/vi.po b/addons/sale/i18n/vi.po index f8f1c71ec94..982941dc7fa 100644 --- a/addons/sale/i18n/vi.po +++ b/addons/sale/i18n/vi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1108,7 +1108,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/zh_CN.po b/addons/sale/i18n/zh_CN.po index 5b2e6724958..3ce545e9056 100644 --- a/addons/sale/i18n/zh_CN.po +++ b/addons/sale/i18n/zh_CN.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1110,7 +1110,7 @@ msgid "Document of the move to the output or to the customer." msgstr "调拨出去或调拨给客户的单据" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/i18n/zh_TW.po b/addons/sale/i18n/zh_TW.po index c8d4a2f28d0..241b0bf84f4 100644 --- a/addons/sale/i18n/zh_TW.po +++ b/addons/sale/i18n/zh_TW.po @@ -13,8 +13,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:17+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-07 04:58+0000\n" +"X-Generator: Launchpad (build 15914)\n" #. module: sale #: field:sale.config.picking_policy,timesheet:0 @@ -1093,7 +1093,7 @@ msgid "Document of the move to the output or to the customer." msgstr "" #. module: sale -#: model:email.template,body_text:sale.email_template_edi_sale +#: model:email.template,body:sale.email_template_edi_sale msgid "" "\n" "Hello${object.partner_order_id.name and ' ' or " diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index 0a2e3a49f62..1e7b5b67c53 100644 --- a/addons/sale/res_config.py +++ b/addons/sale/res_config.py @@ -56,7 +56,7 @@ class sale_configuration(osv.osv_memory): help ="""Allows you to add delivery methods in sale orders and delivery orders. You can define your own carrier and delivery grids for prices. This installs the module delivery."""), - 'time_unit': fields.many2one('product.uom', 'The default working time unit for services is'), + 'time_unit': fields.many2one('product.uom', 'the default working time unit for services is'), 'default_picking_policy' : fields.boolean("deliver all at once when all products are available.", help = "Sales order by default will be configured to deliver all products at once instead of delivering each product when it is available. This may have an impact on the shipping price."), 'group_sale_pricelist':fields.boolean("use pricelists to adapt your price per customers", diff --git a/addons/sale/sale.py b/addons/sale/sale.py index c525ba04df1..553b8993ab8 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -48,7 +48,7 @@ sale_shop() class sale_order(osv.osv): _name = "sale.order" - _inherit = ['ir.needaction_mixin', 'mail.thread'] + _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Sales Order" @@ -769,13 +769,18 @@ class sale_order(osv.osv): template_id = template and template[1] or False res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form') res_id = res and res[1] or False - ctx = dict(context, active_model='sale.order', active_id=ids[0]) - ctx.update({'mail.compose.template_id': template_id}) + ctx = dict(context) + ctx.update({ + 'default_model': 'sale.order', + 'default_res_id': ids[0], + 'default_use_template': True, + 'default_template_id': template_id, + }) return { 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mail.compose.message', - 'views': [(res_id,'form')], + 'views': [(res_id, 'form')], 'view_id': res_id, 'type': 'ir.actions.act_window', 'target': 'new', @@ -1022,24 +1027,20 @@ class sale_order(osv.osv): # OpenChatter methods and notifications # ------------------------------------------------ - def get_needaction_user_ids(self, cr, uid, ids, context=None): - result = super(sale_order, self).get_needaction_user_ids(cr, uid, ids, context=context) - for obj in self.browse(cr, uid, ids, context=context): - if (obj.state == 'manual' or obj.state == 'progress'): - result[obj.id].append(obj.user_id.id) - return result + def needaction_domain_get(self, cr, uid, ids, context=None): + return [('state', '=', 'draft'), ('user_id','=',uid)] def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> has been <b>created</b>.") % (obj.partner_id.name), context=context) + self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>created</b>.") % (obj.partner_id.name), context=context) def confirm_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to Sale Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) + self.message_post(cr, uid, [obj.id], body=_("Quotation for <em>%s</em> <b>converted</b> to Sale Order of %s %s.") % (obj.partner_id.name, obj.amount_total, obj.pricelist_id.currency_id.symbol), context=context) def cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context) + self.message_post(cr, uid, [obj.id], body=_("Sale Order for <em>%s</em> <b>cancelled</b>.") % (obj.partner_id.name), context=context) def delivery_send_note(self, cr, uid, ids, picking_id, context=None): for order in self.browse(cr, uid, ids, context=context): @@ -1048,21 +1049,21 @@ class sale_order(osv.osv): # convert it to the user TZ and re-render it with %Z to add the timezone picking_datetime = fields.DT.datetime.strptime(picking.min_date, DEFAULT_SERVER_DATETIME_FORMAT) picking_date_str = fields.datetime.context_timestamp(cr, uid, picking_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)") - self.message_append_note(cr, uid, [order.id], body=_("Delivery Order <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context) + self.message_post(cr, uid, [order.id], body=_("Delivery Order <em>%s</em> <b>scheduled</b> for %s.") % (picking.name, picking_date_str), context=context) def delivery_end_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Order <b>delivered</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Order <b>delivered</b>."), context=context) def invoice_paid_send_note(self, cr, uid, ids, context=None): - self.message_append_note(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context) + self.message_post(cr, uid, ids, body=_("Invoice <b>paid</b>."), context=context) def invoice_send_note(self, cr, uid, ids, invoice_id, context=None): for order in self.browse(cr, uid, ids, context=context): for invoice in (inv for inv in order.invoice_ids if inv.id == invoice_id): - self.message_append_note(cr, uid, [order.id], body=_("Draft Invoice of %s %s <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context) + self.message_post(cr, uid, [order.id], body=_("Draft Invoice of %s %s <b>waiting for validation</b>.") % (invoice.amount_total, invoice.currency_id.symbol), context=context) def action_cancel_draft_send_note(self, cr, uid, ids, context=None): - return self.message_append_note(cr, uid, ids, body='Sale order has been set in draft.', context=context) + return self.message_post(cr, uid, ids, body=_('Sale order set to draft.'), context=context) sale_order() diff --git a/addons/sale/sale_data.xml b/addons/sale/sale_data.xml index bbee9ca7455..a2ef6ddd802 100644 --- a/addons/sale/sale_data.xml +++ b/addons/sale/sale_data.xml @@ -41,13 +41,14 @@ <function eval="('default',False,'shop_id', [('sale.order', False)], sale_shop_1, True, False, False, False, True)" id="sale_default_set" model="ir.values" name="set"/> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>The Sales Management application has been installed.</value> - <value>This modules allows you to create and send easily quotations and process your sales orders; from the delivery to the invoicing. + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Sales Management application installed!</field> + <field name="body">This application lets you create and send quotations and process your sales orders; from delivery to invoicing. -If you need to manage your sales pipeline (leads, opportunities, phonecalls), you can install the module <i>CRM</i> from the top menu Settings.</value> - </function> +If you need to manage your sales pipeline (leads, opportunities, phonecalls), the <i>CRM</i> application may be useful. Use the Settings menu to install it.</field> + </record> </data> </openerp> diff --git a/addons/sale/sale_demo.xml b/addons/sale/sale_demo.xml index 192d73472c4..c8a3bb74774 100644 --- a/addons/sale/sale_demo.xml +++ b/addons/sale/sale_demo.xml @@ -304,36 +304,33 @@ <record id="message_sale_1" model="mail.message"> <field name="model">sale.order</field> <field name="res_id" ref="sale_order_2"/> - <field name="content_subtype">plain</field> - <field name="body_text">Hi, + <field name="body">Hi, I have a confusion for pricing of Services, I have heard there is a discount above 25 hours. Can you clarify please?</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_demo"/> + <field name="author_id" ref="base.partner_demo"/> </record> <record id="message_sale_2" model="mail.message"> <field name="model">sale.order</field> <field name="res_id" ref="sale_order_2"/> <field name="parent_id" ref="message_sale_1"/> - <field name="content_subtype">plain</field> - <field name="body_text">Hello, + <field name="body">Hello, Sorry but that scheme is not available for now, We would like to know if you confirm the quotation with pricing we sent to you. Thanks, Sales Department</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_root"/> + <field name="author_id" ref="base.partner_root"/> </record> <record id="message_sale_3" model="mail.message"> <field name="model">sale.order</field> <field name="res_id" ref="sale_order_2"/> <field name="parent_id" ref="message_sale_2"/> - <field name="content_subtype">plain</field> - <field name="body_text">Ok, fine, we will intimate you after discussing with our team.</field> + <field name="body">Ok, fine, we will intimate you after discussing with our team.</field> <field name="type">comment</field> - <field name="user_id" ref="base.user_demo"/> + <field name="author_id" ref="base.partner_demo"/> </record> <!-- sale advance demo.. --> <!-- Demo Data for Product --> diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index a0e69efe849..1f077f6601d 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -133,8 +133,8 @@ <field name="model">sale.order</field> <field name="priority">2</field> <field name="arch" type="xml"> - <tree string="Sales Orders" fonts="bold:needaction_pending==True" colors="grey:state=='cancel';blue:state in ('waiting_date','manual');red:state in ('invoice_except','shipping_except')"> - <field name="needaction_pending" invisible="1"/> + <tree string="Sales Orders" fonts="bold:message_unread==True" colors="grey:state=='cancel';blue:state in ('waiting_date','manual');red:state in ('invoice_except','shipping_except')"> + <field name="message_unread" invisible="1"/> <field name="name"/> <field name="date_order"/> <field name="partner_id"/> @@ -262,8 +262,6 @@ <field name="sequence" widget="handle"/> <field name="state" invisible="1"/> <field name="delay" invisible="1"/> - <field name="tax_id" invisible="1"/> - <field name="type" invisible="1"/> <field name="th_weight" invisible="1"/> <field name="product_packaging" invisible="1"/> <field name="product_id" @@ -271,16 +269,18 @@ groups="base.group_user" on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/> <field name="name"/> + <field name="type" invisible="1"/> <field name="product_uom_qty" context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}" on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, False, parent.fiscal_position, True, context)"/> <field name="product_uom" on_change="product_uom_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, False, parent.date_order, context)" groups="product.group_uom" options='{"no_open": true}'/> - <field name="product_uos_qty" groups="product.group_uos"/> - <field name="product_uos" string="UoS" groups="product.group_uos"/> - <field name="discount" groups="sale.group_discount_per_so_line"/> + <field name="product_uos_qty" groups="product.group_uos" invisible="1"/> + <field name="product_uos" string="UoS" groups="product.group_uos" invisible="1"/> + <field name="tax_id" widget="many2many_tags" domain="[('parent_id','=',False),('type_tax_use','<>','purchase')]"/> <field name="price_unit"/> + <field name="discount" groups="sale.group_discount_per_so_line"/> <field name="price_subtotal"/> </tree> </field> @@ -357,13 +357,27 @@ </field> </record> + <!-- inherited view to make the order lines list in the form non-editable + for the members of some usability groups --> + <record id="view_order_form_editable_list" model="ir.ui.view"> + <field name="name">sale.order.form.editable.list</field> + <field name="model">sale.order</field> + <field name="inherit_id" ref="sale.view_order_form"/> + <field name="groups_id" eval="[(6, 0, [ref('product.group_uos'), ref('product.group_stock_packaging'), ref('sale.group_mrp_properties')])]"/> + <field name="arch" type="xml"> + <xpath expr="//field[@name='order_line']/tree" position="attributes"> + <attribute name="editable"/> + </xpath> + </field> + </record> + <record id="view_sales_order_filter" model="ir.ui.view"> <field name="name">sale.order.list.select</field> <field name="model">sale.order</field> <field name="arch" type="xml"> <search string="Search Sales Order"> <field name="name" string="Sales Order" filter_domain="['|',('name','ilike',self),('client_order_ref','ilike',self)]"/> - <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="needaction_pending" domain="[('needaction_pending','=',True)]"/> + <filter icon="terp-mail-message-new" string="Inbox" help="Unread messages" name="message_unread" domain="[('message_unread','=',True)]"/> <separator/> <filter icon="terp-document-new" string="Quotations" name="draft" domain="[('state','in',('draft','sent'))]" help="Sales Order that haven't yet been confirmed"/> <filter icon="terp-check" string="Sales" name="sales" domain="[('state','in',('manual','progress'))]"/> @@ -390,7 +404,8 @@ <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,graph</field> <field name="search_view_id" ref="view_sales_order_filter"/> - <field name="context">{"search_default_sales":1}</field> + <field name="context">{}</field> + <field name="domain">[('state','not in',('draft','sent','cancel'))]</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to create a quotation that can be converted into a sale @@ -431,7 +446,8 @@ <field name="res_model">sale.order</field> <field name="view_type">form</field> <field name="view_mode">tree,form,calendar,graph</field> - <field name="context">{"search_default_draft":1}</field> + <field name="context">{}</field> + <field name="domain">[('state','in',('draft','sent','cancel'))]</field> <field name="search_view_id" ref="view_sales_order_filter"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml index 47a577e77c3..5e1f4e25b52 100644 --- a/addons/sale/security/sale_security.xml +++ b/addons/sale/security/sale_security.xml @@ -3,18 +3,21 @@ <data noupdate="0"> <record id="base.group_sale_salesman" model="res.groups"> - <field name="name">User - Own Leads Only</field> + <field name="name">See Own Leads</field> <field name="category_id" ref="base.module_category_sales_management"/> + <field name="comment">the user will have access to his own data in the sales application.</field> </record> <record id="base.group_sale_salesman_all_leads" model="res.groups"> - <field name="name">User - All Leads</field> + <field name="name">See all Leads</field> <field name="category_id" ref="base.module_category_sales_management"/> <field name="implied_ids" eval="[(4, ref('base.group_sale_salesman'))]"/> + <field name="comment">the user will have access to all records of everyone in the sales application.</field> </record> <record id="base.group_sale_manager" model="res.groups"> <field name="name">Manager</field> + <field name="comment">the user will have an access to the sales configuration as well as statistic reports.</field> <field name="category_id" ref="base.module_category_sales_management"/> <field name="implied_ids" eval="[(4, ref('base.group_sale_salesman_all_leads'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/> diff --git a/addons/sale_analytic_plans/sale_analytic_plans_view.xml b/addons/sale_analytic_plans/sale_analytic_plans_view.xml index 6701ede5ae5..6c788866520 100644 --- a/addons/sale_analytic_plans/sale_analytic_plans_view.xml +++ b/addons/sale_analytic_plans/sale_analytic_plans_view.xml @@ -22,6 +22,11 @@ </field> </record> + <!-- the presence of 'analytics_id' makes the order lines non-editable --> + <record id="sale.view_order_form_editable_list" model="ir.ui.view"> + <field name="groups_id" eval="[(4, ref('analytic.group_analytic_accounting'))]"/> + </record> + <record model="ir.ui.view" id="view_order_line_form2_inherit"> <field name="name">sale.order.line.form2.inherit</field> <field name="model">sale.order.line</field> diff --git a/addons/sale_crm/i18n/fr.po b/addons/sale_crm/i18n/fr.po index e9463b543de..eac4ce9a5b0 100644 --- a/addons/sale_crm/i18n/fr.po +++ b/addons/sale_crm/i18n/fr.po @@ -7,30 +7,30 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-11-25 14:51+0000\n" -"Last-Translator: Numérigraphe <Unknown>\n" +"PO-Revision-Date: 2012-09-03 22:25+0000\n" +"Last-Translator: Nicolas JEUDY <njeudy@simplee.fr>\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: 2012-08-28 06:07+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: sale_crm #: field:sale.order,categ_id:0 msgid "Category" -msgstr "" +msgstr "Catégorie" #. module: sale_crm #: sql_constraint:sale.order:0 msgid "Order Reference must be unique per Company!" -msgstr "" +msgstr "La référence de commande doit être unique par société !" #. module: sale_crm #: code:addons/sale_crm/wizard/crm_make_sale.py:112 #, python-format msgid "Converted to Sales Quotation(%s)." -msgstr "" +msgstr "Convertir en devis (%s)" #. module: sale_crm #: view:crm.make.sale:0 @@ -62,7 +62,7 @@ msgstr "_Créer" #. module: sale_crm #: view:sale.order:0 msgid "My Sales Team(s)" -msgstr "" +msgstr "Mon (mes) équipe(s) de vente" #. module: sale_crm #: help:crm.make.sale,close:0 @@ -75,7 +75,7 @@ msgstr "" #. module: sale_crm #: view:board.board:0 msgid "My Opportunities" -msgstr "" +msgstr "Mes opportunités" #. module: sale_crm #: view:crm.lead:0 @@ -106,7 +106,7 @@ msgstr "Fermer l'opportunité" #. module: sale_crm #: view:board.board:0 msgid "My Planned Revenues by Stage" -msgstr "" +msgstr "Mes revenus planifiés par étape" #. module: sale_crm #: code:addons/sale_crm/wizard/crm_make_sale.py:110 @@ -133,7 +133,7 @@ msgstr "Annuler" #. module: sale_crm #: model:ir.model,name:sale_crm.model_sale_order msgid "Sales Order" -msgstr "Commande de ventes" +msgstr "Commande client" #~ msgid "Crm opportunity quotation" #~ msgstr "Chiffrage de l'opportunité" diff --git a/addons/sale_crm/i18n/zh_CN.po b/addons/sale_crm/i18n/zh_CN.po index bbae3770541..6678287c01e 100644 --- a/addons/sale_crm/i18n/zh_CN.po +++ b/addons/sale_crm/i18n/zh_CN.po @@ -7,30 +7,30 @@ msgstr "" "Project-Id-Version: OpenERP Server 6.0dev\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2012-08-18 17:40+0000\n" -"Last-Translator: Heling Yao <Unknown>\n" +"PO-Revision-Date: 2012-08-31 14:59+0000\n" +"Last-Translator: ccdos <Unknown>\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: 2012-08-28 06:07+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-01 04:55+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: sale_crm #: field:sale.order,categ_id:0 msgid "Category" -msgstr "" +msgstr "分类" #. module: sale_crm #: sql_constraint:sale.order:0 msgid "Order Reference must be unique per Company!" -msgstr "" +msgstr "订单号必须在一个公司范围内唯一" #. module: sale_crm #: code:addons/sale_crm/wizard/crm_make_sale.py:112 #, python-format msgid "Converted to Sales Quotation(%s)." -msgstr "" +msgstr "转换为销售报价单(%s)。" #. module: sale_crm #: view:crm.make.sale:0 @@ -73,7 +73,7 @@ msgstr "在生成销售订单后检查是否关闭商机。" #. module: sale_crm #: view:board.board:0 msgid "My Opportunities" -msgstr "" +msgstr "我的商机" #. module: sale_crm #: view:crm.lead:0 @@ -104,13 +104,13 @@ msgstr "关闭商机" #. module: sale_crm #: view:board.board:0 msgid "My Planned Revenues by Stage" -msgstr "" +msgstr "这阶段我的计划收入" #. module: sale_crm #: code:addons/sale_crm/wizard/crm_make_sale.py:110 #, python-format msgid "Opportunity '%s' is converted to Quotation." -msgstr "" +msgstr "商机 '%s' 被转换为报价单" #. module: sale_crm #: view:sale.order:0 diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py index 6ca9ac8230f..2597c1db56c 100644 --- a/addons/sale_crm/wizard/crm_make_sale.py +++ b/addons/sale_crm/wizard/crm_make_sale.py @@ -106,7 +106,7 @@ class crm_make_sale(osv.osv_memory): case_obj.write(cr, uid, [case.id], {'ref': 'sale.order,%s' % new_id}) new_ids.append(new_id) message = _("Opportunity has been <b>converted</b> to the quotation <em>%s</em>.") % (sale_order.name) - case.message_append_note(body=message) + case.message_post(body=message) if make.close: case_obj.case_close(cr, uid, data) if not new_ids: diff --git a/addons/sale_margin/sale_margin.py b/addons/sale_margin/sale_margin.py index 82a7150b6d1..64e07276c08 100644 --- a/addons/sale_margin/sale_margin.py +++ b/addons/sale_margin/sale_margin.py @@ -70,7 +70,7 @@ class sale_order(osv.osv): return result def _get_order(self, cr, uid, ids, context=None): - return super(self,sale_order)._get_order(cr, uid, ids, context=context) + return super(sale_order, self)._get_order(cr, uid, ids, context=context) _columns = { 'margin': fields.function(_product_margin, string='Margin', help="It gives profitability by calculating the difference between the Unit Price and Cost Price.", store={ diff --git a/addons/sale_mrp/i18n/zh_CN.po b/addons/sale_mrp/i18n/zh_CN.po index 86105255a19..bb04533ef97 100644 --- a/addons/sale_mrp/i18n/zh_CN.po +++ b/addons/sale_mrp/i18n/zh_CN.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 00:37+0000\n" -"PO-Revision-Date: 2011-06-28 05:04+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2012-08-31 14:59+0000\n" +"Last-Translator: ccdos <Unknown>\n" "Language-Team: Chinese (Simplified) <zh_CN@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:34+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-01 04:55+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: sale_mrp #: help:mrp.production,sale_ref:0 @@ -40,12 +40,12 @@ msgstr "销售名称" #. module: sale_mrp #: sql_constraint:mrp.production:0 msgid "Reference must be unique per Company!" -msgstr "" +msgstr "编号必须在公司内唯一!" #. module: sale_mrp #: constraint:mrp.production:0 msgid "Order quantity cannot be negative or zero!" -msgstr "" +msgstr "订单数量不能是负数或者0!" #. module: sale_mrp #: help:mrp.production,sale_name:0 diff --git a/addons/share/i18n/nl.po b/addons/share/i18n/nl.po index 976d28f66c5..c0d68268f2c 100644 --- a/addons/share/i18n/nl.po +++ b/addons/share/i18n/nl.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-06-28 12:52+0000\n" +"PO-Revision-Date: 2012-09-03 17:14+0000\n" "Last-Translator: Erwin <Unknown>\n" "Language-Team: Dutch <nl@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:34+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-04 04:52+0000\n" +"X-Generator: Launchpad (build 15890)\n" #. module: share #: field:share.wizard,embed_option_title:0 @@ -384,6 +384,7 @@ msgid "" "An email notification with instructions has been sent to the following " "people:" msgstr "" +"Een e-mail bevestiging met instructies is verzonden naar de volgende mensen:" #. module: share #: model:ir.model,name:share.model_share_wizard_result_line @@ -415,6 +416,8 @@ msgstr "Kan bewerken" msgid "" "An optional personal message, to be included in the email notification." msgstr "" +"Een optionele persoonlijk bericht, welke kan worden toegevoegd in de e-mail " +"bevestiging." #. module: share #: model:ir.model,name:share.model_res_users @@ -532,6 +535,8 @@ msgid "" "You must configure your email address in the user preferences before using " "the Share button." msgstr "" +"U dient u e-mail adres in te stellen in uw gebruikersinstellingen, voordat u " +"de 'share' knop kunt gebruiken." #. module: share #: help:share.wizard,access_mode:0 diff --git a/addons/share/i18n/ru.po b/addons/share/i18n/ru.po index effec5d5f99..5e430205ec4 100644 --- a/addons/share/i18n/ru.po +++ b/addons/share/i18n/ru.po @@ -8,39 +8,39 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2012-02-08 01:37+0100\n" -"PO-Revision-Date: 2011-04-26 05:57+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2012-09-10 14:15+0000\n" +"Last-Translator: Chertykov Denis <chertykov@gmail.com>\n" "Language-Team: Russian <ru@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-08-28 06:34+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" #. module: share #: field:share.wizard,embed_option_title:0 msgid "Display title" -msgstr "" +msgstr "Название отображения" #. module: share #: view:share.wizard:0 msgid "Access granted!" -msgstr "" +msgstr "Access granted!" #. module: share #: field:share.wizard,user_type:0 msgid "Sharing method" -msgstr "" +msgstr "Метод совместного доступа" #. module: share #: view:share.wizard:0 msgid "Share with these People (one email per line)" -msgstr "" +msgstr "Совместный доступ с этими людьми (один эл.адрес в строке)" #. module: share #: field:share.wizard,name:0 msgid "Share Title" -msgstr "" +msgstr "Название совместного доступа" #. module: share #: model:ir.module.category,name:share.module_category_share @@ -50,19 +50,20 @@ msgstr "Совместный доступ" #. module: share #: field:share.wizard,share_root_url:0 msgid "Share Access URL" -msgstr "" +msgstr "Адрес(URL) совместного доступа" #. module: share #: code:addons/share/wizard/share_wizard.py:782 #, python-format msgid "You may use your current login (%s) and password to view them.\n" msgstr "" +"Вы можете использовать ваш текущий логин (%s) и пароль, для их просмотра.\n" #. module: share #: code:addons/share/wizard/share_wizard.py:601 #, python-format msgid "(Modified)" -msgstr "" +msgstr "(Изменено)" #. module: share #: code:addons/share/wizard/share_wizard.py:769 @@ -71,6 +72,7 @@ msgid "" "The documents are not attached, you can view them online directly on my " "OpenERP server at:" msgstr "" +"Документы не вложены, их можно просмотреть онлайн на OpenERP сервере:" #. module: share #: code:addons/share/wizard/share_wizard.py:579 @@ -88,7 +90,7 @@ msgstr "Адрес совместного доступа" #: code:addons/share/wizard/share_wizard.py:776 #, python-format msgid "These are your credentials to access this protected area:\n" -msgstr "" +msgstr "Это ваши учетные данные для доступа с защищенной области:\n" #. module: share #: code:addons/share/wizard/share_wizard.py:643 @@ -110,7 +112,7 @@ msgstr "Совместный доступ" #: code:addons/share/wizard/share_wizard.py:551 #, python-format msgid "(Duplicated for modified sharing permissions)" -msgstr "" +msgstr "(Дублирован для измененных прав совместного доступа)" #. module: share #: help:share.wizard,domain:0 @@ -125,7 +127,7 @@ msgstr "Вы не можете иметь двух одинаковых поль #. module: share #: model:ir.model,name:share.model_ir_model_access msgid "ir.model.access" -msgstr "" +msgstr "ir.model.access" #. module: share #: view:share.wizard:0 @@ -141,13 +143,13 @@ msgstr "Пользователь" #. module: share #: view:share.wizard:0 msgid "Sharing Options" -msgstr "" +msgstr "Параметры общего доступа" #. module: share #: code:addons/share/wizard/share_wizard.py:765 #, python-format msgid "Hello," -msgstr "" +msgstr "Здравствуйте," #. module: share #: view:share.wizard:0 @@ -158,7 +160,7 @@ msgstr "Закрыть" #: code:addons/share/wizard/share_wizard.py:640 #, python-format msgid "Action and Access Mode are required to create a shared access" -msgstr "" +msgstr "Действие и режим доступа необходимы для создания общего доступа" #. module: share #: view:share.wizard:0 @@ -176,6 +178,7 @@ msgid "" "The documents have been automatically added to your current OpenERP " "documents.\n" msgstr "" +"Документы были автоматически добавлены к вашим текущим документам OpenERP .\n" #. module: share #: view:share.wizard:0 @@ -206,11 +209,13 @@ msgstr "" #: help:share.wizard,name:0 msgid "Title for the share (displayed to users as menu and shortcut name)" msgstr "" +"Название для общего доступа (выводится пользователям как меню и название " +"закладки)" #. module: share #: view:share.wizard:0 msgid "Options" -msgstr "" +msgstr "Настройки" #. module: share #: view:res.groups:0 @@ -225,6 +230,9 @@ msgid "" "Sales, HR, etc.)\n" "It is open source and can be found on http://www.openerp.com." msgstr "" +"OpenERP является мощным и удобным набором бизнес-приложений (CRM, продажи, " +"кадры и т.д.)\n" +"с открытым исходным кодом. Сайт: www.openerp.com ." #. module: share #: field:share.wizard,action_id:0 @@ -234,7 +242,7 @@ msgstr "Действие совместного доступа" #. module: share #: view:share.wizard:0 msgid "Optional: include a personal message" -msgstr "" +msgstr "Дополнительно: включить личное сообщение" #. module: share #: field:res.users,share:0 @@ -246,11 +254,13 @@ msgstr "Совместный доступ с пользователем" #, python-format msgid "Please indicate the emails of the persons to share with, one per line" msgstr "" +"Пожалуйста, укажите эл.адреса лиц для совместного доступа, один адрес в " +"строке" #. module: share #: field:share.wizard,embed_code:0 field:share.wizard.result.line,user_id:0 msgid "unknown" -msgstr "" +msgstr "неизвесно" #. module: share #: help:res.groups,share:0 @@ -295,12 +305,14 @@ msgstr "" #: code:addons/share/wizard/share_wizard.py:767 #, python-format msgid "I've shared %s with you!" -msgstr "" +msgstr "Я предоставил вам доступ к %s !" #. module: share #: help:share.wizard,share_root_url:0 msgid "Main access page for users that are granted shared access" msgstr "" +"Главная страница доступа для пользователей, которым предоставляется " +"совместный доступ" #. module: share #: sql_constraint:res.groups:0 @@ -310,7 +322,7 @@ msgstr "Название группы должно быть уникальным #. module: share #: model:res.groups,name:share.group_share_user msgid "User" -msgstr "" +msgstr "Пользователь" #. module: share #: view:res.groups:0 @@ -333,7 +345,7 @@ msgstr "" #. module: share #: view:share.wizard:0 msgid "Use this Link" -msgstr "" +msgstr "Используйте эту ссылку" #. module: share #: code:addons/share/wizard/share_wizard.py:779 @@ -360,12 +372,13 @@ msgstr "Скопированы права совместного доступа" #. module: share #: model:ir.actions.act_window,name:share.action_share_wizard_step1 msgid "Share your documents" -msgstr "" +msgstr "Поделитесь своими документами" #. module: share #: view:share.wizard:0 msgid "Or insert the following code where you want to embed your documents" msgstr "" +"Или вставьте следующий код туда, где вы хотите вставить ваши документы" #. module: share #: view:share.wizard:0 @@ -373,6 +386,7 @@ msgid "" "An email notification with instructions has been sent to the following " "people:" msgstr "" +"Уведомление с инструкциями было отправлено по эл.почте следующим людям:" #. module: share #: model:ir.model,name:share.model_share_wizard_result_line @@ -389,23 +403,23 @@ msgstr "" #. module: share #: field:share.wizard,view_type:0 msgid "Current View Type" -msgstr "" +msgstr "Тип этого вида" #. module: share #: selection:share.wizard,access_mode:0 msgid "Can view" -msgstr "" +msgstr "Можно просматривать" #. module: share #: selection:share.wizard,access_mode:0 msgid "Can edit" -msgstr "" +msgstr "Можно редактировать" #. module: share #: help:share.wizard,message:0 msgid "" "An optional personal message, to be included in the email notification." -msgstr "" +msgstr "Персональное сообщение, которое может быть включено в эл.письмо." #. module: share #: model:ir.model,name:share.model_res_users @@ -417,7 +431,7 @@ msgstr "res.users" #: code:addons/share/wizard/share_wizard.py:635 #, python-format msgid "Sharing access could not be created" -msgstr "" +msgstr "Общий доступ не был создан" #. module: share #: help:res.users,share:0 @@ -439,7 +453,7 @@ msgstr "Мастер совместного доступа" #: code:addons/share/wizard/share_wizard.py:740 #, python-format msgid "Shared access created!" -msgstr "" +msgstr "Общий доступ создан!" #. module: share #: model:res.groups,comment:share.group_share_user @@ -448,6 +462,10 @@ msgid "" "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 "" +"\n" +"Члены этой группы имеют доступ к мастеру совместного доступа , который " +"позволяет им приглашать внешних пользователей, для просмотра или изменения " +"некоторых своих документов." #. module: share #: model:ir.model,name:share.model_res_groups @@ -464,23 +482,23 @@ msgstr "Пароль" #. module: share #: field:share.wizard,new_users:0 msgid "Emails" -msgstr "" +msgstr "Эл.почта" #. module: share #: field:share.wizard,embed_option_search:0 msgid "Display search view" -msgstr "" +msgstr "Показать поиск" #. module: share #: code:addons/share/wizard/share_wizard.py:197 #, python-format msgid "No email address configured" -msgstr "" +msgstr "Не настроен адрес эл.почты" #. module: share #: field:share.wizard,message:0 msgid "Personal Message" -msgstr "" +msgstr "Личное сообщение" #. module: share #: code:addons/share/wizard/share_wizard.py:763 @@ -495,7 +513,7 @@ msgstr "" #. module: share #: field:share.wizard.result.line,login:0 msgid "Login" -msgstr "" +msgstr "Имя пользователя" #. module: share #: view:res.users:0 @@ -510,7 +528,7 @@ msgstr "Режим доступа" #. module: share #: view:share.wizard:0 msgid "Sharing: preparation" -msgstr "" +msgstr "Совместный доступ: подготовка" #. module: share #: code:addons/share/wizard/share_wizard.py:198 @@ -519,21 +537,23 @@ msgid "" "You must configure your email address in the user preferences before using " "the Share button." msgstr "" +"Вы должны настроить ваш адрес электронной почты в настройках пользователя, " +"прежде чем использовать кнопку \"Совместный доступ\"." #. module: share #: help:share.wizard,access_mode:0 msgid "Access rights to be granted on the shared documents." -msgstr "" +msgstr "Права доступа будут предоставлены на документы совместного доступа" #. openerp-web #: /home/odo/repositories/addons/trunk/share/static/src/xml/share.xml:8 msgid "Link or embed..." -msgstr "" +msgstr "Ссылка или встроить ..." #. openerp-web #: /home/odo/repositories/addons/trunk/share/static/src/xml/share.xml:9 msgid "Share with..." -msgstr "" +msgstr "Совместный доступ с..." #, python-format #~ msgid "" diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index 598bb085967..375c33b1e8c 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -23,6 +23,7 @@ import random import time from urllib import quote_plus import uuid +from openerp import SUPERUSER_ID import simplejson @@ -545,7 +546,7 @@ class share_wizard(osv.TransientModel): _logger.debug("Copying rule %s (%s) on model %s with domain: %s", rule.name, rule.id, model.model, rule.domain_force) else: # otherwise we can simply link the rule to keep it dynamic - rule_obj.write(cr, 1, [rule.id], { + rule_obj.write(cr, SUPERUSER_ID, [rule.id], { 'groups': [(4,group_id)] }) _logger.debug("Linking rule %s (%s) on model %s with domain: %s", rule.name, rule.id, model.model, rule.domain_force) @@ -774,8 +775,8 @@ class share_wizard(osv.TransientModel): 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.send_invite_email(cr, uid, wizard_data, context=context) - self.send_invite_note(cr, uid, model.model, res_id, wizard_data, 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) # CLOSE # A. Not invite: as before @@ -819,9 +820,10 @@ 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_append_note(cr, uid, [res_id], _('System Notification'), body, context=context) + return self.pool.get(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 message_obj = self.pool.get('mail.message') notification_obj = self.pool.get('mail.notification') user = self.pool.get('res.users').browse(cr, UID_ROOT, uid) @@ -854,13 +856,13 @@ class share_wizard(osv.TransientModel): def send_emails(self, cr, uid, wizard_data, context=None): _logger.info('Sending share notifications by email...') - mail_message = self.pool.get('mail.message') + mail_mail = self.pool.get('mail.mail') user = self.pool.get('res.users').browse(cr, UID_ROOT, uid) if not user.email: raise osv.except_osv(_('Email required'), _('The current user must have an email address configured in User Preferences to be able to send outgoing emails.')) # TODO: also send an HTML version of this mail - msg_ids = [] + mail_ids = [] for result_line in wizard_data.result_line_ids: email_to = result_line.user_id.email if not email_to: @@ -883,10 +885,14 @@ class share_wizard(osv.TransientModel): body += "--\n" body += _("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.") - msg_ids.append(mail_message.schedule_with_attach(cr, uid, user.email, [email_to], subject, body, model='share.wizard', context=context)) + mail_ids.append(mail_mail.create(cr, uid, { + 'email_from': user.email, + 'email_to': email_to, + 'subject': subject, + 'body_html': '<pre>%s</pre>' % body}, context=context)) # force direct delivery, as users expect instant notification - mail_message.send(cr, uid, msg_ids, context=context) - _logger.info('%d share notification(s) sent.', len(msg_ids)) + mail_mail.send(cr, uid, mail_ids, context=context) + _logger.info('%d share notification(s) sent.', len(mail_ids)) def onchange_embed_options(self, cr, uid, ids, opt_title, opt_search, context=None): wizard = self.browse(cr, uid, ids[0], context) diff --git a/addons/stock/board_warehouse_view.xml b/addons/stock/board_warehouse_view.xml index 4258721dc03..b223e731e8f 100644 --- a/addons/stock/board_warehouse_view.xml +++ b/addons/stock/board_warehouse_view.xml @@ -14,26 +14,6 @@ </field> </record> - <record id="action_incoming_product_board" model="ir.actions.act_window"> - <field name="name">Incoming Shipments</field> - <field name="res_model">stock.move</field> - <field name="type">ir.actions.act_window</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="domain">[('state','in',('confirmed','assigned')),'|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]</field> - <field name="view_id" ref="stock.view_move_tree_reception_picking"/> - </record> - - <record id="action_outgoing_product_board" model="ir.actions.act_window"> - <field name="name">Delivery Orders</field> - <field name="res_model">stock.move</field> - <field name="type">ir.actions.act_window</field> - <field name="view_type">form</field> - <field name="view_mode">tree,form</field> - <field name="domain">[('state','in',('confirmed','assigned')),'|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]</field> - <field name="view_id" ref="stock.view_move_tree_reception_picking"/> - </record> - <record model="ir.actions.act_window" id="action_stock_incoming_product_delay"> <field name="name">Incoming Products</field> <field name="res_model">report.stock.move</field> @@ -57,14 +37,11 @@ <field name="model">board.board</field> <field name="arch" type="xml"> <form string="Warehouse board" version="7.0"> - <board style="2-1"> + <board style="1-1"> <column> - <action name="%(action_incoming_product_board)d" string="Incoming Shipments" creatable="true"/> - <action name="%(action_outgoing_product_board)d" string="Delivery Orders" creatable="true"/> + <action name="%(action_stock_incoming_product_delay)d" string="Incoming Products"/> </column> - <column> - <action name="%(action_stock_incoming_product_delay)d" string="Incoming Products"/> <action name="%(action_stock_outgoing_product_delay)d" string="Outgoing Products"/> </column> </board> diff --git a/addons/stock/i18n/ru.po b/addons/stock/i18n/ru.po index 65846a6b84d..09143eebcef 100644 --- a/addons/stock/i18n/ru.po +++ b/addons/stock/i18n/ru.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-02-08 01:37+0100\n" -"PO-Revision-Date: 2012-05-10 18:04+0000\n" +"PO-Revision-Date: 2012-09-10 11:41+0000\n" "Last-Translator: Chertykov Denis <chertykov@gmail.com>\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: 2012-08-28 05:57+0000\n" -"X-Generator: Launchpad (build 15864)\n" +"X-Launchpad-Export-Date: 2012-09-11 04:50+0000\n" +"X-Generator: Launchpad (build 15924)\n" #. module: stock #: field:product.product,track_outgoing:0 @@ -698,6 +698,7 @@ msgid "" "There is no inventory Valuation account defined on the product category: " "\"%s\" (id: %d)" msgstr "" +"Не определен счет оценки запасов для категории продукта: \"%s\" (id: %d)" #. module: stock #: view:report.stock.move:0 @@ -1059,7 +1060,7 @@ msgstr "" #. module: stock #: field:product.category,property_stock_valuation_account_id:0 msgid "Stock Valuation Account" -msgstr "" +msgstr "Счет складских запасов" #. module: stock #: code:addons/stock/stock.py:1349 @@ -1529,7 +1530,7 @@ msgstr "Общая информация" #. module: stock #: view:report.stock.inventory:0 msgid "Analysis including future moves (similar to virtual stock)" -msgstr "" +msgstr "Анализ включающий будущие перемещения (подобно виртуальным остаткам)" #. module: stock #: model:ir.actions.act_window,name:stock.action3 view:stock.tracking:0 @@ -1929,7 +1930,7 @@ msgstr "Оценка запасов" #. module: stock #: view:stock.move:0 msgid "Orders planned for today" -msgstr "" +msgstr "Документы запланированные на сегодня" #. module: stock #: view:stock.move:0 view:stock.picking:0 @@ -2078,7 +2079,7 @@ msgstr "Логистическая единица: поддон, ящик, упÐ #. module: stock #: view:stock.location:0 msgid "Customer Locations" -msgstr "" +msgstr "Места хранения заказчиков" #. module: stock #: view:stock.change.product.qty:0 view:stock.change.standard.price:0 @@ -2289,7 +2290,7 @@ msgstr "Тип поставки" #. module: stock #: view:stock.move:0 msgid "Stock moves that are Confirmed, Available or Waiting" -msgstr "" +msgstr "Перемещения которые подтверждены, доступны или в ожидании" #. module: stock #: model:ir.actions.act_window,name:stock.act_product_location_open @@ -2553,7 +2554,7 @@ msgstr "" #. module: stock #: model:stock.location,name:stock.location_opening msgid "opening" -msgstr "" +msgstr "открытие" #. module: stock #: model:ir.actions.report.xml,name:stock.report_product_history @@ -2638,7 +2639,7 @@ msgstr "Ваша компания" #. module: stock #: constraint:stock.move:0 msgid "You can not move products from or to a location of the type view." -msgstr "" +msgstr "Нельзя переместить ТМЦ в/из место хранения типа вид." #. module: stock #: help:stock.tracking,active:0 @@ -2974,7 +2975,7 @@ msgstr "Продукция" #. module: stock #: view:stock.return.picking:0 msgid "Return" -msgstr "" +msgstr "Возврат" #. module: stock #: field:stock.return.picking,invoice_state:0 @@ -2984,7 +2985,7 @@ msgstr "Выставление счета" #. module: stock #: view:stock.picking:0 msgid "Assigned Internal Moves" -msgstr "" +msgstr "Назначенные внутренние перемещения" #. module: stock #: code:addons/stock/stock.py:2379 code:addons/stock/stock.py:2440 @@ -3053,12 +3054,12 @@ msgstr "ТМЦ по категориям" #. module: stock #: selection:stock.picking,state:0 msgid "Waiting Another Operation" -msgstr "" +msgstr "Ожидание другой операции" #. module: stock #: view:stock.location:0 msgid "Supplier Locations" -msgstr "" +msgstr "Места хранения поставщиков" #. module: stock #: field:stock.partial.move.line,wizard_id:0 @@ -3070,7 +3071,7 @@ msgstr "Мастер" #. module: stock #: view:report.stock.move:0 msgid "Completed Stock-Moves" -msgstr "" +msgstr "Вычисленные перемещения" #. module: stock #: model:ir.actions.act_window,name:stock.action_view_stock_location_product @@ -3124,7 +3125,7 @@ msgstr "Место хранения источника" #. module: stock #: view:stock.move:0 msgid " Waiting" -msgstr "" +msgstr " Ожидание" #. module: stock #: view:product.template:0 @@ -3134,7 +3135,7 @@ msgstr "Бухгалтерские проводки" #. module: stock #: model:res.groups,name:stock.group_stock_manager msgid "Manager" -msgstr "" +msgstr "Менеджер" #. module: stock #: report:stock.picking.list:0 @@ -3242,12 +3243,12 @@ msgstr "Причина" #. module: stock #: model:product.template,name:stock.product_icecream_product_template msgid "Ice Cream" -msgstr "" +msgstr "Мороженое" #. module: stock #: model:ir.model,name:stock.model_stock_partial_picking msgid "Partial Picking Processing Wizard" -msgstr "" +msgstr "Запуск мастера частичного комплектования" #. module: stock #: model:ir.actions.act_window,help:stock.action_production_lot_form @@ -3527,7 +3528,7 @@ msgstr "Создать счет" #. module: stock #: view:stock.picking:0 msgid "Confirmed Internal Moves" -msgstr "" +msgstr "Подтвержденные внутренние перемещения" #. module: stock #: model:ir.ui.menu,name:stock.menu_stock_configuration @@ -3584,7 +3585,7 @@ msgstr "Клиенты" #. module: stock #: selection:stock.move,state:0 selection:stock.picking,state:0 msgid "Waiting Availability" -msgstr "" +msgstr "Ожидание поступления" #. module: stock #: code:addons/stock/stock.py:1347 @@ -3600,7 +3601,7 @@ msgstr "Позиции инвентаризации склада" #. module: stock #: view:stock.move:0 msgid "Waiting " -msgstr "" +msgstr "Ожидание " #. module: stock #: code:addons/stock/product.py:427 @@ -3646,7 +3647,7 @@ msgstr "Декабрь" #. module: stock #: view:stock.production.lot:0 msgid "Available Product Lots" -msgstr "" +msgstr "Партии доступных ТМЦ" #. module: stock #: model:ir.actions.act_window,help:stock.action_move_form2 @@ -3789,7 +3790,7 @@ msgstr "Обнулить" #. module: stock #: model:res.groups,name:stock.group_stock_user msgid "User" -msgstr "" +msgstr "Пользователь" #. module: stock #: code:addons/stock/wizard/stock_invoice_onshipping.py:98 @@ -4001,7 +4002,7 @@ msgstr "" #. module: stock #: view:report.stock.move:0 msgid "Future Stock-Moves" -msgstr "" +msgstr "Будущие перемещения" #. module: stock #: model:ir.model,name:stock.model_stock_move_split @@ -4012,7 +4013,7 @@ msgstr "Разбиение в партиях изделий" #: code:addons/stock/wizard/stock_move.py:213 #, python-format msgid "Processing Error" -msgstr "" +msgstr "Ошибка обработки" #. module: stock #: view:report.stock.inventory:0 @@ -4085,12 +4086,12 @@ msgstr "Идентификация партии изделий" #. module: stock #: model:stock.location,name:stock.location_refrigerator_small msgid "Small Refrigerator" -msgstr "" +msgstr "Маленький холодильник" #. module: stock #: model:stock.location,name:stock.location_convenience_shop msgid "Convenient Store" -msgstr "" +msgstr "Удобный магазин" #. module: stock #: field:stock.location,scrap_location:0 view:stock.move.scrap:0 @@ -4140,7 +4141,7 @@ msgstr "" #. module: stock #: constraint:res.partner:0 msgid "Error ! You cannot create recursive associated members." -msgstr "" +msgstr "Ошибка! Вы не можете создавать рекурсивные ссылки на участников." #. module: stock #: code:addons/stock/wizard/stock_partial_picking.py:159 @@ -4161,7 +4162,7 @@ msgstr "" #. module: stock #: constraint:product.category:0 msgid "Error ! You cannot create recursive categories." -msgstr "" +msgstr "Ошибка ! Нельзя создавать рекурсивные категории." #. module: stock #: help:stock.move,move_dest_id:0 diff --git a/addons/stock/product.py b/addons/stock/product.py index e0e55594eca..2016e25a5f6 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -32,7 +32,7 @@ class product_product(osv.osv): moves = move_pool.read_group(cr, uid, [ ('product_id', 'in', ids), ('picking_id.type', '=', 'in'), - ('state','in',('draft','confirmed','assigned','pending')) + ('state','in',('confirmed','assigned','pending')) ], ['product_id'], ['product_id']) for move in moves: product_id = move['product_id'][0] @@ -40,11 +40,11 @@ class product_product(osv.osv): moves = move_pool.read_group(cr, uid, [ ('product_id', 'in', ids), ('picking_id.type', '=', 'out'), - ('state','in',('draft','confirmed','assigned','pending')) + ('state','in',('confirmed','assigned','pending')) ], ['product_id'], ['product_id']) for move in moves: product_id = move['product_id'][0] - res[product_id]['reception_count'] = move['product_id_count'] + res[product_id]['delivery_count'] = move['product_id_count'] return res def get_product_accounts(self, cr, uid, product_id, context=None): @@ -373,7 +373,7 @@ class product_product(osv.osv): "typed as 'internal'."), 'virtual_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), - string='Quantity Available', + string='Future Quantity', help="Forecast quantity (computed as Quantity On Hand " "- Outgoing + Incoming)\n" "In a context with a single Stock Location, this includes " diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 477c458da02..2e0eef44da9 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -50,38 +50,28 @@ </page> </field> </record> - + <record id="view_normal_procurement_locations_form" model="ir.ui.view"> <field name="name">product.normal.procurement.locations.inherit</field> <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> - <notebook position="inside"> - <page string="Procurement & Locations" groups="base.group_user"> - <group> - <group name="delay" string="Delays"> - <field name="sale_delay" attrs="{'readonly':[('sale_ok','=',0)]}"/> - <field name="produce_delay" attrs="{'invisible':[('type','=','service')]}"/> - <field name="warranty"/> - </group> - <group name="store" string="Storage Localisation"> - <field name="loc_rack" attrs="{'readonly':[('type','=','service')]}"/> - <field name="loc_row" attrs="{'readonly':[('type','=','service')]}"/> - <field name="loc_case" attrs="{'readonly':[('type','=','service')]}"/> - </group> - <group name="store" groups="stock.group_locations"> - <separator string="Counter-Part Locations Properties" colspan="2"/> - <field name="property_stock_procurement" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','procurement')]"/> - <field name="property_stock_production" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','production')]"/> - <field name="property_stock_inventory" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','inventory')]"/> - </group> - <group name="misc" string="Miscellaneous"> - <field name="active"/> - <field name="company_id" groups="base.group_multi_company" widget="selection"/> - </group> - </group> - </page> - </notebook> + <group name="procurement" position="inside"> + <group name="delay" string="Delays"> + <label for="produce_delay" attrs="{'invisible':[('type','=','service')]}"/> + <div attrs="{'invisible':[('type','=','service')]}"> + <field name="produce_delay" class="oe_inline"/> days + </div> + <field name="active"/> + </group> + </group> + <group name="inventory" position="inside"> + <group name="store" groups="stock.group_locations" string="Counter-Part Locations Properties"> + <field name="property_stock_procurement" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','procurement')]"/> + <field name="property_stock_production" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','production')]"/> + <field name="property_stock_inventory" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','inventory')]"/> + </group> + </group> </field> </record> @@ -93,8 +83,8 @@ <field name="standard_price" position="replace" version="7.0"> <label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/> <div groups="base.group_user"> - <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}" nolabel="1"/> - - <button name="%(action_view_change_standard_price)d" string="update" + <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}" nolabel="1"/> + <button name="%(action_view_change_standard_price)d" string="- update" type="action" attrs="{'invisible':[('cost_method','<>','average')]}" class="oe_link"/> </div> @@ -132,19 +122,17 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> <group name="status" position="before" version="7.0"> - <group string="Stock and Expected Variations" attrs="{'invisible': [('type', '=', 'service')]}"> + <group string="Stock and Expected Variations" attrs="{'invisible': [('type', '=', 'service')]}" groups="base.group_user"> <label for="qty_available"/> <div> <field name="qty_available" class="oe_inline"/> <button name="%(action_view_change_product_quantity)d" string="update" - type="action" groups="stock.group_stock_manager,stock.group_stock_user" + type="action" class="oe_link"/> </div> - <label for="virtual_available" groups="base.group_user"/> - <div groups="base.group_user"> - <field name="virtual_available" class="oe_inline"/> - (+<field name="incoming_qty" class="oe_inline"/> -<field name="outgoing_qty" class="oe_inline"/>) - </div> + <field name="incoming_qty" class="oe_inline"/> + <field name="outgoing_qty" class="oe_inline"/> + <field name="virtual_available" class="oe_inline"/> </group> <group name="lot" groups="stock.group_tracking_lot,stock.group_production_lot" string="Lots"> <field name="track_production" groups="stock.group_production_lot"/> @@ -164,7 +152,6 @@ <field name="domain">[('picking_id.type','=','in')]</field> <field name="view_id" ref="view_move_tree_reception_picking"/> <field name="context" eval="'{\'search_default_product_id\': [active_id]}'"/> - <field name="search_view_id" ref="view_move_search_reception_incoming_picking"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to register a reception for this product. @@ -185,7 +172,6 @@ <field name="domain">[('picking_id.type','=','out')]</field> <field name="view_id" ref="view_move_tree_reception_picking"/> <field name="context" eval="'{\'search_default_product_id\': [active_id]}'"/> - <field name="search_view_id" ref="view_move_search_reception_outcoming_picking"/> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to add a delivery order for this product. @@ -197,50 +183,27 @@ </field> </record> - <record model="ir.ui.view" id="product.product_kanban_view"> - <field name="name">Product Kanban</field> + <record model="ir.ui.view" id="product_kanban_stock_view"> + <field name="name">Product Kanban Stock</field> <field name="model">product.product</field> + <field name="inherit_id" ref="product.product_kanban_view"/> <field name="arch" type="xml"> - <kanban> - <field name="color"/> - <field name="type"/> - <field name="image_medium"/> - <field name="list_price"/> + <field name="list_price" position="after"> <field name="reception_count"/> <field name="delivery_count"/> - <templates> - <t t-name="kanban-box"> - <div class="oe_product_vignette"> - <div class="oe_product_img"> - <a type="open"><img t-att-src="kanban_image('product.product', 'image_medium', record.id.value)" class="oe_product_photo"/></a> - </div> - <div class="oe_product_desc"> - <h4><a type="open"><field name="name"></field></a></h4> - <ul> - <li t-if="record.code.raw_value">Code: <field name="code"/></li> - <li t-if="record.type.raw_value != 'service'">Stock on hand: <field name="qty_available"/> <field name="uom_id"/></li> - <li t-if="record.type.raw_value != 'service'">Stock available: <field name="virtual_available"/> <field name="uom_id"/></li> - <li>Price: <field name="lst_price"></field></li> - <!-- TODO the 'groups' attribute doesn't work in kanban views --> - <li groups="base.group_user">Cost: <field name="standard_price"></field></li> - </ul> - <div groups="base.group_user"> - <a name="%(action_receive_move)d" type="action"> - <t t-if="record.reception_count.value">Receptions(<t t-esc="record.reception_count.value"/>)</t> - </a> - <a name="%(action_deliver_move)d" type="action"> - <t t-if="record.delivery_count.value">Deliveries(<t t-esc="record.delivery_count.value"/>)</t> - </a> - </div> - </div> - </div> - <script> - $('.oe_product_photo').load(function() { if($(this).width() > $(this).height()) { $(this).addClass('oe_product_photo_wide') } }); - </script> - <div></div> - </t> - </templates> - </kanban> + </field> + <xpath expr="//div[@name='tags']" position="inside"> + <a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value>0"> + <t t-esc="record.reception_count.value"/> Receptions + </a> + <a name="%(action_deliver_move)d" type="action" t-if="record.delivery_count.raw_value>0"> + <t t-esc="record.delivery_count.value"/> Deliveries + </a> + </xpath> + <ul position="inside"> + <li t-if="record.type.raw_value != 'service'">On hand: <field name="qty_available"/> <field name="uom_id"/></li> + <li t-if="record.type.raw_value != 'service'">Available: <field name="virtual_available"/> <field name="uom_id"/></li> + </ul> </field> </record> diff --git a/addons/stock/report/stock_graph.py b/addons/stock/report/stock_graph.py index 5ee58bb9d2c..d92de2805b5 100644 --- a/addons/stock/report/stock_graph.py +++ b/addons/stock/report/stock_graph.py @@ -22,6 +22,7 @@ from pychart import * import pychart.legend import time from report.misc import choice_colors +import tools # # Draw a graph for stocks @@ -41,7 +42,7 @@ class stock_graph(object): product_name=product_name.replace('/', '//') if product_id not in self._datas: self._datas[product_id] = {} - self._names[product_id] = product_name + self._names[product_id] = tools.ustr(product_name) for (dt,stock) in datas: if not dt in self._datas[product_id]: self._datas[product_id][dt]=0 diff --git a/addons/stock/static/src/css/stock.css b/addons/stock/static/src/css/stock.css index 449c1102fce..1efbaf5e3c4 100644 --- a/addons/stock/static/src/css/stock.css +++ b/addons/stock/static/src/css/stock.css @@ -9,8 +9,8 @@ } .oe_product_img { - width: 150px; - height: 150px; + width: 80px; + height: 80px; text-align: center; overflow: hidden; -moz-border-radius: 3px; @@ -25,15 +25,15 @@ } .oe_product_photo { - width: 150px; + width: 80px; height: auto; - clip: rect(5px, 100px, 105px, 0px); + clip: rect(5px, 70px, 75px, 0px); } .oe_product_photo_wide { - height: 150px; + height: 80px; width: auto; - clip: rect(0px, 110px, 100px, 10px); + clip: rect(0px, 80px, 70px, 10px); } .oe_product_desc { diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7f0a744a62e..e93f9bb34ea 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1379,13 +1379,13 @@ class stock_picking(osv.osv): def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s has been <b>created</b>.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s has been <b>created</b>.") % (self._get_document_type(obj.type)), context=context) def scrap_send_note(self, cr, uid, ids, quantity, uom, name, context=None): - return self.message_append_note(cr, uid, ids, body= _("%s %s %s has been <b>moved to</b> scrap.") % (quantity, uom, name), context=context) + return self.message_post(cr, uid, ids, body= _("%s %s %s has been <b>moved to</b> scrap.") % (quantity, uom, name), context=context) def back_order_send_note(self, cr, uid, ids, back_name, context=None): - return self.message_append_note(cr, uid, ids, body=_("Back order <em>%s</em> has been <b>created</b>.") % (back_name), context=context) + return self.message_post(cr, uid, ids, body=_("Back order <em>%s</em> has been <b>created</b>.") % (back_name), context=context) def ship_done_send_note(self, cr, uid, ids, context=None): type_dict = { @@ -1394,11 +1394,11 @@ class stock_picking(osv.osv): 'internal': 'moved', } for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("Products have been <b>%s</b>.") % (type_dict.get(obj.type, 'move done')), context=context) + self.message_post(cr, uid, [obj.id], body=_("Products have been <b>%s</b>.") % (type_dict.get(obj.type, 'move done')), context=context) def ship_cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s has been <b>cancelled</b>.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s has been <b>cancelled</b>.") % (self._get_document_type(obj.type)), context=context) stock_picking() @@ -1577,7 +1577,13 @@ class stock_move(osv.osv): def name_get(self, cr, uid, ids, context=None): res = [] for line in self.browse(cr, uid, ids, context=context): - res.append((line.id, (line.product_id.code or '/')+': '+line.location_id.name+' > '+line.location_dest_id.name)) + name = line.location_id.name+' > '+line.location_dest_id.name + # optional prefixes + if line.product_id.code: + name = line.product_id.code + ': ' + name + if line.picking_id.origin: + name = line.picking_id.origin + '/ ' + name + res.append((line.id, name)) return res def _check_tracking(self, cr, uid, ids, context=None): @@ -2542,7 +2548,7 @@ class stock_move(osv.osv): product_obj = self.pool.get('product.product') for new_move in self.browse(cr, uid, res, context=context): message = _("Product has been consumed with '%s' quantity.") % (new_move.product_qty) - product_obj.message_append_note(cr, uid, [new_move.product_id.id], body=message, context=context) + product_obj.message_post(cr, uid, [new_move.product_id.id], body=message, context=context) self.action_done(cr, uid, res, context=context) diff --git a/addons/stock/stock_data.xml b/addons/stock/stock_data.xml index f4b3ab90d9c..77c82989a21 100644 --- a/addons/stock/stock_data.xml +++ b/addons/stock/stock_data.xml @@ -2,12 +2,14 @@ <openerp> <data noupdate="1"> <!-- notify all employees of module installation --> - <function model="mail.group" name="message_append_note"> - <!-- ids, subject, body, parent_id=False, type='notification', content_subtype='html' --> - <value eval="[ref('mail.group_all_employees')]"/> - <value>Module Warehouse Management has been installed</value> - <value>Manage your product inventoy and stock locations. You can control your stock moves history and planning, perform stock valuation, and trace product lots upstream and downstream (based on serial numbers.)</value> - </function> + <record model="mail.message" id="module_install_notification"> + <field name="model">mail.group</field> + <field name="res_id" ref="mail.group_all_employees"/> + <field name="type">notification</field> + <field name="subject">Warehouse Management application installed!</field> + <field name="body">Manage your product inventoy and stock locations: you can control stock moves history and planning, +watch your stock valuation, and track production lots upstream and downstream (based on serial numbers).</field> + </record> <record id="stock_journal_sequence" model="ir.sequence"> <field name="name">Stock Journal Sequence</field> diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 6927bf68c25..3560b255ab5 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -113,13 +113,14 @@ <field name="state" widget="statusbar" statusbar_visible="draft,confirm"/> </header> <sheet> - <div> - <label for="name" class="oe_edit_only"/> - <field name="name" placeholder="e.g. Annual inventory" class="oe_inline"/> - - <field name="date" class="oe_inline"/> - </div> <group> - <field name="company_id" groups="base.group_multi_company" widget="selection"/> + <group> + <field name="name" placeholder="e.g. Annual inventory"/> + </group> + <group> + <field name="date"/> + <field name="company_id" groups="base.group_multi_company" widget="selection"/> + </group> </group> <notebook> <page string="General Informations"> @@ -267,7 +268,8 @@ </p> </field> </record> - <menuitem id="menu_traceability" name="Traceability" parent="menu_stock_root" sequence="3"/> + <menuitem id="menu_traceability" name="Traceability" parent="menu_stock_root" + sequence="3" groups="stock.group_tracking_lot,stock.group_production_lot"/> <menuitem action="action_tracking_form" id="menu_action_tracking_form" groups="product.group_stock_packaging" sequence="6" @@ -1333,10 +1335,11 @@ <field eval="3" name="priority"/> <field name="arch" type="xml"> <search string="Stock Moves"> - <field name="origin" string="Stock Moves"/> + <field name="origin" domain="['|',('origin','ilike',self),('picking_id','ilike',self)]" string="Reference"/> <field name="date" groups="base.group_no_one"/> + <filter icon="terp-camera_test" string="Ready" name="ready" domain="[('state','=','assigned')]" help="Stock moves that are Available (Ready to process)"/> - <filter icon="terp-stock" string="Future" name="future" domain="[('state','in',('assigned','confirmed','waiting'))]" help="Stock moves that are Confirmed, Available or Waiting"/> + <filter icon="terp-stock" string="To Do" name="future" domain="[('state','in',('assigned','confirmed','waiting'))]" help="Stock moves that are Confirmed, Available or Waiting"/> <filter icon="terp-dialog-close" string="Done" name="done" domain="[('state','=','done')]" help="Stock moves that have been processed"/> <separator/> <filter icon="terp-go-today" string="Today" domain="[('date','<=',time.strftime('%%Y-%%m-%%d 23:59:59')),('date','>=',time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Orders processed Today or planned for Today"/> @@ -1389,8 +1392,10 @@ <field name="priority" eval="6"/> <field name="arch" type="xml"> <tree colors="grey:state == 'cancel'" string="Moves"> - <field name="picking_id" string="Reference"/> + <field name="date" widget="date"/> + <field name="picking_id" string="Reference" invisible="1"/> <field name="origin"/> + <field name="partner_id" string="Supplier"/> <field name="product_id"/> <field name="product_qty"/> <field name="product_uom" string="Unit of Measure" groups="product.group_uom"/> @@ -1413,8 +1418,13 @@ groups="product.group_stock_packaging" icon="terp-stock_effects-object-colorize" states="draft,assigned,confirmed,done"/> - <field name="date" groups="base.group_no_one"/> <field name="state"/> + <button name="%(action_partial_move_server)d" + icon="terp-stock_effects-object-colorize" type="action" + states="assigned" class="oe_highlight"/> + <button name="action_done" states="draft,assigned,confirmed" + icon="gtk-go-forward" type="object" + class="oe_highlight"/> </tree> </field> </record> @@ -1435,54 +1445,6 @@ </field> </record> - <record id="view_move_search_reception_incoming_picking" model="ir.ui.view"> - <field name="name">stock.move.incoming.search2</field> - <field name="model">stock.move</field> - <field eval="6" name="priority"/> - <field name="arch" type="xml"> - <search string="Stock Moves"> - <field name="origin" string="Stock Moves"/> - <filter icon="terp-gtk-go-back-rtl" name="receive" string="Waiting " domain="[('state','in',('confirmed','assigned'))]" help="Stock to be receive"/> - <filter icon="terp-check" name="available" string="Available" domain="[('state','in',('assigned',))]" help="Stock available to be delivered"/> - <filter icon="terp-dialog-close" name="done" string="Done" domain="[('state', '=', 'done')]"/> - <separator/> - <filter icon="terp-go-today" string="Today" domain="[('date','<=',time.strftime('%%Y-%%m-%%d 23:59:59')),('date','>=',time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Orders planned for today"/> - <field name="product_id"/> - <field name="prodlot_id"/> - <group expand="0" string="Group By..."> - <filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_id'}"/> - <filter string="Order" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'origin'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> - <filter string="Order Date" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" groups="base.group_no_one"/> - </group> - </search> - </field> - </record> - - <record id="view_move_search_reception_outcoming_picking" model="ir.ui.view"> - <field name="name">stock.move.outgoing.search2</field> - <field name="model">stock.move</field> - <field eval="6" name="priority"/> - <field name="arch" type="xml"> - <search string="Stock Moves"> - <field name="origin" string="Stock Moves"/> - <filter icon="terp-gtk-go-back-rtl" name="receive" string="Waiting" domain="[('state','in',('confirmed','assigned'))]" help="Stock to be delivered (Available or not)"/> - <filter icon="terp-check" name="available" string="Available" domain="[('state','in',('assigned',))]" help="Stock available to be delivered"/> - <filter icon="terp-dialog-close" name="received" string="Done" domain="[('state','=','done')]"/> - <separator/> - <filter icon="terp-go-today" string="Today" domain="[('date','<=',time.strftime('%%Y-%%m-%%d 23:59:59')),('date','>=',time.strftime('%%Y-%%m-%%d 00:00:00'))]" help="Orders planned for today"/> - <field name="product_id"/> - <field name="prodlot_id"/> - <group expand="0" string="Group By..."> - <filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_id'}"/> - <filter string="Order" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'origin'}"/> - <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/> - <filter string="Order Date" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" groups="base.group_no_one"/> - </group> - </search> - </field> - </record> - <record id="action_reception_picking_move" model="ir.actions.act_window"> <field name="name">Receive Products</field> <field name="res_model">stock.move</field> @@ -1491,8 +1453,7 @@ <field name="view_mode">tree,form</field> <field name="domain">['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]</field> <field name="view_id" ref="view_move_tree_reception_picking"/> - <field name="context" eval="'{\'product_receive\' : True, \'default_location_id\':%d, \'default_location_dest_id\':%d}' % (ref('stock_location_suppliers'),ref('stock_location_stock') )"/> - <field name="search_view_id" ref="view_move_search_reception_incoming_picking"/> + <field name="context" eval="'{\'product_receive\' : True, \'default_location_id\':%d, \'default_location_dest_id\':%d, \'search_default_future\': 1}' % (ref('stock_location_suppliers'),ref('stock_location_stock') )"/> <field name="help" type="html"> <p> Click to register a product reception. @@ -1627,8 +1588,7 @@ <field name="view_mode">tree,form</field> <field name="domain">['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','out')]</field> <field name="view_id" ref="view_move_tree_reception_picking"/> - <field name="context" eval="'{\'default_location_id\':%d, \'default_location_dest_id\':%d}' % (ref('stock_location_stock'),ref('stock_location_customers'))"/> - <field name="search_view_id" ref="view_move_search_reception_outcoming_picking"/> + <field name="context" eval="'{\'default_location_id\':%d, \'default_location_dest_id\':%d, \'search_default_future\': 1}' % (ref('stock_location_stock'),ref('stock_location_customers'))"/> <field name="help" type="html"> <p> You will find in this list all products you have to deliver to diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py index 0cc97e10298..5aa4649af02 100644 --- a/addons/stock/wizard/stock_change_product_qty.py +++ b/addons/stock/wizard/stock_change_product_qty.py @@ -113,7 +113,7 @@ class stock_change_product_qty(osv.osv_memory): for data in self.browse(cr, uid, ids, context=context): location_name = location_obj.browse(cr, uid, data.location_id.id, context=context).name message = _("<b>Quantity has been changed</b> to <em>%s %s </em> for <em>%s</em> location.") % (data.new_quantity, data.product_id.uom_id.name, location_name) - prod_obj.message_append_note(cr, uid, [data.product_id.id], body=message, context=context) + prod_obj.message_post(cr, uid, [data.product_id.id], body=message, context=context) stock_change_product_qty() diff --git a/addons/stock/wizard/stock_partial_move_view.xml b/addons/stock/wizard/stock_partial_move_view.xml index acfc4b1923c..54f15e99dff 100644 --- a/addons/stock/wizard/stock_partial_move_view.xml +++ b/addons/stock/wizard/stock_partial_move_view.xml @@ -44,7 +44,7 @@ <field name="prodlot_id" domain="[('product_id', '=', product_id)]" groups="stock.group_production_lot"/> <field name="update_cost" invisible="1"/> <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/> - <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}"/> + <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/> </tree> </field> </record> @@ -63,7 +63,7 @@ <field name="prodlot_id" domain="[('product_id', '=', product_id)]"/> <field name="update_cost" invisible="1"/> <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/> - <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}"/> + <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/> </group> </form> </field> diff --git a/addons/stock/wizard/stock_partial_picking_view.xml b/addons/stock/wizard/stock_partial_picking_view.xml index 7de85594a26..fb01c93315e 100644 --- a/addons/stock/wizard/stock_partial_picking_view.xml +++ b/addons/stock/wizard/stock_partial_picking_view.xml @@ -71,7 +71,7 @@ <field name="prodlot_id" domain="[('product_id', '=', product_id)]" attrs="{'required':[('tracking','=',True)]}"/> <field name="update_cost" invisible="1"/> <field name="cost" attrs="{'invisible': [('update_cost','=', False)]}"/> - <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}"/> + <field name="currency" attrs="{'invisible': [('update_cost','=', False)]}" groups="base.group_multi_currency"/> </group> </form> </field> diff --git a/addons/stock_location/stock_location_view.xml b/addons/stock_location/stock_location_view.xml index 720c7753df6..ddb2efc21e5 100644 --- a/addons/stock_location/stock_location_view.xml +++ b/addons/stock_location/stock_location_view.xml @@ -38,62 +38,59 @@ <field name="model">product.product</field> <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> - <notebook position="inside"> - <page string="Logistics Flows"> - <field name="flow_pull_ids" colspan="4" nolabel="1"> - <tree string="Pulled flows"> - <field name="name"/> - <field name="location_id" attrs="{'required': [('type_proc', '=', 'move')]}"/> - <field name="type_proc"/> - <field name="journal_id"/> - <field name="company_id" groups="base.group_multi_company"/> - </tree> - <form string="Pulled Paths"> - <group> - <group string="Conditions"> - <field name="name" placeholder="Fulfill needs on location X from location Y "/> - <field name="location_id"/> - </group> - <group string="Action Type"> - <field name="type_proc"/> - <field name="company_id"/> - </group> + <group name="inventory" position="after"> + <field name="flow_pull_ids" colspan="4" nolabel="1"> + <tree string="Pulled flows"> + <field name="name"/> + <field name="location_id" attrs="{'required': [('type_proc', '=', 'move')]}"/> + <field name="type_proc"/> + <field name="journal_id"/> + <field name="company_id" groups="base.group_multi_company"/> + </tree> + <form string="Pulled Paths"> + <group> + <group string="Conditions"> + <field name="name" placeholder="Fulfill needs on location X from location Y "/> + <field name="location_id"/> </group> - <group attrs="{'invisible':[('type_proc','!=','move')]}"> - <field name="location_src_id" attrs="{'required': [('type_proc', '=', 'move')]}"/> - <field name="picking_type"/> - <field name="partner_address_id"/> - <field name="procure_method"/> - <field name="cancel_cascade"/> - <field name="invoice_state"/> - <field name="journal_id"/> + <group string="Action Type"> + <field name="type_proc"/> + <field name="company_id"/> </group> - </form> - </field> - <field name="path_ids"> - <field name="name"/> - <tree string="Pushed flows"> - <field name="location_from_id"/> - <field name="location_dest_id"/> - <field name="auto"/> - <field name="name"/> - <field name="invoice_state"/> - <field name="company_id" groups="base.group_multi_company"/> - </tree> - <form string="Pushed flows"> - <field name="name" placeholder="When receiving at location X, move to location Y"/> - <field name="location_from_id"/> - <field name="location_dest_id"/> - <field name="auto"/> - <field name="delay"/> - <field name="journal_id"/> + </group> + <group attrs="{'invisible':[('type_proc','!=','move')]}"> + <field name="location_src_id" attrs="{'required': [('type_proc', '=', 'move')]}"/> <field name="picking_type"/> + <field name="partner_address_id"/> + <field name="procure_method"/> + <field name="cancel_cascade"/> <field name="invoice_state"/> - <field name="company_id" groups="base.group_multi_company"/> - </form> - </field> - </page> - </notebook> + <field name="journal_id"/> + </group> + </form> + </field> + <field name="path_ids"> + <tree string="Pushed flows"> + <field name="location_from_id"/> + <field name="location_dest_id"/> + <field name="auto"/> + <field name="name"/> + <field name="invoice_state"/> + <field name="company_id" groups="base.group_multi_company"/> + </tree> + <form string="Pushed flows"> + <field name="name" placeholder="When receiving at location X, move to location Y"/> + <field name="location_from_id"/> + <field name="location_dest_id"/> + <field name="auto"/> + <field name="delay"/> + <field name="journal_id"/> + <field name="picking_type"/> + <field name="invoice_state"/> + <field name="company_id" groups="base.group_multi_company"/> + </form> + </field> + </group> </field> </record> </data> diff --git a/addons/survey/__openerp__.py b/addons/survey/__openerp__.py index 1470754cb03..40eb3073d61 100644 --- a/addons/survey/__openerp__.py +++ b/addons/survey/__openerp__.py @@ -56,10 +56,10 @@ user name and password for the invitation of the survey. 'test/survey_question_type.yml', 'test/survey_report.yml', ], - 'css': ['static/src/css/survey.css'], 'installable': True, 'auto_install': False, 'certificate' : '001131639736864143245', - 'images': ['images/survey_answers.jpeg','images/survey_pages.jpeg','images/surveys.jpeg'], + 'images': ['images/survey_answers.jpeg','images/survey_pages.jpeg','images/surveys.jpeg'], + 'css': ['static/src/css/survey.css','static/css/survey.css'], } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/survey/specification/survey.png b/addons/survey/specification/survey.png Binary files differindex 8cf0f7022c1..52acff391d0 100644 --- a/addons/survey/specification/survey.png +++ b/addons/survey/specification/survey.png diff --git a/addons/survey/specification/survey_page.png b/addons/survey/specification/survey_page.png Binary files differindex 2f8d19d5dd8..4fceac4b81b 100644 --- a/addons/survey/specification/survey_page.png +++ b/addons/survey/specification/survey_page.png diff --git a/addons/survey/specification/survey_question.png b/addons/survey/specification/survey_question.png Binary files differindex b939ae2ad47..516606902cb 100644 --- a/addons/survey/specification/survey_question.png +++ b/addons/survey/specification/survey_question.png diff --git a/addons/survey/specification/survey_response.png b/addons/survey/specification/survey_response.png Binary files differindex 67bc1aa9432..af78bd1977d 100644 --- a/addons/survey/specification/survey_response.png +++ b/addons/survey/specification/survey_response.png diff --git a/addons/survey/static/src/css/survey.css b/addons/survey/static/src/css/survey.css index c18f584c9ed..34d686c6e77 100644 --- a/addons/survey/static/src/css/survey.css +++ b/addons/survey/static/src/css/survey.css @@ -1,14 +1,11 @@ -.oe_survey_responsible{ - height: auto; - width: 200px; - font-size: 14px; +.openerp .oe_survey_title { + font-weight: bold; + font-size: 19px; + margin: 8px 0px 8px 0px; + color: #5B5B5B; } -.oe_survey_start_date{ - height: auto; +.openerp .oe_kanban_survey { width: 200px; - font-size: 14px; -} -.oe_survey_rate{ - font-size: 14px; + } diff --git a/addons/survey/survey.py b/addons/survey/survey.py index 93bd48be30c..dde3130e9c5 100644 --- a/addons/survey/survey.py +++ b/addons/survey/survey.py @@ -145,15 +145,33 @@ class survey(osv.osv): return report def fill_survey(self, cr, uid, ids, context=None): + sur_obj = self.read(cr, uid, ids,['title'], context=context) + for sur in sur_obj: + name = sur['title'] + context.update({'active':False,'survey_id': ids[0]}) return { 'view_type': 'form', 'view_mode': 'form', 'res_model': 'survey.question.wiz', 'type': 'ir.actions.act_window', 'target': 'new', - 'context': {'survey_id': ids[0]} + 'name': name, + 'context': context + } + def test_survey(self, cr, uid, ids, context=None): + sur_obj = self.read(cr, uid, ids,['title'], context=context) + for sur in sur_obj: + name = sur['title'] + context.update({'active':True,'survey_id': ids[0]}) + return { + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'survey.question.wiz', + 'type': 'ir.actions.act_window', + 'target': 'new', + 'name': name, + 'context': context } - survey() class survey_history(osv.osv): diff --git a/addons/survey/survey_demo.xml b/addons/survey/survey_demo.xml index 3bfac20d44b..6f89095c699 100644 --- a/addons/survey/survey_demo.xml +++ b/addons/survey/survey_demo.xml @@ -13,6 +13,7 @@ <field name="max_response_limit">20</field> <field name="type" ref="survey_type2"/> <field name="state">open</field> + <field name="color">5</field> <field name="tot_comp_survey">1</field> <field name="tot_start_survey">1</field> <field name="response_user">5</field> @@ -1402,6 +1403,7 @@ <record id="survey_0" model="survey"> <field eval=""""OpenERP Feedback"""" name="title"/> <field name="responsible_id" ref="base.user_demo"/> + <field name="color">4</field> <field name="max_response_limit">20</field> <field name="type" ref="survey_type2"/> <field name="state">open</field> diff --git a/addons/survey/survey_view.xml b/addons/survey/survey_view.xml index b8c6f97e45b..8f855084069 100644 --- a/addons/survey/survey_view.xml +++ b/addons/survey/survey_view.xml @@ -26,8 +26,12 @@ </header> <sheet> <div class="oe_button_box oe_right"> - <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" string="Test Survey" type="action" icon="gtk-new" context="{'active':True,'survey_id': active_id}" attrs="{'invisible':[('id','=',0)]}"/> - <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" string="Answer Survey" type="action" icon="gtk-execute" context="{'survey_id': active_id}" attrs="{'invisible':[('state','!=','open')]}"/> + <button name="test_survey" states="open,draft,close,cancel" string="Test Survey" type="object" icon="gtk-new" context="{'survey_id': active_id}" attrs="{'invisible':[('id','=',0)]}"/> + <button name="fill_survey" states="open" string="Answer Survey" type="object" icon="gtk-execute" context="{'survey_id': active_id}" attrs="{'invisible':[('state','!=','open')]}"/> + <button name="action_print_survey" states="open,draft,close,cancel" string="Print Answer" type="object"/> + <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" + string="Edit Survey" type="action" context="{'active':True,'edit' : True,'survey_id': active_id}"/> + </div> <div class="oe_title"> <label for="title" class="oe_edit_only"/> @@ -49,8 +53,6 @@ </group> <notebook> <page string="Survey Details"> - <field name="note" colspan="4" placeholder="Survey description..."/> - <label for="page_ids"/> <field name="page_ids" colspan="4" mode="tree"> <form string="Survey Page" version="7.0"> <sheet> @@ -58,9 +60,7 @@ <h1> <field name="title"/> </h1> - <group> - <field name="note" placeholder="Description on the survey page..." nolabel="1"/> - </group> + <field name="note" placeholder="Description on the survey page..."/> <newline/> <group> <label for="question_ids"/> @@ -210,6 +210,7 @@ </sheet> </form> </field> + <field name="note" placeholder="Survey description..."/> </page> <page string="Invited User"> <field name="invited_user_ids" readonly="1"/> @@ -254,13 +255,11 @@ <field name="tot_start_survey" string="Started"/> <field name="tot_comp_survey" string="Completed"/> <field name="state"/> - <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" - string="Test Survey" type="action" icon="gtk-new" - context="{'active':True,'survey_id': active_id}"/> - <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" - string="Answer Survey" type="action" icon="gtk-execute" context="{'survey_id': active_id}" attrs="{'invisible':[('state','!=','open')]}"/> - <button name="%(action_view_survey_question_message)d" states="open,draft,close,cancel" - string="Edit Survey" type="action" icon="gtk-edit" context="{'active':True,'edit' : True,'survey_id': active_id}"/> + <button name="test_survey" states="open,draft,close,cancel" + string="Test Survey" type="object" icon="gtk-new" + context="{'survey_id': active_id}"/> + <button name="fill_survey" states="open" + string="Answer Survey" type="object" icon="gtk-execute" context="{'survey_id': active_id}" attrs="{'invisible':[('state','!=','open')]}"/> <button name="action_print_survey" states="open,draft,close,cancel" string="Print Answer" type="object" icon="gtk-print"/> </tree> @@ -295,35 +294,30 @@ <kanban> <field name="title"/> <field name="date_open"/> + <field name="tot_comp_survey"/> + <field name="max_response_limit"/> + <field name="responsible_id"/> <field name="color"/> <templates> <t t-name="kanban-box"> - <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card"> + <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_survey"> <div class="oe_dropdown_toggle oe_dropdown_kanban"> <span class="oe_e">i</span> <ul class="oe_dropdown_menu"> - <li><a type="edit">Edit...</a></li> - <li><a type="delete">Delete</a></li> + <t t-if="widget.view.is_action_enabled('edit')"><li><a type="edit">Edit...</a></li></t> + <t t-if="widget.view.is_action_enabled('delete')"><li><a type="delete">Delete</a></li></t> <li><ul class="oe_kanban_colorpicker" data-field="color"/></li> </ul> </div> <div class="oe_kanban_content"> <h3 class="oe_kanban_ellipsis"><t t-esc="record.title.raw_value.toString()"></t></h3> - <div class="oe_survey_responsible"> - <t t-if="record.responsible_id.raw_value">By: <field name="responsible_id"/></t> - </div><br/> - <div class="oe_survey_start_date"> - <t t-if="record.date_open.raw_value"> - Opened at - <t t-esc="record.date_open.raw_value.getDate()"/> - <t t-esc="record.date_open.raw_value.toString('MMM')"/>, - <t t-esc="record.date_open.raw_value.getFullYear()"/> - </t> - </div><br/> - <div class="oe_survey_rate"> - Rate: - <a name="fill_survey" type="object">Fill</a> - </div> + <div> + Answers: <field name="tot_comp_survey"/> + <t t-if="record.max_response_limit.raw_value > 0"> / <field name="max_response_limit"/></t> + </div> + <br/> + <a name="fill_survey" type="object">Answer Survey</a> + <img t-att-src="kanban_image('res.users', 'image_small', record.responsible_id.raw_value)" t-att-title="record.responsible_id.value" width="24" height="24" class="oe_kanban_avatar oe_right"/> </div> </div> </t> diff --git a/addons/survey/wizard/survey_answer.py b/addons/survey/wizard/survey_answer.py index 66098b45fa8..2b774a22feb 100644 --- a/addons/survey/wizard/survey_answer.py +++ b/addons/survey/wizard/survey_answer.py @@ -146,8 +146,18 @@ class survey_question_wiz(osv.osv_memory): pre_button = True if flag: pag_rec = page_obj.browse(cr, uid, p_id, context=context) - xml_form = etree.Element('form', {'string': tools.ustr(pag_rec.title)}) - xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'}) + xml_form = etree.Element('form', {'string': tools.ustr(pag_rec.title or sur_rec.title)}) + if context.has_key('active') and context.get('active',False) and context.has_key('edit'): + context.update({'page_id' : tools.ustr(p_id),'page_number' : sur_name_rec.page_no , 'transfer' : sur_name_read.transfer}) + xml_group3 = etree.SubElement(xml_form, 'group', {'col': '4', 'colspan': '4'}) + etree.SubElement(xml_group3, 'button', {'string' :'Add Page','icon': "gtk-new", 'type' :'object','name':"action_new_page", 'context' : tools.ustr(context)}) + etree.SubElement(xml_group3, 'button', {'string' :'Edit Page','icon': "gtk-edit", 'type' :'object','name':"action_edit_page", 'context' : tools.ustr(context)}) + etree.SubElement(xml_group3, 'button', {'string' :'Delete Page','icon': "gtk-delete", 'type' :'object','name':"action_delete_page", 'context' : tools.ustr(context)}) + etree.SubElement(xml_group3, 'button', {'string' :'Add Question','icon': "gtk-new", 'type' :'object','name':"action_new_question", 'context' : tools.ustr(context)}) + + # FP Note + xml_group = xml_form + if context.has_key('response_id') and context.get('response_id', False) \ and int(context.get('response_id',0)[0]) > 0: # TODO: l10n, cleanup this code to make it readable. Or template? @@ -162,12 +172,11 @@ class survey_question_wiz(osv.osv_memory): if wiz_id: fields["wizardid_" + str(wiz_id)] = {'type':'char', 'size' : 255, 'string':"", 'views':{}} - etree.SubElement(xml_form, 'field', {'invisible':'1','name': "wizardid_" + str(wiz_id),'default':str(lambda *a: 0)}) + etree.SubElement(xml_form, 'field', {'invisible':'1','name': "wizardid_" + str(wiz_id),'default':str(lambda *a: 0),'modifiers':'{"invisible":true}'}) if pag_rec.note: - xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'}) for que_test in pag_rec.note.split('\n'): - etree.SubElement(xml_group, 'label', {'string': to_xml(tools.ustr(que_test)), 'align':"0.0"}) + etree.SubElement(xml_form, 'label', {'string': to_xml(tools.ustr(que_test)), 'align':"0.0"}) que_ids = pag_rec.question_ids qu_no = 0 @@ -180,19 +189,16 @@ class survey_question_wiz(osv.osv_memory): star = '*' else: star = '' - xml_group = etree.SubElement(xml_form, 'group', {'col': '2', 'colspan': '4'}) - if context.has_key('active') and context.get('active',False) and \ context.has_key('edit'): - xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '2'}) - etree.SubElement(xml_group, 'separator', {'string': star+to_xml(separator_string), 'colspan': '3'}) + etree.SubElement(xml_form, 'separator', {'string': star+to_xml(separator_string)}) + xml_group1 = etree.SubElement(xml_form, 'group', {'col': '2', 'colspan': '2'}) context.update({'question_id' : tools.ustr(que.id),'page_number': sur_name_rec.page_no , 'transfer' : sur_name_read.transfer, 'page_id' : p_id}) etree.SubElement(xml_group1, 'button', {'string':'','icon': "gtk-edit", 'type' :'object', 'name':"action_edit_question", 'context' : tools.ustr(context)}) etree.SubElement(xml_group1, 'button', {'string':'','icon': "gtk-delete", 'type' :'object','name':"action_delete_question", 'context' : tools.ustr(context)}) else: - xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'}) - etree.SubElement(xml_group, 'separator', {'string': star+to_xml(separator_string), 'colspan': '4'}) + etree.SubElement(xml_form, 'separator', {'string': star+to_xml(separator_string)}) ans_ids = que_rec.answer_choice_ids xml_group = etree.SubElement(xml_form, 'group', {'col': '1', 'colspan': '4'}) @@ -352,36 +358,29 @@ class survey_question_wiz(osv.osv_memory): etree.SubElement(xml_group, 'field', {'readonly' :str(readonly), 'name': tools.ustr(que.id) + "_other", 'nolabel':"1" ,'colspan':"4"}) fields[tools.ustr(que.id) + "_other"] = {'type': 'text', 'string': '', 'views':{}} - etree.SubElement(xml_form, 'separator', {'colspan': '4'}) - xml_group = etree.SubElement(xml_form, 'group', {'col': '6', 'colspan': '4'}) - etree.SubElement(xml_group, 'field', {'name': 'progress_bar_' + tools.ustr(page_number) , 'widget':'progressbar'}) - fields['progress_bar_' + tools.ustr(page_number)] = {'type':'float', 'string':"Progress", 'views':{}} - etree.SubElement(xml_group, 'label', {'string': tools.ustr(page_number+ 1) + "/" + tools.ustr(total_pages)}) - etree.SubElement(xml_group, 'button', {'icon': "gtk-cancel", 'special': "cancel",'string':"Cancel"}) - + xml_footer = etree.SubElement(xml_form, 'footer', {'col': '8', 'colspan': '1', 'width':"100%"}) + if pre_button: - etree.SubElement(xml_group, 'button', {'colspan':"1",'icon':"gtk-go-back",'name':"action_previous",'string':"Previous",'type':"object"}) + etree.SubElement(xml_footer, 'label', {'string': ""}) + etree.SubElement(xml_footer, 'button', {'name':"action_previous",'string':"Previous",'type':"object"}) but_string = "Next" if int(page_number) + 1 == total_pages: but_string = "Done" - if context.has_key('active') and context.get('active',False) and int(page_number) + 1 == total_pages and context.has_key('response_id') and context.has_key('response_no') and context.get('response_no',0) + 1 == len(context.get('response_id',0)): - etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'special' : 'cancel','string': tools.ustr("Done") ,'context' : tools.ustr(context)}) + etree.SubElement(xml_footer, 'label', {'string': ""}) + etree.SubElement(xml_footer, 'button', {'special' : 'cancel','string': tools.ustr("Done") ,'context' : tools.ustr(context), 'class':"oe_highlight"}) elif context.has_key('active') and context.get('active', False) and int(page_number) + 1 == total_pages and context.has_key('response_id'): - etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'name':"action_forward_next",'string': tools.ustr("Next Answer") ,'type':"object",'context' : tools.ustr(context)}) + etree.SubElement(xml_footer, 'label', {'string': ""}) + etree.SubElement(xml_footer, 'button', {'name':"action_forward_next",'string': tools.ustr("Next Answer") ,'type':"object",'context' : tools.ustr(context), 'class':"oe_highlight"}) elif context.has_key('active') and context.get('active',False) and int(page_number) + 1 == total_pages: - etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'special': "cancel", 'string' : 'Done', 'context' : tools.ustr(context)}) + etree.SubElement(xml_footer, 'label', {'string': ""}) + etree.SubElement(xml_footer, 'button', {'special': "cancel", 'string' : 'Done', 'context' : tools.ustr(context), 'class':"oe_highlight"}) else: - etree.SubElement(xml_group, 'button', {'icon': "gtk-go-forward", 'name':"action_next",'string': tools.ustr(but_string) ,'type':"object",'context' : tools.ustr(context)}) - - if context.has_key('active') and context.get('active',False) and context.has_key('edit'): - etree.SubElement(xml_form, 'separator', {'string' : '','colspan': '4'}) - context.update({'page_id' : tools.ustr(p_id),'page_number' : sur_name_rec.page_no , 'transfer' : sur_name_read.transfer}) - xml_group3 = etree.SubElement(xml_form, 'group', {'col': '4', 'colspan': '4'}) - etree.SubElement(xml_group3, 'button', {'string' :'Add Page','icon': "gtk-new", 'type' :'object','name':"action_new_page", 'context' : tools.ustr(context)}) - etree.SubElement(xml_group3, 'button', {'string' :'Edit Page','icon': "gtk-edit", 'type' :'object','name':"action_edit_page", 'context' : tools.ustr(context)}) - etree.SubElement(xml_group3, 'button', {'string' :'Delete Page','icon': "gtk-delete", 'type' :'object','name':"action_delete_page", 'context' : tools.ustr(context)}) - etree.SubElement(xml_group3, 'button', {'string' :'Add Question','icon': "gtk-new", 'type' :'object','name':"action_new_question", 'context' : tools.ustr(context)}) + etree.SubElement(xml_footer, 'label', {'string': ""}) + etree.SubElement(xml_footer, 'button', {'name':"action_next",'string': tools.ustr(but_string) ,'type':"object",'context' : tools.ustr(context), 'class':"oe_highlight"}) + etree.SubElement(xml_footer, 'label', {'string': "or"}) + etree.SubElement(xml_footer, 'button', {'special': "cancel",'string':"Exit",'class':"oe_link"}) + etree.SubElement(xml_footer, 'label', {'string': tools.ustr(page_number+ 1) + "/" + tools.ustr(total_pages), 'class':"oe_survey_title_page oe_right"}) root = xml_form.getroottree() result['arch'] = etree.tostring(root) @@ -393,40 +392,53 @@ class survey_question_wiz(osv.osv_memory): # 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'): + 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 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'] - context.update({'response_id':response_id}) report = self.create_report(cr, uid, [survey_id], 'report.survey.browse.response', survey_data.title,context) attachments = {} - file = open(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") - file_data = "" - while 1: - line = file.readline() - file_data += line - if not line: - break - - attachments[survey_data.title + ".pdf"] = file_data - file.close() - os.remove(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") - + pdf_filename = addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf" + if os.path.exists(pdf_filename): + file = open(pdf_filename) + file_data = "" + while 1: + line = file.readline() + file_data += line + if not line: + break + + attachments[survey_data.title + ".pdf"] = file_data + file.close() + os.remove(addons.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 if user_email and resp_email: user_name = user_obj.browse(cr, uid, uid, context=context).name mail = "Hello " + survey_data.responsible_id.name + ",\n\n " + str(user_name) + " has given the Response Of " + survey_data.title + " Survey.\nThe Response has been attached herewith.\n\n Thanks." - mail_message.schedule_with_attach(cr, uid, user_email, [resp_email], "Survey Answer Of " + str(user_name) , mail, attachments=attachments, context=context) + vals = {'state': 'outgoing', + 'subject': "Survey Answer Of " + user_name, + 'body_html': '<pre>%s</pre>' % mail, + 'email_to': [resp_email], + 'email_from': user_email} + if attachments: + vals['attachment_ids'] = [(0,0,{'name': a_name, + 'datas_fname': a_name, + 'datas': str(a_content).encode('base64')}) + for a_name, a_content in attachments] + self.pool.get('mail.mail').create(cr, uid, vals, context=context) xml_form = etree.Element('form', {'string': _('Complete Survey Answer')}) + xml_footer = etree.SubElement(xml_form, 'footer', {'col': '6', 'colspan': '4' ,'class': 'oe_survey_title_height'}) + etree.SubElement(xml_form, 'separator', {'string': 'Complete Survey', 'colspan': "4"}) etree.SubElement(xml_form, 'label', {'string': 'Thanks for your Answer'}) etree.SubElement(xml_form, 'newline') - etree.SubElement(xml_form, 'button', {'icon': "gtk-go-forward", 'special':"cancel",'string':"OK",'colspan':"2"}) + etree.SubElement(xml_footer, 'button', {'special':"cancel",'string':"OK",'colspan':"2",'class':'oe_highlight'}) root = xml_form.getroottree() result['arch'] = etree.tostring(root) result['fields'] = {} @@ -435,7 +447,7 @@ class survey_question_wiz(osv.osv_memory): def create_report(self, cr, uid, res_ids, report_name=False, file_name=False, context=None): """ - If any user give answer of survey then last create report of this answer and if 'Email Notification on Answer' set True in survey then send mail on responsible person of this survey and attach survey answer report in pdf format. + If any user give answer of survey then last create report of this answer and if 'E-mail Notification on Answer' set True in survey then send mail on responsible person of this survey and attach survey answer report in pdf format. """ if not report_name or not res_ids: return (False, Exception('Report name and Resources ids are required !!!')) @@ -463,7 +475,6 @@ class survey_question_wiz(osv.osv_memory): except Exception,e: return (False, str(e)) - return (True, ret_file_name) def default_get(self, cr, uid, fields_list, context=None): @@ -480,7 +491,6 @@ class survey_question_wiz(osv.osv_memory): value[field] = tot_per response_obj = self.pool.get('survey.response') surv_name_wiz = self.pool.get('survey.name.wiz') - if context.has_key('response_id') and context.get('response_id') and int(context['response_id'][0]) > 0: data = super(survey_question_wiz, self).default_get(cr, uid, fields_list, context) response_ans = response_obj.browse(cr, uid, context['response_id'][context['response_no']]) @@ -527,11 +537,11 @@ class survey_question_wiz(osv.osv_memory): value[field] = ans.answer else: + if not context.has_key('sur_name_id'): return value if context.has_key('active') and context.get('active',False): return value - sur_name_read = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False)) ans_list = [] @@ -539,7 +549,6 @@ class survey_question_wiz(osv.osv_memory): for field in fields_list: if field in list(val): value[field] = val[field] - return value def create(self, cr, uid, vals, context=None): @@ -593,8 +602,9 @@ class survey_question_wiz(osv.osv_memory): 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) else: - self.pool.get(context.get('object',False)).write(cr, uid, [int(context.get('cur_id',False))], {'response' : response_id}) - if sur_name_read['store_ans'] and type(sur_name_read['store_ans']) == dict: + self.pool.get(context.get('object',False)).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(): for field in vals: if field.split('_')[0] == val['question_id']: diff --git a/addons/survey/wizard/survey_answer.xml b/addons/survey/wizard/survey_answer.xml index 3460ad41bb3..b02f9fb2be5 100644 --- a/addons/survey/wizard/survey_answer.xml +++ b/addons/survey/wizard/survey_answer.xml @@ -5,8 +5,8 @@ <!-- Survey Question Wizard --> - <record id="view_survey_question_message" model="ir.ui.view"> - <field name="name">Answer Survey</field> + <record id="view_survey_question_message" model="ir.ui.view"> + <field name="name">Survey Answer</field> <field name="model">survey.question.wiz</field> <field name="arch" type="xml"> <form string="Your Messages" version="7.0"> @@ -26,7 +26,6 @@ </record> <record id="action_view_survey_question_message" model="ir.actions.act_window"> - <field name="name">Answer Survey</field> <field name="type">ir.actions.act_window</field> <field name="res_model">survey.question.wiz</field> <field name="view_type">form</field> diff --git a/addons/survey/wizard/survey_send_invitation.py b/addons/survey/wizard/survey_send_invitation.py index da439959ec5..5677a985e09 100644 --- a/addons/survey/wizard/survey_send_invitation.py +++ b/addons/survey/wizard/survey_send_invitation.py @@ -116,7 +116,6 @@ class survey_send_invitation(osv.osv_memory): existing = "" created = "" error = "" - user_exists = False new_user = [] attachments = {} current_sur = survey_ref.browse(cr, uid, context.get('active_id'), context=context) @@ -150,8 +149,14 @@ class survey_send_invitation(osv.osv_memory): mail = record['mail']%{'login':partner.email, 'passwd':user.password, \ 'name' : partner.name} if record['send_mail_existing']: - mail_message.schedule_with_attach(cr, uid, record['mail_from'], [partner.email] , \ - record['mail_subject_existing'] , mail, context=context) + vals = { + 'state': 'outgoing', + 'subject': record['mail_subject_existing'], + 'body_html': '<pre>%s</pre>' % mail, + 'email_to': partner.email, + 'email_from': record['mail_from'], + } + self.pool.get('mail.mail').create(cr, uid, vals, context=context) existing+= "- %s (Login: %s, Password: %s)\n" % (user.name, partner.email, \ user.password) continue @@ -160,8 +165,19 @@ class survey_send_invitation(osv.osv_memory): out+= partner.email + ',' + passwd + '\n' mail= record['mail'] % {'login' : partner.email, 'passwd' : passwd, 'name' : partner.name} if record['send_mail']: - ans = mail_message.schedule_with_attach(cr, uid, record['mail_from'], [partner.email], \ - record['mail_subject'], mail, attachments=attachments, context=context) + vals = { + 'state': 'outgoing', + 'subject': record['mail_subject'], + 'body_html': '<pre>%s</pre>' % mail, + 'email_to': partner.email, + 'email_from': record['mail_from'], + } + if attachments: + vals['attachment_ids'] = [(0,0,{'name': a_name, + 'datas_fname': a_name, + 'datas': str(a_content).encode('base64')}) + for a_name, a_content in attachments] + ans = self.pool.get('mail.mail').create(cr, uid, vals, context=context) if ans: res_data = {'name': partner.name or _('Unknown'), 'login': partner.email, diff --git a/addons/warning/warning_view.xml b/addons/warning/warning_view.xml index 3f3bbefdbab..fd12ce53ac9 100644 --- a/addons/warning/warning_view.xml +++ b/addons/warning/warning_view.xml @@ -45,16 +45,14 @@ <field name="arch" type="xml"> <notebook position="inside"> <page string="Warnings"> - <group colspan="2" col="2"> - <separator string="Warning when Selling this Product" colspan="4"/> - <field name="sale_line_warn" nolabel="1" /> - <field name="sale_line_warn_msg" colspan="3" nolabel="1" + <group string="Warning when Selling this Product"> + <field name="sale_line_warn"/> + <field name="sale_line_warn_msg" attrs="{'required':[('sale_line_warn','!=','no-message')],'readonly':[('sale_line_warn','=','no-message')]}"/> </group> - <group colspan="2" col="2"> - <separator string="Warning when Purchasing this Product" colspan="4" /> - <field name="purchase_line_warn" nolabel="1" /> - <field name="purchase_line_warn_msg" colspan="3" nolabel="1" + <group string="Warning when Purchasing this Product"> + <field name="purchase_line_warn"/> + <field name="purchase_line_warn_msg" attrs="{'required':[('purchase_line_warn','!=','no-message')],'readonly':[('purchase_line_warn','=','no-message')]}"/> </group> </page> diff --git a/addons/web_linkedin/web_linkedin_view.xml b/addons/web_linkedin/web_linkedin_view.xml index 8b4fa617bf3..05a5e32e41a 100644 --- a/addons/web_linkedin/web_linkedin_view.xml +++ b/addons/web_linkedin/web_linkedin_view.xml @@ -18,8 +18,8 @@ <field name="model">sale.config.settings</field> <field name="inherit_id" ref="base_setup.view_sale_config_settings"/> <field name="arch" type="xml"> - <div name="linkedin" position="inside"> - <form string="Configure Linkedin" version="7.0" class="oe_form_configuration"> + <div name="LinkedIn" position="inside"> + <div attrs="{'invisible':[('module_web_linkedin','=',False)]}"> <p> To use the LinkedIn module with this database, an API Key is required. Please follow this procedure: </p> @@ -34,12 +34,11 @@ <li>The programming tool is Javascript</li> </ul> </li> - <li>Copy the API key down here:</li> + <li>Copy the <strong>API key</strong> here: + <field name="api_key" attrs="{'required':[('module_web_linkedin','=',True)]}" class="oe_inline"/> + </li> </ul> - <group> - <field name="api_key"/> - </group> - </form> + </div> </div> </field> </record> |
