for tax in self.tax_line:
if tax.manual:
continue
- key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id)
+ key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
tax_key.append(key)
if key not in compute_taxes:
raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
val['account_id'] = tax['account_paid_id'] or line.account_id.id
val['account_analytic_id'] = tax['account_analytic_paid_id']
- key = (val['tax_code_id'], val['base_code_id'], val['account_id'], val['account_analytic_id'])
+ # If the taxes generate moves on the same financial account as the invoice line
+ # and no default analytic account is defined at the tax level, propagate the
+ # analytic account from the invoice line to the tax line. This is necessary
+ # in situations were (part of) the taxes cannot be reclaimed,
+ # to ensure the tax move is allocated to the proper analytic account.
+ if not val.get('account_analytic_id') and line.account_analytic_id and val['account_id'] == line.account_id.id:
+ val['account_analytic_id'] = line.account_analytic_id.id
+
+ key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
if not key in tax_grouped:
tax_grouped[key] = val
else:
'module_product_email_template': fields.boolean('Send products tools and information at the invoice confirmation',
help='With this module, link your products to a template to send complete information and tools to your customer.\n'
'For instance when invoicing a training, the training agenda and materials will automatically be send to your customers.'),
+ 'module_account_bank_statement_import_ofx': fields.boolean('Import of Bank Statements in .OFX Format',
+ help='Get your bank statements from you bank and import them in Odoo in .OFX format.\n'
+ '-that installs the module account_bank_statement_import.'),
+ 'module_account_bank_statement_import_qif': fields.boolean('Import of Bank Statements in .QIF Format.',
+ help='Get your bank statements from you bank and import them in Odoo in .QIF format.\n'
+ '-that installs the module account_bank_statement_import_qif.'),
'group_proforma_invoices': fields.boolean('Allow pro-forma invoices',
implied_group='account.group_proforma_invoices',
help="Allows you to put invoices in pro-forma state."),
<field name="paypal_account" placeholder="e.g. sales@odoo.com" class="oe_inline"/>
</div>
</div>
+ <label for="id" string="Bank Statements"/>
+ <div>
+ <div>
+ <field name="module_account_bank_statement_import_ofx" class="oe_inline"/>
+ <label for="module_account_bank_statement_import_ofx"/>
+ </div>
+ <div>
+ <field name="module_account_bank_statement_import_qif" class="oe_inline"/>
+ <label for="module_account_bank_statement_import_qif"/>
+ </div>
+ </div>
</group>
<separator name="analytic_account" string="Analytic Accounting" invisible="1"/>
<group name="analytic_account_sale" invisible="1">
-
I check that the past accounts are taken into account in partner credit
-
- !assert {model: res.partner, id: base.res_partner_2, string: Total Receivable does not takes unreconciled moves of previous years}:
- - credit == 1040.0
+ !python {model: res.partner}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('base.res_partner_2')).credit
+ assert float_compare(credit, 1040.0, precision_digits=2) == 0, "Total Receivable does not takes unreconciled moves of previous years"
I verify that account move is created
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
move_obj = self.pool.get('account.move')
inv = self.browse(cr, uid, ref('account_invoice_supplier0'))
move = inv.move_id
amt_compute = move_obj._amount_compute(cr, uid, list(ids), 'amount', None, {'lang': u'en_US', 'active_model': 'ir.ui.menu',
'active_ids': [ref('menu_action_move_journal_line_form')], 'tz': False, 'active_id': ref('menu_action_move_journal_line_form')}, where ='')
move_amount = amt_compute.values()
- assert(inv.move_id and move.period_id.id == get_period and move_amount[0] == 3100.0), ('Journal Entries has not been created')
+ assert(inv.move_id and move.period_id.id == get_period and float_compare(move_amount[0], 3100.0, precision_digits=2) == 0), ('Journal Entries has not been created')
-
I cancel the account move which is in posted state and verifies that it gives warning message
-
I verify that 'Period Sum' and 'Year sum' of the tax code are the expected values
-
!python {model: account.tax.code}: |
+ from openerp.tools import float_compare
tax_code = self.browse(cr, uid, ref('tax_case'))
- assert(tax_code.sum_period == 100.0 and tax_code.sum == 100.0), "Incorrect 'Period Sum' / 'Year sum' expected twice 100.0, got period=%r and year=%r)" % (tax_code.sum_period,tax_code.sum)
+ assert(float_compare(tax_code.sum_period, 100.0, precision_digits=2) == 0 and float_compare(tax_code.sum, 100.0, precision_digits=2) == 0), "Incorrect 'Period Sum' / 'Year sum' expected twice 100.0, got period=%r and year=%r)" % (tax_code.sum_period,tax_code.sum)
-
!python {model: account.invoice}: |
import time
+ from openerp.tools import float_compare
edi_document = {
"__id": "account:b33adf8a-decd-11f0-a4de-702a04e25700.random_invoice_763jsms",
"__module": "account",
for inv_line in invoice_new.invoice_line:
if inv_line.name == 'PC Assemble SC234':
assert inv_line.uos_id.name == "Unit" , "uom is not same"
- assert inv_line.price_unit == 10 , "price unit is not same"
- assert inv_line.quantity == 1 , "product qty is not same"
- assert inv_line.price_subtotal == 10, "price sub total is not same"
+ assert float_compare(inv_line.price_unit, 10, precision_digits=2) == 0, "price unit is not same"
+ assert float_compare(inv_line.quantity, 1, precision_digits=2) == 0, "product qty is not same"
+ assert float_compare(inv_line.price_subtotal, 10, precision_digits=2) == 0, "price sub total is not same"
elif inv_line.name == 'PC on Demand':
assert inv_line.uos_id.name == "Unit" , "uom is not same"
- assert inv_line.price_unit == 100 , "price unit is not same"
- assert inv_line.quantity == 5 , "product qty is not same"
- assert inv_line.price_subtotal == 500, "price sub total is not same"
+ assert float_compare(inv_line.price_unit, 100, precision_digits=2) == 0, "price unit is not same"
+ assert float_compare(inv_line.quantity, 5, precision_digits=2) == 0, "product qty is not same"
+ assert float_compare(inv_line.price_subtotal, 500, precision_digits=2) == 0, "price sub total is not same"
else:
raise AssertionError('unknown invoice line: %s' % inv_line)
for inv_tax in invoice_new.tax_line:
-
!python {model: account.invoice}: |
import time
+ from openerp.tools import float_compare
edi_document = {
"__id": "account:b22acf7a-ddcd-11e0-a4db-701a04e25543.random_invoice_763jsms",
"__module": "account",
for inv_line in invoice_new.invoice_line:
if inv_line.name == 'Basic PC':
assert inv_line.uos_id.name == "PCE" , "uom is not same"
- assert inv_line.price_unit == 10 , "price unit is not same"
- assert inv_line.quantity == 1 , "product qty is not same"
- assert inv_line.price_subtotal == 10, "price sub total is not same"
+ assert float_compare(inv_line.price_unit, 10, precision_digits=2) == 0, "price unit is not same"
+ assert float_compare(inv_line.quantity, 1, precision_digits=2) == 0, "product qty is not same"
+ assert float_compare(inv_line.price_subtotal, 10, precision_digits=2) == 0, "price sub total is not same"
elif inv_line.name == 'Medium PC':
assert inv_line.uos_id.name == "PCE" , "uom is not same"
- assert inv_line.price_unit == 100 , "price unit is not same"
- assert inv_line.quantity == 5 , "product qty is not same"
- assert inv_line.price_subtotal == 500, "price sub total is not same"
+ assert float_compare(inv_line.price_unit, 100, precision_digits=2) == 0, "price unit is not same"
+ assert float_compare(inv_line.quantity, 5, precision_digits=2) == 0, "product qty is not same"
+ assert float_compare(inv_line.price_subtotal, 500, precision_digits=2) == 0, "price sub total is not same"
else:
raise AssertionError('unknown invoice line: %s' % inv_line)
for inv_tax in invoice_new.tax_line:
I test the generated invoice
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
aid = ref('account_analytic_analysis.contract_main')
ids = self.search(cr, uid, [('invoice_line.account_analytic_id','=',aid)], context=context)
assert len(ids)>=1, 'No invoice created for the contract'
for invoice in self.browse(cr, uid, ids,context=context):
- assert invoice.amount_untaxed == 150.0, "The invoice total is different than 150!"
+ assert float_compare(invoice.amount_untaxed, 150.0, precision_digits=2) == 0, "The invoice total is different than 150!"
-
I check the Stock Interim account (Received) is credited successfully.
-
- !assert {model: account.account, id : account_anglo_stock_input, string : Stock Interim account (Received) is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_input')).credit
+ float_compare(credit, 9, precision_digits=2) == 0, "Stock Interim account (Received) is not credited successfully."
-
I check the Stock valuation account is debited sucessfully.
-
- !assert {model: account.account, id : account_anglo_stock_valuation, string : Stock valuation account is not debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_valuation')).debit
+ float_compare(debit, 9, precision_digits=2) == 0, "Stock valuation account is not debited successfully."
-
I Validate Invoice of Purchase Order.
-
-
I check the Stock Interim account (Received) is debited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_stock_input, string : Stock Interim account (Received) is not debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_input')).debit
+ float_compare(debit, 9, precision_digits=2) == 0, "Stock Interim account (Received) is not debited successfully."
-
I check the Price difference creditor Account is debited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_price_difference, string : Price difference creditor Account is not debited successfully.}:
- - debit == 1
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_price_difference')).debit
+ float_compare(debit, 1, precision_digits=2) == 0, "Price difference creditor Account is not debited successfully."
-
I check Payable(creditor) Account is Credited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_payable, string : Payable(creditor) Account is not Credited successfully.}:
- - credit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_payable')).credit
+ float_compare(credit, 10, precision_digits=2) == 0, "Payable(creditor) Account is not Credited successfully."
-
I open the Invoice.
-
-
I check Payable(Creditors) Account is Debited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_payable, string : Payable(Creditors) Account is not Debited successfully.}:
- - debit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_payable')).debit
+ assert float_compare(debit, 10, precision_digits=2) == 0, "Payable(Creditors) Account is not Debited successfully."
-
I check Bank/Cash account is credited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_cash, string: Bank/Cash account is not credited successfully.}:
- - credit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_cash')).credit
+ float_compare(credit, 10, precision_digits=2) == 0, "Bank/Cash account is not credited successfully."
-
I create an Outgoing Picking order
-
-
I check Stock Interim account (Delivery) is debited successfully.
-
- !assert {model: account.account, id : account_anglo_stock_output, string : Stock Interim account (Delivery) is not debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_output')).debit
+ float_compare(debit, 9, precision_digits=2) == 0, "Stock Interim account (Delivery) is not debited successfully."
-
I check the Stock valuation account is credited sucessfully.
-
- !assert {model: account.account, id : account_anglo_stock_valuation, string : Stock valuation account is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_valuation')).credit
+ float_compare(credit, 9, precision_digits=2) == 0, "Stock valuation account is not credited successfully."
-
As the Invoice state of the picking order is To be invoiced. I create invoice for my outgoing picking order.
-
-
I check Income Account is Credited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_income, string : Income Account is not Credited successfully.}:
- - credit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_income')).credit
+ float_compare(credit, 20, precision_digits=2) == 0, "Income Account is not Credited successfully."
-
I check Cost of goods sold account for debit.
-
- !assert {model: account.account, id : account_anglo_cogs, string : Cost of goods sale is not Debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_cogs')).debit
+ float_compare(debit, 9, precision_digits=2) == 0, "Cost of goods sale is not Debited successfully."
-
I check Stock Interim account (Delivery)
-
- !assert {model: account.account, id : account_anglo_stock_output, string : Stock Interim account (Delivery) is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_output')).credit
+ float_compare(credit, 9, precision_digits=2) == 0, "Stock Interim account (Delivery) is not credited successfully."
-
I check Receivable(Debtor) Account for debit.
-
- !assert {model: account.account, id : account_anglo_receivable, string : Receivable(Debtors) Account is not Debited successfully.}:
- - debit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_receivable')).debit
+ float_compare(debit, 20, precision_digits=2) == 0, "Receivable(Debtors) Account is not Debited successfully."
-
I pay the invoice.
-
-
I check Receivable(Debtor) Account for credit.
-
- !assert {model: account.account, id : account_anglo_receivable, string : Receivable(Debtors) Account is not Credited successfully.}:
- - credit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_receivable')).credit
+ float_compare(credit, 20, precision_digits=2) == 0, "Receivable(Debtors) Account is not Credited successfully."
-
I check Bank/Cash account is debited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_cash, string: Bank/Cash account is not successfully credited.}:
- - debit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_cash')).debit
+ float_compare(debit, 20, precision_digits=2) == 0, "Bank/Cash account is not successfully credited."
-
I check the Stock Interim account (Received) is credit successfully.
-
- !assert {model: account.account, id : account_anglo_stock_input_fifo, string : Stock Interim account (Received) is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_input_fifo')).credit
+ assert float_compare(credit, 9, precision_digits=2) == 0, "Stock Interim account (Received) is not credited successfully."
-
I check the Stock valuation account is debit sucessfully.
-
- !assert {model: account.account, id : account_anglo_stock_valuation_fifo, string : Stock valuation account is not debited successfully.}:
- - debit == 9
+ !python {model: account.account, id}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_valuation_fifo')).debit
+ assert float_compare(debit, 9, precision_digits=2) == 0, "Stock valuation account is not debited successfully."
-
I Validate Invoice of Purchase Order after having changed the price to 10.
-
-
I check the Stock Interim account (Received) is debited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_stock_input_fifo, string : Stock Interim account (Received) is not debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_input_fifo')).debit
+ assert float_compare(debit, 9, precision_digits=2) == 0, "Stock Interim account (Received) is not debited successfully."
-
I check the Price difference creditor Account is debited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_price_difference_fifo, string : Price difference creditor Account is not debited successfully.}:
- - debit == 1
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_price_difference_fifo')).debit
+ assert float_compare(debit, 1, precision_digits=2) == 0, "Price difference creditor Account is not debited successfully."
-
I check Payable(creditor) Account is Credited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_payable_fifo, string : Payable(creditor) Account is not Credited successfully.}:
- - credit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_payable_fifo')).credit
+ assert float_compare(credit, 10, precision_digits=2) == 0, "Payable(creditor) Account is not Credited successfully."
-
I pay the invoice.
-
-
I check Payable(Creditors) Account is Debited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_payable_fifo, string : Payable(Creditors) Account is not Debited successfully.}:
- - debit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_payable_fifo')).debit
+ assert float_compare(debit, 10, precision_digits=2) == 0, "Payable(Creditors) Account is not Debited successfully."
-
I check Bank/Cash account is credited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_cash_fifo, string: Bank/Cash account is not credited successfully.}:
- - credit == 10
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_cash_fifo')).credit
+ assert float_compare(credit, 10, precision_digits=2) == 0, "Bank/Cash account is not credited successfully."
-
I create an Outgoing Picking order
-
-
I check Stock Interim account (Delivery) is debited successfully.
-
- !assert {model: account.account, id : account_anglo_stock_output_fifo, string : Stock Interim account (Delivery) is not debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_stock_output_fifo')).debit
+ assert float_compare(debit, 9, precision_digits=2) == 0, "Stock Interim account (Delivery) is not debited successfully."
-
I check the Stock valuation account is credited sucessfully.
-
- !assert {model: account.account, id : account_anglo_stock_valuation_fifo, string : Stock valuation account is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_valuation_fifo')).credit
+ assert float_compare(credit, 9, precision_digits=2) == 0, "Stock valuation account is not credited successfully."
-
As the Invoice state of the picking order is To be invoiced. I create invoice for my outgoing picking order.
-
-
I check Income Account is Credited sucessfully when Invoice validated.
-
- !assert {model: account.account, id : account_anglo_income_fifo, string : Income Account is not Credited successfully.}:
- - credit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_income_fifo')).credit
+ assert float_compare(credit, 20, precision_digits=2) == 0, "Income Account is not Credited successfully."
-
I check Cost of goods sold account for debit.
-
- !assert {model: account.account, id : account_anglo_cogs_fifo, string : Cost of goods sale is not Debited successfully.}:
- - debit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_cogs_fifo')).debit
+ assert float_compare(debit, 9, precision_digits=2) == 0, "Cost of goods sale is not Debited successfully."
-
I check Stock Interim account (Delivery)
-
- !assert {model: account.account, id : account_anglo_stock_output_fifo, string : Stock Interim account (Delivery) is not credited successfully.}:
- - credit == 9
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_stock_output_fifo')).credit
+ assert float_compare(credit, 9, precision_digits=2) == 0, "Stock Interim account (Delivery) is not credited successfully."
-
I check Receivable(Debtor) Account for debit.
-
- !assert {model: account.account, id : account_anglo_receivable_fifo, string : Receivable(Debtors) Account is not Debited successfully.}:
- - debit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_receivable_fifo')).debit
+ assert float_compare(debit, 20, precision_digits=2) == 0, "Receivable(Debtors) Account is not Debited successfully."
-
I pay the invoice.
-
-
I check Receivable(Debtor) Account for credit.
-
- !assert {model: account.account, id : account_anglo_receivable_fifo, string : Receivable(Debtors) Account is not Credited successfully.}:
- - credit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ credit = self.browse(cr, uid, ref('account_anglo_receivable_fifo')).credit
+ assert float_compare(credit, 20, precision_digits=2) == 0, "Receivable(Debtors) Account is not Credited successfully."
-
I check Bank/Cash account is debited sucessfully after invoice paid.
-
- !assert {model: account.account, id : account_anglo_cash_fifo, string: Bank/Cash account is not successfully credited.}:
- - debit == 20
+ !python {model: account.account}: |
+ from openerp.tools import float_compare
+ debit = self.browse(cr, uid, ref('account_anglo_cash_fifo')).debit
+ assert float_compare(debit, 20, precision_digits=2) == 0, "Bank/Cash account is not successfully credited."
--- /dev/null
+# -*- encoding: utf-8 -*-
+
+import account_bank_statement_import
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- encoding: utf-8 -*-
+{
+ 'name': 'Account Bank Statement Import',
+ 'version': '1.0',
+ 'author': 'OpenERP SA',
+ 'depends': ['account'],
+ 'demo': [],
+ 'description' : """Generic Wizard to Import Bank Statements. Includes the import of files in .OFX format""",
+ 'data' : [
+ 'account_bank_statement_import_view.xml',
+ ],
+ 'demo': [
+ 'demo/fiscalyear_period.xml',
+ 'demo/partner_bank.xml',
+ ],
+ 'auto_install': False,
+ 'installable': True,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+import logging
+_logger = logging.getLogger(__name__)
+
+_IMPORT_FILE_TYPE = [('none', _('No Import Format Available'))]
+
+def add_file_type(selection_value):
+ global _IMPORT_FILE_TYPE
+ if _IMPORT_FILE_TYPE[0][0] == 'none':
+ _IMPORT_FILE_TYPE = [selection_value]
+ else:
+ _IMPORT_FILE_TYPE.append(selection_value)
+
+class account_bank_statement_import(osv.TransientModel):
+ _name = 'account.bank.statement.import'
+ _description = 'Import Bank Statement'
+
+ def _get_import_file_type(self, cr, uid, context=None):
+ return _IMPORT_FILE_TYPE
+
+ _columns = {
+ 'data_file': fields.binary('Bank Statement File', required=True, help='Get you bank statements in electronic format from your bank and select them here.'),
+ 'file_type': fields.selection(_get_import_file_type, 'File Type', required=True),
+ 'journal_id': fields.many2one('account.journal', 'Journal', required=True, help="The journal for which the bank statements will be created"),
+ }
+
+ def _get_first_file_type(self, cr, uid, context=None):
+ return self._get_import_file_type(cr, uid, context=context)[0][0]
+
+ def _get_default_journal(self, cr, uid, context=None):
+ company_id = self.pool.get('res.company')._company_default_get(cr, uid, 'account.bank.statement', context=context)
+ journal_ids = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'bank'), ('company_id', '=', company_id)], context=context)
+ return journal_ids and journal_ids[0] or False
+
+ _defaults = {
+ 'file_type': _get_first_file_type,
+ 'journal_id': _get_default_journal,
+ }
+
+ def _detect_partner(self, cr, uid, identifying_string, identifying_field='acc_number', context=None):
+ """Try to find a bank account and its related partner for the given 'identifying_string', looking on the field 'identifying_field'.
+
+ :param identifying_string: varchar
+ :param identifying_field: varchar corresponding to the name of a field of res.partner.bank
+ :returns: tuple(ID of the bank account found or False, ID of the partner for the bank account found or False)
+ """
+ partner_id = False
+ bank_account_id = False
+ if identifying_string:
+ ids = self.pool.get('res.partner.bank').search(cr, uid, [(identifying_field, '=', identifying_string)], context=context)
+ if ids:
+ bank_account_id = ids[0]
+ partner_id = self.pool.get('res.partner.bank').browse(cr, uid, bank_account_id, context=context).partner_id.id
+ else:
+ #create the bank account, not linked to any partner. The reconciliation will link the partner manually
+ #chosen at the bank statement final confirmation time.
+ try:
+ type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
+ type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
+ bank_code = type_id.code
+ except ValueError:
+ bank_code = 'bank'
+ acc_number = identifying_field == 'acc_number' and identifying_string or _('Undefined')
+ bank_account_vals = {
+ 'acc_number': acc_number,
+ 'state': bank_code,
+ }
+ bank_account_vals[identifying_field] = identifying_string
+ bank_account_id = self.pool.get('res.partner.bank').create(cr, uid, bank_account_vals, context=context)
+ return bank_account_id, partner_id
+
+ def import_bank_statement(self, cr, uid, bank_statement_vals=False, context=None):
+ """ Get a list of values to pass to the create() of account.bank.statement object, and returns a list of ID created using those values"""
+ statement_ids = []
+ for vals in bank_statement_vals:
+ statement_ids.append(self.pool.get('account.bank.statement').create(cr, uid, vals, context=context))
+ return statement_ids
+
+ def process_none(self, cr, uid, data_file, journal_id=False, context=None):
+ raise osv.except_osv(_('Error'), _('No available format for importing bank statement. You can install one of the file format available through the module installation.'))
+
+ def parse_file(self, cr, uid, ids, context=None):
+ """ Process the file chosen in the wizard and returns a list view of the imported bank statements"""
+ data = self.browse(cr, uid, ids[0], context=context)
+ vals = getattr(self, "process_%s" % data.file_type)(cr, uid, data.data_file, data.journal_id.id, context=context)
+ statement_ids = self.import_bank_statement(cr, uid, vals, context=context)
+ model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
+ action = self.pool[model].read(cr, uid, action_id, context=context)
+ action['domain'] = "[('id', 'in', [" + ', '.join(map(str, statement_ids)) + "])]"
+ return action
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+<?xml version="1.0" ?>
+<openerp>
+ <data>
+
+ <record id="account_bank_statement_import_view" model="ir.ui.view">
+ <field name="name">Import Bank Statements</field>
+ <field name="model">account.bank.statement.import</field>
+ <field name="priority">1</field>
+ <field name="arch" type="xml">
+ <form string="Import Bank Statements" version="7.0">
+ <group>
+ <group>
+ <field name="data_file"/>
+ <field name="file_type"/>
+ <field name="journal_id" domain="[('type', '=', 'bank')]" context="{'default_type':'bank'}"/>
+ </group>
+ <group>
+ <b colspan="2"> How to import your bank statement in OpenERP.</b>
+ <label string= "1. Go to your bank account website." colspan="2"/>
+ <label string= "2. Download your bank statements in the right format. (.OFX, .QIF or CODA are accepted)" colspan="2"/>
+ <label string= "3. Upload right here the bank statements file into OpenERP. Click Import." colspan="2"/>
+ </group>
+ </group>
+ <footer>
+ <button name="parse_file" string="_Import" type="object" class="oe_highlight"/>
+ or
+ <button string="Cancel" class="oe_link" special="cancel"/>
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_account_bank_statement_import" model="ir.actions.act_window">
+ <field name="name">Import Bank Statements</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">account.bank.statement.import</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form</field>
+ <field name="target">new</field>
+ <field name="view_id" ref="account_bank_statement_import_view"/>
+ </record>
+
+ <menuitem parent="account.menu_finance_bank_and_cash" id="menu_account_bank_statement_import" action="action_account_bank_statement_import" sequence="11"/>
+
+ </data>
+</openerp>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <!--
+ Fiscal year
+ -->
+
+ <record id="data_fiscalyear_2013" model="account.fiscalyear">
+ <field eval="'Fiscal Year X 2013'" name="name"/>
+ <field eval="'FY2013'" name="code"/>
+ <field eval="'2013-01-01'" name="date_start"/>
+ <field eval="'2013-12-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+
+ <!--
+ Fiscal Periods 2013
+ -->
+
+ <record id="period_1_2013" model="account.period">
+ <field eval="'01/2013'" name="code"/>
+ <field eval="'X 01/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-01-01'" name="date_start"/>
+ <field eval="'2013-01-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+
+ <record id="period_2_2013" model="account.period">
+ <field eval="'02/2013'" name="code"/>
+ <field eval="'X 02/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-02-01'" name="date_start"/>
+ <field eval="'2013-02-28'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_3_2013" model="account.period">
+ <field eval="'03/2013'" name="code"/>
+ <field eval="'X 03/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-03-01'" name="date_start"/>
+ <field eval="'2013-03-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_4_2013" model="account.period">
+ <field eval="'04/2013'" name="code"/>
+ <field eval="'X 04/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-04-01'" name="date_start"/>
+ <field eval="'2013-04-30'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_5_2013" model="account.period">
+ <field eval="'05/2013'" name="code"/>
+ <field eval="'X 05/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-05-01'" name="date_start"/>
+ <field eval="'2013-05-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_6_2013" model="account.period">
+ <field eval="'06/2013'" name="code"/>
+ <field eval="'X 06/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-06-01'" name="date_start"/>
+ <field eval="'2013-06-30'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_7_2013" model="account.period">
+ <field eval="'07/2013'" name="code"/>
+ <field eval="'X 07/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-07-01'" name="date_start"/>
+ <field eval="'2013-07-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_8_2013" model="account.period">
+ <field eval="'08/2013'" name="code"/>
+ <field eval="'X 08/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-08-01'" name="date_start"/>
+ <field eval="'2013-08-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_9_2013" model="account.period">
+ <field eval="'09/2013'" name="code"/>
+ <field eval="'X 09/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-09-01'" name="date_start"/>
+ <field eval="'2013-09-30'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_10_2013" model="account.period">
+ <field eval="'10/2013'" name="code"/>
+ <field eval="'X 10/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-10-01'" name="date_start"/>
+ <field eval="'2013-10-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_11_2013" model="account.period">
+ <field eval="'11/2013'" name="code"/>
+ <field eval="'X 11/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-11-01'" name="date_start"/>
+ <field eval="'2013-11-30'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ <record id="period_12_2013" model="account.period">
+ <field eval="'12/2013'" name="code"/>
+ <field eval="'X 12/2013'" name="name"/>
+ <field name="fiscalyear_id" ref="data_fiscalyear_2013"/>
+ <field eval="'2013-12-01'" name="date_start"/>
+ <field eval="'2013-12-31'" name="date_stop"/>
+ <field name="company_id" ref="base.main_company"/>
+ </record>
+ </data>
+</openerp>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="ofx_partner_bank_1" model="res.partner.bank">
+ <field name="owner_name">Agrolait</field>
+ <field name="acc_number">00987654321</field>
+ <field name="partner_id" ref="base.res_partner_2"></field>
+ <field name="state">bank</field>
+ <field name="bank" ref="base.res_bank_1"/>
+ </record>
+
+ <record id="ofx_partner_bank_2" model="res.partner.bank">
+ <field name="owner_name">China Export</field>
+ <field name="acc_number">00987654322</field>
+ <field name="partner_id" ref="base.res_partner_3"></field>
+ <field name="state">bank</field>
+ <field name="bank" ref="base.res_bank_1"/>
+ </record>
+
+ <record id="qif_partner_bank_1" model="res.partner.bank">
+ <field name="owner_name">Delta PC</field>
+ <field name="acc_number">10987654320</field>
+ <field name="partner_id" ref="base.res_partner_4"></field>
+ <field name="state">bank</field>
+ <field name="bank" ref="base.res_bank_1"/>
+ </record>
+
+ <record id="qif_partner_bank_2" model="res.partner.bank">
+ <field name="owner_name">Epic Technologies</field>
+ <field name="acc_number">10987654322</field>
+ <field name="partner_id" ref="base.res_partner_5"></field>
+ <field name="state">bank</field>
+ <field name="bank" ref="base.res_bank_1"/>
+ </record>
+
+ </data>
+</openerp>
--- /dev/null
+# -*- encoding: utf-8 -*-
+
+import account_bank_statement_import_ofx
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- encoding: utf-8 -*-
+{
+ 'name': 'Import OFX Bank Statement',
+ 'version': '1.0',
+ 'author': 'OpenERP SA',
+ 'depends': ['account_bank_statement_import'],
+ 'demo': [],
+ 'description' : """
+Module to import OFX bank statements.
+======================================
+
+This module allows you to import the machine readable OFX Files in Odoo: they are parsed and stored in human readable format in
+Accounting \ Bank and Cash \ Bank Statements.
+
+Bank Statements may be generated containing a subset of the OFX information (only those transaction lines that are required for the
+creation of the Financial Accounting records).
+
+ """,
+ 'data' : [],
+ 'demo': [],
+ 'auto_install': False,
+ 'installable': True,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+
+import logging
+import base64
+import os
+
+from openerp.osv import osv
+from openerp.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+from openerp.addons.account_bank_statement_import import account_bank_statement_import as ibs
+ibs.add_file_type(('ofx', 'OFX'))
+
+try:
+ from ofxparse import OfxParser as ofxparser
+except ImportError:
+ _logger.warning("OFX parser unavailable because the `ofxparse` Python library cannot be found."
+ "It can be downloaded and installed from `https://pypi.python.org/pypi/ofxparse`.")
+ ofxparser = None
+
+class account_bank_statement_import(osv.TransientModel):
+ _inherit = 'account.bank.statement.import'
+
+ def process_ofx(self, cr, uid, data_file, journal_id=False, context=None):
+ """ Import a file in the .OFX format"""
+ if ofxparser is None:
+ raise osv.except_osv(_("Error"), _("OFX parser unavailable because the `ofxparse` Python library cannot be found."
+ "It can be downloaded and installed from `https://pypi.python.org/pypi/ofxparse`."))
+ try:
+ tempfile = open("temp.ofx", "w+")
+ tempfile.write(base64.decodestring(data_file))
+ tempfile.read()
+ pathname = os.path.dirname('temp.ofx')
+ path = os.path.join(os.path.abspath(pathname), 'temp.ofx')
+ ofx = ofxparser.parse(file(path))
+ except:
+ raise osv.except_osv(_('Import Error!'), _('Please check OFX file format is proper or not.'))
+ line_ids = []
+ total_amt = 0.00
+ try:
+ for transaction in ofx.account.statement.transactions:
+ bank_account_id, partner_id = self._detect_partner(cr, uid, transaction.payee, identifying_field='owner_name', context=context)
+ vals_line = {
+ 'date': transaction.date,
+ 'name': transaction.payee + ': ' + transaction.memo,
+ 'ref': transaction.id,
+ 'amount': transaction.amount,
+ 'partner_id': partner_id,
+ 'bank_account_id': bank_account_id,
+ }
+ total_amt += float(transaction.amount)
+ line_ids.append((0, 0, vals_line))
+ except Exception, e:
+ raise osv.except_osv(_('Error!'), _("Following problem has been occurred while importing your file, Please verify the file is proper or not.\n\n %s" % e.message))
+ st_start_date = ofx.account.statement.start_date or False
+ st_end_date = ofx.account.statement.end_date or False
+ period_obj = self.pool.get('account.period')
+ if st_end_date:
+ period_ids = period_obj.find(cr, uid, st_end_date, context=context)
+ else:
+ period_ids = period_obj.find(cr, uid, st_start_date, context=context)
+ vals_bank_statement = {
+ 'name': ofx.account.routing_number,
+ 'balance_start': ofx.account.statement.balance,
+ 'balance_end_real': float(ofx.account.statement.balance) + total_amt,
+ 'period_id': period_ids and period_ids[0] or False,
+ 'journal_id': journal_id
+ }
+ vals_bank_statement.update({'line_ids': line_ids})
+ os.remove(path)
+ return [vals_bank_statement]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+<?xml version="1.0" encoding="ASCII"?>
+<?OFX OFXHEADER="200" VERSION="211" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>
+<OFX>
+ <SIGNONMSGSRSV1>
+ <SONRS>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <DTSERVER>20130831165153.000[-8:PST]</DTSERVER>
+ <LANGUAGE>ENG</LANGUAGE>
+ </SONRS>
+ </SIGNONMSGSRSV1>
+ <BANKMSGSRSV1>
+ <STMTTRNRS>
+ <TRNUID>0</TRNUID>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <STMTRS>
+ <CURDEF>USD</CURDEF>
+ <BANKACCTFROM>
+ <BANKID>000000123</BANKID>
+ <ACCTID>123456</ACCTID>
+ <ACCTTYPE>CHECKING</ACCTTYPE>
+ </BANKACCTFROM>
+ <BANKTRANLIST>
+ <DTSTART>20130801</DTSTART>
+ <DTEND>20130831165153.000[-8:PST]</DTEND>
+ <STMTTRN>
+ <TRNTYPE>POS</TRNTYPE>
+ <DTPOSTED>20130824080000</DTPOSTED>
+ <TRNAMT>-80</TRNAMT>
+ <FITID>219378</FITID>
+ <NAME>Agrolait</NAME>
+ </STMTTRN>
+ </BANKTRANLIST>
+ <BANKTRANLIST>
+ <DTSTART>20130801</DTSTART>
+ <DTEND>20130831165153.000[-8:PST]</DTEND>
+ <STMTTRN>
+ <TRNTYPE>POS</TRNTYPE>
+ <DTPOSTED>20130824080000</DTPOSTED>
+ <TRNAMT>-90</TRNAMT>
+ <FITID>219379</FITID>
+ <NAME>China Export</NAME>
+ </STMTTRN>
+ </BANKTRANLIST>
+ <BANKTRANLIST>
+ <DTSTART>20130801</DTSTART>
+ <DTEND>20130831165153.000[-8:PST]</DTEND>
+ <STMTTRN>
+ <TRNTYPE>POS</TRNTYPE>
+ <DTPOSTED>20130824080000</DTPOSTED>
+ <TRNAMT>-100</TRNAMT>
+ <FITID>219380</FITID>
+ <NAME>Axelor Scuba</NAME>
+ </STMTTRN>
+ </BANKTRANLIST>
+ <BANKTRANLIST>
+ <DTSTART>20130801</DTSTART>
+ <DTEND>20130831165153.000[-8:PST]</DTEND>
+ <STMTTRN>
+ <TRNTYPE>POS</TRNTYPE>
+ <DTPOSTED>20130824080000</DTPOSTED>
+ <TRNAMT>-90</TRNAMT>
+ <FITID>219381</FITID>
+ <NAME>China Scuba</NAME>
+ </STMTTRN>
+ </BANKTRANLIST>
+ <LEDGERBAL>
+ <BALAMT>2156.56</BALAMT>
+ <DTASOF>20130831165153</DTASOF>
+ </LEDGERBAL>
+ </STMTRS>
+ </STMTTRNRS>
+ </BANKMSGSRSV1>
+ <CREDITCARDMSGSRSV1>
+ <CCSTMTTRNRS>
+ <TRNUID>0</TRNUID>
+ <STATUS>
+ <CODE>0</CODE>
+ <SEVERITY>INFO</SEVERITY>
+ </STATUS>
+ <CCSTMTRS>
+ <CURDEF>USD</CURDEF>
+ <CCACCTFROM>
+ <ACCTID>123412341234</ACCTID>
+ </CCACCTFROM>
+ <BANKTRANLIST>
+ </BANKTRANLIST>
+ <LEDGERBAL>
+ <BALAMT>-562.00</BALAMT>
+ <DTASOF>20130831165153</DTASOF>
+ </LEDGERBAL>
+ </CCSTMTRS>
+ </CCSTMTTRNRS>
+ </CREDITCARDMSGSRSV1>
+</OFX>
--- /dev/null
+from . import test_import_bank_statement
+
+checks = [
+ test_import_bank_statement
+]
--- /dev/null
+from openerp.tests.common import TransactionCase
+from openerp.modules.module import get_module_resource
+
+class TestOfxFile(TransactionCase):
+ """Tests for import bank statement ofx file format (account.bank.statement.import)
+ """
+
+ def setUp(self):
+ super(TestOfxFile, self).setUp()
+ self.statement_import_model = self.registry('account.bank.statement.import')
+ self.bank_statement_model = self.registry('account.bank.statement')
+
+ def test_ofx_file_import(self):
+ try:
+ from ofxparse import OfxParser as ofxparser
+ except ImportError:
+ #the Python library isn't installed on the server, the OFX import is unavailable and the test cannot be run
+ return True
+ cr, uid = self.cr, self.uid
+ ofx_file_path = get_module_resource('account_bank_statement_import_ofx', 'test_ofx_file', 'test_ofx.ofx')
+ ofx_file = open(ofx_file_path, 'rb').read().encode('base64')
+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
+ file_type='ofx',
+ data_file=ofx_file,
+ ))
+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
+ statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '000000123')])[0]
+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
+ self.assertEquals(bank_st_record.balance_start, 2156.56)
+ self.assertEquals(bank_st_record.balance_end_real, 1796.56)
--- /dev/null
+# -*- coding: utf-8 -*-
+
+import account_bank_statement_import_qif
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+
+{
+ 'name': 'Import QIF Bank Statement',
+ 'version': '1.0',
+ 'author': 'OpenERP SA',
+ 'description': '''
+Module to import QIF bank statements.
+======================================
+
+This module allows you to import the machine readable QIF Files in Odoo: they are parsed and stored in human readable format in
+Accounting \ Bank and Cash \ Bank Statements.
+
+Bank Statements may be generated containing a subset of the QIF information (only those transaction lines that are required for the
+creation of the Financial Accounting records).
+''',
+ 'images' : [],
+ 'depends': ['account_bank_statement_import'],
+ 'demo': [],
+ 'data': [],
+ 'auto_install': False,
+ 'installable': True,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- coding: utf-8 -*-
+
+import dateutil.parser
+import base64
+from tempfile import TemporaryFile
+
+from openerp.tools.translate import _
+from openerp.osv import osv
+
+from openerp.addons.account_bank_statement_import import account_bank_statement_import as ibs
+
+ibs.add_file_type(('qif', 'QIF'))
+
+class account_bank_statement_import(osv.TransientModel):
+ _inherit = "account.bank.statement.import"
+
+ def process_qif(self, cr, uid, data_file, journal_id=False, context=None):
+ """ Import a file in the .QIF format"""
+ try:
+ fileobj = TemporaryFile('wb+')
+ fileobj.write(base64.b64decode(data_file))
+ fileobj.seek(0)
+ file_data = ""
+ for line in fileobj.readlines():
+ file_data += line
+ fileobj.close()
+ if '\r' in file_data:
+ data_list = file_data.split('\r')
+ else:
+ data_list = file_data.split('\n')
+ header = data_list[0].strip()
+ header = header.split(":")[1]
+ except:
+ raise osv.except_osv(_('Import Error!'), _('Please check QIF file format is proper or not.'))
+ line_ids = []
+ vals_line = {}
+ total = 0
+ if header == "Bank":
+ vals_bank_statement = {}
+ for line in data_list:
+ line = line.strip()
+ if not line:
+ continue
+ if line[0] == 'D': # date of transaction
+ vals_line['date'] = dateutil.parser.parse(line[1:], fuzzy=True).date()
+ if vals_line.get('date') and not vals_bank_statement.get('period_id'):
+ period_ids = self.pool.get('account.period').find(cr, uid, vals_line['date'], context=context)
+ vals_bank_statement.update({'period_id': period_ids and period_ids[0] or False})
+ elif line[0] == 'T': # Total amount
+ total += float(line[1:].replace(',', ''))
+ vals_line['amount'] = float(line[1:].replace(',', ''))
+ elif line[0] == 'N': # Check number
+ vals_line['ref'] = line[1:]
+ elif line[0] == 'P': # Payee
+ bank_account_id, partner_id = self._detect_partner(cr, uid, line[1:], identifying_field='owner_name', context=context)
+ vals_line['partner_id'] = partner_id
+ vals_line['bank_account_id'] = bank_account_id
+ vals_line['name'] = 'name' in vals_line and line[1:] + ': ' + vals_line['name'] or line[1:]
+ elif line[0] == 'M': # Memo
+ vals_line['name'] = 'name' in vals_line and vals_line['name'] + ': ' + line[1:] or line[1:]
+ elif line[0] == '^': # end of item
+ line_ids.append((0, 0, vals_line))
+ vals_line = {}
+ elif line[0] == '\n':
+ line_ids = []
+ else:
+ pass
+ else:
+ raise osv.except_osv(_('Error!'), _('Cannot support this Format !Type:%s.') % (header,))
+ vals_bank_statement.update({'balance_end_real': total,
+ 'line_ids': line_ids,
+ 'journal_id': journal_id})
+ return [vals_bank_statement]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+!Type:Bank
+D8/12/13
+T-1,000.00
+PDelta PC
+^
+D8/15/13
+T-75.46
+PWalts Drugs
+^
+D3/3/13
+T-379.00
+PEpic Technologies
+^
+D3/4/13
+T-20.28
+PYOUR LOCAL SUPERMARKET
+^
+D3/3/13
+T-421.35
+PSPRINGFIELD WATER UTILITY
+^
--- /dev/null
+from . import test_import_bank_statement
+checks = [
+ test_import_bank_statement
+]
+
--- /dev/null
+from openerp.tests.common import TransactionCase
+from openerp.modules.module import get_module_resource
+
+class TestQifFile(TransactionCase):
+ """Tests for import bank statement qif file format (account.bank.statement.import)
+ """
+
+ def setUp(self):
+ super(TestQifFile, self).setUp()
+ self.statement_import_model = self.registry('account.bank.statement.import')
+ self.bank_statement_model = self.registry('account.bank.statement')
+ self.bank_statement_line_model = self.registry('account.bank.statement.line')
+
+ def test_qif_file_import(self):
+ from openerp.tools import float_compare
+ cr, uid = self.cr, self.uid
+ qif_file_path = get_module_resource('account_bank_statement_import_qif', 'test_qif_file', 'test_qif.qif')
+ qif_file = open(qif_file_path, 'rb').read().encode('base64')
+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
+ file_type='qif',
+ data_file=qif_file,
+ ))
+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
+ line_id = self.bank_statement_line_model.search(cr, uid, [('name', '=', 'YOUR LOCAL SUPERMARKET')])[0]
+ statement_id = self.bank_statement_line_model.browse(cr, uid, line_id).statement_id.id
+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
+ assert float_compare(bank_st_record.balance_end_real, -1896.09, 2) == 0
I check that first invoice move is correct for debtor account (debit - credit == 150.0)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_jan"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 150.0), "Invoice move is not correct for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 150.0, precision_digits=2) == 0), "Invoice move is not correct for debtors account"
-
I create the second invoice on 1st February for 100 USD
-
I check that second invoice move is correct for debtor account (debit - credit == 80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_feb"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 80), "Invoice move is not correct for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 80, precision_digits=2) == 0), "Invoice move is not correct for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
I fill amounts 180 for the invoice of 200$ and 70 for the invoice of 100$>
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case1'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 200.00:
+ if float_compare(item.amount_unreconciled, 200.00, precision_digits=2) == 0:
data += [(item.id, 180.0)]
else:
data += [(item.id, 70.0)]
I check that writeoff amount computed is -10.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == -10.0), "Writeoff amount is not -10.0"
+ assert (float_compare(voucher_id.writeoff_amount, -10.0, precision_digits=2) == 0), "Writeoff amount is not -10.0"
-
I confirm the voucher
-
I check that my write-off is correct. 9 debit and 10 amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -180.00:
- assert move_line.credit == 162.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == -70.00:
- assert move_line.credit == 63.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == 10.00:
- assert move_line.debit == 9.00, "Writeoff amount is wrong."
- elif move_line.amount_currency == 240.00:
- assert move_line.debit == 216.00, "Bank entry is wrong."
+ if float_compare(move_line.amount_currency, -180.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 162.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, -70.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 63.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, 10.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 9.00, precision_digits=2) == 0, "Writeoff amount is wrong."
+ elif float_compare(move_line.amount_currency, 240.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 216.00, precision_digits=2) == 0, "Bank entry is wrong."
else:
assert False, "Unrecognized journal entry"
-
I check the residual amount of Invoice1, should be 20 in amount_currency
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_jan"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 20.0) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 20.0, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_feb"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 30.0) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 30.0, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
On the first April, I create the second voucher of payment with values 45 USD, journal USD,
-
I fill amounts 20 for the invoice of 200$ and 30 for the invoice of 100$
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_2_case1'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 20.00:
+ if float_compare(item.amount_unreconciled, 20.00, precision_digits=2) == 0:
data += [(item.id, 20.0)]
else:
data += [(item.id, 30.0)]
I check that writeoff amount computed is -5.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == -5.0), "Writeoff amount is not -5.0"
+ assert (float_compare(voucher_id.writeoff_amount, -5.0, precision_digits=2) == 0), "Writeoff amount is not -5.0"
-
I confirm the voucher
-
I check that my writeoff is correct. 4.75 debit and 5 amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
reconcile_a = reconcile_b = False
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -20.00:
+ if float_compare(move_line.amount_currency, -20.00, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 200$ is not fully reconciled"
reconcile_b = move_line.reconcile_id.id
- elif move_line.amount_currency == -30.00:
+ elif float_compare(move_line.amount_currency, -30.00, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 100$ is not fully reconciled"
reconcile_a = move_line.reconcile_id.id
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -20.00:
- assert move_line.credit == 19.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == -30.00:
- assert move_line.credit == 28.50, "Debtor account has wrong entry."
- elif move_line.amount_currency == 5.00:
- assert move_line.debit == 4.75, "Writeoff amount is wrong."
- elif move_line.debit == 11.5 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.0 and move_line.reconcile_id.id == reconcile_a, "Exchange difference entry for the invoice of 100$ is wrong"
- elif move_line.credit == 11.5:
- assert move_line.amount_currency == 0.0
- elif move_line.debit == 31.0 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.0 and move_line.reconcile_id.id == reconcile_b, "Exchange difference entry for the invoice of 200$ is wrong"
- elif move_line.credit == 31.0:
- assert move_line.amount_currency == 0.0
- elif move_line.amount_currency == 45.00:
- assert move_line.debit == 42.75, "Bank entry is wrong."
+ if float_compare(move_line.amount_currency, -20.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 19.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, -30.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 28.50, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, 5.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 4.75, precision_digits=2) == 0, "Writeoff amount is wrong."
+ elif float_compare(move_line.debit, 11.5, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.0, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_a, "Exchange difference entry for the invoice of 100$ is wrong"
+ elif float_compare(move_line.credit, 11.5, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.0, precision_digits=2) == 0
+ elif float_compare(move_line.debit, 31.0, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.0, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_b, "Exchange difference entry for the invoice of 200$ is wrong"
+ elif float_compare(move_line.credit, 31.0, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.0, precision_digits=2) == 0
+ elif float_compare(move_line.amount_currency, 45.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 42.75, precision_digits=2) == 0, "Bank entry is wrong."
else:
assert False, "Unrecognized journal entry"
-
I check the residual amount of Invoice1, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_jan"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_feb"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
I check that first invoice move is correct for debtor account (debit - credit == 150.0)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_jan_payment_rate"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 150.0), "Invoice move is not correct for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 150.0, precision_digits=2) == 0), "Invoice move is not correct for debtors account"
-
I create the second invoice on 1st February for 100 USD
-
I check that second invoice move is correct for debtor account (debit - credit == 80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_feb_payment_rate"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 80), "Invoice move is not correct for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 80, precision_digits=2) == 0), "Invoice move is not correct for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
I fill amounts 180 for the invoice of 200$ and 70 for the invoice of 100$>
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case1_payment_rate'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 200.00:
+ if float_compare(item.amount_unreconciled, 200.00, precision_digits=2) == 0:
data += [(item.id, 180.0)]
else:
data += [(item.id, 70.0)]
I check that writeoff amount computed is -10.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = ref('account_voucher_1_case1_payment_rate')
voucher_id = self.browse(cr, uid, voucher)
- assert (voucher_id.writeoff_amount == -10.0), "Writeoff amount is not -10.0"
+ assert (float_compare(voucher_id.writeoff_amount, -10.0, precision_digits=2) == 0), "Writeoff amount is not -10.0"
-
I confirm the voucher
-
I check that my write-off is correct. 8 debit and 10 amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = ref('account_voucher_1_case1_payment_rate')
voucher_id = self.browse(cr, uid, voucher)
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -180.00:
- assert move_line.credit == 144.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == -70.00:
- assert move_line.credit == 56.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == 10.00:
- assert move_line.debit == 8.00, "Writeoff amount is wrong: Got a debit of %s (expected 8,00€)" % (move_line.debit)
- elif move_line.amount_currency == 240.00:
- assert move_line.debit == 192.00, "Bank entry is wrong."
+ if float_compare(move_line.amount_currency, -180.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 144.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, -70.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 56.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, 10.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 8.00, precision_digits=2) == 0, "Writeoff amount is wrong: Got a debit of %s (expected 8,00€)" % (move_line.debit)
+ elif float_compare(move_line.amount_currency, 240.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 192.00, precision_digits=2) == 0, "Bank entry is wrong."
else:
assert False, "Unrecognized journal entry"
-
I check the residual amount of Invoice1, should be 20 in amount_currency and 6 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_jan_payment_rate"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual == 6.0) , "Residual amount is not correct for first Invoice"
- assert (move_line.amount_residual_currency == 20.0) , "Residual amount in currency is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual, 6.0, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 20.0, precision_digits=2) == 0) , "Residual amount in currency is not correct for first Invoice"
-
I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_feb_payment_rate"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual == 24.0) , "Residual amount is not correct for second Invoice"
- assert (move_line.amount_residual_currency == 30.0) , "Residual amount in currency is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual, 24.0, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 30.0, precision_digits=2) == 0) , "Residual amount in currency is not correct for second Invoice"
I check that first invoice move is correct for debtor account(debit - credit == -150)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == -150.00), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, -150.00, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I create the second invoice on 1st February for 100 USD
-
I check that second invoice move is correct for debtor account (debit - credit == -80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == -80), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, -80, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
I fill amounts 180 for the invoice of 200$ and 70 for the invoice of 100$
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case2_suppl'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 200.00:
+ if float_compare(item.amount_unreconciled, 200.00, precision_digits=2) == 0:
data += [(item.id, 180.0)]
else:
data += [(item.id, 70.0)]
I check that writeoff amount computed is -15.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == -15.0), "Writeoff amount is not -15.0"
+ assert (float_compare(voucher_id.writeoff_amount, -15.0, precision_digits=2) == 0), "Writeoff amount is not -15.0"
-
I check that currency rate difference is 34.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.currency_rate_difference == 34.0), "Currency rate difference is not 34.0"
+ assert (float_compare(voucher_id.currency_rate_difference, 34.0, precision_digits=2) == 0), "Currency rate difference is not 34.0"
-
I confirm the voucher
-
I check that my writeoff is correct. -15 in credit with no amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == 180.00:
- assert move_line.debit == 135.00, "Creditor account has wrong entry."
- elif move_line.amount_currency == 70.00:
- assert move_line.debit == 56.00, "Debtor account has wrong entry."
- elif move_line.debit == 34.00:
- assert move_line.amount_currency == 0.00, "Incorrect Currency Difference."
- elif move_line.debit == 15.00:
- assert move_line.amount_currency == 0.00, "Writeoff amount is wrong."
+ if float_compare(move_line.amount_currency, 180.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 135.00, precision_digits=2) == 0, "Creditor account has wrong entry."
+ elif float_compare(move_line.amount_currency, 70.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 56.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.debit, 34.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Incorrect Currency Difference."
+ elif float_compare(move_line.debit, 15.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Writeoff amount is wrong."
-
I check the residual amount of Invoice1, should be 20 in residual currency and 15 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 20.0 and move_line.amount_residual == 15) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 20.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 15, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2, should be 30 in residual currency and 24 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 30 and move_line.amount_residual == 24) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 30, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 24, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
-
I create the second voucher of payment with values 45 USD, journal USD,
-
I fill amounts 20 for the invoice of 200$ and 30 for the invoice of 100$>
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_2_case2_suppl'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 20.00:
+ if float_compare(item.amount_unreconciled, 20.00, precision_digits=2) == 0:
data += [(item.id, 20.0)]
else:
data += [(item.id, 30.0)]
I check that writeoff amount computed is -5.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == 5.0), "Writeoff amount is not 5.0"
+ assert (float_compare(voucher_id.writeoff_amount, 5.0, precision_digits=2) == 0), "Writeoff amount is not 5.0"
-
I check that currency rate difference is 8.50
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.currency_rate_difference == 8.50), "Currency rate difference is not 8.50"
+ assert (float_compare(voucher_id.currency_rate_difference, 8.50, precision_digits=2) == 0), "Currency rate difference is not 8.50"
-
I confirm the voucher
-
I check that my writeoff is correct. 4.75 in credit and 5 in amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == 20.00:
- assert move_line.debit == 15.00, "Debtor account has wrong entry."
- elif move_line.amount_currency == 30.00:
- assert move_line.debit == 24.00, "Debtor account has wrong entry."
- elif move_line.debit == 8.50:
- assert move_line.amount_currency == 0.00, "Incorrect Currency Difference."
- elif move_line.amount_currency == -5.00:
- assert move_line.credit == 4.75, "Writeoff amount is wrong."
+ if float_compare(move_line.amount_currency, 20.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 15.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, 30.00, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 24.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.debit, 8.50, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Incorrect Currency Difference."
+ elif float_compare(move_line.amount_currency, -5.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 4.75, precision_digits=2) == 0, "Writeoff amount is wrong."
-
I check the residual amount of invoice 1, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_suppl"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
-
I check the residual amount of invoice 2, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_suppl"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
I check that first invoice move is correct for debtor account(debit - credit == 150)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 150.00), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 150.00, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I create the second invoice on 1st February for 100 USD
-
I check that second invoice move is correct for debtor account (debit - credit == 80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 80), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 80, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
I fill amounts 130 for the invoice of 200$ and 70 for the invoice of 100$
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
import time
from openerp import netsvc
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case2a'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 150.00:
+ if float_compare(item.amount_unreconciled, 150.00, precision_digits=2) == 0:
data += [(item.id, 130.0)]
else:
data += [(item.id, 70.0)]
I check the residual amount of Invoice1, should be 55.56 in residual currency and 20 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 55.56 and move_line.amount_residual == 20) , "Residual amount is not correct for first Invoice. Got %s USD (%s EUR)" %(move_line.amount_residual_currency, move_line.amount_residual)
+ assert (float_compare(move_line.amount_residual_currency, 55.56, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 20, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice. Got %s USD (%s EUR)" %(move_line.amount_residual_currency, move_line.amount_residual)
-
I check the residual amuont of Invoice2, should be 22.22 in residual currency and 10 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 22.22 and move_line.amount_residual == 10) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 22.22, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 10, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
-
I create the second voucher of payment with values 80 USD, journal USD
-
!python {model: account.voucher}: |
import time
from openerp import netsvc
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_2_case2a'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 55.56:
+ if float_compare(item.amount_unreconciled, 55.56, precision_digits=2) == 0:
data += [(item.id, 55.56)]
else:
data += [(item.id, 22.22)]
I check that writeoff amount computed is 2.22
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (round(voucher_id.writeoff_amount, 2) == 2.22), "Writeoff amount is not 2.22$"
+ assert (float_compare(round(voucher_id.writeoff_amount, 2), 2.22, precision_digits=2) == 0), "Writeoff amount is not 2.22$"
-
I confirm the voucher
-
I check that my writeoff is correct. 2.11 in credit and 2.22 in amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
reconcile_a = reconcile_b = False
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -22.22:
+ if float_compare(move_line.amount_currency, -22.22, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 200$ is not fully reconciled"
reconcile_b = move_line.reconcile_id.id
- elif move_line.amount_currency == -55.56:
+ elif float_compare(move_line.amount_currency, -55.56, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 100$ is not fully reconciled"
reconcile_a = move_line.reconcile_id.id
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -55.56:
- assert move_line.credit == 52.78, "Debtor account has wrong entry."
- elif move_line.amount_currency == -22.22:
- assert move_line.credit == 21.11, "Debtor account has wrong entry."
- elif move_line.credit == 11.11 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.00 and move_line.reconcile_id.id == reconcile_b, "Incorrect Currency Difference."
- elif move_line.credit == 32.78 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.00 and move_line.reconcile_id.id == reconcile_a, "Incorrect Currency Difference."
- elif move_line.amount_currency == 2.22:
- assert move_line.credit == 2.11, "Writeoff amount is wrong."
+ if float_compare(move_line.amount_currency, -55.56, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 52.78, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, -22.22, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 21.11, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.credit, 11.11, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_b, "Incorrect Currency Difference."
+ elif float_compare(move_line.credit, 32.78, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_a, "Incorrect Currency Difference."
+ elif float_compare(move_line.amount_currency, 2.22, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 2.11, precision_digits=2) == 0, "Writeoff amount is wrong."
-
I check the residual amount of invoice 1, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of invoice 2, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
I check that first invoice move is correct for debtor account(debit - credit == 150)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_michal"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 150.00), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 150.00, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I create the second invoice on 1st February for 100 USD
-
I check that second invoice move is correct for debtor account (debit - credit == 80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_michal"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 80), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 80, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
I fill amounts 130 for the invoice of 200$ and 70 for the invoice of 100$>
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case2b'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 150.00:
+ if float_compare(item.amount_unreconciled, 150.00, precision_digits=2) == 0:
data += [(item.id, 130.0)]
else:
data += [(item.id, 70.0)]
I check that the debtor account has 2 new lines with 144.44 and 77.78 in amount_currency columns and their credit columns are 130 and 70
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.credit == 70.0:
- assert move_line.amount_currency == -77.78, "Wrong debtor entry"
- if move_line.credit == 130.0:
- assert move_line.amount_currency == -144.44, "Wrong debtor entry"
+ if float_compare(move_line.credit, 70.0, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, -77.78, precision_digits=2) == 0, "Wrong debtor entry"
+ if float_compare(move_line.credit, 130.0, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, -144.44, precision_digits=2) == 0, "Wrong debtor entry"
-
I check the residual amount of Invoice1, should be 55.56 in residual currency and 20 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_michal"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 55.56 and move_line.amount_residual == 20) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 55.56, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 20, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
I check the residual amount of Invoice2, should be 22.22 in residual currency and 10 in amount_residual
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_michal"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 22.22 and move_line.amount_residual == 10) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 22.22, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 10, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
-
I create the second voucher of payment with values 80 USD, journal USD
-
and I fully reconcil the 2 previous invoices
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_2_case2b'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 55.56:
+ if float_compare(item.amount_unreconciled, 55.56, precision_digits=2) == 0:
data += [(item.id, 55.56)]
else:
data += [(item.id, 22.22)]
I check that writeoff amount computed is 2.22
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (round(voucher_id.writeoff_amount, 2) == 2.22), "Writeoff amount is not 2.22$"
+ assert (float_compare(round(voucher_id.writeoff_amount, 2), 2.22, precision_digits=2) == 0), "Writeoff amount is not 2.22$"
-
I confirm the voucher
-
I check that my writeoff is correct. 2.11 in credit and 2.22 in amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
reconcile_a = reconcile_b = False
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -22.22:
+ if float_compare(move_line.amount_currency, -22.22, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 200$ is not fully reconciled"
reconcile_b = move_line.reconcile_id.id
- elif move_line.amount_currency == -55.56:
+ elif float_compare(move_line.amount_currency, -55.56, precision_digits=2) == 0:
assert move_line.reconcile_id.id, "The invoice of 100$ is not fully reconciled"
reconcile_a = move_line.reconcile_id.id
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.amount_currency == -55.56:
- assert move_line.credit == 52.78, "Debtor account has wrong entry."
- elif move_line.amount_currency == -22.22:
- assert move_line.credit == 21.11, "Debtor account has wrong entry."
- elif move_line.credit == 11.11 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.00 and move_line.reconcile_id.id == reconcile_b, "Incorrect Currency Difference."
- elif move_line.credit == 32.78 and move_line.account_id.reconcile:
- assert move_line.amount_currency == 0.00 and move_line.reconcile_id.id == reconcile_a, "Incorrect Currency Difference."
- elif move_line.amount_currency == 2.22:
- assert move_line.credit == 2.11, "Writeoff amount is wrong."
+ if float_compare(move_line.amount_currency, -55.56, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 52.78, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.amount_currency, -22.22, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 21.11, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.credit, 11.11, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_b, "Incorrect Currency Difference."
+ elif float_compare(move_line.credit, 32.78, precision_digits=2) == 0 and move_line.account_id.reconcile:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0 and move_line.reconcile_id.id == reconcile_a, "Incorrect Currency Difference."
+ elif float_compare(move_line.amount_currency, 2.22, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 2.11, precision_digits=2) == 0, "Writeoff amount is wrong."
-
I check the residual amount of invoice 1, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_michal"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
-
I check the residual amount of invoice 2, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_michal"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for second Invoice"
I check that first invoice move is correct for debtor account(debit - credit == 150)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 150.00), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 150.00, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I create the second invoice on 1st February for 80 EUR
-
I check that second invoice move is correct for debtor account (debit - credit == 80)
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur"))
assert invoice_id.move_id, "Move not created for open invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.debit - move_line.credit == 80.00), "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 80.00, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
I fill amounts 100 for the invoice of 150€ and 20 for the invoice of 80€
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case3'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 150.00:
+ if float_compare(item.amount_unreconciled, 150.00, precision_digits=2) == 0:
data += [(item.id, 100.0)]
else:
data += [(item.id, 20.0)]
I check that writeoff amount computed is 0.00
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0.00"
+ assert (float_compare(voucher_id.writeoff_amount, 0.00, precision_digits=2) == 0), "Writeoff amount is not 0.00"
-
I confirm the voucher
-
I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 20 and 100 respectively
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.credit == 20.00:
- assert move_line.amount_currency == 0.00, "Debtor account has wrong entry."
- elif move_line.credit == 100.00:
- assert move_line.amount_currency == 0.00, "Debtor account has wrong entry."
+ if float_compare(move_line.credit, 20.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.credit, 100.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Debtor account has wrong entry."
-
I check the residual amount of Invoice1 is 50
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 50.0 and move_line.amount_residual == 50.0) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 50.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 50.0, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2 is 60
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 60.0 and move_line.amount_residual == 60.0) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 60.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 60.0, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
-
I create the second voucher of payment with values 120€, journal EUR, and check to let open the debtor overpaid amount
-
I fill amounts 50 for the invoice of 150€ and 70 for the invoice of 80€
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_2_case3'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 50.00:
+ if float_compare(item.amount_unreconciled, 50.00, precision_digits=2) == 0:
data += [(item.id, 50.0)]
- elif item.amount_unreconciled == 60.00:
+ elif float_compare(item.amount_unreconciled, 60.00, precision_digits=2) == 0:
data += [(item.id, 70.00)]
for line_id, amount in data:
self.pool.get('account.voucher.line').write(cr, uid, [line_id], {'amount': amount})
I check that writeoff amount computed is 0.00
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'),('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (voucher_id.writeoff_amount == 0.00), "Writeoff amount is not 0"
+ assert (float_compare(voucher_id.writeoff_amount, 0.00, precision_digits=2) == 0), "Writeoff amount is not 0"
-
I confirm the voucher
-
I check that the debtor account has 2 new lines with 0.00 and 0.00 in amount_currency columns and their credit are 70 and 50
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if move_line.credit == 70.00:
- assert move_line.amount_currency == 0.00, "Debtor account has wrong entry."
- elif move_line.credit == 50.00:
- assert move_line.amount_currency == 0.00, "Debtor account has wrong entry."
+ if float_compare(move_line.credit, 70.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.credit, 50.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Debtor account has wrong entry."
-
I check the residual amount of Invoice1 is 0
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_eur"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0 and move_line.amount_residual == 0) , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0, precision_digits=2) == 0) , "Residual amount is not correct for first Invoice"
-
I check the residual amuont of Invoice2 is -10
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_second_invoice_feb_eur"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == -10.0 and move_line.amount_residual == -10.0) , "Residual amount is not correct for second Invoice"
+ assert (float_compare(move_line.amount_residual_currency, -10.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, -10.0, precision_digits=2) == 0) , "Residual amount is not correct for second Invoice"
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert float_compare(move_line.debit - move_line.credit, 149.39, 2) == 0, "Invoice move is incorrect for debtors account"
+ assert (float_compare(move_line.debit - move_line.credit, 149.39, precision_digits=2) == 0), "Invoice move is incorrect for debtors account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
!python {model: account.voucher}: |
import time
from openerp import netsvc
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_1_case4'))
data = []
for item in voucher_id.line_cr_ids:
- if item.amount_unreconciled == 186.74:
+ if float_compare(item.amount_unreconciled, 186.74, precision_digits=2) == 0:
data += [(item.id, 186.74)]
else:
data += [(item.id, 0.0)]
I check that writeoff amount computed is 13.26
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))])
voucher_id = self.browse(cr, uid, voucher[0])
- assert (round(voucher_id.writeoff_amount,2) == 13.26), "Writeoff amount is not 13.26 CHF"
+ assert (float_compare(round(voucher_id.writeoff_amount,2), 13.26, precision_digits=2) == 0), "Writeoff amount is not 13.26 CHF"
-
I confirm the voucher
-
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)])
assert move_lines, "Voucher move has no lines"
for move_line in move_line_obj.browse(cr, uid, move_lines):
- if float_compare(move_line.amount_currency, 200, 2) == 0:
- assert float_compare(move_line.debit, 160.00, 2) == 0, "Bank account has wrong entry."
- elif float_compare(move_line.amount_currency, -298.78, 2) == 0:
- assert float_compare(move_line.credit, 149.39, 2) == 0, "Debtor account has wrong entry."
- elif move_line.debit == 0.00 and move_line.credit == 0.00:
- assert move_line.amount_currency == 98.78, "Incorrect Currency Difference, got %s as amount_currency (expected 98.78)." % (move_line.amount_currency)
+ if float_compare(move_line.amount_currency, 200, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 160.00, precision_digits=2) == 0, "Bank account has wrong entry."
+ elif float_compare(move_line.amount_currency, -298.78, precision_digits=2) == 0 :
+ assert float_compare(move_line.credit, 149.39, precision_digits=2) == 0, "Debtor account has wrong entry."
+ elif float_compare(move_line.debit, 0.00, precision_digits=2) == 0 and float_compare(move_line.credit, 0.00, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 98.78, precision_digits=2) == 0, "Incorrect Currency Difference, got %s as amount_currency (expected 98.78)." % (move_line.amount_currency)
assert move_line.currency_id.id == ref('base.CAD'), "Incorrect Currency Difference, got %s (expected 'CAD')" % (move_line.currency_id.name)
- elif move_line.credit == 10.61:
- assert move_line.amount_currency == -13.26, "Writeoff amount is wrong."
+ elif float_compare(move_line.credit, 10.61, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, -13.26, precision_digits=2) == 0, "Writeoff amount is wrong."
else:
assert False, "Wrong entry"
-
I check the residual amount of Invoice1, should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_cad"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for first Invoice"
-
!record {model: res.currency.rate, id: nov_usd}:
currency_id: base.USD
- name: !eval "'%s-11-01 00:00:00' %(datetime.now().year)"
+ name: !eval "'%s-11-01' %(datetime.now().year)"
rate: 1.8
-
I create currency USD in OpenERP for December of 1.5 Rate
-
!record {model: res.currency.rate, id: dec_usd}:
currency_id: base.USD
- name: !eval "'%s-12-01 00:00:00' %(datetime.now().year)"
+ name: !eval "'%s-12-01' %(datetime.now().year)"
rate: 1.5
-
I set the income and expense currency accounts on the main company
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert float_compare(move_line.debit - move_line.credit, -555.56, 2) == 0, \
- "Invoice move is incorrect for creditor account"
- assert float_compare(move_line.amount_currency, -1000, 2) == 0, \
- "Amount currency is incorrect for creditor account"
+ assert (float_compare(move_line.debit - move_line.credit, -555.56, precision_digits=2) == 0), "Invoice move is incorrect for creditor account"
+ assert (float_compare(move_line.amount_currency, -1000, precision_digits=2) == 0), "Amount currency is incorrect for creditor account"
-
I set the context that will be used for the encoding of all the vouchers of this file
-
-
!python {model: account.voucher}: |
import time
+ from openerp.tools import float_compare
vals = {}
voucher_id = self.browse(cr, uid, ref('account_voucher_case_5_supplier_flow'))
for item in voucher_id.line_dr_ids:
- if item.amount_unreconciled == 1000.00:
+ if float_compare(item.amount_unreconciled, 1000.00, precision_digits=2) == 0:
self.pool.get('account.voucher.line').write(cr, uid, [item.id], {'amount': 1000})
assert (voucher_id.state=='draft'), "Voucher is not in draft state"
-
I check that writeoff amount computed is -50.0
-
- !assert {model: account.voucher, id: account_voucher_case_5_supplier_flow}:
- - writeoff_amount == -50.0
+ !python {model: account.voucher}: |
+ from openerp.tools import float_compare
+ writeoff_amount = self.browse(cr, uid, ref('account_voucher_case_5_supplier_flow')).writeoff_amount
+ assert float_compare(writeoff_amount, -50.0, precision_digits=2) == 0
-
I confirm the voucher
-
I check that my writeoff is correct. 33.34€ in credit with -$50 as amount currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher_id = self.browse(cr, uid, ref('account_voucher_case_5_supplier_flow'))
for move_line in voucher_id.move_id.line_id:
- if move_line.amount_currency == -950.00:
- assert move_line.credit == 633.33, "Wrong bank entry."
- elif move_line.credit == 111.11 or move_line.debit == 111.11:
- assert move_line.amount_currency == 0.00, "Incorrect Currency Difference."
- elif move_line.credit == 33.34:
- assert move_line.amount_currency == -50.0, "Writeoff amount is wrong."
- elif move_line.debit == 666.67:
- assert move_line.amount_currency == 1000.0, "Wrong supplier entry."
+ if float_compare(move_line.amount_currency, -950.00, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 633.33, precision_digits=2) == 0, "Wrong bank entry."
+ elif float_compare(move_line.credit, 111.11, precision_digits=2) == 0 or float_compare(move_line.debit, 111.11, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 0.00, precision_digits=2) == 0, "Incorrect Currency Difference."
+ elif float_compare(move_line.credit, 33.34, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, -50.0, precision_digits=2) == 0, "Writeoff amount is wrong."
+ elif float_compare(move_line.debit, 666.67, precision_digits=2) == 0:
+ assert float_compare(move_line.amount_currency, 1000.0, precision_digits=2) == 0, "Wrong supplier entry."
else:
assert False, "Wrong entry. Unrecognized account move line"
-
I check the residual amount of invoice, it should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_supplier_invoice_november"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for supplier invoice"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for supplier invoice"
I fill amount 1400 for the invoice of 1400$
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
data = []
voucher = self.browse(cr, uid, ref('account_voucher_eur_usd_case'))
for item in voucher.line_cr_ids:
- if item.amount_unreconciled == 1400:
+ if float_compare(item.amount_unreconciled, 1400, precision_digits=2) == 0:
data += [(item.id, 1400)]
assert data, "Credit line not found"
for line_id, amount in data:
I check that writeoff amount computed is -50.0
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.browse(cr, uid, ref('account_voucher_eur_usd_case'))
- assert (voucher.writeoff_amount == -50.0), "Writeoff amount is not -50.0"
+ assert (float_compare(voucher.writeoff_amount, -50.0, precision_digits=2) == 0), "Writeoff amount is not -50.0"
-
I confirm the voucher
-
I check that my bank account is correct. 964.29 debit and 1350 amount_currency
-
!python {model: account.voucher}: |
+ from openerp.tools import float_compare
voucher = self.browse(cr, uid, ref('account_voucher_eur_usd_case'))
for move_line in voucher.move_ids:
- if move_line.amount_currency == 1350:
- assert move_line.debit == 964.29,"debtor account is not correct"
- if move_line.amount_currency == 50:
- assert move_line.debit == 35.71,"write off bank account is not correct"
- if move_line.amount_currency == 0.0:
- assert move_line.credit == 1000,"total reconcile is not correct of invoice"
+ if float_compare(move_line.amount_currency, 1350, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 964.29, precision_digits=2) == 0,"debtor account is not correct"
+ if float_compare(move_line.amount_currency, 50, precision_digits=2) == 0:
+ assert float_compare(move_line.debit, 35.71, precision_digits=2) == 0,"write off bank account is not correct"
+ if float_compare(move_line.amount_currency, 0.0, precision_digits=2) == 0:
+ assert float_compare(move_line.credit, 1000, precision_digits=2) == 0,"total reconcile is not correct of invoice"
-
I check that the move of payment in invoice is valid
-
I check the residual amount of invoice should be 0 in residual currency and 0 in amount_residual and paid
-
!python {model: account.invoice}: |
+ from openerp.tools import float_compare
invoice_id = self.browse(cr, uid, ref("account_invoice_eur_usd"))
move_line_obj = self.pool.get('account.move.line')
move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)])
move_line = move_line_obj.browse(cr, uid, move_lines[0])
- assert move_line.amount_residual_currency == 0.0, "Residual amount is not correct for Invoice"
- assert move_line.amount_residual == 0.0, "Residual amount is not correct for Invoice"
- assert invoice_id.state == 'paid', "Invoice is not paid"
+ assert (float_compare(move_line.amount_residual_currency, 0.0, precision_digits=2) == 0 and float_compare(move_line.amount_residual, 0.0, precision_digits=2) == 0 and invoice_id.state == 'paid') , "Residual amount is not correct for Invoice"
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
- <page string="Geo Localization" name="geo_localization">
+ <page string="Geo Location" name="geo_location">
<group colspan="2" col="2">
- <separator string="Geo Localization" colspan="2"/>
+ <separator string="Geo Location" colspan="2"/>
<button
string="Geo Localize"
name="geo_localize"
_columns = {
'partner_weight': fields.integer('Grade Weight',
help="Gives the probability to assign a lead to this partner. (0 means no assignation.)"),
- 'opportunity_assigned_ids': fields.one2many('crm.lead', 'partner_assigned_id',\
- 'Assigned Opportunities'),
'grade_id': fields.many2one('res.partner.grade', 'Grade'),
'activation' : fields.many2one('res.partner.activation', 'Activation', select=1),
'date_partnership' : fields.date('Partnership Date'),
<field name="model">res.partner</field>
<field name="inherit_id" ref="base_geolocalize.view_crm_partner_geo_form"/>
<field name="arch" type="xml">
- <xpath expr="//page[@name='geo_localization']" position="replace">
- <!-- <xpath expr="//notebook[last()]" position="inside"> -->
- <page string="Geo Localization" name="geo_localization">
+ <xpath expr="//page[@name='geo_location']" position="inside">
+ <group>
<group>
- <group>
- <separator string="Partner Activation" colspan="2"/>
- <field name="grade_id" widget="selection"/>
- <field name="activation" widget="selection"/>
- <field name="partner_weight"/>
- </group>
- <group>
- <separator string="Partner Review" colspan="2"/>
- <field name="date_review"/>
- <field name="date_review_next"/>
- <field name="date_partnership"/>
- </group>
+ <separator string="Partner Activation" colspan="2"/>
+ <field name="grade_id" widget="selection"/>
+ <field name="activation" widget="selection"/>
+ <field name="partner_weight"/>
+ <field name="assigned_partner_id"/>
</group>
- <group colspan="2" col="2">
- <separator string="Geo Localization" colspan="2"/>
- <button
- string="Geo Localize"
- name="geo_localize"
- colspan="2"
- icon="gtk-apply"
- type="object"/>
- <field name="partner_latitude"/>
- <field name="partner_longitude"/>
- <field name="date_localization"/>
+ <group>
+ <separator string="Partner Review" colspan="2"/>
+ <field name="date_review"/>
+ <field name="date_review_next"/>
+ <field name="date_partnership"/>
</group>
- <newline/>
-
- <field name="opportunity_assigned_ids" colspan="4" nolabel="1">
- <tree string="Assigned Opportunities">
- <field name="create_date"/>
- <field name="name"/>
- <field name="type"/>
- <field name="probability" invisible="1"/>
- <field name="stage_id"/>
- <field name="section_id"
- invisible="context.get('invisible_section', True)"
- groups="base.group_multi_salesteams"/>
- <field name="user_id" />
- <button string="Convert to Opportunity"
- name="convert_opportunity"
- type="object"
- icon="gtk-convert"
- attrs="{'invisible':[('type','=','opportunity')]}" />
- <button name="case_escalate" string="Escalate"
- type="object"
- icon="gtk-go-up"
- attrs="{'invisible':[('probability', '=', 100)]}" />
- </tree>
- </field>
- </page>
+ <group>
+ <button name="%(crm.relate_partner_opportunities)d" string="Assigned Opportunities" type="action" />
+ </group>
+ </group>
</xpath>
</field>
</record>
I check sale order after added delivery cost.
-
!python {model: sale.order.line}: |
+ from openerp.tools import float_compare
line_ids = self.search(cr, uid, [('order_id','=', ref('sale_normal_delivery_charges')), ('product_id','=', ref('product_product_delivery'))])
assert len(line_ids), "Delivery cost is not Added"
line_data = self.browse(cr ,uid ,line_ids[0] ,context)
- assert line_data.price_subtotal == 10, "Delivey cost is not correspond."
+ assert float_compare(line_data.price_subtotal, 10, precision_digits=2) == 0, "Delivey cost is not correspond."
-
I confirm the sale order.
-
I check sale order after added delivery cost.
-
!python {model: sale.order.line}: |
+ from openerp.tools import float_compare
line_ids = self.search(cr, uid, [('order_id','=', ref('sale_free_delivery_charges')), ('product_id','=', ref('product_product_delivery'))])
assert len(line_ids), "Delivery cost is not Added"
line_data = self.browse(cr ,uid ,line_ids[0] ,context)
- assert line_data.price_subtotal == 0, "Delivey cost is not correspond."
+ assert float_compare(line_data.price_subtotal, 0, precision_digits=2) == 0, "Delivey cost is not correspond."
-
I set default delivery policy.
{}
-
!python {model: sale.config.settings}: |
- self.execute(cr, uid, [ref('default_delivery_policy')], context=context)
\ No newline at end of file
+ self.execute(cr, uid, [ref('default_delivery_policy')], context=context)
import wizard
import report
import res_partner
+import res_config
'event_data.xml',
'report/report_event_registration_view.xml',
'res_partner_view.xml',
+ 'res_config_view.xml',
'email_template.xml',
'views/event.xml',
],
--- /dev/null
+# -*- coding: utf-8 -*-
+
+from openerp.osv import fields, osv
+
+class event_config_settings(osv.TransientModel):
+ _name='marketing.config.settings'
+ _inherit='marketing.config.settings'
+ _columns = {
+ 'module_event_sale': fields.boolean(
+ 'Sale different type of ticket',
+ help='Install the event_sale module'),
+ 'module_website_event_track': fields.boolean(
+ 'Organize few days event with track,full agenda,own menu in website'),
+ }
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="view_event_configuration" model="ir.ui.view">
+ <field name="model">marketing.config.settings</field>
+ <field name="inherit_id" ref="marketing.view_marketing_configuration"/>
+ <field name="arch" type="xml">
+ <div name="config_setting" position="inside">
+ <separator string="Event Features"/>
+ <group>
+ <label for="id" string="Settings"/>
+ <div>
+ <div name="module_event_sale">
+ <field name="module_event_sale" class="oe_inline"/>
+ <label for="module_event_sale"/>
+ </div>
+ <div name="module_website_event_track">
+ <field name="module_website_event_track"/>
+ <label for="module_website_event_track"/>
+ </div>
+ </div>
+ </group>
+ </div>
+ </field>
+ </record>
+ </data>
+</openerp>
+
+
+
_columns = {
'name': fields.function(_model_name_get_fnc, type="char", string='Name', store=True),
'modelname': fields.char('Model name', required=True),
- 'brand_id': fields.many2one('fleet.vehicle.model.brand', 'Model Brand', required=True, help='Brand of the vehicle'),
+ 'brand_id': fields.many2one('fleet.vehicle.model.brand', 'Make', required=True, help='Make of the vehicle'),
'vendors': fields.many2many('res.partner', 'fleet_vehicle_model_vendors', 'model_id', 'partner_id', string='Vendors'),
'image': fields.related('brand_id', 'image', type="binary", string="Logo"),
'image_medium': fields.related('brand_id', 'image_medium', type="binary", string="Logo (medium)"),
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
_columns = {
- 'name': fields.char('Brand Name', required=True),
+ 'name': fields.char('Make', required=True),
'image': fields.binary("Logo",
help="This field holds the image used as logo for the brand, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
<search string="Vehicles costs" >
<field name="brand_id" />
<group expand="1" string="Group By">
- <filter name="groupby_brand" context="{'group_by' : 'brand_id'}" string="Brand"/>
+ <filter name="groupby_brand" context="{'group_by' : 'brand_id'}" string="Make"/>
</group>
</search>
</field>
<p class="oe_view_nocontent_create">
Click to create a new model.
</p><p>
- You can define several models (e.g. A3, A4) for each brand (Audi).
+ You can define several models (e.g. A3, A4) for each make (Audi).
</p>
</field>
</record>
<field name="name">fleet.vehicle.model.brand.tree</field>
<field name="model">fleet.vehicle.model.brand</field>
<field name="arch" type="xml">
- <tree string="Model Brand">
+ <tree string="Model Make">
<field name="name" />
</tree>
</field>
<field name="name">fleet.vehicle.model.brand.form</field>
<field name="model">fleet.vehicle.model.brand</field>
<field name="arch" type="xml">
- <form string="Model Brand">
+ <form string="Model Make">
<sheet>
<group>
<div>
</record>
<record model='ir.actions.act_window' id='fleet_vehicle_model_brand_act'>
- <field name="name">Model brand of Vehicle</field>
+ <field name="name">Model make of Vehicle</field>
<field name="res_model">fleet.vehicle.model.brand</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
- Click to create a new brand.
+ Click to create a new make.
</p>
</field>
</record>
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+# Copyright (C) 2004-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
_name = "hr.job"
_description = "Job Position"
- _inherit = ['mail.thread', 'ir.needaction_mixin']
+ _inherit = ['mail.thread']
_columns = {
'name': fields.char('Job Name', required=True, select=True),
'expected_employees': fields.function(_get_nbr_employees, string='Total Forecasted Employees',
class hr_department(osv.osv):
+ _name = "hr.department"
+ _description = "HR Department"
+ _inherit = ['mail.thread', 'ir.needaction_mixin']
def _dept_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
res = self.name_get(cr, uid, ids, context=context)
return dict(res)
- _name = "hr.department"
_columns = {
'name': fields.char('Department Name', required=True),
'complete_name': fields.function(_dept_name_get_fnc, type="char", string='Name'),
'company_id': fields.many2one('res.company', 'Company', select=True, required=False),
'parent_id': fields.many2one('hr.department', 'Parent Department', select=True),
'child_ids': fields.one2many('hr.department', 'parent_id', 'Child Departments'),
- 'manager_id': fields.many2one('hr.employee', 'Manager'),
+ 'manager_id': fields.many2one('hr.employee', 'Manager', track_visibility='onchange'),
'member_ids': fields.one2many('hr.employee', 'department_id', 'Members', readonly=True),
'jobs_ids': fields.one2many('hr.job', 'department_id', 'Jobs'),
'note': fields.text('Note'),
res.append((record['id'], name))
return res
-
-class res_users(osv.osv):
- _name = 'res.users'
- _inherit = 'res.users'
- _columns = {
- 'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
- }
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+ def create(self, cr, uid, vals, context=None):
+ # TDE note: auto-subscription of manager done by hand, because currently
+ # the tracking allows to track+subscribe fields linked to a res.user record
+ # An update of the limited behavior should come, but not currently done.
+ manager_id = vals.get("manager_id")
+ new_id = super(hr_department, self).create(cr, uid, vals, context=context)
+ if manager_id:
+ employee = self.pool.get('hr.employee').browse(cr, uid, manager_id, context=context)
+ if employee.user_id:
+ self.message_subscribe_users(cr, uid, [new_id], user_ids=[employee.user_id.id], context=context)
+ return new_id
+
+ def write(self, cr, uid, ids, vals, context=None):
+ # TDE note: auto-subscription of manager done by hand, because currently
+ # the tracking allows to track+subscribe fields linked to a res.user record
+ # An update of the limited behavior should come, but not currently done.
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ manager_id = vals.get("manager_id")
+ if manager_id:
+ employee = self.pool.get('hr.employee').browse(cr, uid, manager_id, context=context)
+ if employee.user_id:
+ self.message_subscribe_users(cr, uid, [ids], user_ids=[employee.user_id.id], context=context)
+ return super(hr_department, self).write(cr, uid, ids, vals, context=context)
<record id="employee" model="hr.employee">
<field name="name">Administrator</field>
<field name="user_id" ref="base.user_root"/>
+ <field name="address_id" ref="base.partner_root"/>
+ <field name="address_home_id" ref="base.partner_root"/>
<field name="image" type="base64" file="hr/static/img/employee-image.png"/>
</record>
sequence="90"/>
<menuitem id="menu_hr_reporting" parent="base.menu_reporting" name="Human Resources" sequence="40" />
<menuitem id="menu_hr_main" parent="menu_hr_root" name="Human Resources" sequence="0"/>
- <menuitem id="menu_hr_configuration" name="Configuration" parent="hr.menu_hr_root" groups="base.group_hr_manager" sequence="50"/>
+ <menuitem id="menu_hr_configuration" name="Configuration" parent="hr.menu_hr_root" groups="base.group_hr_user" sequence="50"/>
<menuitem id="menu_hr_reporting_timesheet" name="Reports"
parent="menu_hr_reporting" sequence="6"/>
</notebook>
</sheet>
<div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
<field name="name">hr.employee.tree</field>
<field name="model">hr.employee</field>
<field name="arch" type="xml">
- <tree string="Employees">
+ <tree string="Employees" fonts="bold:message_unread==True">
<field name="name"/>
<field name="work_phone"/>
<field name="work_email"/>
<field name="job_id"/>
<field name="parent_id"/>
<field name="coach_id" invisible="1"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<field name="name" string="Employees" filter_domain="['|',('work_email','ilike',self),('name','ilike',self)]"/>
<field name="department_id" />
<field name="category_ids" groups="base.group_hr_user"/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Manager" icon="terp-personal" domain="[]" context="{'group_by':'parent_id'}"/>
<filter string="Coach" icon="terp-personal" domain="[]" context="{'group_by':'coach_id'}"/>
<field name="message_is_follower"/>
<field name="message_follower_ids"/>
<field name="message_ids"/>
+ <field name="message_summary"/>
+ <field name="message_unread"/>
<templates>
<t t-name="kanban-box">
<div class="oe_employee_vignette">
<li t-if="record.work_email.raw_value"><a t-attf-href="mailto:#{record.work_email.value}"><field name="work_email"/></a></li>
</ul>
<div class="oe_kanban_footer_left">
+ <t t-raw="record.message_summary.raw_value"/>
<span title='Messages'><span class='oe_e'>9</span><t t-esc="record.message_ids.raw_value.length"/></span>
<span title='Followers'><span class='oe_e'>+</span><t t-esc="record.message_follower_ids.raw_value.length"/></span>
+
</div>
<div class="oe_followers" groups="base.group_user">
<button t-if="record.message_is_follower.raw_value" name="action_unfollow" type="object" class="oe_follower oe_following">
</record>
<menuitem action="open_view_categ_form" id="menu_view_employee_category_form"
- parent="hr.menu_hr_configuration" sequence="1" groups="base.group_no_one"/>
+ parent="hr.menu_hr_configuration" sequence="1" groups="base.group_no_one,base.group_hr_manager"/>
<record id="hr_employee_normal_action_tree" model="ir.actions.act_window">
<field name="name">Employees</field>
<field name="name">hr.job.tree</field>
<field name="model">hr.job</field>
<field name="arch" type="xml">
- <tree string="Job">
+ <tree string="Job" fonts="bold:message_unread==True">
<field name="name"/>
<field name="department_id"/>
<field name="no_of_employee"/>
<field name="expected_employees"/>
<field name="no_of_hired_employee"/>
<field name="state"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<filter domain="[('state','=','open')]" string="In Position"/>
<filter domain="[('state','=','recruit')]" string="In Recruitment" name="in_recruitment"/>
<field name="department_id"/>
+ <separator/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Department" domain="[]" context="{'group_by':'department_id'}"/>
<filter string="Status" domain="[]" context="{'group_by':'state'}"/>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
</group>
</sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
</form>
</field>
</record>
<search string="Departments">
<field name="name" string="Department"/>
<field name="manager_id" />
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
</search>
</field>
</record>
</p>
</field>
</record>
- <menuitem action="open_module_tree_department" id="menu_hr_department_tree" parent="hr.menu_hr_configuration" sequence="5"/>
+ <menuitem action="open_module_tree_department" id="menu_hr_department_tree" parent="hr.menu_hr_configuration" sequence="5" groups="base.group_hr_manager,base.group_hr_user"/>
</data>
</openerp>
class res_users(osv.Model):
""" Update of res.users class
- - if adding groups to an user, check if base.group_user is in it
- (member of 'Employee'), create an employee form linked to it.
- """
+
+ - add field for the related employee of the user
+ - if adding groups to an user, check if base.group_user is in it (member of
+ 'Employee'), create an employee form linked to it. """
_name = 'res.users'
_inherit = ['res.users']
_columns = {
+ 'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
'display_employees_suggestions': fields.boolean("Display Employees Suggestions"),
}
</record>
<menuitem
- sequence="35" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" groups="base.group_hr_attendance" name="Attendance"/>
+ sequence="35" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" groups="base.group_hr_attendance,base.group_hr_manager" name="Attendance"/>
<menuitem action="open_view_attendance_reason" id="menu_open_view_attendance_reason" parent="hr.menu_open_view_attendance_reason_new_config" groups="base.group_hr_attendance"/>
<record id="hr_attendance_employee" model="ir.ui.view">
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+# Copyright (C) 2004-Today OpenERP S.A. (<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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
+
import time
from openerp.osv import fields, osv
res = {}
obj_contract = self.pool.get('hr.contract')
for emp in self.browse(cr, uid, ids, context=context):
- contract_ids = obj_contract.search(cr, uid, [('employee_id','=',emp.id),], order='date_start', context=context)
+ contract_ids = obj_contract.search(cr, uid, [('employee_id', '=', emp.id)], order='date_start', context=context)
if contract_ids:
res[emp.id] = contract_ids[-1:][0]
else:
'name': fields.char('Contract Type', required=True),
}
+
class hr_contract(osv.osv):
_name = 'hr.contract'
_description = 'Contract'
+ _inherit = ['mail.thread', 'ir.needaction_mixin']
+
+ _track = {
+ 'state': {
+ 'hr_contract.mt_contract_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending',
+ 'hr_contract.mt_contract_close': lambda self, cr, uid, obj, ctx=None: obj.state == 'close',
+ },
+ }
+
_columns = {
'name': fields.char('Contract Reference', required=True),
'employee_id': fields.many2one('hr.employee', "Employee", required=True),
- 'department_id': fields.related('employee_id','department_id', type='many2one', relation='hr.department', string="Department", readonly=True),
+ 'department_id': fields.many2one('hr.department', string="Department"),
'type_id': fields.many2one('hr.contract.type', "Contract Type", required=True),
'job_id': fields.many2one('hr.job', 'Job Title'),
'date_start': fields.date('Start Date', required=True),
'date_end': fields.date('End Date'),
'trial_date_start': fields.date('Trial Start Date'),
'trial_date_end': fields.date('Trial End Date'),
- 'working_hours': fields.many2one('resource.calendar','Working Schedule'),
- 'wage': fields.float('Wage', digits=(16,2), required=True, help="Basic Salary of the employee"),
+ 'working_hours': fields.many2one('resource.calendar', 'Working Schedule'),
+ 'wage': fields.float('Wage', digits=(16, 2), required=True, help="Basic Salary of the employee"),
'advantages': fields.text('Advantages'),
'notes': fields.text('Notes'),
'permit_no': fields.char('Work Permit No', required=False, readonly=False),
'visa_no': fields.char('Visa No', required=False, readonly=False),
'visa_expire': fields.date('Visa Expire Date'),
+ 'state': fields.selection(
+ [('draft', 'New'), ('open', 'Running'), ('pending', 'To Renew'), ('close', 'Expired')],
+ string='Status', track_visibility='onchange',
+ help='Status of the contract'),
}
def _get_type(self, cr, uid, context=None):
_defaults = {
'date_start': lambda *a: time.strftime("%Y-%m-%d"),
- 'type_id': _get_type
+ 'type_id': _get_type,
+ 'state': 'draft',
}
def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
if not employee_id:
- return {'value': {'job_id': False}}
+ return {'value': {'job_id': False, 'department_id': False}}
emp_obj = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context)
- job_id = False
+ job_id = dept_id = False
if emp_obj.job_id:
job_id = emp_obj.job_id.id
- return {'value': {'job_id': job_id}}
+ if emp_obj.department_id:
+ dept_id = emp_obj.department_id.id
+ return {'value': {'job_id': job_id, 'department_id': dept_id}}
def _check_dates(self, cr, uid, ids, context=None):
for contract in self.read(cr, uid, ids, ['date_start', 'date_end'], context=context):
- if contract['date_start'] and contract['date_end'] and contract['date_start'] > contract['date_end']:
- return False
+ if contract['date_start'] and contract['date_end'] and contract['date_start'] > contract['date_end']:
+ return False
return True
_constraints = [
(_check_dates, 'Error! Contract start-date must be less than contract end-date.', ['date_start', 'date_end'])
]
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+ def set_as_pending(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state': 'pending'}, context=context)
+
+ def set_as_close(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state': 'close'}, context=context)
<field name="name">Subcontractor</field>
</record>
+ <!-- Contract-related subtypes for messaging / Chatter -->
+ <record id="mt_contract_pending" model="mail.message.subtype">
+ <field name="name">To Renew</field>
+ <field name="res_model">hr.contract</field>
+ <field name="default" eval="True"/>
+ <field name="description">Contract about to expire</field>
+ </record>
+ <record id="mt_contract_close" model="mail.message.subtype">
+ <field name="name">Expired</field>
+ <field name="res_model">hr.contract</field>
+ <field name="default" eval="False"/>
+ <field name="description">Contract expired</field>
+ </record>
+ <!-- Department-related (parent) subtypes for messaging / Chatter -->
+ <record id="mt_department_contract_pending" model="mail.message.subtype">
+ <field name="name">Contract to Renew</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_contract_pending')"/>
+ <field name="relation_field">department_id</field>
+ <field name="description">Contract about to expire</field>
+ </record>
+
+ <!-- base action rule about "Expiring Soon" contracts -->
+ <record id="contract_open" model="ir.filters">
+ <field name="name">Open Contracts</field>
+ <field name="model_id">hr.contract</field>
+ <field name="domain">[('state', '=', 'open')]</field>
+ <field name="user_id" eval="False"/>
+ </record>
+ <record id="contract_set_as_pending" model="ir.actions.server">
+ <field name="name">HR Contract: set as pending</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="condition">True</field>
+ <field name="type">ir.actions.server</field>
+ <field name="state">code</field>
+ <field name="code">object.set_as_pending()</field>
+ </record>
+ <record id="rule_contract_1_set_as_pending" model="base.action.rule">
+ <field name="name">HR Contract: check for pending</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="sequence">50</field>
+ <field name="kind">on_time</field>
+ <field name="filter_id" ref="contract_open"/>
+ <field name="trg_date_id" ref="hr_contract.field_hr_contract_date_end"/>
+ <field name="trg_date_range">-7</field>
+ <field name="trg_date_range_type">day</field>
+ <field name="server_action_ids" eval="[(6, 0, [ref('contract_set_as_pending')])]"/>
+ </record>
+ <record id="rule_contract_2_set_as_pending" model="base.action.rule">
+ <field name="name">HR Contract: check for pending</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="sequence">51</field>
+ <field name="kind">on_time</field>
+ <field name="filter_id" ref="contract_open"/>
+ <field name="trg_date_id" ref="hr_contract.field_hr_contract_visa_expire"/>
+ <field name="trg_date_range">-60</field>
+ <field name="trg_date_range_type">day</field>
+ <field name="server_action_ids" eval="[(6, 0, [ref('contract_set_as_pending')])]"/>
+ </record>
+ <!-- base action rule about "Expired" contracts -->
+ <record id="contract_set_as_close" model="ir.actions.server">
+ <field name="name">HR Contract: set as close</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="condition">True</field>
+ <field name="type">ir.actions.server</field>
+ <field name="state">code</field>
+ <field name="code">object.set_as_close()</field>
+ </record>
+ <record id="rule_contract_3_set_as_close" model="base.action.rule">
+ <field name="name">HR Contract: check for close</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="sequence">52</field>
+ <field name="kind">on_time</field>
+ <field name="trg_date_id" ref="hr_contract.field_hr_contract_date_end"/>
+ <field name="trg_date_range">1</field>
+ <field name="trg_date_range_type">day</field>
+ <field name="server_action_ids" eval="[(6, 0, [ref('contract_set_as_close')])]"/>
+ </record>
+ <record id="rule_contract_4_set_as_close" model="base.action.rule">
+ <field name="name">HR Contract: check for close</field>
+ <field name="model_id" ref="model_hr_contract"/>
+ <field name="sequence">53</field>
+ <field name="kind">on_time</field>
+ <field name="trg_date_id" ref="hr_contract.field_hr_contract_visa_expire"/>
+ <field name="trg_date_range">1</field>
+ <field name="trg_date_range_type">day</field>
+ <field name="server_action_ids" eval="[(6, 0, [ref('contract_set_as_close')])]"/>
+ </record>
+
</data>
</openerp>
<field name="context">{'search_default_employee_id': [active_id], 'default_employee_id': active_id}</field>
</record>
- <menuitem id="next_id_56" name="Contract" parent="hr.menu_hr_configuration" sequence="30" groups="base.group_no_one"/>
+ <menuitem id="next_id_56" name="Contract" parent="hr.menu_hr_configuration" sequence="30" groups="base.group_no_one,base.group_hr_manager"/>
<record id="hr_hr_employee_view_form2" model="ir.ui.view">
<field name="name">hr.hr.employee.view.form2</field>
<field name="model">hr.employee</field>
<field name="model">hr.contract</field>
<field name="arch" type="xml">
<search string="Search Contract">
- <field name="name" string="Contracts"/>
- <field name="date_start"/>
- <field name="date_end"/>
- <field name="working_hours"/>
- <field name="employee_id"/>
- <group expand="0" string="Group By">
- <filter string="Employee" icon="terp-personal" domain="[]" context="{'group_by':'employee_id'}"/>
- <filter string="Working Schedule" icon="terp-go-week" domain="[]" context="{'group_by':'working_hours'}"/>
- <filter string="Job" icon="terp-gtk-select-all" domain="[]" context="{'group_by':'job_id'}"/>
- <filter string="Contract Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'type_id'}"/>
- </group>
- </search>
+ <field name="name" string="Contracts"/>
+ <field name="date_start"/>
+ <field name="date_end"/>
+ <field name="working_hours"/>
+ <field name="employee_id"/>
+ <field name="department_id"/>
+ <field name="state"/>
+ <filter string="To Renew" name="to_renew" domain="[('state', '=', 'pending')]"/>
+ <separator />
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
+ <group expand="0" string="Group By">
+ <filter string="Employee" domain="[]" context="{'group_by':'employee_id'}"/>
+ <filter string="Job" domain="[]" context="{'group_by':'job_id'}"/>
+ <filter string="Contract Type" domain="[]" context="{'group_by':'type_id'}"/>
+ </group>
+ </search>
</field>
</record>
<field name="model">hr.contract</field>
<field name="arch" type="xml">
<form string="Contract">
+ <header>
+ <field name="state" widget="statusbar" clickable="1"/>
+ </header>
<sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<field name="job_id"/>
</group>
<group>
+ <field name="department_id"/>
<field name="type_id"/>
</group>
</group>
</page>
</notebook>
</sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
</form>
</field>
</record>
<field name="name">hr.contract.view.tree</field>
<field name="model">hr.contract</field>
<field name="arch" type="xml">
- <tree string="Contracts">
+ <tree string="Contracts" fonts="bold:message_unread == True">
<field name="name"/>
<field name="employee_id"/>
<field name="type_id"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="wage" invisible="1"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+# Copyright (C) 2004-Today OpenERP S.A. (<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
class hr_evaluation(osv.Model):
_name = "hr_evaluation.evaluation"
- _inherit = "mail.thread"
+ _inherit = ['mail.thread']
_description = "Employee Appraisal"
_columns = {
'date': fields.date("Appraisal Deadline", required=True, select=True),
('wait', 'Plan In Progress'),
('progress', 'Waiting Appreciation'),
('done', 'Done'),
- ], 'Status', required=True, readonly=True, copy=False),
+ ], 'Status', required=True, readonly=True, track_visibility='onchange', copy=False),
'date_close': fields.date('Ending Date', select=True),
}
_defaults = {
class hr_evaluation_interview(osv.Model):
_name = 'hr.evaluation.interview'
- _inherit = 'mail.thread'
+ _inherit = ['mail.thread']
_rec_name = 'user_to_review_id'
_description = 'Appraisal Interview'
_columns = {
<field eval="'run_employee_evaluation'" name="function" />
<field eval="'(False,)'" name="args" />
</record>
- </data>
-
+ </data>
</openerp>
</record>
<menuitem name="Appraisal" parent="hr.menu_hr_root" id="menu_eval_hr" sequence="25"/>
- <menuitem name="Periodic Appraisal" parent="hr.menu_hr_configuration" id="menu_eval_hr_config" sequence="4"/>
+ <menuitem name="Periodic Appraisal" parent="hr.menu_hr_configuration" id="menu_eval_hr_config" sequence="4" groups="base.group_hr_manager"/>
<menuitem parent="hr.menu_hr_configuration" id="menu_open_view_hr_evaluation_plan_tree"
- action="open_view_hr_evaluation_plan_tree" sequence="15"/>
+ action="open_view_hr_evaluation_plan_tree" sequence="15" groups="base.group_hr_manager"/>
<record model="ir.ui.view" id="view_hr_evaluation_plan_phase_form">
<field name="name">hr_evaluation.plan.phase.form</field>
<field name="name">hr_evaluation.evaluation.tree</field>
<field name="model">hr_evaluation.evaluation</field>
<field name="arch" type="xml">
- <tree colors="blue:state == 'draft';black:state in ('wait','progress');gray:state in('done','cancel')" string="Appraisal">
+ <tree colors="blue:state == 'draft';black:state in ('wait','progress');gray:state in('done','cancel')" string="Appraisal"
+ fonts="bold: message_unread == True">
<field name="employee_id"/>
<field name="plan_id"/>
<field name="date"/>
<field name="rating"/>
<field name="state"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<field name="arch" type="xml">
<search string="Search Appraisal">
<field name="date"/>
+ <field name="employee_id"/>
+ <field name="plan_id"/>
<filter icon="terp-check" string="Pending" domain="[('state','=','wait')]" help="Appraisal that are in Plan In Progress state"/>
<filter icon="terp-camera_test" string="In progress" domain="[('state','=','progress')]" help="Appraisal that are in waiting appreciation state"/>
- <field name="employee_id" />
- <field name="plan_id"/>
+ <separator/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand='0' string='Group by...'>
<filter string='Employee' icon="terp-personal" domain="[]" context="{'group_by' : 'employee_id'}" />
<filter string='Plan' icon="terp-stock_align_left_24" domain="[]" context="{'group_by' : 'plan_id'}" />
<field name="name">hr_evaluation.interview.tree</field>
<field name="model">hr.evaluation.interview</field>
<field name="arch" type="xml">
- <tree string="Interview Appraisal">
+ <tree string="Interview Appraisal" fonts="bold: message_unread == True">
<field name="deadline" string="Deadline Date"/>
<field name="survey_id" domain="[('res_model','=','hr_evaluation')]"/>
<field name="user_id" string="Interviewer"/>
<button name="action_print_survey" string="Print Survey" type="object" icon="gtk-print" attrs="{'readonly':[('survey_id','=',False)]}"/>
<button name="%(mail.action_email_compose_message_wizard)d" string="Send Reminder Email" icon="terp-mail-message-new" type="action" states="waiting_answer"/>
<field name="state"/>
+ <field name="message_unread" invisible="1"/>
<button string="Send Request" name="survey_req_waiting_answer" states="draft" type="object" icon="gtk-yes" />
<button string="Done" name="survey_req_done" states="waiting_answer" type="object" icon="gtk-jump-to" />
</tree>
<field name="model">hr.evaluation.interview</field>
<field name="arch" type="xml">
<search string="Search Appraisal">
- <field name="deadline"/>
- <filter icon="terp-gtk-go-back-rtl" string="To Do" name="todo" domain="[('state','=','waiting_answer')]"/>
<field name="user_to_review_id"/>
<field name="user_id" string="Interviewer"/>
+ <field name="deadline"/>
+ <filter icon="terp-gtk-go-back-rtl" string="To Do" name="todo" domain="[('state','=','waiting_answer')]"/>
+ <separator/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
<filter string="Interviewer" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Survey" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'survey_id'}"/>
return user.company_id.currency_id.id
_name = "hr.expense.expense"
- _inherit = ['mail.thread']
+ _inherit = ['mail.thread', 'ir.needaction_mixin']
_description = "Expense"
_order = "id desc"
_track = {
'id': fields.integer('Sheet ID', readonly=True),
'date': fields.date('Date', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."),
+ 'employee_payable_account_id': fields.many2one('account.account', 'Employee Account', help="Employee payable account"),
'employee_id': fields.many2one('hr.employee', "Employee", required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'user_id': fields.many2one('res.users', 'User', required=True),
'date_confirm': fields.date('Confirmation Date', select=True, copy=False,
help="Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
'user_valid': fields.many2one('res.users', 'Validation By', readonly=True, copy=False,
states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
- 'account_move_id': fields.many2one('account.move', 'Ledger Posting', copy=False),
+ 'account_move_id': fields.many2one('account.move', 'Ledger Posting', copy=False, track_visibility="onchange"),
'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', copy=True,
readonly=True, states={'draft':[('readonly',False)]} ),
'note': fields.text('Note'),
def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
emp_obj = self.pool.get('hr.employee')
department_id = False
+ employee_payable_account_id = False
company_id = False
if employee_id:
employee = emp_obj.browse(cr, uid, employee_id, context=context)
department_id = employee.department_id.id
company_id = employee.company_id.id
- return {'value': {'department_id': department_id, 'company_id': company_id}}
+ if employee.address_home_id and employee.address_home_id.property_account_payable:
+ employee_payable_account_id = employee.address_home_id.property_account_payable.id
+ return {'value': {'department_id': department_id, 'company_id': company_id, 'employee_payable_account_id': employee_payable_account_id}}
def expense_confirm(self, cr, uid, ids, context=None):
for expense in self.browse(cr, uid, ids):
+ if not expense.line_ids:
+ raise osv.except_osv(_('Error!'),_('You cannot submit expense which has no expense line.'))
if expense.employee_id and expense.employee_id.parent_id.user_id:
self.message_subscribe_users(cr, uid, [expense.id], user_ids=[expense.employee_id.parent_id.user_id.id])
return self.write(cr, uid, ids, {'state': 'confirm', 'date_confirm': time.strftime('%Y-%m-%d')}, context=context)
'''
move_obj = self.pool.get('account.move')
for exp in self.browse(cr, uid, ids, context=context):
- if not exp.employee_id.address_home_id:
- raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
- if not exp.employee_id.address_home_id.property_account_payable.id:
- raise osv.except_osv(_('Error!'), _('The employee must have a payable account set on his home address.'))
+ if not exp.employee_payable_account_id:
+ raise osv.except_osv(_('Error!'), _('No employee account payable found for the expense '))
+
company_currency = exp.company_id.currency_id.id
- diff_currency_p = exp.currency_id.id <> company_currency
-
+ diff_currency_p = exp.currency_id.id != company_currency
+
#create the move that will contain the accounting entries
move_id = move_obj.create(cr, uid, self.account_move_get(cr, uid, exp.id, context=context), context=context)
-
+
#one account.move.line per expense line (+taxes..)
eml = self.move_line_get(cr, uid, exp.id, context=context)
-
+
#create one more move line, a counterline for the total on payable account
total, total_currency, eml = self.compute_expense_totals(cr, uid, exp, company_currency, exp.name, eml, context=context)
- acc = exp.employee_id.address_home_id.property_account_payable.id
+
+ acc = exp.employee_payable_account_id.id or False
eml.append({
'type': 'dest',
'name': '/',
})
#convert eml into an osv-valid format
- lines = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.employee_id.address_home_id, exp.date_confirm, context=context)), eml)
+ lines = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.employee_id.company_id.partner_id, exp.date_confirm, context=context)), eml)
journal_id = move_obj.browse(cr, uid, move_id, context).journal_id
# post the journal entry if 'Skip 'Draft' State for Manual Entries' is checked
if journal_id.entry_posted:
<record id="mt_expense_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="res_model">hr.expense.expense</field>
+ <field name="default" eval="False"/>
<field name="description">Expense approved</field>
</record>
<record id="mt_expense_refused" model="mail.message.subtype">
<field name="name">Refused</field>
<field name="res_model">hr.expense.expense</field>
+ <field name="default" eval="False"/>
<field name="description">Expense refused</field>
</record>
<record id="mt_expense_confirmed" model="mail.message.subtype">
<field name="name">To Approve</field>
<field name="res_model">hr.expense.expense</field>
<field name="description">Expense confirmed, waiting confirmation</field>
+ <field name="default" eval="True"/>
+ </record>
+ <!-- Department (Parent) related subtypes for messaging / Chatter -->
+ <record id="mt_department_expense_confirmed" model="mail.message.subtype">
+ <field name="name">Expenses To Approve</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_expense_confirmed')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="10"/>
</record>
</data>
<field name="name">hr.expense.expense.tree</field>
<field name="model">hr.expense.expense</field>
<field name="arch" type="xml">
- <tree string="Expenses" colors="blue:state=='draft'">
+ <tree string="Expenses" colors="blue:state=='draft'" fonts="bold: message_unread == True">
<field name="employee_id"/>
<field name="department_id" invisible="1"/>
<field name="date"/>
<field name="user_id" invisible="1"/>
- <field name="name"/>
+ <field name="name" string="Expense Sheet"/>
<field name="currency_id" groups="base.group_multi_currency"/>
<field name="amount" sum="Total Amount"/>
<field name="state"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<field name="employee_id"/>
<field name="date"/>
<field name="department_id"/>
- <field name="name"/>
+ <field name="name" string="Expense Sheet"/>
<field name="amount"/>
<field name="state"/>
<button name="confirm" states="draft" string="Confirm" type="workflow" icon="gtk-apply"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group>
- <field name="name"/>
+ <field name="name" string="Expense Sheet" placeholder="e.g. Business travel at Chicago"/>
<field name="user_valid" attrs="{'invisible': [('state','=','draft')]}" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_hr_user']}"/>
<field name="currency_id" groups="base.group_multi_currency" on_change="onchange_currency_id(currency_id, company_id)"/>
</group>
+ <group>
+ <field name="journal_id" widget="selection" attrs="{'readonly':[('state','=','done')]}" domain="[('type', '=', 'purchase')]" string="Journal" groups="account.group_account_user"/>
+ <field name="employee_payable_account_id" widget="selection" attrs="{'readonly':[('state','=','done')]}" domain="[('type', '=', 'payable')]" groups="account.group_account_user"/>
+ </group>
+ <group>
+ <label class="oe_grey" groups="account.group_account_user" string="If this field is empty, entries will be generated in the purchase journal."/>
+ </group>
</group>
<notebook>
- <page string="Description">
+ <page string="Expense Lines">
<field name="line_ids" context="{'currency_id': currency_id, 'default_analytic_account': context.get('analytic_account', '')}">
<form string="Expense Lines">
<group>
<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="name" string="Description"/>
<field name="ref"/>
<field domain="[('type','in',['normal','contract'])]" name="analytic_account" groups="analytic.group_analytic_accounting"/>
<field name="uom_id" on_change="onchange_uom(product_id, uom_id, context)"/>
</field>
<group>
<div>
- <separator string="Notes"/>
+ <separator string="Description"/>
<field name="note" placeholder="Free Notes"/>
</div>
<group class="oe_subtotal_footer oe_right">
</group>
</group>
</page>
- <page string="Accounting" groups="account.group_account_user">
- <group>
- <group string="Accounting Data">
- <field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/>
- <field name="account_move_id"/>
- </group>
- </group>
- </page>
</notebook>
</sheet>
<div class="oe_chatter">
<search string="Expense">
<field name="name" string="Expenses"/>
<field name="date"/>
- <filter icon="terp-document-new" domain="[('state','=','draft')]" string="New" help="New Expense"/>
- <filter icon="terp-camera_test" domain="[('state','=','confirm')]" string="To Approve" help="Confirmed Expenses"/>
- <filter icon="terp-dolar" domain="['|',('state','=','accepted'),('state','=','done')]" string="To Pay" help="Expenses to Invoice"/>
- <separator/>
- <filter domain="[('user_id', '=', uid)]" string="My Expenses"/>
<field name="employee_id"/>
<field name="department_id" string="Department" context="{'invisible_department': False}"/>
+ <filter domain="[('state','=','draft')]" string="New" help="New Expense"/>
+ <filter domain="[('state','=','confirm')]" string="To Approve" name="confirm" help="Confirmed Expenses"/>
+ <filter domain="[('state','=','accepted')]" string="To Pay" name="approved" help="Expenses to Invoice"/>
+ <separator />
+ <filter domain="[('user_id', '=', uid)]" string="My Expenses"/>
+ <separator />
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<group expand="0" string="Group By">
- <filter string="Employee" icon="terp-personal" domain="[]" context="{'group_by':'employee_id'}"/>
- <filter string="Department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id'}"/>
- <filter string="Expenses Month" icon="terp-go-month" domain="[]" context="{'group_by':'date'}" help="Expenses by Month"/>
+ <filter string="Employee" domain="[]" context="{'group_by':'employee_id'}"/>
+ <filter string="Department" domain="[]" context="{'group_by':'department_id'}"/>
+ <filter string="Expenses Month" domain="[]" context="{'group_by':'date'}" help="Expenses by Month"/>
</group>
</search>
</field>
</field>
</record>
-
<record id="view_product_hr_expense_form" model="ir.ui.view">
<field name="name">product.template.expense.form</field>
<field name="model">product.template</field>
</field>
</record>
- <menuitem id="menu_hr_product" name="Expense Categories" parent="hr.menu_hr_configuration" action="hr_expense_product"/>
+ <record id="action_approved_expense" model="ir.actions.act_window">
+ <field name="name">Expenses</field>
+ <field name="res_model">hr.expense.expense</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="view_expenses_tree"/>
+ <field name="domain">[('state','=','accepted')]</field>
+ </record>
+
+ <record id="action_request_approve_expense" model="ir.actions.act_window">
+ <field name="name">Expenses to Approve</field>
+ <field name="res_model">hr.expense.expense</field>
+ <field name="view_type">form</field>
+ <field name="context">{'search_default_confirm':1, 'needaction_menu_ref': 'hr_expense.menu_expense_all'}</field>
+ <field name="search_view_id" ref="view_hr_expense_filter"/>
+ <field name="view_id" ref="view_expenses_tree"/>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click to register new expenses.
+ </p><p>
+ OpenERP will ensure the whole process is followed; the expense
+ sheet is validated by manager(s), the employee is reimbursed
+ from his expenses, some expenses must be re-invoiced to the
+ customers.
+ </p>
+ </field>
+ </record>
+
+ <menuitem id="menu_expense_approved" parent="account.menu_finance_payables" action="action_approved_expense" sequence="101"/>
+ <menuitem id="menu_hr_product" name="Expense Categories" parent="hr.menu_hr_configuration" action="hr_expense_product" groups="base.group_hr_manager"/>
<menuitem id="next_id_49" name="Expenses" sequence="15" parent="hr.menu_hr_root"/>
<menuitem action="expense_all" id="menu_expense_all" name="Expenses" parent="next_id_49"/>
-
+ <menuitem action="action_request_approve_expense" id="menu_expense_to_approve" name="Expenses to Approve" parent="next_id_49" groups="base.group_hr_user"/>
+
</data>
</openerp>
#
##############################################################################
+
+import calendar
import datetime
+from datetime import date
import math
import time
from operator import attrgetter
_inherit = ['mail.thread', 'ir.needaction_mixin']
_track = {
'state': {
+ 'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm',
+ 'hr_holidays.mt_holidays_first_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate1',
'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate',
'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'refuse',
- 'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm',
},
}
- def _employee_get(self, cr, uid, context=None):
+ def _employee_get(self, cr, uid, context=None):
emp_id = context.get('default_employee_id', False)
if emp_id:
return emp_id
\nThe status is \'To Approve\', when holiday request is confirmed by user.\
\nThe status is \'Refused\', when holiday request is refused by manager.\
\nThe status is \'Approved\', when holiday request is approved by manager.'),
+ 'payslip_status': fields.boolean(string='Payslip Status',
+ help='Check this field when the leave has been taken into account in the payslip.'),
+ 'report_note': fields.text('Comment by Manager'),
'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True),
'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True, copy=False),
'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, copy=False),
'state': 'confirm',
'type': 'remove',
'user_id': lambda obj, cr, uid, context: uid,
- 'holiday_type': 'employee'
+ 'holiday_type': 'employee',
+ 'payslip_status': False,
}
_constraints = [
(_check_date, 'You can not have 2 leaves that overlaps on same day!', ['date_from','date_to']),
"""
Update the number_of_days.
"""
-
# date_to has to be greater than date_from
if (date_from and date_to) and (date_from > date_to):
raise osv.except_osv(_('Warning!'),_('The start date must be anterior to the end date.'))
result['value']['number_of_days_temp'] = round(math.floor(diff_day))+1
else:
result['value']['number_of_days_temp'] = 0
-
return result
def create(self, cr, uid, values, 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
- self.holidays_first_validate_notificate(cr, uid, ids, context=context)
- return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager})
+ return self.write(cr, uid, ids, {'state': 'validate1', 'manager_id': manager}, context=context)
def holidays_validate(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
- self.write(cr, uid, ids, {'state':'validate'})
+ self.write(cr, uid, ids, {'state': 'validate'}, context=context)
data_holiday = self.browse(cr, uid, ids)
for record in data_holiday:
if record.double_validation:
'Please verify also the leaves waiting for validation.'))
return True
- # -----------------------------
- # OpenChatter and notifications
- # -----------------------------
+ def set_payslip_status(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'payslip_status': True}, context=context)
- def _needaction_domain_get(self, cr, uid, context=None):
- 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 unset_payslip_status(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'payslip_status': False}, 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],
- _("Request approved, waiting second validation."), context=context)
class resource_calendar_leaves(osv.osv):
_inherit = "resource.calendar.leaves"
}
-
-class hr_employee(osv.osv):
- _inherit="hr.employee"
+class hr_employee(osv.Model):
+ _inherit = "hr.employee"
def create(self, cr, uid, vals, context=None):
# don't pass the value of remaining leave if it's 0 at the creation time, otherwise it will trigger the inverse
return result
def _leaves_count(self, cr, uid, ids, field_name, arg, context=None):
+ res = {}
Holidays = self.pool['hr.holidays']
- return {
- employee_id: Holidays.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
- for employee_id in ids
- }
+ date_begin = date.today().replace(day=1)
+ date_end = date_begin.replace(day=calendar.monthrange(date_begin.year, date_begin.month)[1])
+ for employee_id in ids:
+ leaves = Holidays.search_count(cr, uid, [('employee_id', '=', employee_id), ('type', '=', 'remove')], context=context)
+ approved_leaves = Holidays.search_count(cr, uid, [('employee_id', '=', employee_id), ('type', '=', 'remove'), ('date_from', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('date_from', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('state', '=', 'validate'), ('payslip_status', '=', False)], context=context)
+ res[employee_id] = {'leaves_count': leaves, 'approved_leaves_count': approved_leaves}
+ return res
_columns = {
'remaining_leaves': fields.function(_get_remaining_days, string='Remaining Legal Leaves', fnct_inv=_set_remaining_days, type="float", help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. Total based on all the leave types without overriding limit.'),
- 'current_leave_state': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
+ 'current_leave_state': fields.function(
+ _get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
selection=[('draft', 'New'), ('confirm', 'Waiting Approval'), ('refuse', 'Refused'),
- ('validate1', 'Waiting Second Approval'), ('validate', 'Approved'), ('cancel', 'Cancelled')]),
- 'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type",type='many2one', relation='hr.holidays.status'),
+ ('validate1', 'Waiting Second Approval'), ('validate', 'Approved'), ('cancel', 'Cancelled')]),
+ 'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type", type='many2one', relation='hr.holidays.status'),
'leave_date_from': fields.function(_get_leave_status, multi='leave_status', type='date', string='From Date'),
'leave_date_to': fields.function(_get_leave_status, multi='leave_status', type='date', string='To Date'),
- 'leaves_count': fields.function(_leaves_count, type='integer', string='Leaves'),
-
+ 'leaves_count': fields.function(_leaves_count, multi='_leaves_count', type='integer', string='Number of Leaves (current month)'),
+ 'approved_leaves_count': fields.function(_leaves_count, multi='_leaves_count', type='integer', string='Approved Leaves not in Payslip', help="These leaves are approved but not taken into account for payslip"),
}
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
+
<!-- After installation of the module, open the related menu -->
<record id="base.open_menu" model="ir.actions.todo">
<field name="action_id" ref="hr.action_client_hr_menu"/>
<!-- Holidays-related subtypes for messaging / Chatter -->
<record id="mt_holidays_confirmed" model="mail.message.subtype">
- <field name="name">To Approve</field>
+ <field name="name">Confirmed</field>
+ <field name="res_model">hr.holidays</field>
+ <field name="description">Request created and waiting confirmation</field>
+ </record>
+ <record id="mt_holidays_first_validated" model="mail.message.subtype">
+ <field name="name">Waiting Second Validation</field>
<field name="res_model">hr.holidays</field>
- <field name="description">Request confirmed and waiting approval</field>
+ <field name="default" eval="False"/>
+ <field name="description">Request validated, waiting second validation</field>
</record>
<record id="mt_holidays_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="description">Request refused</field>
</record>
+ <!-- Department related subtypes for messaging / Chatter -->
+ <record id="mt_department_holidays_confirmed" model="mail.message.subtype">
+ <field name="name">Leaves/Allocations: first approval</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_holidays_confirmed')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="6"/>
+ </record>
+ <record id="mt_department_holidays_first_validated" model="mail.message.subtype">
+ <field name="name">Leaves/Allocations: second approval</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_holidays_first_validated')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="7"/>
+ </record>
+ <record id="mt_department_holidays_approved" model="mail.message.subtype">
+ <field name="name">Leaves/Allocation Approved</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_holidays_approved')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="8"/>
+ </record>
+ <record id="mt_department_holidays_refused" model="mail.message.subtype">
+ <field name="name">Leaves/Allocation Refused</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_holidays_refused')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="9"/>
+ </record>
+
</data>
</openerp>
<field name="arch" type="xml">
<search string="Search Leave">
<field name="name"/>
+ <filter domain="[('state','=','draft')]" string="To Confirm"/>
+ <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
+ <filter domain="[('state','=','validate')]" string="Validated" name="validated"/>
<separator/>
- <filter icon="terp-check" domain="[('state','=','draft')]" string="To Confirm"/>
- <filter icon="terp-camera_test" domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
- <filter icon="terp-camera_test" domain="[('state','=','validate')]" string="Validated" name="validated"/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
+ <separator/>
+ <filter string="Approved Leaves" name="validated" domain="[('state','=','validate')]"/>
+ <separator/>
+ <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="base.group_hr_manager"/>
<separator/>
<filter icon="terp-go-year" name="year" string="Year" domain="[('holiday_status_id.active','=',True)]" help="Filters only on allocations and requests that belong to an holiday type that is 'active' (active field is True)"/>
<separator/>
<field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" on_change="onchange_employee(employee_id)" groups="base.group_hr_user"/>
<field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('type', '=', 'remove'),('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','=','employee')]}"/>
<field name="department_id" attrs="{'readonly':['|', ('type','=','add'),('holiday_type','=','category')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/>
+ <field name="payslip_status" groups="base.group_hr_manager" attrs="{'invisible':[('type','=','add')]}"/>
</group>
+ <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..." attrs="{'invisible': [('type', '=', 'remove')]}"/>
+ <div groups="base.group_hr_manager" attrs="{'invisible':[('type','=','add')]}">
+ <separator string="Comment by Manager"/>
+ <field name="report_note" placeholder="e.g. Report to the next month..."/>
+ </div>
</group>
- <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..." attrs="{'invisible': [('type', '=', 'remove')]}"/>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="name">hr.holidays.allocation.tree</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
- <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests">
+ <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests"
+ fonts="bold: message_unread == True">
<field name="employee_id"/>
<field name="holiday_type"/>
<field name="category_id"/>
<field name="manager_id" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="date_from" invisible="1"/>
- <!--field name="type"/-->
+ <field name="message_unread" invisible="1"/>
<field name="state"/>
</tree>
</field>
</record>
-
- <!-- Holidays: Leaves Management -->
- <record model="ir.ui.view" id="allocation_company_new">
- <field name="name">Leaves Management</field>
+ <record model="ir.ui.view" id="view_holiday_allocation_tree_customize">
+ <field name="name">hr.holidays.allocation.tree.customize</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
- <form string="Leaves Management">
- <header>
- <button string="Submit to Manager" name="confirm" states="draft" type="workflow" icon="gtk-yes"/>
- <button string="Approve" name="validate" states="confirm" type="workflow" icon="gtk-apply"/>
- <button string="Refuse" name="refuse" states="confirm,validate,draft" type="workflow" icon="gtk-no"/>
- <button string="Reset to Draft" name="reset" states="confirm" type="workflow" groups="base.group_hr_manager"/>
- <field name="state"/>
- </header>
- <group col="4">
- <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')]}"/>
- <field name="date_to" on_change="onchange_date_from(date_to, date_from)" attrs="{'readonly':[('type','=','add')], 'required':[('type','=','remove')]}"/>
- <field name="number_of_days_temp"/>
- <field name="manager_id"/>
- </group>
- <field name="name" placeholder="Add a reason..."/>
- </form>
+ <tree string="Allocation Requests" editable="top">
+ <field name="employee_id"/>
+ <field name="holiday_type"/>
+ <field name="holiday_status_id"/>
+ <field name="name" readonly="1"/>
+ <field name="date_from" required="1" on_change="onchange_date_from(date_to, date_from)"/>
+ <field name="date_to" required="1" on_change="onchange_date_to(date_to, date_from)"/>
+ <field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
+ <field name="state"/>
+ <field name="report_note" groups="base.group_hr_manager"/>
+ <field name="payslip_status" invisible="1"/>
+ <button string="To Report in Payslip" name="set_payslip_status"
+ type="object" class="oe_link oe_right" icon="gtk-normal"
+ attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
+ <button string="Reported in last payslips" name="unset_payslip_status"
+ type="object" class="oe_link oe_right" icon="gtk-yes"
+ attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
+ </tree>
</field>
</record>
<field name="model">hr.holidays</field>
<field name="priority">20</field>
<field name="arch" type="xml">
- <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leaves Summary">
+ <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"/>
</field>
</record>
+ <record model="ir.ui.view" id="view_holiday_employee">
+ <field name="name">hr.holidays.report_employee_tree</field>
+ <field name="model">hr.holidays</field>
+ <field name="priority">21</field>
+ <field name="arch" type="xml">
+ <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Employee's Leave">
+ <field name="employee_id"/>
+ <field name="type"/>
+ <field name="name"/>
+ <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="holiday_status_id"/>
+ <field name="state"/>
+ <field name="report_note"/>
+ <field name="payslip_status" invisible="1"/>
+ <button string="To Report in Payslip" name="set_payslip_status"
+ type="object" class="oe_link oe_right"
+ attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
+ <button string="Reported in last payslips" name="unset_payslip_status"
+ type="object" class="oe_link oe_right"
+ attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
+ </tree>
+ </field>
+ </record>
+
<record model="ir.ui.view" id="view_holiday">
<field name="name">hr.holidays.tree</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
- <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests">
+ <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests"
+ fonts="bold: message_unread == True">
<field name="employee_id"/>
<field name="holiday_type" string="Mode" groups="base.group_no_one"/>
<field name="holiday_status_id"/>
<field name="date_to"/>
<field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
<field name="state"/>
+ <field name="payslip_status" invisible="1"/>
+ <button string="To Report in Payslip" name="set_payslip_status"
+ type="object" class="oe_link oe_right"
+ attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
+ <button string="Reported in last payslips" name="unset_payslip_status"
+ type="object" class="oe_link oe_right"
+ attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
<field name="category_id" invisible="1"/>
<field name="department_id" invisible="not context.get('set_visible',False)"/>
<field name="manager_id" invisible="1"/>
<field name="user_id" invisible="1"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
<field name="res_model">hr.holidays</field>
<field name="view_type">form</field>
<field name="view_id" ref="edit_holiday_new"/>
- <field name="context">{'default_type': 'remove', 'search_default_my_leaves':1}</field>
- <field name="domain">[('type','=','remove')]</field>
+ <field name="context">{
+ 'default_type': 'remove',
+ 'search_default_my_leaves':1,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_company_allocation',
+ ]
+ }</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">
<field name="name">Requests to Approve</field>
<field name="res_model">hr.holidays</field>
<field name="view_type">form</field>
- <field name="context">{'default_type': 'remove', 'search_default_approve':1}</field>
+ <field name="context">{
+ 'default_type': 'remove',
+ 'search_default_approve':1,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_ask_holidays_new',
+ 'hr_holidays.menu_open_company_allocation',
+ 'hr_holidays.menu_open_employee_leave',
+ ]
+ }</field>
<field name="domain">[('type','=','remove')]</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
<field name="name">Allocation Requests</field>
<field name="res_model">hr.holidays</field>
<field name="view_type">form</field>
- <field name="context">{'default_type':'add', 'search_default_my_leaves':1}</field>
- <field name="domain">[('type','=','add')]</field>
+ <field name="context">{
+ 'default_type':'add',
+ 'search_default_my_leaves':1,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_company_allocation',
+ ]
+ }</field>
+ <field name="domain">[('type','=','add'), ('employee_id.user_id','=', uid)]</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</record>
<field name="name">Allocation Requests to Approve</field>
<field name="res_model">hr.holidays</field>
<field name="view_type">form</field>
- <field name="context">{'default_type': 'add', 'search_default_approve':1}</field>
+ <field name="context">{
+ 'default_type': 'add',
+ 'search_default_approve':1,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_allocation_holidays',
+ 'hr_holidays.menu_open_company_allocation'
+ ]
+ }</field>
<field name="domain">[('type','=','add')]</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" eval="view_holiday_simple"/>
- <field name="context">{'search_default_group_type': 1}</field>
- <field name="domain">[('holiday_type','=','employee'), ('state', '!=', 'refuse')]</field>
+ <field name="context">{
+ 'search_default_group_type': 1,
+ 'search_default_year': 1 ,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_ask_holidays_new',
+ 'hr_holidays.menu_request_approve_holidays',
+ 'hr_holidays.menu_open_allocation_holidays',
+ 'hr_holidays.menu_request_approve_allocation',
+ 'hr_holidays.menu_open_employee_leave',
+ ]
+ }</field>
+ <field name="domain">[('holiday_type','=','employee')]</field>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</record>
<menuitem name="Leaves Summary" parent="menu_open_ask_holidays" id="menu_open_company_allocation" action="open_company_allocation" sequence="40"/>
+
+ <record model="ir.actions.act_window" id="open_employee_leaves">
+ <field name="name">Employee's Leaves</field>
+ <field name="res_model">hr.holidays</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" eval="view_holiday_employee"/>
+ <field name="context">{'default_type': 'remove', 'search_default_gray': 1, 'search_default_year': 1, 'search_default_group_employee': 1}</field>
+ <field name="domain">[('type','=','remove')]</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <menuitem name="Employee's Leaves" parent="menu_open_ask_holidays" id="menu_open_employee_leave" groups="base.group_hr_manager,base.group_hr_user" action="open_employee_leaves" sequence="41"/>
<!-- Holidays status -->
<record id="view_holidays_status_filter" model="ir.ui.view">
<field name="search_view_id" ref="view_hr_holidays_status_search"/>
</record>
- <menuitem sequence="3" id="hr.menu_open_view_attendance_reason_config" parent="hr.menu_hr_configuration" name="Leaves"/>
- <menuitem name="Leaves Types" action="open_view_holiday_status" id="menu_open_view_holiday_status" parent="hr.menu_hr_configuration" sequence="10"/>
+ <menuitem sequence="3" id="hr.menu_open_view_attendance_reason_config" parent="hr.menu_hr_configuration" name="Leaves" groups="base.group_hr_manager"/>
+ <menuitem name="Leaves Types" action="open_view_holiday_status" id="menu_open_view_holiday_status" parent="hr.menu_hr_configuration" sequence="10" groups="base.group_hr_manager"/>
<!-- Holiday on resource leave -->
<record id="resource_calendar_leave_form_inherit" model="ir.ui.view">
<field name="domain">[('type','=','remove')]</field>
<field name="view_id" eval="view_holiday"/>
</record>
+
+ <record id="act_hr_employee_holiday_request_approved" model="ir.actions.act_window">
+ <field name="name">Leaves to be reported in Payslip</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.holidays</field>
+ <field name="src_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{'search_default_employee_id': [active_id], 'search_default_validated': True, 'search_default_gray': True}</field>
+ <field name="domain">[('date_from','>=', context_today().strftime("%Y-%m-1")), ('date_from','<', ((context_today() + relativedelta(months=1)).strftime('%Y-%m-1')) )]</field>
+ <field name="view_id" eval="view_holiday_allocation_tree_customize"/>
+ </record>
<!-- Assing leave -->
<record id="hr_holidays_leaves_assign_tree_view" model="ir.ui.view">
</group>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
+ <button name="%(act_hr_employee_holiday_request_approved)d"
+ icon="fa-calendar"
+ class="oe_stat_button"
+ type="action"
+ groups="base.group_hr_user">
+ <field name="approved_leaves_count" widget="statinfo"/>
+ </button>
<button name="%(act_hr_employee_holiday_request)d"
- type="action"
- class="oe_stat_button"
- icon="fa-calendar"
- groups="base.group_hr_user">
- <field name="leaves_count" widget="statinfo" string="Leaves"/>
+ type="action"
+ class="oe_stat_button"
+ icon="fa-calendar"
+ groups="base.group_hr_user">
+ <field name="leaves_count" widget="statinfo"/>
</button>
</xpath>
</field>
_inherit = ['mail.thread', 'ir.needaction_mixin']
_track = {
+ 'emp_id': {
+ 'hr_recruitment.mt_applicant_hired': lambda self, cr, uid, obj, ctx=None: obj.emp_id,
+ },
'stage_id': {
# this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages
'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1,
'day_close': fields.function(_compute_day, string='Days to Close', \
multi='day_close', type="float", store=True),
'color': fields.integer('Color Index'),
- 'emp_id': fields.many2one('hr.employee', string='Employee', help='Employee linked to the applicant.'),
+ 'emp_id': fields.many2one('hr.employee', string='Employee', track_visibility='onchange', help='Employee linked to the applicant.'),
'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
'attachment_number': fields.function(_get_attachment_number, string='Number of Attachments', type="integer"),
}
'hr.applicant', self._columns['alias_id'], 'name', alias_prefix='job+', alias_defaults={'job_id': 'id'}, context=context)
def create(self, cr, uid, vals, context=None):
+ # TDE note: shouldn't it be in mail_create_nolog ?
alias_context = dict(context, alias_model_name='hr.applicant', alias_parent_model_name=self._name)
job_id = super(hr_job, self).create(cr, uid, vals, context=alias_context)
job = self.browse(cr, uid, job_id, context=context)
_columns = {
'name': fields.char('Name', required=True, translate=True),
}
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<field name="name">New Applicant</field>
<field name="res_model">hr.applicant</field>
<field name="default" eval="False"/>
+ <field name="hidden" eval="True"/>
<field name="description">Applicant created</field>
</record>
<record id="mt_applicant_stage_changed" model="mail.message.subtype">
<record id="mt_applicant_hired" model="mail.message.subtype">
<field name="name">Applicant Hired</field>
<field name="res_model">hr.applicant</field>
- <field name="default" eval="False"/>
+ <field name="default" eval="True"/>
<field name="description">Applicant hired</field>
</record>
+
<!-- Job-related subtypes for messaging / Chatter -->
<record id="mt_job_applicant_new" model="mail.message.subtype">
<field name="name">Applicant Created</field>
<record id="mt_job_applicant_stage_changed" model="mail.message.subtype">
<field name="name">Applicant Stage Changed</field>
<field name="res_model">hr.job</field>
- <field name="default" eval="True"/>
+ <field name="default" eval="False"/>
<field name="parent_id" eval="ref('mt_applicant_stage_changed')"/>
<field name="relation_field">job_id</field>
</record>
<menuitem name="Recruitment"
id="menu_hr_recruitment_recruitment"
parent="hr.menu_hr_configuration"
+ groups="base.group_hr_manager"
sequence="40"/>
<act_window
I check that Invoice is created for this timesheet.
-
!python {model: account.analytic.line}: |
+ from openerp.tools import float_compare
aline = self.browse(cr, uid, ref('account_analytic_line_developyamlforhrmodule0'))
analytic_account_obj = self.pool.get('account.analytic.account')
data = self.pool.get('hr.timesheet.invoice.create').read(cr, uid, [ref("hr_timesheet_invoice_create_0")], [], context)[0]
assert product == product_exp
assert aline.invoice_id, "Invoice created, but analytic line wasn't updated."
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
- assert invoice_id.amount_untaxed == 187.5, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
- assert invoice_id.amount_tax == 50, "Invoice tax mismatch: %s" % invoice_id.amount_tax
+ assert float_compare(invoice_id.amount_untaxed, 187.5, precision_digits=2) == 0, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
+ assert float_compare(invoice_id.amount_tax, 50, precision_digits=2) == 0, "Invoice tax mismatch: %s" % invoice_id.amount_tax
I check that Invoice is created for this timesheet.
-
!python {model: account.analytic.line}: |
+ from openerp.tools import float_compare
aline = self.browse(cr, uid, ref('account_analytic_line_developyamlforhrmodule1'))
analytic_account_obj = self.pool.get('account.analytic.account')
data = self.pool.get('hr.timesheet.invoice.create').read(cr, uid, [ref("hr_timesheet_invoice_create_0")], [], context)[0]
assert product == product_exp
assert aline.invoice_id, "Invoice created, but analytic line wasn't updated."
assert aline.invoice_id == invoice_id, "Invoice doesn't match the one at analytic line"
- assert invoice_id.amount_untaxed == 187.5, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
- assert invoice_id.amount_tax == 40, "Invoice tax mismatch: %s" % invoice_id.amount_tax
+ assert float_compare(invoice_id.amount_untaxed, 187.5, precision_digits=2) == 0, "Invoice amount mismatch: %s" % invoice_id.amount_untaxed
+ assert float_compare(invoice_id.amount_tax, 40, precision_digits=2) == 0, "Invoice tax mismatch: %s" % invoice_id.amount_tax
class hr_timesheet_sheet(osv.osv):
_name = "hr_timesheet_sheet.sheet"
- _inherit = "mail.thread"
+ _inherit = ['mail.thread', 'ir.needaction_mixin']
_table = 'hr_timesheet_sheet_sheet'
_order = "id desc"
- _description="Timesheet"
+ _description = "Timesheet"
+
+ _track = {
+ 'state': {
+ 'hr_timesheet_sheet.mt_timesheet_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm',
+ 'hr_timesheet_sheet.mt_timesheet_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'done',
+ },
+ }
def _total(self, cr, uid, ids, name, args, context=None):
""" Compute the attendances, analytic lines timesheets and differences between them
('draft','Open'),
('confirm','Waiting Approval'),
('done','Approved')], 'Status', select=True, required=True, readonly=True,
+ track_visibility='onchange',
help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed timesheet. \
\n* The \'Confirmed\' status is used for to confirm the timesheet by user. \
\n* The \'Done\' status is used when users timesheet is accepted by his/her senior.'),
<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"/>
+ <!-- Timesheet sheet related subtypes for messaging / Chatter -->
+ <record id="mt_timesheet_confirmed" model="mail.message.subtype">
+ <field name="name">Waiting Approval</field>
+ <field name="res_model">hr_timesheet_sheet.sheet</field>
+ <field name="default" eval="True"/>
+ <field name="description">waiting approval</field>
+ </record>
+ <record id="mt_timesheet_approved" model="mail.message.subtype">
+ <field name="name">Approved</field>
+ <field name="res_model">hr_timesheet_sheet.sheet</field>
+ <field name="default" eval="True"/>
+ <field name="description">Timesheet approved</field>
+ </record>
+ <!-- Department (Parent) related subtypes for messaging / Chatter -->
+ <record id="mt_department_timesheet_confirmed" model="mail.message.subtype">
+ <field name="name">Timesheets to Approve</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_timesheet_confirmed')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="5"/>
+ </record>
+ <record id="mt_department_timesheet_approved" model="mail.message.subtype">
+ <field name="name">Timesheets Approved</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_timesheet_approved')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="5"/>
+ </record>
+
</data>
</openerp>
</notebook>
</sheet>
<div class="oe_chatter">
- <field name="message_ids" widget="mail_thread"/>
<field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
<field name="date_from"/>
<filter name="new" string="In Draft" domain="[('state','in',('draft', 'new'))]" help="Unvalidated Timesheets"/>
<filter name="to_approve" string="To Approve" domain="[('state','=','confirm')]" help="Confirmed Timesheets"/>
+ <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<field name="employee_id"/>
<field name="department_id"/>
<group expand="0" string="Group By">
</record>
<record id="act_hr_timesheet_sheet_form" model="ir.actions.act_window">
- <field name="name">Timesheets to Validate</field>
+ <field name="name">Timesheets to Approve</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hr_timesheet_sheet.sheet</field>
<field name="view_type">form</field>
</record>
<menuitem action="act_hr_timesheet_sheet_form" id="menu_act_hr_timesheet_sheet_form" parent="hr_attendance.menu_hr_time_tracking"
- sequence="2" groups="base.group_hr_user"/>
+ sequence="11" groups="base.group_hr_user"/>
<!--
Company inheritancy
<field name="model">hr_timesheet_sheet.sheet</field>
<field eval="10" name="priority"/>
<field name="arch" type="xml">
- <tree colors="blue:state == 'draft';black:state in ('confirm','new');gray:state == 'done'" string="Timesheets">
+ <tree colors="blue:state == 'draft';black:state in ('confirm','new');gray:state == 'done'" fonts="bold: message_unread == True" string="Timesheets">
<field name="employee_id"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="total_timesheet" widget="float_time"/>
<field name="total_difference" widget="float_time" groups="base.group_hr_attendance"/>
<field name="state"/>
+ <field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
If required, you can manually adjust the descriptions via the CODA configuration menu.
''',
'images': ['images/coda_logs.jpeg', 'images/import_coda_logs.jpeg'],
- 'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba'],
+ 'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba', 'account_bank_statement_import'],
'demo': ['l10n_be_coda_demo.xml'],
'data': [
- 'l10n_be_coda_wizard.xml',
'l10n_be_coda_view.xml',
],
'auto_install': False,
</record>
<menuitem name="Bank Statement Lines" parent="account.menu_finance_bank_and_cash" id="menu_account_bank_statement_line_coda" action="action_account_bank_statement_line_coda" sequence="8" groups="base.group_no_one"/>
- <menuitem name="Import CODA File" parent="account.menu_finance_bank_and_cash" id="menu_account_coda_import" action="action_account_coda_import" sequence="10"/>
</data>
</openerp>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" ?>
-<openerp>
- <data>
-
- <record id="account_coda_import_view" model="ir.ui.view">
- <field name="name">Import CODA File</field>
- <field name="model">account.coda.import</field>
- <field name="priority">1</field>
- <field name="arch" type="xml">
- <form string="Import CODA File">
- <group col="2">
- <field name="coda_data" filename="coda_fname"/>
- </group>
- <footer>
- <button name="coda_parsing" string="_Import" type="object" class="oe_highlight"/>
- or
- <button string="Cancel" class="oe_link" special="cancel"/>
- </footer>
- </form>
- </field>
- </record>
-
- <record id="action_account_coda_import" model="ir.actions.act_window">
- <field name="name">Import CODA File</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">account.coda.import</field>
- <field name="view_type">form</field>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- <field name="view_id" ref="account_coda_import_view"/>
- </record>
-
- </data>
-</openerp>
+++ /dev/null
-0000011011172505 00178299 DE MEYER LUC KREDBEBB 00820512013 00000 2\r
-12135BE33737018595246 EUR0000000011812700270710NOVIAT NV KBC-Business Comfortrekening 003\r
-2100010000OL44483FW SCTOFBIONLO1000000000435000110111001010000MEDEDELING 11011113501 0\r
-2200010000 GKCCBEBB 1 0\r
-2300010000BE41063012345610 PARTNER 1 0 1\r
-3100010001OL44483FW SCTOFBIONLO001010001001PARTNER 1 0 0\r
-2100020000OL4414AC8BOVSOVSOVERS0000000003044450110111001500001101240283842818 11011113501 0\r
-2200020000 BBRUBEBB 1 0\r
-2300020000BE61310126985517 PARTNER 2 0 1\r
-3100020001OL4414AC8BOVSOVSOVERS001500001001PARTNER 2 1 0\r
-3200020001MOLENSTRAAT 60 9340 LEDE 0 0\r
-2100030000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0\r
-2100030001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0\r
-2100030002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0\r
-2100040000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0\r
-2100040001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0\r
-2100040002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0\r
-2100050000AOGM00160BSCTOBOGOVER0000000000063740110111001500000TERUGGAVE 37232481 8400083296 . 11011113501 0\r
-2200050000 362/363 KREDBEBB 1 0\r
-2300050000BE43730004200601 KBC VERZEKERINGEN NV 0 1\r
-3100050001AOGM00160BSCTOBOGOVER001500001001KBC VERZEKERINGEN NV 1 0\r
-3200050001VAN OVERSTRAETENPLEIN 2 3000 LEUVEN 0 0\r
-8135BE44734024486445 EUR0000000013527810110111 0\r
-9 000022000000001393080000000003108190 2\r
--- /dev/null
+0000011011172505 00178299 DE MEYER LUC KREDBEBB 00820512013 00000 2\r
+12135BE33737018595246 EUR0000000011812700270710NOVIAT NV KBC-Business Comfortrekening 003\r
+2100010000OL44483FW SCTOFBIONLO1000000000435000110111001010000MEDEDELING 11011113501 0\r
+2200010000 GKCCBEBB 1 0\r
+2300010000BE41063012345610 PARTNER 1 0 1\r
+3100010001OL44483FW SCTOFBIONLO001010001001PARTNER 1 0 0\r
+2100020000OL4414AC8BOVSOVSOVERS0000000003044450110111001500001101240283842818 11011113501 0\r
+2200020000 BBRUBEBB 1 0\r
+2300020000BE61310126985517 PARTNER 2 0 1\r
+3100020001OL4414AC8BOVSOVSOVERS001500001001PARTNER 2 1 0\r
+3200020001MOLENSTRAAT 60 9340 LEDE 0 0\r
+2100030000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0\r
+2100030001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0\r
+2100030002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0\r
+2100040000AFECA0CVA IKLINNINNIG1000000000479040110111313410000 KBC-INVESTERINGSKREDIET 737-6543210-21 11011113510 0\r
+2100040001AFECA0CVA IKLINNINNIG1000000000419920110111813410660 11011113500 0\r
+2100040002AFECA0CVA IKLINNINNIG1000000000059120110111813410020 11011113510 0\r
+2100050000AOGM00160BSCTOBOGOVER0000000000063740110111001500000TERUGGAVE 37232481 8400083296 . 11011113501 0\r
+2200050000 362/363 KREDBEBB 1 0\r
+2300050000BE43730004200601 KBC VERZEKERINGEN NV 0 1\r
+3100050001AOGM00160BSCTOBOGOVER001500001001KBC VERZEKERINGEN NV 1 0\r
+3200050001VAN OVERSTRAETENPLEIN 2 3000 LEUVEN 0 0\r
+8135BE44734024486445 EUR0000000013527810110111 0\r
+9 000022000000001393080000000003108190 2\r
--- /dev/null
+from . import test_import_bank_statement
+checks = [
+ test_import_bank_statement
+]
+
--- /dev/null
+from openerp.tests.common import TransactionCase
+from openerp.modules.module import get_module_resource
+
+class TestCodaFile(TransactionCase):
+ """Tests for import bank statement coda file format (account.bank.statement.import)
+ """
+
+ def setUp(self):
+ super(TestCodaFile, self).setUp()
+ self.statement_import_model = self.registry('account.bank.statement.import')
+ self.bank_statement_model = self.registry('account.bank.statement')
+
+ def test_coda_file_import(self):
+ cr, uid = self.cr, self.uid
+ bank_temp_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'account', 'conf_bnk')
+ partner_id_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'main_partner')
+ company_id_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'base', 'main_company')
+ self.bank_temp_id = bank_temp_ref and bank_temp_ref[1] or False
+ self.partner_id = partner_id_ref and partner_id_ref[1] or False
+ self.company_id = company_id_ref and company_id_ref[1] or False
+ coda_file_path = get_module_resource('l10n_be_coda', 'test_coda_file', 'Ontvangen_CODA.2013-01-11-18.59.15.txt')
+ coda_file = open(coda_file_path, 'rb').read().encode('base64')
+ bank_account_id = self.registry('res.partner.bank').create(cr, uid, dict(
+ state = 'bank',
+ acc_number = 'BE33737018595246',
+ bank_name = 'Reserve',
+ partner_id = self.partner_id,
+ company_id = self.company_id
+ ))
+ bank_statement_id = self.statement_import_model.create(cr, uid, dict(
+ file_type = 'coda',
+ data_file = coda_file,
+ ))
+ self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
+ statement_id = self.bank_statement_model.search(cr, uid, [('name', '=', '135')])[0]
+ bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)
+ self.assertEquals(bank_st_record.balance_start, 11812.70)
+ self.assertEquals(bank_st_record.balance_end_real, 13527.81)
+
import base64
import time
-from openerp.osv import fields, osv
+from openerp.osv import osv
from openerp.tools.translate import _
from openerp import tools
_logger = logging.getLogger(__name__)
-class account_coda_import(osv.osv_memory):
- _name = 'account.coda.import'
- _description = 'Import CODA File'
- _columns = {
- 'coda_data': fields.binary('CODA File', required=True),
- 'coda_fname': fields.char('CODA Filename', required=True),
- 'note': fields.text('Log'),
- }
+from openerp.addons.account_bank_statement_import import account_bank_statement_import as coda_ibs
- _defaults = {
- 'coda_fname': lambda *a: '',
- }
+coda_ibs.add_file_type(('coda', 'CODA'))
- def coda_parsing(self, cr, uid, ids, context=None, batch=False, codafile=None, codafilename=None):
+class account_bank_statement_import(osv.TransientModel):
+ _inherit = "account.bank.statement.import"
+
+ def process_coda(self, cr, uid, codafile=None, journal_id=False, context=None):
if context is None:
context = {}
- if batch:
- codafile = str(codafile)
- codafilename = codafilename
- else:
- data = self.browse(cr, uid, ids)[0]
- try:
- codafile = data.coda_data
- codafilename = data.coda_fname
- except:
- raise osv.except_osv(_('Error'), _('Wizard in incorrect state. Please hit the Cancel button'))
- return {}
recordlist = unicode(base64.decodestring(codafile), 'windows-1252', 'strict').split('\n')
statements = []
for line in recordlist:
raise osv.except_osv(_('Error') + ' R1004', _("No matching Bank Account (with Account Journal) found.\n\nPlease set-up a Bank Account with as Account Number '%s' and as Currency '%s' and an Account Journal.") % (statement['acc_number'], statement['currency']))
statement['description'] = rmspaces(line[90:125])
statement['balance_start'] = float(rmspaces(line[43:58])) / 1000
- if line[42] == '1': #1 = Debit, the starting balance is negative
+ if line[42] == '1': # 1 = Debit, the starting balance is negative
statement['balance_start'] = - statement['balance_start']
statement['balance_start_date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[58:64]), '%d%m%y'))
statement['accountHolder'] = rmspaces(line[64:90])
statement['balance_end_real'] = statement['balance_start'] + statement['balancePlus'] - statement['balanceMin']
for i, statement in enumerate(statements):
statement['coda_note'] = ''
+ statement_line = []
balance_start_check_date = (len(statement['lines']) > 0 and statement['lines'][0]['entryDate']) or statement['date']
cr.execute('SELECT balance_end_real \
FROM account_bank_statement \
ORDER BY date DESC,id DESC LIMIT 1', (statement['journal_id'].id, balance_start_check_date))
res = cr.fetchone()
balance_start_check = res and res[0]
- if balance_start_check == None:
+ if balance_start_check is None:
if statement['journal_id'].default_debit_account_id and (statement['journal_id'].default_credit_account_id == statement['journal_id'].default_debit_account_id):
balance_start_check = statement['journal_id'].default_debit_account_id.balance
else:
statement['coda_note'] = _("The CODA Statement %s Starting Balance (%.2f) does not correspond with the previous Closing Balance (%.2f) in journal %s!") % (statement['description'] + ' #' + statement['paperSeqNumber'], statement['balance_start'], balance_start_check, statement['journal_id'].name)
if not(statement.get('period_id')):
raise osv.except_osv(_('Error') + ' R3006', _(' No transactions or no period in coda file !'))
- data = {
+ statement_data = {
'name': statement['paperSeqNumber'],
'date': statement['date'],
'journal_id': statement['journal_id'].id,
'balance_start': statement['balance_start'],
'balance_end_real': statement['balance_end_real'],
}
- statement['id'] = self.pool.get('account.bank.statement').create(cr, uid, data, context=context)
for line in statement['lines']:
if line['type'] == 'information':
statement['coda_note'] = "\n".join([statement['coda_note'], line['type'].title() + ' with Ref. ' + str(line['ref']), 'Date: ' + str(line['entryDate']), 'Communication: ' + line['communication'], ''])
if 'counterpartyAddress' in line and line['counterpartyAddress'] != '':
note.append(_('Counter Party Address') + ': ' + line['counterpartyAddress'])
line['name'] = "\n".join(filter(None, [line['counterpartyName'], line['communication']]))
- partner_id = None
structured_com = ""
- bank_account_id = False
if line['communication_struct'] and 'communication_type' in line and line['communication_type'] == '101':
structured_com = line['communication']
+ bank_account_id = False
+ partner_id = False
if 'counterpartyNumber' in line and line['counterpartyNumber']:
- ids = self.pool.get('res.partner.bank').search(cr, uid, [('acc_number', '=', str(line['counterpartyNumber']))])
- if ids:
- bank_account_id = ids[0]
- partner_id = self.pool.get('res.partner.bank').browse(cr, uid, bank_account_id, context=context).partner_id.id
- else:
- #create the bank account, not linked to any partner. The reconciliation will link the partner manually
- #chosen at the bank statement final confirmation time.
- try:
- type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
- type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
- bank_code = type_id.code
- except ValueError:
- bank_code = 'bank'
- bank_account_id = self.pool.get('res.partner.bank').create(cr, uid, {'acc_number': str(line['counterpartyNumber']), 'state': bank_code}, context=context)
+ bank_account_id, partner_id = self._detect_partner(cr, uid, str(line['counterpartyNumber']), identifying_field='acc_number', context=context)
if 'communication' in line and line['communication'] != '':
note.append(_('Communication') + ': ' + line['communication'])
- data = {
+ line_data = {
'name': line['name'],
'note': "\n".join(note),
'date': line['entryDate'],
'amount': line['amount'],
'partner_id': partner_id,
- 'statement_id': statement['id'],
'ref': structured_com,
'sequence': line['sequence'],
'bank_account_id': bank_account_id,
}
- self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
+ statement_line.append((0, 0, line_data))
if statement['coda_note'] != '':
- self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context)
- model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')
- action = self.pool[model].browse(cr, uid, action_id, context=context)
- return {
- 'name': action.name,
- 'view_type': action.view_type,
- 'view_mode': action.view_mode,
- 'res_model': action.res_model,
- 'domain': action.domain,
- 'context': action.context,
- 'type': 'ir.actions.act_window',
- 'search_view_id': action.search_view_id.id,
- 'views': [(v.view_id.id, v.view_mode) for v in action.view_ids]
- }
+ statement_data.update({'coda_note': statement['coda_note']})
+ statement_data.update({'journal_id': journal_id, 'line_ids': statement_line})
+ return [statement_data]
def rmspaces(s):
'wizard/invite_view.xml',
'wizard/mail_compose_message_view.xml',
'mail_message_subtype.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',
+ 'res_config_view.xml',
'res_partner_view.xml',
'data/mail_data.xml',
'data/mail_group_data.xml',
from openerp.osv import fields, osv
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
+import openerp.tools as tools
_logger = logging.getLogger(__name__)
'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"),
'headers': fields.text('Headers', copy=False),
+ 'failure_reason': fields.text('Failure Reason', help="Failure reason. This is usually the exception thrown by the email server, stored to ease the debugging of mailing issues.", readonly=1),
# Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification
# and during unlink() we will not cascade delete the parent and its attachments
'notification': fields.boolean('Is Notification',
mail.write({'state': 'sent', 'message_id': res})
mail_sent = True
else:
- mail.write({'state': 'exception'})
+ mail.write({'state': 'exception', 'failure_reason': _('Error without exception. Probably due do sending an email without computed recipients.')})
mail_sent = False
# /!\ can't use mail.state here, as mail.refresh() will cause an error
mail.id, mail.message_id)
raise
except Exception as e:
- _logger.exception('failed sending mail.mail %s', mail.id)
- mail.write({'state': 'exception'})
+ failure_reason = tools.ustr(e)
+ _logger.exception('failed sending mail (id: %s) due to %s', mail.id, failure_reason)
+ mail.write({'state': 'exception', 'failure_reason': failure_reason})
self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=False)
if raise_exception:
if isinstance(e, AssertionError):
<page string="Attachments">
<field name="attachment_ids"/>
</page>
+ <page string="Failure Reason" attrs="{'invisible': [('state', '!=', 'exception')]}">
+ <field name="failure_reason"/>
+ </page>
</notebook>
</sheet>
</form>
<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}</field>
+ <field name="context">{}</field>
<field name="search_view_id" ref="view_mail_search"/>
</record>
##############################################################################
import urlparse
+import datetime
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp.osv import osv, fields
_inherit = 'base.config.settings'
_columns = {
+ 'fail_counter': fields.integer('Fail Mail', readonly=True),
'alias_domain': fields.char('Alias Domain',
help="If you have setup a catch-all email domain redirected to "
"the Odoo server, enter the domain name here."),
}
+ def get_default_fail_counter(self, cr, uid, ids, context=None):
+ previous_date = datetime.datetime.now() - datetime.timedelta(days=30)
+ return {
+ 'fail_counter': self.pool.get('mail.mail').search(cr, uid, [('date', '>=', previous_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)), ('state', '=', 'exception')], count=True, context=context),
+ }
+
def get_default_alias_domain(self, cr, uid, ids, context=None):
alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", default=None, context=context)
if alias_domain is None:
<field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml">
+ <xpath expr="//button[@string='Configure outgoing email servers']" position='after'>
+ <button class="oe_inline oe_link" style="display: inline-block;" name= "%(action_view_mail_mail)d" type="action"
+ context="{'search_default_exception': 1, 'search_default_outgoing': 0}"
+ attrs="{'invisible': [('fail_counter','=',0)]}">
+ <span> -- </span>
+ <i class="fa fa-exclamation-triangle"></i> <field class="oe_inline" name="fail_counter"/>
+ <span>failed emails</span>
+ </button>
+ </xpath>
<xpath expr="//div[@name='email']" position='inside'>
<div>
<label for="alias_domain" class="oe_inline"/>
.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body .oe_mail_cleaned {
display: none;
}
-
/* a) Indented Messages */
.openerp .oe_mail .oe_msg_indented{
mail_compose.send_mail(cr, user_raoul.id, [compose_id], {'mail_post_autofollow': True, 'mail_create_nosubscribe': True})
group_pigs.refresh()
message = group_pigs.message_ids[0]
-
+ # Test: mail_mail: notifications have been deleted
+ self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', message.id)]),'message_send: mail.mail message should have been auto-deleted!')
# Test: mail.group: followers (c and d added by auto follow key; raoul not added by nosubscribe key)
pigs_pids = [p.id for p in group_pigs.message_follower_ids]
test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id]
# Test: Pigs and Bird did receive their message
test_msg_ids = self.mail_message.search(cr, uid, [], limit=2)
+ mail_ids = self.mail_mail.search(cr, uid, [('mail_message_id', '=', message2.id)])
+ mail_record_id = self.mail_mail.browse(cr, uid, mail_ids)[0]
+ self.assertTrue(mail_record_id, "'message_send: mail.mail message should have in processing mail queue'" )
+ #check mass mail state...
+ test_mail_ids = self.mail_mail.search(cr, uid, [('state', '=', 'exception')])
+ self.assertNotIn(mail_ids, test_mail_ids, 'compose wizard: Mail sending Failed!!')
self.assertIn(message1.id, test_msg_ids, 'compose wizard: Pigs did not receive its mass mailing message')
self.assertIn(message2.id, test_msg_ids, 'compose wizard: Bird did not receive its mass mailing message')
or
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</header>
- <separator string="Mass Mailing"/>
- <group>
- <label for="id" string="Settings"/>
- <div>
- <div name="module_mass_mailing">
- <field name="module_mass_mailing" class="oe_inline"/>
- <label for="module_mass_mailing"/>
+ <div name="config_setting">
+ <separator string="Mass Mailing"/>
+ <group>
+ <label for="id" string="Settings"/>
+ <div>
+ <div name="module_mass_mailing">
+ <field name="module_mass_mailing" class="oe_inline"/>
+ <label for="module_mass_mailing"/>
+ </div>
</div>
- </div>
- </group>
- <separator string="Marketing Campaigns"/>
- <group>
- <label for="id" string="Settings"/>
- <div>
- <div name="module_marketing_campaign">
- <field name="module_marketing_campaign" class="oe_inline"/>
- <label for="module_marketing_campaign"/>
+ </group>
+ <separator string="Marketing Campaigns"/>
+ <group>
+ <label for="id" string="Settings"/>
+ <div>
+ <div name="module_marketing_campaign">
+ <field name="module_marketing_campaign" class="oe_inline"/>
+ <label for="module_marketing_campaign"/>
+ </div>
</div>
- </div>
- </group>
+ </group>
+ </div>
</form>
</field>
</record>
<template id="head" inherit_id="website.layout" name="Mail customization">
<xpath expr="//head" position="inside">
<script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.editor.js" groups="base.group_website_publisher"></script>
+ </xpath>
+</template>
+
+<template id="assets_frontend" inherit_id="website.assets_frontend" name="Mail Frontend Assets">
+ <xpath expr="." position="inside">
<script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.js"></script>
- <link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/>
</xpath>
</template>
# -*- coding: utf-8 -*-
import logging
-import simplejson
-import os
-import openerp
-import time
-import random
from openerp import http
from openerp.http import request
-from openerp.addons.web.controllers.main import module_boot, login_redirect
+from openerp.addons.web.controllers.main import login_redirect
_logger = logging.getLogger(__name__)
if not request.session.uid:
return login_redirect()
- modules = simplejson.dumps(module_boot(request.db))
- init = """
- var wc = new s.web.WebClient();
- wc.show_application = function(){
- wc.action_manager.do_action("pos.ui");
- };
- wc.setElement($(document.body));
- wc.start();
- """
-
- html = request.registry.get('ir.ui.view').render(request.cr, request.session.uid,'point_of_sale.index',{
- 'modules': modules,
- 'init': init,
- })
-
- return html
+ return request.render('point_of_sale.index')
l.product_id as product_id
from pos_order_line as l
left join pos_order s on (s.id=l.order_id)
- left join product_template pt on (pt.id=l.product_id)
+ left join product_product p on (l.product_id=p.id)
+ left join product_template pt on (p.product_tmpl_id=pt.id)
left join product_uom u on (u.id=pt.uom_id)
group by
s.date_order, s.partner_id,s.state,
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
- <notebook position="inside">
- <page string="Point of Sale">
- <group>
- <field name="ean13" />
- <button name="edit_ean" type="object" string="Edit" />
- </group>
- </page>
- </notebook>
+ <group name="point_of_sale" position="replace">
+ <group string="Point of Sale">
+ <field name="ean13" />
+ <button name="edit_ean" type="object" string="Edit" />
+ </group>
+ </group>
</field>
</record>
-
I test that the total of the attached invoice is correct
-
- !assert {model: pos.order, id: pos_order_pos1, string: Invoice inconsistent with its origin order}:
- - invoice_id.amount_total == 1795.5
+ !python {model: pos.order}: |
+ from openerp.tools import float_compare
+ amount_total = self.browse(cr, uid, ref('pos_order_pos1')).amount_total
+ assert float_compare(amount_total, 1752.75, precision_digits=2) == 0, "Invoice not correct"
<t t-call-assets="web.assets_common" t-css="false" />
<t t-call-assets="web.assets_backend" t-css="false" />
- <script type="text/javascript" id="loading-script">
+ <script type="text/javascript" id="loading-script" t-raw="init">
$(function() {
- var s = new openerp.init(<t t-raw='modules' />);
- <t t-raw='init' />
+ var s = new openerp.init();
+ var wc = new s.web.WebClient();
+
+ wc.show_application = function() {
+ wc.action_manager.do_action("pos.ui");
+ };
+
+ wc.setElement($(document.body));
+ wc.start();
});
</script>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
- <page string="Sales & Purchases" position="inside">
- <group>
+ <page name="sales_purchases" position="inside">
+ <group name="property_invoice_type" position="after">
<group name="pricelists" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
<field name="property_product_pricelist" groups="product.group_sale_pricelist"/>
</group>
I check sale price of Assemble Computer
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'pricelist': ref("customer_pricelist"), 'quantity':1})
product = self.browse(cr, uid, ref("product_product_4"), context=context)
- assert product.price == (product.lst_price-product.lst_price*(0.10)), "Wrong sale price: Assemble Computer."
+ assert float_compare(product.price, (product.lst_price-product.lst_price*(0.10)), precision_digits=2) == 0, "Wrong sale price: Assemble Computer."
-
I check sale price of Laptop.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
product = self.browse(cr, uid, ref("product_product_25"), context=context)
- assert product.price == product.lst_price + 1, "Wrong sale price: Laptop."
+ assert float_compare(product.price, product.lst_price + 1, precision_digits=2) == 0, "Wrong sale price: Laptop."
-
I check sale price of IT component.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
product = self.browse(cr, uid, ref("product_product_7"), context=context)
- assert product.price == product.lst_price, "Wrong sale price: IT component."
-
+ assert float_compare(product.price, product.lst_price, precision_digits=2) == 0, "Wrong sale price: IT component."
-
I check sale price of IT component if more than 3 Unit.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'quantity':5})
product = self.browse(cr, uid, ref("product_product_26"), context=context)
- assert product.price == product.lst_price-product.lst_price*(0.05), "Wrong sale price: IT component if more than 3 Unit."
+ assert float_compare(product.price, product.lst_price-product.lst_price*(0.05), precision_digits=2) == 0, "Wrong sale price: IT component if more than 3 Unit."
-
I check sale price of LCD Monitor.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'quantity':1})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert product.price == product.lst_price, "Wrong sale price: LCD Monitor."
-
+ assert float_compare(product.price, product.lst_price, precision_digits=2) == 0, "Wrong sale price: LCD Monitor."
-
I check sale price of LCD Monitor on end of year.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'quantity':1, 'date': '2011-12-31'})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert product.price == product.lst_price-product.lst_price*(0.30), "Wrong sale price: LCD Monitor on end of year."
-
+ assert float_compare(product.price, product.lst_price-product.lst_price*(0.30), precision_digits=2) == 0, "Wrong sale price: LCD Monitor on end of year."
-
I check cost price of LCD Monitor.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'quantity':1, 'date': False, 'partner': ref('base.res_partner_4'), 'pricelist': ref("supplier_pricelist")})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert product.price == 792, "wrong cost price: LCD Monitor."
+ assert float_compare(product.price, 792, precision_digits=2) == 0, "Wrong cost price: LCD Monitor."
-
I check cost price of LCD Monitor if more than 3 Unit.
-
!python {model: product.product}: |
+ from openerp.tools import float_compare
context.update({'quantity':3})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert product.price == 787, "wrong cost price: LCD Monitor if more than 3 Unit."
-
+ assert float_compare(product.price, 787, precision_digits=2) == 0, "Wrong cost price: LCD Monitor if more than 3 Unit."
-
I print the sale prices report.
-
project_id = super(project, self).create(cr, uid, vals, context=create_context)
project_rec = self.browse(cr, uid, project_id, context=context)
- self.pool.get('mail.alias').write(cr, uid, [project_rec.alias_id.id], {'alias_parent_thread_id': project_id, 'alias_defaults': {'project_id': project_id}}, context)
+ ir_values = self.pool.get('ir.values').get_default( cr, uid, 'project.config.settings', 'generate_project_alias' )
+ values = { 'alias_parent_thread_id': project_id, 'alias_defaults': {'project_id': project_id}}
+ if ir_values:
+ values = dict(values, alias_name=vals['name'])
+ self.pool.get('mail.alias').write(cr, uid, [project_rec.alias_id.id], values, context=context)
return project_id
def write(self, cr, uid, ids, vals, context=None):
'group_manage_delegation_task': fields.boolean("Allow task delegation",
implied_group='project.group_delegate_task',
help="Allows you to delegate tasks to other users."),
+ 'generate_project_alias': fields.boolean("Automatically generate an email alias at the project creation",
+ help="Odoo will generate an email alias at the project creation from project name."),
}
def get_default_time_unit(self, cr, uid, fields, context=None):
return {'value': {'group_tasks_work_on_tasks': True}}
return {}
+ def set_default_generate_project_alias(self, cr, uid, ids, context=None):
+ config_value = self.browse(cr, uid, ids, context=context).generate_project_alias
+ self.pool.get('ir.values').set_default(cr, uid, 'project.config.settings', 'generate_project_alias', config_value)
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
</div>
</div>
</group>
+ <group>
+ <label for="id" string="Communication"/>
+ <div>
+ <div>
+ <field name="generate_project_alias" class="oe_inline"/>
+ <label for="generate_project_alias" />
+ </div>
+ </div>
+ </group>
<separator string="Helpdesk & Support"/>
<group>
<label for="id" string="Support"/>
Then I import a sample EDI document of a sale order (v7.0)
-
!python {model: edi.edi}: |
+ from openerp.tools import float_compare
purchase_order_pool = self.pool.get('purchase.order')
edi_document = {
"__id": "sale:724f9v70-dv70-1v70-8v70-701a04e25v70.sale_order_test",
assert bank_info.acc_number == "Guys bank: 123477777-156113", 'Expected "Guys bank: 123477777-156113", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Default Purchase Pricelist' , "Default Purchase Pricelist was not automatically assigned"
- assert order_new.amount_total == 350, "Amount total is not same"
- assert order_new.amount_untaxed == 350, "untaxed amount is not same"
+ assert float_compare(order_new.amount_total, 350, precision_digits=2) == 0, "Amount total is not same"
+ assert float_compare(order_new.amount_untaxed, 350, precision_digits=2) == 0, "untaxed amount is not same"
assert len(order_new.order_line) == 2, "Purchase order lines number mismatch"
for purchase_line in order_new.order_line:
if purchase_line.name == 'PC Assemble SC234':
assert purchase_line.product_uom.name == "Unit" , "uom is not same"
- assert purchase_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,)
+ assert float_compare(purchase_line.price_unit, 150 , precision_digits=2) == 0, "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 1 , "product qty is not same"
elif purchase_line.name == 'PC on Demand':
assert purchase_line.product_uom.name == "Unit" , "uom is not same"
- assert purchase_line.price_unit == 20 , "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,)
+ assert float_compare(purchase_line.price_unit, 20 , precision_digits=2) == 0, "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 10 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % purchase_line)
"Then I import a sample EDI document of a sale order (v6.1 - to test backwards compatibility)"
-
!python {model: edi.edi}: |
+ from openerp.tools import float_compare
purchase_order_pool = self.pool.get('purchase.order')
edi_document = {
"__id": "sale:724f93ec-ddd0-11e0-88ec-701a04e25543.sale_order_test",
assert bank_info.acc_number == "Another bank: 123477700-156113", 'Expected "Another bank: 123477700-156113", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Default Purchase Pricelist' , "Default Purchase Pricelist was not automatically assigned"
- assert order_new.amount_total == 350, "Amount total is not same"
- assert order_new.amount_untaxed == 350, "untaxed amount is not same"
+ assert float_compare(order_new.amount_total, 350, precision_digits=2) == 0, "Amount total is not same"
+ assert float_compare(order_new.amount_untaxed, 350, precision_digits=2) == 0, "untaxed amount is not same"
assert len(order_new.order_line) == 2, "Purchase order lines number mismatch"
for purchase_line in order_new.order_line:
if purchase_line.name == 'Basic PC':
assert purchase_line.product_uom.name == "PCE" , "uom is not same"
- assert purchase_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,)
+ assert float_compare(purchase_line.price_unit, 150 , precision_digits=2) == 0, "unit price is not same, got %s, expected 150"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 1 , "product qty is not same"
elif purchase_line.name == 'Medium PC':
assert purchase_line.product_uom.name == "PCE" , "uom is not same"
- assert purchase_line.price_unit == 20 , "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,)
+ assert float_compare(purchase_line.price_unit, 20 , precision_digits=2) == 0, "unit price is not same, got %s, expected 20"%(purchase_line.price_unit,)
assert purchase_line.product_qty == 10 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % purchase_line)
-
I check the total untaxed amount of the RFQ is correctly computed
-
- !assert {model: purchase.order, id: purchase_order_1, string: The amount of RFQ is not correctly computed}:
- - round(sum([l.price_subtotal for l in order_line]), 2) == round(amount_untaxed, 2)
+ !python {model: purchase.order}: |
+ from openerp.tools import float_compare
+ po = self.browse(cr, uid, ref('purchase_order_1'))
+ assert float_compare(sum([l.price_subtotal for l in po.order_line]), po.amount_untaxed, precision_digits=2) == 0, "The amount of RFQ is not correctly computed"
-
I confirm the RFQ.
-
I check that the invoice details which is generated after confirmed RFQ.
-
!python {model: purchase.order}: |
+ from openerp.tools import float_compare
purchase_order = self.browse(cr, uid, ref("purchase_order_1"))
assert len(purchase_order.invoice_ids) >= 1, "Invoice is not generated more or less than one"
for invoice in purchase_order.invoice_ids:
assert invoice.currency_id.id == purchase_order.pricelist_id.currency_id.id ,"Invoice currency is not correspond with purchase order"
assert invoice.origin == purchase_order.name,"Invoice origin is not correspond with purchase order"
assert invoice.company_id.id == purchase_order.company_id.id ,"Invoice company is not correspond with purchase order"
- assert invoice.amount_untaxed == purchase_order.amount_untaxed, "Invoice untaxed amount is not correspond with purchase order"
- assert invoice.amount_tax == purchase_order.amount_tax, "Invoice tax amount is not correspond with purchase order"
- assert invoice.amount_total == purchase_order.amount_total, "Invoice total amount is not correspond with purchase order"
+ assert float_compare(invoice.amount_untaxed, purchase_order.amount_untaxed, precision_digits=2) == 0, "Invoice untaxed amount is not correspond with purchase order"
+ assert float_compare(invoice.amount_tax, purchase_order.amount_tax, precision_digits=2) == 0, "Invoice tax amount is not correspond with purchase order"
+ assert float_compare(invoice.amount_total, purchase_order.amount_total, precision_digits=2) == 0, "Invoice total amount is not correspond with purchase order"
assert len(invoice.invoice_line) == len(purchase_order.order_line), "Lines of Invoice and Purchase Order are not correspond"
-
I check that Reception details after confirmed RFQ.
I check requisition details which created after run procurement.
-
!python {model: procurement.order}: |
+ from openerp.tools import float_compare
procurement_ids = self.search(cr, uid, [('requisition_id','!=', False)])
for procurement in self.browse(cr, uid, procurement_ids, context=context):
requisition = procurement.requisition_id
line = requisition.line_ids[0]
assert line.product_id.id == procurement.product_id.id, "Product is not correspond."
assert line.product_uom_id.id == procurement.product_uom.id, "UOM is not correspond."
- assert line.product_qty == procurement.product_qty, "Quantity is not correspond."
+ assert float_compare(line.product_qty, procurement.product_qty, precision_digits=2) == 0, "Quantity is not correspond."
-
I send the purchase order associated to the requisition.
-
First, I generate the resource detail and check the resource Efficiency assigned.
-
!python {model: resource.resource}: |
+ from openerp.tools import float_compare
resources = self.generate_resources(cr, uid, [ref('base.user_root'),ref('base.user_demo')], ref('timesheet_group1'), context)
for resource in resources.values():
- assert resource.get('efficiency', 0.0) == 1.0, 'Invalid resource generated.'
+ assert float_compare(resource.get('efficiency', 0.0), 1.0, precision_digits=2) == 0, 'Invalid resource generated.'
-
I check per day work hour availability of the Resource based on Working Calendar Assigned to each resource, for first day of the week.
-
!python {model: resource.resource}: |
+ from openerp.tools import float_compare
calendar_pool = self.pool.get('resource.calendar')
resources = self.browse(cr, uid, [ref('resource_analyst'), ref('resource_designer'), ref('resource_developer')], context)
from datetime import datetime, timedelta
dt = now - timedelta(days=now.weekday())
for resource in resources:
result = calendar_pool.working_hours_on_day(cr, uid, resource.calendar_id, dt, context)
- assert result == 9.0, 'Wrong calculation of day work hour availability of the Resource (found %d).' % result
+ assert float_compare(result, 9.0, precision_digits=2) == 0, 'Wrong calculation of day work hour availability of the Resource (found %d).' % result
-
Now, resource "Developer" drafted leave on Thursday in this week.
-
'name': fields.char('Order Reference', required=True, copy=False,
readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
'origin': fields.char('Source Document', help="Reference of the document that generated this sales order request."),
- 'client_order_ref': fields.char('Reference/Description', copy=False),
+ 'client_order_ref': fields.char('Customer Reference', copy=False),
'state': fields.selection([
('draft', 'Draft Quotation'),
('sent', 'Quotation Sent'),
<field name="journal_id" ref="account.sales_journal"/>
<field name="section_id" ref="sales_team.section_sales_department"/>
<field name="state">draft</field>
- <field name="type">in_invoice</field>
+ <field name="type">out_invoice</field>
<field name="account_id" ref="account.a_recv"/>
<field name="name">Test invoice 1</field>
</record>
<header>
<button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
<button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
+ <button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="action_quotation_send" string="Send by Email" type="object" states="sent,progress,manual" groups="base.group_user"/>
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="print_quotation" string="Print" type="object" states="sent,progress,manual" groups="base.group_user"/>
<button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
- <button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
<button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
attrs="{'invisible': [('invoice_exists', '=', False)]}" groups="base.group_user"/>
<button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
type="action" states="manual" class="oe_highlight" groups="base.group_user"/>
<button name="copy_quotation" states="cancel" string="New Copy of Quotation" type="object"/>
- <button name="cancel" states="draft,sent" string="Cancel Quotation" groups="base.group_user"/>
+ <button name="cancel" states="draft,sent" string="Cancel" groups="base.group_user"/>
<button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
<button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
<field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
</group>
<group>
<field name="date_order"/>
- <field name="client_order_ref"/>
<field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist" on_change="onchange_pricelist_id(pricelist_id,order_line)"/>
<field name="currency_id" invisible="1"/>
</group>
<field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice', 'base.group_sale_salesman_all_leads']}"/>
<field name="section_id" options="{'no_create': True}" groups="base.group_multi_salesteams"/>
<field groups="base.group_no_one" name="origin"/>
+ <field name="client_order_ref"/>
</group>
<group name="sale_pay">
<field name="payment_term" options="{'no_create': True}"/>
"Then I import a sample EDI document of a purchase order (v7.0)"
-
!python {model: edi.edi}: |
+ from openerp.tools import float_compare
sale_order_pool = self.pool.get('sale.order')
edi_document = {
"__id": "purchase:5af12v70-dv70-1v70-bv70-701a04e25v70.purchase_order_test",
assert bank_info.acc_number == "Another bank: 032465700-156700", 'Expected "Another bank: 032465700-156700", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list was not automatically assigned"
- assert order_new.amount_total == 350, "Amount total is wrong"
- assert order_new.amount_untaxed == 350, "Untaxed amount is wrong"
+ assert float_compare(order_new.amount_total, 350, precision_digits=2) == 0, "Amount total is wrong"
+ assert float_compare(order_new.amount_untaxed, 350, precision_digits=2) == 0, "Untaxed amount is wrong"
assert len(order_new.order_line) == 2, "Sale order lines mismatch"
for sale_line in order_new.order_line:
if sale_line.name == 'PC Assemble SC234':
assert sale_line.product_uom.name == "Unit" , "uom is not same"
- assert sale_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
+ assert float_compare(sale_line.price_unit, 150 , precision_digits=2) == 0, "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 1 , "product qty is not same"
elif sale_line.name == 'PC on Demand':
assert sale_line.product_uom.name == "Unit" , "uom is not same"
- assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
+ assert float_compare(sale_line.price_unit, 100, precision_digits=2) == 0, "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 2 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % sale_line)
"Then I import a sample EDI document of a purchase order (v6.1 - to test backwards compatibility)"
-
!python {model: edi.edi}: |
+ from openerp.tools import float_compare
sale_order_pool = self.pool.get('sale.order')
edi_document = {
"__id": "purchase:5af1272e-dd26-11e0-b65e-701a04e25543.purchase_order_test",
assert bank_info.acc_number == "Ladies bank: 032465789-156113", 'Expected "Ladies bank: 032465789-156113", got %s' % bank_info.acc_number
assert order_new.pricelist_id.name == 'Public Pricelist' , "Public Price list was not automatically assigned"
- assert order_new.amount_total == 350, "Amount total is wrong"
- assert order_new.amount_untaxed == 350, "Untaxed amount is wrong"
+ assert float_compare(order_new.amount_total, 350, precision_digits=2) == 0, "Amount total is wrong"
+ assert float_compare(order_new.amount_untaxed, 350, precision_digits=2) == 0, "Untaxed amount is wrong"
assert len(order_new.order_line) == 2, "Sale order lines mismatch"
for sale_line in order_new.order_line:
if sale_line.name == 'Basic PC':
assert sale_line.product_uom.name == "PCE" , "uom is not same"
- assert sale_line.price_unit == 150 , "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
+ assert float_compare(sale_line.price_unit, 150, precision_digits=2) == 0, "unit price is not same, got %s, expected 150"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 1 , "product qty is not same"
elif sale_line.name == 'Medium PC':
assert sale_line.product_uom.name == "PCE" , "uom is not same"
- assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
+ assert float_compare(sale_line.price_unit, 100 , precision_digits=2) == 0, "unit price is not same, got %s, expected 100"%(sale_line.price_unit,)
assert sale_line.product_uom_qty == 2 , "product qty is not same"
else:
raise AssertionError('unknown order line: %s' % sale_line)
-
I verify that the onchange was correctly triggered
-
- !assert {model: sale.order, id: sale.sale_order_test1, string: The onchange function of product was not correctly triggered}:
- - order_line[0].name == u'[A8767] Apple In-Ear Headphones'
- - order_line[0].price_unit == 79.0
- - order_line[0].product_uom_qty == 8
- - order_line[0].product_uom.id == ref('product.product_uom_unit')
+ !python {model: sale.order}: |
+ from openerp.tools import float_compare
+ order_line = self.browse(cr, uid, ref('sale.sale_order_test1')).order_line
+ assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
+ assert float_compare(order_line[0].price_unit, 79.0, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
+ assert order_line[0].product_uom_qty == 8, "The onchange function of product was not correctly triggered"
+ assert order_line[0].product_uom.id == ref('product.product_uom_unit'), "The onchange function of product was not correctly triggered"
-
I create another sale order
-
I verify that the onchange was correctly triggered
-
- !assert {model: sale.order, id: sale.sale_order_test2, string: The onchange function of product was not correctly triggered}:
- - order_line[0].name == u'[A8767] Apple In-Ear Headphones'
- - order_line[0].price_unit == 79.0 * 12
- - order_line[0].product_uom.id == ref('product.product_uom_dozen')
- - order_line[0].product_uom_qty == 16
\ No newline at end of file
+ !python {model: sale.order}: |
+ from openerp.tools import float_compare
+ order_line = self.browse(cr, uid, ref('sale.sale_order_test2')).order_line
+ assert order_line[0].name == u'[A8767] Apple In-Ear Headphones', "The onchange function of product was not correctly triggered"
+ assert float_compare(order_line[0].price_unit, 79.0 * 12, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
+ assert order_line[0].product_uom.id == ref('product.product_uom_dozen'), "The onchange function of product was not correctly triggered"
+ assert order_line[0].product_uom_qty == 16, "The onchange function of product was not correctly triggered"
+
<field name="user_id" position="after">
<field name="categ_ids" widget="many2many_tags"/>
</field>
- <field name="origin" position="after">
+ <field name="client_order_ref" position="after">
<field name="campaign_id" />
<field name="medium_id" />
<field name="source_id" />
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
- <page string="Sales & Purchases" position="inside">
- <group colspan="2" col="2" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
- <separator string="Invoicing" colspan="2"/>
- <field name="property_invoice_type"/>
+ <group name="invoicing" position="replace">
+ <group string="Invoicing">
+ <group cols="2" colspan="2" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
+ <field name="property_invoice_type"/>
+ </group>
</group>
- </page>
+ </group>
</field>
</record>
-
First I check the total amount of the Quotation before Approved.
-
- !assert {model: sale.order, id: sale_order_service, string: The amount of the Quotation is not correctly computed}:
- - sum([l.price_subtotal for l in order_line]) == amount_untaxed
+ !python {model: sale.order}: |
+ from openerp.tools import float_compare
+ so = self.browse(cr, uid, ref('sale_order_service'))
+ float_compare(sum([l.price_subtotal for l in so.order_line]), so.amount_untaxed, precision_digits=2) == 0, "The amount of the Quotation is not correctly computed"
-
I set an explicit invoicing partner that is different from the main SO Customer
-
I check the invoice details after dispatched delivery.
-
!python {model: sale.order}: |
+ from openerp.tools import float_compare
order = self.browse(cr, uid, ref("sale_order_service"))
assert order.invoice_ids, "Invoice is not created."
ac = order.partner_invoice_id.property_account_receivable.id
assert inv_line.product_id.id == so_line.product_id.id or False,"Product is not correspond"
assert inv_line.account_id.id == ac,"Account of Invoice line is not corresponding."
assert inv_line.uos_id.id == (so_line.product_uos and so_line.product_uos.id or so_line.product_uom.id), "Product UOS is not correspond."
- assert inv_line.price_unit == so_line.price_unit , "Price Unit is not correspond."
+ assert float_compare(inv_line.price_unit, so_line.price_unit , precision_digits=2) == 0, "Price Unit is not correspond."
assert inv_line.quantity == (so_line.product_uos and so_line.product_uos_qty or so_line.product_uom_qty), "Product qty is not correspond."
assert inv_line.price_subtotal == so_line.price_subtotal, "Price sub total is not correspond."
-
-
I verify that the onchange of product on sale order line was correctly triggered
-
- !assert {model: sale.order, id: sale_order_onchange1, string: The onchange function of product was not correctly triggered}:
- - order_line[0].name == u'Devil Worship Book'
- - order_line[0].price_unit == 66.6
+ !python {model: sale.order}: |
+ from openerp.tools import float_compare
+ order_line = self.browse(cr, uid, ref('sale_order_onchange1')).order_line
+ assert order_line[0].name == u'Devil Worship Book', "The onchange function of product was not correctly triggered"
+ assert float_compare(order_line[0].price_unit, 66.6, precision_digits=2) == 0, "The onchange function of product was not correctly triggered"
# -*- coding: utf-8 -*-
import logging
-import simplejson
from openerp import http
from openerp.http import request
-from openerp.addons.web.controllers.main import module_boot
_logger = logging.getLogger(__name__)
-html_template = """<!DOCTYPE html>
-<html>
- <head>
- <title>Barcode Scanner</title>
-
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
- <meta http-equiv="content-type" content="text/html, charset=utf-8" />
-
- <meta name="viewport" content=" width=1024, user-scalable=no">
- <meta name="apple-mobile-web-app-capable" content="yes">
- <meta name="mobile-web-app-capable" content="yes">
-
- <link rel="shortcut icon" sizes="80x51" href="/stock/static/src/img/scan.png">
- <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
- <link rel="stylesheet" href="/stock/static/src/css/barcode.css" />
- <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css" />
- <link rel="stylesheet" href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css" />
- <link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css" />
- <script type="text/javascript" src="/web/js/web.assets_common"></script>
- <script type="text/javascript" src="/web/js/web.assets_backend"></script>
- <script type="text/javascript">
- $(function() {
- var s = new openerp.init(%(modules)s);
- %(init)s
- });
- </script>
- </head>
- <body>
- <div class="openerp openerp_webclient_container">
- <table class="oe_webclient">
- <tr>
- <td class="oe_application"/>
- </tr>
- </table>
- </div>
-
- <!--[if lte IE 8]>
- <script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
- <script>CFInstall.check({mode: "overlay"});</script>
- <![endif]-->
- </body>
-</html>
-"""
-
class BarcodeController(http.Controller):
- @http.route(['/barcode/web/'], type='http', auth='user')
+ @http.route(['/stock/barcode/'], type='http', auth='user')
def a(self, debug=False, **k):
if not request.session.uid:
- return http.local_redirect('/web/login?redirect=/barcode/web')
+ return http.local_redirect('/web/login?redirect=/stock/barcode/')
- r = html_template % {
- 'modules': simplejson.dumps(module_boot(request.db)),
- 'init': """
- var wc = new s.web.WebClient();
- wc.show_application = function(){
- wc.action_manager.do_action("stock.ui", {});
- };
- wc.do_push_state = function(state){};
- wc.setElement($(document.body));
- wc.start();
- """
- }
- return r
+ return request.render('stock.barcode_index')
Bill of Materials for 1 SHE100 Unit
-+-------------+----------+------------
++-------------+----------+-----------+
| Product Code| Quantity | UoM |
+====================================+
|SIDEPAN | 2 | PCE |
Bill of Materials for 2 SIDEPAN Units
-+-------------+----------+------------
++-------------+----------+-----------+
| Product Code| Quantity | UoM |
+====================================+
| WOOD002 | 0.17| M |
-+-------------+----------+------------
++-------------+----------+-----------+
The SIDEPAN is made from an order using the workflow shown. The WOOD002 is purchased on order and the other products are all found in stock. An order for the product SHE100 will then generate two production orders (SHE100 and SIDEPAN) then produce two purchase orders for the product WOOD002. Product WOOD002 is used in the production of both SHE100 and SIDEPAN. Set the lead times on the product forms to the following:
<field name="model">res.partner</field>
<field name="inherit_id" ref="product.view_partner_property_form"/>
<field name="arch" type="xml">
- <group name="pricelists" position="after">
- <group groups="stock.group_locations">
+ <xpath expr="//group[@name='mailing']" position="after">
+ <group groups="stock.group_locations" string="Warehouse">
<field name="property_stock_customer" domain="[('usage','=','customer')]"/>
<field name="property_stock_supplier" domain="[('usage','=','supplier')]"/>
</group>
- </group>
+ </xpath>
</field>
</record>
var self = this;
this.rows = [];
this.search_filter = "";
- jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
- return function( elem ) {
- return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
- };
- });
},
get_header: function(){
return this.getParent().get_header();
@api.cr_uid_ids_context
def open_barcode_interface(self, cr, uid, picking_ids, context=None):
- final_url="/barcode/web/#action=stock.ui&picking_id="+str(picking_ids[0])
+ final_url="/stock/barcode/#action=stock.ui&picking_id="+str(picking_ids[0])
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
@api.cr_uid_ids_context
_order = 'sequence'
def open_barcode_interface(self, cr, uid, ids, context=None):
- final_url = "/barcode/web/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
+ final_url = "/stock/barcode/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
return {'type': 'ir.actions.act_url', 'url': final_url, 'target': 'self'}
def _get_tristate_values(self, cr, uid, ids, field_name, arg, context=None):
-->
<openerp>
<data>
- <template id="assets_backend" name="stock assets" inherit_id="web.assets_backend">
- <xpath expr="." position="inside">
- <link rel="stylesheet" href="/stock/static/src/css/stock.css"/>
- <script type="text/javascript" src="/stock/static/src/js/widgets.js"></script>
- </xpath>
- </template>
+
+<template id="assets_backend" name="stock assets" inherit_id="web.assets_backend">
+ <xpath expr="." position="inside">
+ <link rel="stylesheet" href="/stock/static/src/css/stock.css"/>
+ <script type="text/javascript" src="/stock/static/src/js/widgets.js"></script>
+ </xpath>
+</template>
+
+<template id="barcode_index" name="Barcode Scanner"><!DOCTYPE html>
+<html>
+ <head>
+ <title>Barcode Scanner</title>
+
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
+ <meta http-equiv="content-type" content="text/html, charset=utf-8" />
+
+ <meta name="viewport" content=" width=1024, user-scalable=no"/>
+ <meta name="apple-mobile-web-app-capable" content="yes"/>
+ <meta name="mobile-web-app-capable" content="yes"/>
+
+ <link rel="shortcut icon" sizes="80x51" href="/stock/static/src/img/scan.png"/>
+ <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
+
+ <link rel="stylesheet" href="/stock/static/src/css/barcode.css" />
+ <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css" />
+ <link rel="stylesheet" href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css" />
+ <link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css" />
+
+ <t t-call-assets="web.assets_common" t-css="false" />
+ <t t-call-assets="web.assets_backend" t-css="false" />
+
+ <script type="text/javascript" id="loading-script" t-raw="init">
+ $(function() {
+ var s = new openerp.init();
+ var wc = new s.web.WebClient();
+
+ wc.show_application = function() {
+ wc.action_manager.do_action("stock.ui", {});
+ };
+
+ wc.do_push_state = function(state){};
+ wc.setElement($(document.body));
+ wc.start();
+ });
+ </script>
+
+ </head>
+ <body>
+ <div class='openerp openerp_webclient_container'>
+ <table class='oe_webclient'>
+ <tr>
+ <td class='oe_application' />
+ </tr>
+ </table>
+ </div>
+ </body>
+</html>
+</template>
+
</data>
</openerp>
+ lpad(obj.getSeconds(),2);
};
+// jQuery custom plugins
+jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
+ return function( elem ) {
+ return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
+ };
+});
+
openerp.declare = declare;
return openerp;
return Tour.error(next, "Can't reach the next step");
}
}
- checkNext();
+ setTimeout(checkNext, 0);
},
nextStep: function (step) {
var state = Tour.getState();
step.onload();
}
- if (next) {
+ if (state.mode === "test") {
setTimeout(function () {
- if (Tour.getState()) {
+ Tour.autoNextStep(state.tour, step);
+ if (next && Tour.getState()) {
Tour.waitNextStep();
}
- if (state.mode === "test") {
- setTimeout(function(){
- Tour.autoNextStep(state.tour, step);
- }, Tour.defaultDelay);
- }
- }, next.wait || 0);
- } else {
- setTimeout(function(){
- Tour.autoNextStep(state.tour, step);
- }, Tour.defaultDelay);
+ }, step.wait || Tour.defaultDelay);
+ } else if (next) {
+ setTimeout(Tour.waitNextStep, next.wait || 0);
+ }
+ if (!next) {
Tour.endTour();
}
},
}
}
- Tour.testtimer = setTimeout(autoStep, 100);
+ Tour.testtimer = setTimeout(autoStep, 0);
},
autoDragAndDropSnippet: function (selector) {
var $thumbnail = $(selector).first();
return request.redirect(redirect)
@http.route('/website/customize_template_get', type='json', auth='user', website=True)
- def customize_template_get(self, xml_id, full=False):
+ def customize_template_get(self, xml_id, full=False, bundles=False):
""" Lists the templates customizing ``xml_id``. By default, only
returns optional templates (which can be toggled on and off), if
``full=True`` returns all templates customizing ``xml_id``
+ ``bundles=True`` returns also the asset bundles
"""
imd = request.registry['ir.model.data']
view_model, view_theme_id = imd.get_object_reference(
user_groups = set(user.groups_id)
views = request.registry["ir.ui.view"]\
- ._views_get(request.cr, request.uid, xml_id, context=request.context)
+ ._views_get(request.cr, request.uid, xml_id, bundles=bundles, context=request.context)
done = set()
result = []
for v in views:
# Returns all views (called and inherited) related to a view
# Used by translation mechanism, SEO and optional templates
- def _views_get(self, cr, uid, view_id, options=True, context=None, root=True):
+ def _views_get(self, cr, uid, view_id, options=True, bundles=False, context=None, root=True):
""" For a given view ``view_id``, should return:
* the view itself
result = [view]
node = etree.fromstring(view.arch)
- for child in node.xpath("//t[@t-call]"):
+ xpath = "//t[@t-call]"
+ if bundles:
+ xpath += "| //t[@t-call-assets]"
+ for child in node.xpath(xpath):
try:
- called_view = self._view_obj(cr, uid, child.get('t-call'), context=context)
+ called_view = self._view_obj(cr, uid, child.get('t-call', child.get('t-call-assets')), context=context)
except ValueError:
continue
if called_view not in result:
- result += self._views_get(cr, uid, called_view, options=options, context=context)
+ result += self._views_get(cr, uid, called_view, options=options, bundles=bundles, context=context)
extensions = view.inherit_children_ids
if not options:
.navbar.navbar-inverse .cke_top {
background: transparent;
border: none;
- -webkit-box-shadow: none;
-moz-box-shadow: none;
+ -webkit-box-shadow: none;
box-shadow: none;
-ms-filter: "alpha(opacity=50)";
}
padding: 2px;
margin: 0;
z-index: 20000;
- background: #414141, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #646060), color-stop(100%, #262626));
- background: #414141, -webkit-linear-gradient(#646060, #262626);
background: #414141, -moz-linear-gradient(#646060, #262626);
- background: #414141, -o-linear-gradient(#646060, #262626);
+ background: #414141, -webkit-linear-gradient(#646060, #262626);
background: #414141, linear-gradient(#646060, #262626);
- -webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
box-sizing: border-box;
}
.oe_website_editorbar li {
/* ---- RTE ---- {{{ */
.oe_editable .btn, .btn.oe_editable {
- -webkit-user-select: auto;
-moz-user-select: auto;
+ -ms-user-select: auto;
+ -webkit-user-select: auto;
user-select: auto;
cursor: text !important;
}
height: 660px;
background-color: black;
border: 2px solid #1c1f1f;
- -webkit-border-radius: 10px;
-moz-border-radius: 10px;
- -ms-border-radius: 10px;
- -o-border-radius: 10px;
+ -webkit-border-radius: 10px;
border-radius: 10px;
margin: auto;
top: 0;
color: #1c1f1f;
}
.oe_mobile_preview.modal .modal-content .modal-header .close {
- color: lightgrey;
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
+ color: lightgray;
+ filter: progid:DXImageTransform.Microsoft.Alpha(enabled=false);
opacity: 1;
}
.oe_mobile_preview.modal .modal-content .modal-header .close:hover {
color: #e00101;
- filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
+ filter: progid:DXImageTransform.Microsoft.Alpha(enabled=false);
opacity: 1;
}
.oe_mobile_preview.modal .modal-content .modal-body {
}
.oe_seo_configuration .oe_seo_keyword {
padding: 0.2em 0.4em 0.2em 0.5em;
- -webkit-border-radius: 0.4em;
-moz-border-radius: 0.4em;
- -ms-border-radius: 0.4em;
- -o-border-radius: 0.4em;
+ -webkit-border-radius: 0.4em;
border-radius: 0.4em;
}
.oe_seo_configuration li.oe_seo_preview_g {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);
opacity: 0;
}
+
+.oe_include_bundles {
+ font-weight: normal;
+ padding: 0 8px;
+}
z-index: -1000
+opacity(0)
+.oe_include_bundles
+ font-weight: normal
+ padding: 0 8px
+
// }}}
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
template: 'website.ace_view_editor',
events: {
'change #ace-view-list': 'displaySelectedView',
+ 'click .js_include_bundles': 'loadTemplates',
'click button[data-action=save]': 'saveViews',
'click button[data-action=format]': 'formatXml',
'click button[data-action=close]': 'close',
var self = this;
self.aceEditor = ace.edit(self.$('#ace-view-editor')[0]);
self.aceEditor.setTheme("ace/theme/monokai");
- var viewId = $(document.documentElement).data('view-xmlid');
- openerp.jsonRpc('/website/customize_template_get', 'call', {
- 'xml_id': viewId,
- 'full': true,
- }).then(function (views) {
- self.loadViews.call(self, views);
- self.open.call(self);
- var curentHash = window.location.hash;
- var indexOfView = curentHash.indexOf("?view=");
- if (indexOfView >= 0) {
- var viewId = parseInt(curentHash.substring(indexOfView + 6, curentHash.length), 10);
- self.$('#ace-view-list').val(viewId).change();
- } else {
- if (views.length >= 2) {
- var mainTemplate = views[1];
- self.$('#ace-view-list').val(mainTemplate.id).trigger('change');
- }
- window.location.hash = hash;
- }
- });
+ self.loadTemplates();
var $editor = self.$('.ace_editor');
function resizeEditor (target) {
resizeEditor(readEditorWidth());
resizeEditorHeight(this.getParent().$el.outerHeight()+2);
},
+ loadTemplates: function () {
+ var self = this;
+ var args = {
+ xml_id: $(document.documentElement).data('view-xmlid'),
+ full: true,
+ bundles: this.$('.js_include_bundles')[0].checked
+ };
+ return openerp
+ .jsonRpc('/website/customize_template_get', 'call', args)
+ .then(function (views) {
+ self.loadViews.call(self, views);
+ self.open.call(self);
+ var curentHash = window.location.hash;
+ var indexOfView = curentHash.indexOf("?view=");
+ if (indexOfView >= 0) {
+ var viewId = parseInt(curentHash.substring(indexOfView + 6, curentHash.length), 10);
+ self.$('#ace-view-list').val(viewId).change();
+ } else {
+ if (views.length >= 2) {
+ var mainTemplate = views[1];
+ self.$('#ace-view-list').val(mainTemplate.id).trigger('change');
+ }
+ window.location.hash = hash;
+ }
+ });
+ },
loadViews: function (views) {
- var $viewList = this.$('#ace-view-list');
+ var $viewList = this.$('#ace-view-list').empty();
_(this.buildViewGraph(views)).each(function (view) {
if (!view.id) { return; }
---------------------------------------------------- */
var templates_def = $.Deferred().resolve();
website.add_template_file = function(template) {
+ var def = $.Deferred();
templates_def = templates_def.then(function() {
- var def = $.Deferred();
openerp.qweb.add_template(template, function(err) {
if (err) {
def.reject(err);
});
return def;
});
+ return def;
};
website.add_template_file('/website/static/src/xml/website.xml');
<select id="ace-view-list" class="form-control oe_view_list">
<!-- filled in JS -->
</select>
+ <label class="oe_include_bundles">
+ <input type="checkbox" class="js_include_bundles"/> Include Asset Bundles
+ </label>
<button data-action="save" type="submit" class="btn btn-success">Save</button>
<button data-action="format" type="button" class="btn btn-warning">Format</button>
<button data-action="close" type="button" class="btn btn-info">Close</button>
<h3 class="page-header mt16">1. Define Keywords <small>describing your page content</small></h3>
<div class="form-horizontal" role="form">
<div class="form-group">
- <label for="seo_page_keywords" class="col-lg-2 control-label">Add keyword:</label>
- <div class="col-lg-3 col-sm-4">
+ <label for="seo_page_keywords" class="col-sm-3 control-label">Add keyword:</label>
+ <div class="col-sm-4">
<div class="input-group">
<input type="text" name="seo_page_keywords" class="form-control" maxlength="30"/>
<span class="input-group-btn">
<h3 class="page-header">2. Reference Your Page <small>using above suggested keywords</small></h3>
<div class="form-horizontal mt16" role="form">
<div class="form-group">
- <label for="seo_page_title" class="col-lg-2 control-label">Title</label>
- <div class="col-lg-8">
+ <label for="seo_page_title" class="col-sm-2 control-label">Title</label>
+ <div class="col-sm-8">
<input type="text" name="seo_page_title" class="form-control" maxlength="70" size="70"/>
</div>
</div>
<div class="form-group">
- <label for="seo_page_description" class="col-lg-2 control-label">Description</label>
- <div class="col-lg-8">
+ <label for="seo_page_description" class="col-sm-2 control-label">Description</label>
+ <div class="col-sm-8">
<textarea name="seo_page_description" class="form-control" rows="3" cols="70" t-att-maxlength="widget.maxDescriptionSize"/>
</div>
</div>
<li class="dropdown" t-ignore="true" t-if="website.user_id != user_id">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<b>
- <span t-esc="user_id.name"/>
+ <span t-esc="(len(user_id.name)>25) and (user_id.name[:23]+'...') or user_id.name"/>
<span class="caret"></span>
</b>
</a>
'use strict';
var website = openerp.website,
- qweb = openerp.qweb;
- website.add_template_file('/website_blog/static/src/xml/website_blog.inline.discussion.xml');
+ qweb = openerp.qweb;
+
website.blog_discussion = openerp.Class.extend({
init: function(options) {
var self = this ;
public_user: false,
};
self.settings = $.extend({}, defaults, options);
- self.do_render(self);
+
+ // TODO: bundlify qweb templates
+ website.add_template_file('/website_blog/static/src/xml/website_blog.inline.discussion.xml').then(function () {
+ self.do_render(self);
+ });
},
do_render: function(data) {
var self = this;
<openerp>
<data>
-<template id="assets_editor" inherit_id="website.assets_editor" name="Blog Editor" groups="base.group_document_user">
+<template id="assets_editor" inherit_id="website.assets_editor" name="Blog Editor"> <!-- groups="base.group_document_user" -->
<xpath expr="." position="inside">
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.editor.js"></script>
<script type="text/javascript" src="/website_blog/static/src/js/website.tour.blog.js"></script>
</xpath>
</template>
+<template id="assets_frontend" inherit_id="website.assets_frontend" name="Blog Front-end assets">
+ <xpath expr="." position="inside">
+ <link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
+
+ <script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
+ <script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
+ <script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
+ </xpath>
+</template>
+
<!-- Layout add nav and footer -->
<template id="header_footer_custom" inherit_id="website.footer_default" name="Footer News Blog Link">
<xpath expr="//div[@id='info']/ul" position="inside">
<!-- Page -->
<template id="index" name="Blog Navigation">
<t t-call="website.layout">
- <t t-set="head">
- <link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
- <script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
- <script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
- <script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
- </t>
<div id="wrap" class="js_blog">
<t t-raw="0"/>
</div>
_columns = {
'website_published': fields.boolean('Published On Website', copy=False),
}
+ _defaults = {
+ 'website_published': True,
+ }
-.ribbon-wrapper {
+.oe_sponsor.ribbon-wrapper {
width: 60px;
height: 60px;
z-index: 5;
right: 0;
}
-.ribbon {
+.oe_sponsor.ribbon {
font: bold 13px Sans-Serif;
color: #404040;
text-align: center;
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
}
-.ribbon.ribbon_Gold {
+.oe_sponsor.ribbon.ribbon_Gold {
background-color: #FDE21B;
background-image: -webkit-gradient(linear, left top, left bottom, from(#E9CE0C), to(#FDE21B));
background-image: -webkit-linear-gradient(top, #E9CE0C, #FDE21B);
background-image: -o-linear-gradient(top, #E9CE0C, #FDE21B);
}
-.ribbon.ribbon_Silver {
+.oe_sponsor.ribbon.ribbon_Silver {
background-color: #CCCCCC;
background-image: -webkit-gradient(linear, left top, left bottom, from(#BBBBBB), to(#CCCCCC));
background-image: -webkit-linear-gradient(top, #BBBBBB, #CCCCCC);
background-image: -o-linear-gradient(top, #BBBBBB, #CCCCCC);
}
-.ribbon.ribbon_Bronze {
+.oe_sponsor.ribbon.ribbon_Bronze {
background-color: #DB9141;
background-image: -webkit-gradient(linear, left top, left bottom, from(#C2792A), to(#DB9141));
background-image: -webkit-linear-gradient(top, #C2792A, #DB9141);
$(document).ready(function() {
- jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
- return function( elem ) {
- return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
- };
- });
-
$("#event_track_search").bind('keyup', function(e){
var change_text = $(this).val();
$('.event_track').removeClass('invisible');
<openerp>
<data>
-<template name="Sponsors" id="event_sponsor" optional="enabled" inherit_id="website_event.layout">
- <xpath expr="//t[@t-call='website.layout']" position="inside">
- <t t-set="head">
- <link rel='stylesheet' href='/website_event_track/static/src/css/website_event_track.css'/>
- <t t-raw="head or ''"/>
- </t>
+<template id="assets_frontend" inherit_id="website.assets_frontend" name="Website Event Track Assets">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
</xpath>
+</template>
+
+<template name="Sponsors" id="event_sponsor" optional="enabled" inherit_id="website_event.layout">
<xpath expr="//div[@id='wrap']" position="inside">
<div class="container mt32 mb16 hidden-print" t-if="event.sponsor_ids">
<section data-snippet-id="title">
<h2 class="text-center mb32">Our Sponsors</h2>
</section>
<div class="row">
- <div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center mb16" t-foreach="event.sponsor_ids" t-as="sponsor">
+ <div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center mb16 oe_sponsor" t-foreach="event.sponsor_ids" t-as="sponsor">
<t t-if="sponsor.url">
<a t-att-href="sponsor.url" style="position: relative; display: inline-block;">
<span t-field="sponsor.image_medium"
<template id="agenda">
<t t-call="website_event.layout">
- <t t-set="head">
- <script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
- <t t-raw="head or ''"/>
- </t>
<section class="container">
<h1 class="text-center" t-field="event.name"/>
<div class="form-inline pull-right">
create_context = dict(context, mail_create_nolog=True)
post_id = super(Post, self).create(cr, uid, vals, context=create_context)
post = self.browse(cr, SUPERUSER_ID, post_id, context=context) # SUPERUSER_ID to avoid read access rights issues when creating
+ # deleted or closed questions
+ if post.parent_id and (post.parent_id.state == 'close' or post.parent_id.active == False):
+ osv.except_osv(_('Error !'), _('Posting answer on [Deleted] or [Closed] question is prohibited'))
# karma-based access
if post.parent_id and not post.can_ask:
raise KarmaError('Not enough karma to create a new question')
</field>
</record>
- <!-- Update user prefrence form!-->
- <record id="view_users_form_simple_modif_forum" model="ir.ui.view">
- <field name="name">res.users.preferences.form</field>
- <field name="model">res.users</field>
- <field name="inherit_id" ref="base.view_users_form_simple_modif"/>
- <field name="arch" type="xml">
- <group name="preferences" position="before">
- <div style="margin-top: 19px;">
- <field name="website_published" class="pull-right" widget="website_button"/>
- </div>
- </group>
- </field>
- </record>
-
</data>
</openerp>
<data>
<!-- Editor custo -->
-<template id="editor_head" inherit_id="website.editor_head"
- name="Event Editor">
+<template id="assets_editor" inherit_id="website.assets_editor" name="Forum Editor Assets">
<xpath expr="." position="inside">
<link rel='stylesheet' href="/website_forum/static/src/css/website_forum.css"/>
</xpath>
</template>
+<template id="assets_forum" name="Forum Assets">
+ <link rel='stylesheet' href="/web/static/lib/jquery.textext/jquery.textext.css"/>
+ <link rel='stylesheet' href='/website_forum/static/src/css/website_forum.css'/>
+
+ <script type="text/javascript" src="/website_forum/static/src/js/website_forum.js"/>
+ <script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"/>
+ <script type="text/javascript" src="/web/static/lib/ckeditor/ckeditor.js"/>
+ <script type="text/javascript">
+ CKEDITOR.config.toolbar = [['Bold','Italic','Underline','Strike'],['NumberedList','BulletedList', 'Blockquote']
+ ,['Outdent','Indent','Link','Unlink','Image'],] ;
+ </script>
+</template>
+
<!-- Layout add nav and footer -->
<template id="header_footer_custom" inherit_id="website.footer_default"
name="Footer Questions Link">
<template id="header" name="Forum Index">
<t t-call="website.layout">
<t t-set="head">
- <link rel='stylesheet' href="/web/static/lib/jquery.textext/jquery.textext.css"/>
- <link rel='stylesheet' href='/website_forum/static/src/css/website_forum.css'/>
- <script type="text/javascript" src="/website_forum/static/src/js/website_forum.js"/>
- <script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"/>
- <script type="text/javascript" src="/web/static/lib/ckeditor/ckeditor.js"/>
- <script type="text/javascript">
- CKEDITOR.config.toolbar = [['Bold','Italic','Underline','Strike'],['NumberedList','BulletedList', 'Blockquote']
- ,['Outdent','Indent','Link','Unlink','Image'],] ;
- </script>
+ <t t-call-assets="website_forum.assets_forum"/>
</t>
<div class="container mt16">
<div class="navbar navbar-default">
<!-- Edition: ask your question -->
<template id="ask_question">
<t t-call="website_forum.header">
- <h1 class="mt0">Ask your Question</h1>
+ <h1 class="mt0">Ask Your Question</h1>
+ <p>
+ To improve your chance getting an answer:
+ </p>
<ul>
- <li> please, try to make your question interesting to others </li>
- <li> provide enough details and, if possible, give an example </li>
- <li> be clear and concise, avoid unnecessary introductions (Hi, ... Thanks...) </li>
+ <li>Set a clear, explicit and concise question title
+ (check
+ <a href="#" data-placement="top" data-toggle="popover" data-content="Inventory Date Problem, Task remaining hours, Can you help solve solve my tax computation problem in Canada?" title="Click to get bad question samples">bad examples</a>
+ and
+ <a href="#" data-placement="bottom" data-toggle="popover" data-content="How to create a physical inventory at an anterior date?, How is the 'remaining hours' field computed on tasks?, How to configure TPS and TVQ's canadian taxes?" title="Click to get good question titles">good examples</a>
+ ),
+ </li>
+ <li>Avoid unnecessary introductions (Hi,... Please... Thanks...),</li>
+ <li>Provide enough details and, if possible, give an example.</li>
</ul>
+ <script type="text/javascript">
+ $(function () {
+ $("[data-toggle='popover']").popover();
+ });
+ </script>
<form t-attf-action="/forum/#{ slug(forum) }/question/new" method="post" role="form" class="tag_text">
<input type="text" name="question_name" required="True" t-attf-value="#{question_name}"
- class="form-control" placeholder="Enter your Question"/>
- <h5 class="mt20">Please enter a descriptive question (should finish by a '?')</h5>
+ class="form-control mb16" placeholder="Your Question Title..."/>
<input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
<textarea name="content" required="True" class="form-control load_editor">
<t t-esc="question_content"/>
<!-- Specific Post Layout -->
<template id="post_description_full" name="Question Navigation">
<t t-call="website_forum.header">
- <div t-attf-class="question #{not question.active and 'alert alert-danger' or ''}">
+ <div t-attf-class="question">
<div class="col-md-2 hidden-xs text-center">
<t t-call="website_forum.vote">
<t t-set="post" t-value="question"/>
t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
</div>
</div>
- <div class="col-md-10">
+ <div t-attf-class="col-md-10 #{not question.active and 'alert alert-danger' or ''}">
<h1 class="mt0">
<a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
<span t-if="not question.active"><b> [Deleted]</b></span>
</div>
</div>
</div>
- <div t-if="not question.uid_has_answered">
+ <div t-if="not question.uid_has_answered and question.state != 'close' and question.active != False">
<t t-call="website_forum.post_answer"/>
</div>
<div t-if="question.uid_has_answered" class="mb16">
class="close comment_delete">&times;</button>
<span t-field="message.body"/>
<t t-set="required_karma" t-value="message.author_id.id == user.partner_id.id and object.forum_id.karma_comment_convert_own or object.forum_id.karma_comment_convert_all"/>
- <t t-call="website_forum.link_button">
- <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
- <t t-set="label" t-value="'Convert as an answer'"/>
- <t t-set="karma" t-value="user.karma<required_karma and required_karma or 0"/>
- <t t-set="classes" t-value="'fa-magic pull-right'"/>
+ <t t-if="(object.parent_id and object.parent_id.state != 'close' and object.parent_id.active != False) or (not object.parent_id and object.state != 'close' and object.active != False)">
+ <t t-set="allow_post_comment" t-value="True" />
+ </t>
+ <t t-if="allow_post_comment">
+ <t t-call="website_forum.link_button" >
+ <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
+ <t t-set="label" t-value="'Convert as an answer'"/>
+ <t t-set="karma" t-value="user.karma<required_karma and required_karma or 0"/>
+ <t t-set="classes" t-value="'fa-magic pull-right'"/>
+ </t>
</t>
<a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
def _get_ids(self, cr, uid, ids, flds, args, context=None):
return {i: i for i in ids}
+ def _set_private(self, cr, uid, ids, field_name, value, arg, context=None):
+ return self.write(cr, uid, ids, {'website_published': not value}, context=context)
+
+ def _get_private(self, cr, uid, ids, field_name, arg, context=None):
+ return dict((rec.id, not rec.website_published) for rec in self.browse(cr, uid, ids, context=context))
+
+ def _search_private(self, cr, uid, obj, name, args, context=None):
+ return [('website_published', '=', not args[0][2])]
+
_columns = {
'website_published': fields.boolean(
'Publish', help="Publish on the website", copy=False),
+ 'website_private': fields.function(
+ _get_private, fnct_inv=_set_private, fnct_search=_search_private,
+ type='boolean', string='Private Profile'),
'website_description': fields.html(
'Website Partner Full Description'
),
<field name="arch" type="xml">
<data>
<field name="active" position="after">
- <field name="website_published"/>
+ <field name="website_private"/>
</field>
</data>
</field>
# only if he knows the private token
order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id)
now = time.strftime('%Y-%m-%d')
+ dummy, action = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'sale', 'action_quotations')
if token:
if token != order.access_token:
return request.website.render('website.404')
'message': message and int(message) or False,
'option': bool(filter(lambda x: not x.line_id, order.options)),
'order_valid': (not order.validity_date) or (now <= order.validity_date),
- 'days_valid': max(days, 0)
+ 'days_valid': max(days, 0),
+ 'action': action
}
return request.website.render('website_quote.so_quotation', values)
<em t-esc="quotation.name"/>
<small t-field="quotation.state"/>
<div groups="base.group_website_publisher" t-ignore="true" class="pull-right css_editable_mode_hidden">
- <a class="btn btn-info hidden-print" t-att-href="'/web#return_label=Website&model=%s&id=%s' % (quotation._name, quotation.id)">Update Quote</a>
+ <a class="btn btn-info hidden-print" t-att-href="'/web#return_label=Website&model=%s&id=%s&action=%s&view_type=form' % (quotation._name, quotation.id, action)">Update Quote</a>
</div>
</h1>
</div>
<a id="offer"/>
- <div t-field="quotation.website_description"/>
+ <div t-field="quotation.website_description" class="oe_no_empty"/>
<t t-foreach="quotation.order_line" t-as="line">
<a t-att-id="line.id"/>
- <div t-field="line.website_description"/>
+ <div t-field="line.website_description" class="oe_no_empty"/>
</t>
<div class="oe_structure"/>
table of content automatically.
</p>
</div>
- <div id="template_introduction" t-field="template.website_description"/>
+ <div id="template_introduction" t-field="template.website_description" class="oe_no_empty"/>
<t t-foreach="template.quote_line" t-as="line">
<div class="alert alert-info alert-dismissable" t-ignore="True">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
this content will appear on the quotation only if this
product is put on the quote.
</div>
- <div t-field="line.website_description"/>
+ <div t-field="line.website_description" class="oe_no_empty"/>
</t>
<t t-foreach="template.options" t-as="option_line">
<div class="alert alert-info alert-dismissable" t-ignore="True">
this content will appear on the quotation only if this
product is used in the quote.
</div>
- <div t-field="option_line.website_description"/>
+ <div t-field="option_line.website_description" class="oe_no_empty"/>
</t>
<section id="terms" class="container" t-if="template.note">
<h1 class="page-header" t-ignore="True">Terms & Conditions</h1>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
- <xpath expr="//header/button[@name='invoice_corrected']" position="after">
- <button name="open_quotation" string="View Quotation" type="object"
+ <xpath expr="//header/button[@name='action_button_confirm']" position="before">
+ <button name="open_quotation" string="Preview Quotation" type="object"
attrs="{'invisible': [('template_id','=',False)]}"/>
</xpath>
<xpath expr="//page[@string='Order Lines']" position="after">
val = {
'name': value.split("\n")[0],
- 'address': escape("\n".join(value.split("\n")[1:])),
+ 'address': escape("\n".join(value.split("\n")[1:])).strip(),
'phone': field_browse.phone,
'mobile': field_browse.mobile,
'fax': field_browse.fax,
pass
class AssetsBundle(object):
- # Sass installation:
- #
- # sudo gem install sass compass bootstrap-sass
- #
- # If the following error is encountered:
- # 'ERROR: Cannot load compass.'
- # Use this:
- # sudo gem install compass --pre
- cmd_sass = ['sass', '--stdin', '-t', 'compressed', '--unix-newlines', '--compass', '-r', 'bootstrap-sass']
cache = openerp.tools.lru.LRU(32)
rx_css_import = re.compile("(@import[^;{]+;?)", re.M)
- rx_sass_import = re.compile("""(@import\s?['"]([^'"]+)['"])""")
+ rx_preprocess_imports = re.compile("""(@import\s?['"]([^'"]+)['"](;?))""")
rx_css_split = re.compile("\/\*\! ([a-f0-9-]+) \*\/")
def __init__(self, xmlid, debug=False, cr=None, uid=None, context=None, registry=None):
media = el.get('media')
if el.tag == 'style':
if atype == 'text/sass' or src.endswith('.sass'):
- self.stylesheets.append(SassAsset(self, inline=el.text, media=media))
+ self.stylesheets.append(SassStylesheetAsset(self, inline=el.text, media=media))
+ elif atype == 'text/less' or src.endswith('.less'):
+ self.stylesheets.append(LessStylesheetAsset(self, inline=el.text, media=media))
else:
self.stylesheets.append(StylesheetAsset(self, inline=el.text, media=media))
elif el.tag == 'link' and el.get('rel') == 'stylesheet' and self.can_aggregate(href):
if href.endswith('.sass') or atype == 'text/sass':
- self.stylesheets.append(SassAsset(self, url=href, media=media))
+ self.stylesheets.append(SassStylesheetAsset(self, url=href, media=media))
+ elif href.endswith('.less') or atype == 'text/less':
+ self.stylesheets.append(LessStylesheetAsset(self, url=href, media=media))
else:
self.stylesheets.append(StylesheetAsset(self, url=href, media=media))
elif el.tag == 'script' and not src:
response = []
if debug:
if css and self.stylesheets:
- self.compile_sass()
+ self.preprocess_css()
for style in self.stylesheets:
response.append(style.to_html())
if js:
# Invalidate cache on version mismach
self.cache.pop(key)
if key not in self.cache:
- self.compile_sass()
+ self.preprocess_css()
content = '\n'.join(asset.minify() for asset in self.stylesheets)
if self.css_errors:
}
""" % message.replace('"', '\\"')
- def compile_sass(self):
+ def preprocess_css(self):
"""
- Checks if the bundle contains any sass content, then compiles it to css.
- Css compilation is done at the bundle level and not in the assets
- because they are potentially interdependant.
+ Checks if the bundle contains any sass/less content, then compiles it to css.
"""
- sass = [asset for asset in self.stylesheets if isinstance(asset, SassAsset)]
- if not sass:
+ to_preprocess = [asset for asset in self.stylesheets if isinstance(asset, PreprocessedCSS)]
+ if not to_preprocess:
return
- source = '\n'.join([asset.get_source() for asset in sass])
+ to_compile = [asset for asset in to_preprocess if type(asset) == type(to_preprocess[0])]
+ if len(to_preprocess) != len(to_compile):
+ self.css_errors.append("You can't mix css preprocessors languages in the same bundle. (%s)" % self.xmlid)
+ command = to_compile[0].get_command()
+ source = '\n'.join([asset.get_source() for asset in to_compile])
# move up all @import rules to the top and exclude file imports
imports = []
def push(matchobj):
ref = matchobj.group(2)
- line = '@import "%s"' % ref
+ line = '@import "%s"%s' % (ref, matchobj.group(3))
if '.' not in ref and line not in imports and not ref.startswith(('.', '/', '~')):
imports.append(line)
return ''
- source = re.sub(self.rx_sass_import, push, source)
+ source = re.sub(self.rx_preprocess_imports, push, source)
imports.append(source)
source = u'\n'.join(imports)
try:
- compiler = Popen(self.cmd_sass, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ compiler = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
except Exception:
- msg = "Could not find 'sass' program needed to compile sass/scss files"
+ msg = "Could not execute command %r" % command[0]
_logger.error(msg)
self.css_errors.append(msg)
return
result = compiler.communicate(input=source.encode('utf-8'))
if compiler.returncode:
- error = self.get_sass_error(''.join(result), source=source)
+ error = self.get_preprocessor_error(''.join(result), source=source)
_logger.warning(error)
self.css_errors.append(error)
return
fragments = self.rx_css_split.split(compiled)[1:]
while fragments:
asset_id = fragments.pop(0)
- asset = next(asset for asset in sass if asset.id == asset_id)
+ asset = next(asset for asset in to_compile if asset.id == asset_id)
asset._content = fragments.pop(0)
- def get_sass_error(self, stderr, source=None):
+ def get_preprocessor_error(self, stderr, source=None):
# TODO: try to find out which asset the error belongs to
error = stderr.split('Load paths')[0].replace(' Use --trace for backtrace.', '')
+ if 'Cannot load compass' in error:
+ error += "Maybe you should install the compass gem using this extra argument:\n\n" \
+ " $ sudo gem install compass --pre\n"
error += "This error occured while compiling the bundle '%s' containing:" % self.xmlid
for asset in self.stylesheets:
- if isinstance(asset, SassAsset):
+ if isinstance(asset, PreprocessedCSS):
error += '\n - %s' % (asset.url if asset.url else '<inline sass>')
return error
else:
return '<style type="text/css"%s>%s</style>' % (media, self.with_header())
-class SassAsset(StylesheetAsset):
+class PreprocessedCSS(StylesheetAsset):
html_url = '%s.css'
- rx_indent = re.compile(r'^( +|\t+)', re.M)
- indent = None
- reindent = ' '
def minify(self):
return self.with_header()
name=url,
url=url,
), context=self.context)
- return super(SassAsset, self).to_html()
+ return super(PreprocessedCSS, self).to_html()
+
+ def get_source(self):
+ content = self.inline or self._fetch_content()
+ return "/*! %s */\n%s" % (self.id, content)
+
+ def get_command(self):
+ raise NotImplementedError
+
+class SassStylesheetAsset(PreprocessedCSS):
+ rx_indent = re.compile(r'^( +|\t+)', re.M)
+ indent = None
+ reindent = ' '
def get_source(self):
content = textwrap.dedent(self.inline or self._fetch_content())
def fix_indent(m):
+ # Indentation normalization
ind = m.group()
if self.indent is None:
self.indent = ind
pass
return "/*! %s */\n%s" % (self.id, content)
+ def get_command(self):
+ return ['sass', '--stdin', '-t', 'compressed', '--unix-newlines', '--compass',
+ '-r', 'bootstrap-sass']
+
+class LessStylesheetAsset(PreprocessedCSS):
+ def get_command(self):
+ webpath = openerp.http.addons_manifest['web']['addons_path']
+ lesspath = os.path.join(webpath, 'web', 'static', 'lib', 'bootstrap', 'less')
+ return ['lessc', '-', '--clean-css', '--no-js', '--no-color', '--include-path=%s' % lesspath]
+
def rjsmin(script):
""" Minify js with a clever regex.
Taken from http://opensource.perlig.de/rjsmin
'title': fields.many2one('res.partner.title', 'Title'),
'parent_id': fields.many2one('res.partner', 'Related Company', select=True),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override
- 'ref': fields.char('Contact Reference', select=1),
+ 'ref': fields.char('Internal Reference', select=1),
'lang': fields.selection(_lang_get, 'Language',
help="If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English."),
'tz': fields.selection(_tz_get, 'Timezone', size=64,
<group>
<field name="ref"/>
<field name="lang"/>
- <field name="date"/>
</group>
- <group>
+ </group>
+ <group>
+ <group string="Mailing" name="mailing">
<field name="active"/>
</group>
</group>
+ <group>
+ <group name="invoicing"/>
+ <group name="point_of_sale"/>
+ </group>
</page>
</notebook>
</sheet>
# Default legacy command
command = "server"
+ # TODO: find a way to properly discover addons subcommands without importing the world
# Subcommand discovery
if len(args) and not args[0].startswith("-"):
- logging.disable(logging.CRITICAL)
- for m in module.get_modules():
- m = 'openerp.addons.' + m
- __import__(m)
- #try:
- #except Exception, e:
- # raise
- # print e
- logging.disable(logging.NOTSET)
+ # logging.disable(logging.CRITICAL)
+ # for m in module.get_modules():
+ # m = 'openerp.addons.' + m
+ # __import__(m)
+ # logging.disable(logging.NOTSET)
command = args[0]
args = args[1:]
# properly comparable using normal operarors, for example:
# (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2)
# (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
-version_info = (8, 0, 0, RELEASE_CANDIDATE, 1)
+version_info = (9, 0, 0, ALPHA, 1)
version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '')
series = serie = major_version = '.'.join(map(str, version_info[:2]))