mirror of
https://github.com/OCA/account-financial-tools.git
synced 2025-02-02 12:47:26 +02:00
Merge branch '8.0' into 8.0-add-account_constraint_chronology
This commit is contained in:
15
.coveragerc
Normal file
15
.coveragerc
Normal file
@@ -0,0 +1,15 @@
|
||||
[report]
|
||||
include =
|
||||
*/account-financial-tools/*
|
||||
|
||||
omit =
|
||||
*/tests/*
|
||||
*__init__.py
|
||||
|
||||
# Regexes for lines to exclude from consideration
|
||||
exclude_lines =
|
||||
# Have to re-enable the standard pragma
|
||||
pragma: no cover
|
||||
|
||||
# Don't complain about null context checking
|
||||
if context is None:
|
||||
25
.travis.yml
Normal file
25
.travis.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
# Config file .travis.yml
|
||||
|
||||
language: python
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
|
||||
env:
|
||||
- VERSION="8.0" ODOO_REPO="odoo/odoo"
|
||||
- VERSION="8.0" ODOO_REPO="OCA/OCB"
|
||||
|
||||
virtualenv:
|
||||
system_site_packages: true
|
||||
|
||||
install:
|
||||
- git clone https://github.com/oca/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
|
||||
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
|
||||
- travis_install_nightly ${VERSION}
|
||||
|
||||
script:
|
||||
- travis_run_flake8
|
||||
- travis_run_tests
|
||||
|
||||
after_success:
|
||||
coveralls
|
||||
@@ -18,5 +18,3 @@
|
||||
#
|
||||
##############################################################################
|
||||
from . import account_invoice
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -18,20 +18,18 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Cancel invoice, check on payment order",
|
||||
"version" : "1.0",
|
||||
"depends" : ["account",
|
||||
"account_payment",
|
||||
"account_cancel"
|
||||
],
|
||||
"author" : "Camptocamp",
|
||||
"name": "Cancel invoice, check on payment order",
|
||||
"version": "1.0",
|
||||
"depends": ["account",
|
||||
"account_payment",
|
||||
"account_cancel"],
|
||||
"author": "Camptocamp",
|
||||
"description": """
|
||||
Prevents to cancel an invoice which has already been imported in a
|
||||
payment order.
|
||||
""",
|
||||
'website': 'http://www.camptocamp.com',
|
||||
'data' : [],
|
||||
'installable': False,
|
||||
'data': [],
|
||||
'installable': True,
|
||||
'active': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -45,10 +45,11 @@ class account_invoice(orm.Model):
|
||||
payment_orders = cr.dictfetchone()
|
||||
if payment_orders:
|
||||
raise osv.except_osv(
|
||||
_('Error !'),
|
||||
_("Invoice already imported in the payment "
|
||||
"order (%s) at %s on line %s" %
|
||||
(payment_orders['payment_name'],
|
||||
payment_orders['payment_date'],
|
||||
payment_orders['name'])))
|
||||
return super(account_invoice,self).action_cancel(cr, uid, ids, *args)
|
||||
_('Error !'),
|
||||
_("Invoice already imported in the payment "
|
||||
"order (%s) at %s on line %s" %
|
||||
(payment_orders['payment_name'],
|
||||
payment_orders['payment_date'],
|
||||
payment_orders['name']))
|
||||
)
|
||||
return super(account_invoice, self).action_cancel(cr, uid, ids, *args)
|
||||
|
||||
@@ -19,5 +19,3 @@
|
||||
##############################################################################
|
||||
|
||||
from . import account_invoice
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Cancel invoice, check on bank statement",
|
||||
"version" : "1.0",
|
||||
"depends" : ["base",
|
||||
"account",
|
||||
"account_voucher",
|
||||
"account_cancel",
|
||||
],
|
||||
"author" : "Camptocamp",
|
||||
"description": """Constraint forbidding to cancel an invoice already imported in bank statement with a voucher.
|
||||
"name": "Cancel invoice, check on bank statement",
|
||||
"version": "1.0",
|
||||
"depends": ["base",
|
||||
"account",
|
||||
"account_voucher",
|
||||
"account_cancel"],
|
||||
"author": "Camptocamp",
|
||||
"description": """
|
||||
Constraint forbidding to cancel an invoice already
|
||||
imported in bank statement with a voucher.
|
||||
""",
|
||||
'website': 'http://www.camptocamp.com',
|
||||
'date' : [],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import osv, orm
|
||||
|
||||
|
||||
class account_invoice(orm.Model):
|
||||
_inherit = "account.invoice"
|
||||
|
||||
@@ -31,25 +32,26 @@ class account_invoice(orm.Model):
|
||||
invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
|
||||
for invoice in invoices:
|
||||
if invoice['move_id']:
|
||||
## This invoice have a move line, we search move_line concerned by this move
|
||||
# This invoice have a move line, we search move_line concerned by this move
|
||||
cr.execute("""SELECT abs.name AS statement_name,
|
||||
abs.date AS statement_date,
|
||||
absl.name
|
||||
FROM account_bank_statement_line AS absl
|
||||
INNER JOIN account_bank_statement AS abs
|
||||
ON absl.statement_id = abs.id
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM account_voucher_line JOIN account_move_line ON
|
||||
(account_voucher_line.move_line_id = account_move_line.id)
|
||||
WHERE voucher_id=absl.voucher_id
|
||||
AND account_move_line.move_id = %s )""",
|
||||
(invoice['move_id'][0],))
|
||||
ON absl.statement_id = abs.id
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM account_voucher_line JOIN account_move_line ON
|
||||
(account_voucher_line.move_line_id = account_move_line.id)
|
||||
WHERE voucher_id=absl.voucher_id
|
||||
AND account_move_line.move_id = %s )""",
|
||||
(invoice['move_id'][0],))
|
||||
statement_lines = cr.dictfetchone()
|
||||
if statement_lines:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Invoice already imported in bank statment (%s) at %s on line %s'
|
||||
% (statement_lines['statement_name'],
|
||||
statement_lines['statement_date'],
|
||||
statement_lines['name'],)))
|
||||
raise osv.except_osv(
|
||||
_('Error!'),
|
||||
_('Invoice already imported in bank statment (%s) at %s on line %s'
|
||||
% (statement_lines['statement_name'],
|
||||
statement_lines['statement_date'],
|
||||
statement_lines['name'],)))
|
||||
|
||||
return super(account_invoice,self).action_cancel(cr, uid, ids, context=context)
|
||||
return super(account_invoice, self).action_cancel(cr, uid, ids, context=context)
|
||||
|
||||
@@ -96,23 +96,77 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
('ready', 'Step 2'),
|
||||
('done', 'Wizard completed')
|
||||
], 'Status', readonly=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True, ondelete='set null'),
|
||||
'company_id': fields.many2one(
|
||||
'res.company',
|
||||
'Company',
|
||||
required=True,
|
||||
ondelete='set null'
|
||||
),
|
||||
'chart_template_id': fields.many2one(
|
||||
'account.chart.template', 'Chart Template', ondelete='cascade',
|
||||
required=True),
|
||||
'code_digits': fields.integer('# of digits', required=True, help="No. of digits to use for account code. Make sure it is the same number as existing accounts."),
|
||||
'lang': fields.selection(_get_lang_selection_options, 'Language', size=5, help="For records searched by name (taxes, tax codes, fiscal positions), the template name will be matched against the record name on this language."),
|
||||
'update_tax_code': fields.boolean('Update tax codes', help="Existing tax codes are updated. Tax codes are searched by name."),
|
||||
'update_tax': fields.boolean('Update taxes', help="Existing taxes are updated. Taxes are searched by name."),
|
||||
'update_account': fields.boolean('Update accounts', help="Existing accounts are updated. Accounts are searched by code."),
|
||||
'update_fiscal_position': fields.boolean('Update fiscal positions', help="Existing fiscal positions are updated. Fiscal positions are searched by name."),
|
||||
'update_children_accounts_parent': fields.boolean("Update children accounts parent",
|
||||
help="Update the parent of accounts that seem (based on the code) to be children of the newly created ones. If you had an account 430 with a child 4300000, and a 4300 account is created, the 4300000 parent will be set to 4300."),
|
||||
'continue_on_errors': fields.boolean("Continue on errors", help="If set, the wizard will continue to the next step even if there are minor errors (for example the parent account of a new account couldn't be set)."),
|
||||
'tax_code_ids': fields.one2many('wizard.update.charts.accounts.tax.code', 'update_chart_wizard_id', 'Tax codes', ondelete='cascade'),
|
||||
'tax_ids': fields.one2many('wizard.update.charts.accounts.tax', 'update_chart_wizard_id', 'Taxes', ondelete='cascade'),
|
||||
'account_ids': fields.one2many('wizard.update.charts.accounts.account', 'update_chart_wizard_id', 'Accounts', ondelete='cascade'),
|
||||
'fiscal_position_ids': fields.one2many('wizard.update.charts.accounts.fiscal.position', 'update_chart_wizard_id', 'Fiscal positions', ondelete='cascade'),
|
||||
'account.chart.template',
|
||||
'Chart Template',
|
||||
ondelete='cascade',
|
||||
required=True
|
||||
),
|
||||
'code_digits': fields.integer(
|
||||
'# of digits',
|
||||
required=True,
|
||||
help="No. of digits to use for account code. "
|
||||
"Make sure it is the same number as existing accounts."
|
||||
),
|
||||
'lang': fields.selection(
|
||||
_get_lang_selection_options,
|
||||
'Language',
|
||||
size=5,
|
||||
help="For records searched by name (taxes, tax codes, fiscal positions), "
|
||||
"the template name will be matched against the record name on this language."
|
||||
),
|
||||
'update_tax_code': fields.boolean(
|
||||
'Update tax codes',
|
||||
help="Existing tax codes are updated."
|
||||
" Tax codes are searched by name."
|
||||
),
|
||||
'update_tax': fields.boolean(
|
||||
'Update taxes',
|
||||
help="Existing taxes are updated. Taxes are searched by name."
|
||||
),
|
||||
'update_account': fields.boolean(
|
||||
'Update accounts',
|
||||
help="Existing accounts are updated. Accounts are searched by code."
|
||||
),
|
||||
'update_fiscal_position': fields.boolean(
|
||||
'Update fiscal positions',
|
||||
help="Existing fiscal positions are updated. Fiscal positions are searched by name."
|
||||
),
|
||||
'update_children_accounts_parent': fields.boolean(
|
||||
"Update children accounts parent",
|
||||
help="Update the parent of accounts that seem (based on the code)"
|
||||
" to be children of the newly created ones."
|
||||
" If you had an account 430 with a child 4300000, and a 4300 "
|
||||
"account is created, the 4300000 parent will be set to 4300."
|
||||
),
|
||||
'continue_on_errors': fields.boolean(
|
||||
"Continue on errors",
|
||||
help="If set, the wizard will continue to the next step even if "
|
||||
"there are minor errors (for example the parent account "
|
||||
"of a new account couldn't be set)."
|
||||
),
|
||||
'tax_code_ids': fields.one2many(
|
||||
'wizard.update.charts.accounts.tax.code',
|
||||
'update_chart_wizard_id',
|
||||
'Tax codes',
|
||||
ondelete='cascade'
|
||||
),
|
||||
'tax_ids': fields.one2many(
|
||||
'wizard.update.charts.accounts.tax',
|
||||
'update_chart_wizard_id',
|
||||
'Taxes',
|
||||
ondelete='cascade'
|
||||
),
|
||||
'account_ids': fields.one2many('wizard.update.charts.accounts.account',
|
||||
'update_chart_wizard_id', 'Accounts', ondelete='cascade'),
|
||||
'fiscal_position_ids': fields.one2many('wizard.update.charts.accounts.fiscal.position',
|
||||
'update_chart_wizard_id', 'Fiscal positions', ondelete='cascade'),
|
||||
'new_tax_codes': fields.integer('New tax codes', readonly=True),
|
||||
'new_taxes': fields.integer('New taxes', readonly=True),
|
||||
'new_accounts': fields.integer('New accounts', readonly=True),
|
||||
@@ -208,7 +262,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
|
||||
_defaults = {
|
||||
'state': 'init',
|
||||
'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, [uid], context)[0].company_id.id,
|
||||
'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(
|
||||
cr, uid, [uid], context)[0].company_id.id,
|
||||
'chart_template_id': _get_chart,
|
||||
'update_tax_code': True,
|
||||
'update_tax': True,
|
||||
@@ -316,10 +371,10 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
if acc_templ.type != 'view':
|
||||
if code and len(code) <= wizard.code_digits:
|
||||
code = '%s%s' % (code, '0' * (wizard.code_digits - len(code)))
|
||||
acc_ids = acc_obj.search(cr, uid, [
|
||||
('code', '=', code),
|
||||
('company_id', '=', wizard.company_id.id)
|
||||
], context=context)
|
||||
acc_ids = acc_obj.search(cr, uid,
|
||||
[('code', '=', code),
|
||||
('company_id', '=', wizard.company_id.id)],
|
||||
context=context)
|
||||
acc_templ_mapping[acc_templ.id] = acc_ids and acc_ids[0] or False
|
||||
return acc_templ_mapping[acc_templ.id]
|
||||
|
||||
@@ -344,7 +399,7 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
context=None):
|
||||
"""
|
||||
Search for, and load, tax code templates to create/update.
|
||||
|
||||
|
||||
@param chart_template_ids: IDs of the chart templates to look on,
|
||||
calculated once in the calling method.
|
||||
"""
|
||||
@@ -363,13 +418,16 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
children_tax_code_template = tax_code_templ_obj.search(cr, uid, [(
|
||||
'parent_id', 'child_of', [root_tax_code_id])], order='id',
|
||||
context=context)
|
||||
for tax_code_template in tax_code_templ_obj.browse(cr, uid,
|
||||
children_tax_code_template, context=context):
|
||||
for tax_code_template in tax_code_templ_obj.browse(
|
||||
cr, uid,
|
||||
children_tax_code_template, context=context):
|
||||
# Ensure the tax code template is on the map (search for the mapped
|
||||
# tax code id).
|
||||
tax_code_id = self._map_tax_code_template(cr, uid, wizard,
|
||||
tax_code_template_mapping,
|
||||
tax_code_template, context=context)
|
||||
tax_code_id = self._map_tax_code_template(
|
||||
cr, uid, wizard,
|
||||
tax_code_template_mapping,
|
||||
tax_code_template, context=context
|
||||
)
|
||||
if not tax_code_id:
|
||||
new_tax_codes += 1
|
||||
wiz_tax_code_obj.create(cr, uid, {
|
||||
@@ -413,7 +471,7 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
def _find_taxes(self, cr, uid, wizard, chart_template_ids, context=None):
|
||||
"""
|
||||
Search for, and load, tax templates to create/update.
|
||||
|
||||
|
||||
@param chart_template_ids: IDs of the chart templates to look on,
|
||||
calculated once in the calling method.
|
||||
"""
|
||||
@@ -527,9 +585,11 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
'=',
|
||||
wizard.chart_template_id.id)]
|
||||
if root_account_id:
|
||||
acc_templ_criteria = (['|'] + acc_templ_criteria +
|
||||
['&', ('parent_id', 'child_of', [root_account_id]),
|
||||
('chart_template_id', '=', False)])
|
||||
acc_templ_criteria = (
|
||||
['|'] + acc_templ_criteria +
|
||||
['&', ('parent_id', 'child_of', [root_account_id]),
|
||||
('chart_template_id', '=', False)]
|
||||
)
|
||||
acc_ids = acc_templ_obj.search(cr, uid, acc_templ_criteria,
|
||||
context=context)
|
||||
acc_ids.sort()
|
||||
@@ -577,17 +637,15 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
'update_account_id': account_id,
|
||||
'notes': notes,
|
||||
}, context)
|
||||
return {
|
||||
'new': new_accounts,
|
||||
return {'new': new_accounts,
|
||||
'updated': updated_accounts,
|
||||
'mapping': acc_templ_mapping
|
||||
}
|
||||
'mapping': acc_templ_mapping}
|
||||
|
||||
def _find_fiscal_positions(self, cr, uid, wizard, chart_template_ids,
|
||||
context=None):
|
||||
"""
|
||||
Search for, and load, fiscal position templates to create/update.
|
||||
|
||||
|
||||
@param chart_template_ids: IDs of the chart templates to look on,
|
||||
calculated once in the calling method.
|
||||
"""
|
||||
@@ -608,7 +666,7 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
chart_template_ids)],
|
||||
context=context)
|
||||
for fp_templ in fp_templ_obj.browse(cr, uid, fp_template_ids,
|
||||
context=context):
|
||||
context=context):
|
||||
# Ensure the fiscal position template is on the map (search for the
|
||||
# mapped fiscal position id).
|
||||
fp_id = self._map_fp_template(cr, uid, wizard, fp_templ_mapping,
|
||||
@@ -651,8 +709,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
break
|
||||
if not found:
|
||||
notes += _("Tax mapping not found on the fiscal position instance: %s -> %s.\n") % (
|
||||
fp_tax_templ.tax_src_id.name,
|
||||
fp_tax_templ.tax_dest_id and fp_tax_templ.tax_dest_id.name or _('None'))
|
||||
fp_tax_templ.tax_src_id.name,
|
||||
fp_tax_templ.tax_dest_id and fp_tax_templ.tax_dest_id.name or _('None'))
|
||||
modified = True
|
||||
elif fp_templ.tax_ids and not fp.tax_ids:
|
||||
notes += _("The template has taxes the fiscal position instance does not.\n")
|
||||
@@ -673,10 +731,11 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
notes += _("Account mapping not found on the fiscal "
|
||||
"position instance: %s -> %s.\n") % \
|
||||
(fp_acc_templ.account_src_id.name,
|
||||
fp_acc_templ.account_dest_id.name)
|
||||
notes += _(
|
||||
"Account mapping not found on the fiscal "
|
||||
"position instance: %s -> %s.\n") \
|
||||
% (fp_acc_templ.account_src_id.name,
|
||||
fp_acc_templ.account_dest_id.name)
|
||||
modified = True
|
||||
elif fp_templ.account_ids and not fp.account_ids:
|
||||
notes += _("The template has accounts the fiscal position "
|
||||
@@ -755,7 +814,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
tax_code_template_mapping = {}
|
||||
for wiz_tax_code in wizard.tax_code_ids:
|
||||
tax_code_template = wiz_tax_code.tax_code_id
|
||||
tax_code_name = (root_tax_code_id == tax_code_template.id) and wizard.company_id.name or tax_code_template.name
|
||||
tax_code_name = ((root_tax_code_id == tax_code_template.id) and
|
||||
wizard.company_id.name or tax_code_template.name)
|
||||
# Ensure the parent tax code template is on the map.
|
||||
self._map_tax_code_template(cr, uid, wizard,
|
||||
tax_code_template_mapping,
|
||||
@@ -766,7 +826,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
'name': tax_code_name,
|
||||
'code': tax_code_template.code,
|
||||
'info': tax_code_template.info,
|
||||
'parent_id': tax_code_template.parent_id and tax_code_template_mapping.get(tax_code_template.parent_id.id),
|
||||
'parent_id': (tax_code_template.parent_id and
|
||||
tax_code_template_mapping.get(tax_code_template.parent_id.id)),
|
||||
'company_id': wizard.company_id.id,
|
||||
'sign': tax_code_template.sign,
|
||||
}
|
||||
@@ -792,8 +853,13 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
tax_code_template_mapping[tax_code_template.id] = tax_code_id
|
||||
if modified:
|
||||
# Detect errors
|
||||
if tax_code_template.parent_id and not tax_code_template_mapping.get(tax_code_template.parent_id.id):
|
||||
log.add(_("Tax code %s: The parent tax code %s can not be set.\n") % (tax_code_name, tax_code_template.parent_id.name), True)
|
||||
if (tax_code_template.parent_id and
|
||||
not tax_code_template_mapping.get(tax_code_template.parent_id.id)):
|
||||
log.add(
|
||||
_("Tax code %s: The parent tax code %s can not be set.\n") % (
|
||||
tax_code_name, tax_code_template.parent_id.name),
|
||||
True
|
||||
)
|
||||
return {
|
||||
'new': new_tax_codes,
|
||||
'updated': updated_tax_codes,
|
||||
@@ -838,13 +904,16 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
'python_compute': tax_template.python_compute,
|
||||
'python_compute_inv': tax_template.python_compute_inv,
|
||||
'python_applicable': tax_template.python_applicable,
|
||||
#'tax_group': tax_template.tax_group,
|
||||
'base_code_id': tax_template.base_code_id and tax_code_template_mapping.get(tax_template.base_code_id.id),
|
||||
'tax_code_id': tax_template.tax_code_id and tax_code_template_mapping.get(tax_template.tax_code_id.id),
|
||||
'base_code_id': (tax_template.base_code_id and
|
||||
tax_code_template_mapping.get(tax_template.base_code_id.id)),
|
||||
'tax_code_id': (tax_template.tax_code_id and
|
||||
tax_code_template_mapping.get(tax_template.tax_code_id.id)),
|
||||
'base_sign': tax_template.base_sign,
|
||||
'tax_sign': tax_template.tax_sign,
|
||||
'ref_base_code_id': tax_template.ref_base_code_id and tax_code_template_mapping.get(tax_template.ref_base_code_id.id),
|
||||
'ref_tax_code_id': tax_template.ref_tax_code_id and tax_code_template_mapping.get(tax_template.ref_tax_code_id.id),
|
||||
'ref_base_code_id': (tax_template.ref_base_code_id and
|
||||
tax_code_template_mapping.get(tax_template.ref_base_code_id.id)),
|
||||
'ref_tax_code_id': (tax_template.ref_tax_code_id and
|
||||
tax_code_template_mapping.get(tax_template.ref_tax_code_id.id)),
|
||||
'ref_base_sign': tax_template.ref_base_sign,
|
||||
'ref_tax_sign': tax_template.ref_tax_sign,
|
||||
'include_base_amount': tax_template.include_base_amount,
|
||||
@@ -874,20 +943,43 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
if modified:
|
||||
# Add to the dict of taxes waiting for accounts.
|
||||
taxes_pending_for_accounts[tax_id] = {
|
||||
'account_collected_id': tax_template.account_collected_id and tax_template.account_collected_id.id or False,
|
||||
'account_paid_id': tax_template.account_paid_id and tax_template.account_paid_id.id or False,
|
||||
'account_collected_id': (tax_template.account_collected_id and
|
||||
tax_template.account_collected_id.id or False),
|
||||
'account_paid_id': (tax_template.account_paid_id and
|
||||
tax_template.account_paid_id.id or False),
|
||||
}
|
||||
# Detect errors
|
||||
if tax_template.parent_id and not tax_template_mapping.get(tax_template.parent_id.id):
|
||||
log.add(_("Tax %s: The parent tax %s can not be set.\n") % (tax_template.name, tax_template.parent_id.name), True)
|
||||
log.add(
|
||||
_("Tax %s: The parent tax %s can not be set.\n") % (
|
||||
tax_template.name, tax_template.parent_id.name),
|
||||
True
|
||||
)
|
||||
if tax_template.base_code_id and not tax_code_template_mapping.get(tax_template.base_code_id.id):
|
||||
log.add(_("Tax %s: The tax code for the base %s can not be set.\n") % (tax_template.name, tax_template.base_code_id.name), True)
|
||||
log.add(
|
||||
_("Tax %s: The tax code for the base %s can not be set.\n") % (
|
||||
tax_template.name, tax_template.base_code_id.name),
|
||||
True
|
||||
)
|
||||
if tax_template.tax_code_id and not tax_code_template_mapping.get(tax_template.tax_code_id.id):
|
||||
log.add(_("Tax %s: The tax code for the tax %s can not be set.\n") % (tax_template.name, tax_template.tax_code_id.name), True)
|
||||
if tax_template.ref_base_code_id and not tax_code_template_mapping.get(tax_template.ref_base_code_id.id):
|
||||
log.add(_("Tax %s: The tax code for the base refund %s can not be set.\n") % (tax_template.name, tax_template.ref_base_code_id.name), True)
|
||||
log.add(
|
||||
_("Tax %s: The tax code for the tax %s can not be set.\n") % (
|
||||
tax_template.name, tax_template.tax_code_id.name),
|
||||
True
|
||||
)
|
||||
if (tax_template.ref_base_code_id and
|
||||
not tax_code_template_mapping.get(tax_template.ref_base_code_id.id)):
|
||||
log.add(
|
||||
_("Tax %s: The tax code for the base refund %s can not be set.\n") % (
|
||||
tax_template.name, tax_template.ref_base_code_id.name),
|
||||
True
|
||||
)
|
||||
if tax_template.ref_tax_code_id and not tax_code_template_mapping.get(tax_template.ref_tax_code_id.id):
|
||||
log.add(_("Tax %s: The tax code for the tax refund %s can not be set.\n") % (tax_template.name, tax_template.ref_tax_code_id.name), True)
|
||||
log.add(
|
||||
_("Tax %s: The tax code for the tax refund %s can not be set.\n") % (
|
||||
tax_template.name, tax_template.ref_tax_code_id.name),
|
||||
True
|
||||
)
|
||||
return {
|
||||
'new': new_taxes,
|
||||
'updated': updated_taxes,
|
||||
@@ -920,7 +1012,11 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
try:
|
||||
account_account.write(cr, uid, children_ids, {'parent_id': parent_account.id}, context=context)
|
||||
except orm.except_orm, ex:
|
||||
log.add(_("Exception setting the parent of account %s children: %s - %s.\n") % (parent_account.code, ex.name, ex.value), True)
|
||||
log.add(
|
||||
_("Exception setting the parent of account %s children: %s - %s.\n") % (
|
||||
parent_account.code, ex.name, ex.value),
|
||||
True
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -945,7 +1041,9 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
self._map_tax_template(cr, uid, wizard, tax_template_mapping,
|
||||
tax_template, context)
|
||||
# Get the tax ids
|
||||
tax_ids = [tax_template_mapping[tax_template.id] for tax_template in account_template.tax_ids if tax_template_mapping[tax_template.id]]
|
||||
tax_ids = [tax_template_mapping[tax_template.id]
|
||||
for tax_template in account_template.tax_ids
|
||||
if tax_template_mapping[tax_template.id]]
|
||||
# Calculate the account code (we need to add zeros to non-view
|
||||
# account codes)
|
||||
code = account_template.code or ''
|
||||
@@ -956,7 +1054,6 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
# Values
|
||||
vals = {
|
||||
'name': (root_account_id == account_template.id) and wizard.company_id.name or account_template.name,
|
||||
#'sign': account_template.sign,
|
||||
'currency_id': account_template.currency_id and account_template.currency_id.id or False,
|
||||
'code': code,
|
||||
'type': account_template.type,
|
||||
@@ -964,7 +1061,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
'reconcile': account_template.reconcile,
|
||||
'shortcut': account_template.shortcut,
|
||||
'note': account_template.note,
|
||||
'parent_id': account_template.parent_id and account_template_mapping.get(account_template.parent_id.id) or False,
|
||||
'parent_id': (account_template.parent_id
|
||||
and account_template_mapping.get(account_template.parent_id.id) or False),
|
||||
'tax_ids': [(6, 0, tax_ids)],
|
||||
'company_id': wizard.company_id.id,
|
||||
}
|
||||
@@ -998,7 +1096,11 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
if modified:
|
||||
# Detect errors
|
||||
if account_template.parent_id and not account_template_mapping.get(account_template.parent_id.id):
|
||||
log.add(_("Account %s: The parent account %s can not be set.\n") % (code, account_template.parent_id.code), True)
|
||||
log.add(
|
||||
_("Account %s: The parent account %s can not be set.\n") % (
|
||||
code, account_template.parent_id.code),
|
||||
True
|
||||
)
|
||||
# Set this account as the parent of the accounts that seem to
|
||||
# be its children (brothers starting with the same code).
|
||||
if wizard.update_children_accounts_parent:
|
||||
@@ -1037,7 +1139,8 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
self._map_account_template(cr, uid, wizard, acc_templ_mapping,
|
||||
acc_templ, context=context)
|
||||
if value['account_collected_id'] or value['account_paid_id']:
|
||||
if acc_templ_mapping.get(value['account_collected_id']) and acc_templ_mapping.get(value['account_paid_id']):
|
||||
if (acc_templ_mapping.get(value['account_collected_id']) and
|
||||
acc_templ_mapping.get(value['account_paid_id'])):
|
||||
vals = {
|
||||
'account_collected_id': acc_templ_mapping[value['account_collected_id']],
|
||||
'account_paid_id': acc_templ_mapping[value['account_paid_id']],
|
||||
@@ -1108,9 +1211,17 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
fiscalpositions_taxes.create(cr, uid, vals_tax)
|
||||
# Check for errors
|
||||
if not tax_template_mapping.get(fp_tax.tax_src_id.id):
|
||||
log.add(_("Fiscal position %s: The source tax %s can not be set.\n") % (fp_template.name, fp_tax.tax_src_id.code), True)
|
||||
log.add(
|
||||
_("Fiscal position %s: The source tax %s can not be set.\n") % (
|
||||
fp_template.name, fp_tax.tax_src_id.code),
|
||||
True
|
||||
)
|
||||
if fp_tax.tax_dest_id and not tax_template_mapping.get(fp_tax.tax_dest_id.id):
|
||||
log.add(_("Fiscal position %s: The destination tax %s can not be set.\n") % (fp_template.name, fp_tax.tax_dest_id.name), True)
|
||||
log.add(
|
||||
_("Fiscal position %s: The destination tax %s can not be set.\n") % (
|
||||
fp_template.name, fp_tax.tax_dest_id.name),
|
||||
True
|
||||
)
|
||||
# (Re)create the account mappings
|
||||
for fp_account in fp_template.account_ids:
|
||||
# Ensure the related account templates are on the map.
|
||||
@@ -1126,15 +1237,24 @@ class wizard_update_charts_accounts(orm.TransientModel):
|
||||
# Create the fp account mapping
|
||||
vals_account = {
|
||||
'account_src_id': acc_templ_mapping.get(fp_account.account_src_id.id),
|
||||
'account_dest_id': fp_account.account_dest_id and acc_templ_mapping.get(fp_account.account_dest_id.id),
|
||||
'account_dest_id': (fp_account.account_dest_id and
|
||||
acc_templ_mapping.get(fp_account.account_dest_id.id)),
|
||||
'position_id': fp_id,
|
||||
}
|
||||
fiscalpositions_account.create(cr, uid, vals_account)
|
||||
# Check for errors
|
||||
if not acc_templ_mapping.get(fp_account.account_src_id.id):
|
||||
log.add(_("Fiscal position %s: The source account %s can not be set.\n") % (fp_template.name, fp_account.account_src_id.code), True)
|
||||
log.add(
|
||||
_("Fiscal position %s: The source account %s can not be set.\n") % (
|
||||
fp_template.name, fp_account.account_src_id.code),
|
||||
True
|
||||
)
|
||||
if fp_account.account_dest_id and not acc_templ_mapping.get(fp_account.account_dest_id.id):
|
||||
log.add(_("Fiscal position %s: The destination account %s can not be set.\n") % (fp_template.name, fp_account.account_dest_id.code), True)
|
||||
log.add(
|
||||
_("Fiscal position %s: The destination account %s can not be set.\n") % (
|
||||
fp_template.name, fp_account.account_dest_id.code),
|
||||
True
|
||||
)
|
||||
|
||||
log.add(_("Created or updated fiscal position %s.\n")
|
||||
% fp_template.name)
|
||||
@@ -1207,13 +1327,28 @@ class wizard_update_charts_accounts_tax_code(orm.TransientModel):
|
||||
"""
|
||||
_name = 'wizard.update.charts.accounts.tax.code'
|
||||
_columns = {
|
||||
'tax_code_id': fields.many2one('account.tax.code.template', 'Tax code template', required=True, ondelete='set null'),
|
||||
'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'),
|
||||
'tax_code_id': fields.many2one(
|
||||
'account.tax.code.template',
|
||||
'Tax code template',
|
||||
required=True,
|
||||
ondelete='set null'
|
||||
),
|
||||
'update_chart_wizard_id': fields.many2one(
|
||||
'wizard.update.charts.accounts',
|
||||
'Update chart wizard',
|
||||
required=True,
|
||||
ondelete='cascade'
|
||||
),
|
||||
'type': fields.selection([
|
||||
('new', 'New template'),
|
||||
('updated', 'Updated template'),
|
||||
], 'Type'),
|
||||
'update_tax_code_id': fields.many2one('account.tax.code', 'Tax code to update', required=False, ondelete='set null'),
|
||||
'update_tax_code_id': fields.many2one(
|
||||
'account.tax.code',
|
||||
'Tax code to update',
|
||||
required=False,
|
||||
ondelete='set null'
|
||||
),
|
||||
'notes': fields.text('Notes'),
|
||||
}
|
||||
_defaults = {
|
||||
@@ -1229,12 +1364,14 @@ class wizard_update_charts_accounts_tax(orm.TransientModel):
|
||||
_name = 'wizard.update.charts.accounts.tax'
|
||||
_columns = {
|
||||
'tax_id': fields.many2one('account.tax.template', 'Tax template', required=True, ondelete='set null'),
|
||||
'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'),
|
||||
'update_chart_wizard_id': fields.many2one(
|
||||
'wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'),
|
||||
'type': fields.selection([
|
||||
('new', 'New template'),
|
||||
('updated', 'Updated template'),
|
||||
], 'Type'),
|
||||
'update_tax_id': fields.many2one('account.tax', 'Tax to update', required=False, ondelete='set null'),
|
||||
'update_tax_id': fields.many2one(
|
||||
'account.tax', 'Tax to update', required=False, ondelete='set null'),
|
||||
'notes': fields.text('Notes'),
|
||||
}
|
||||
|
||||
@@ -1253,13 +1390,28 @@ class wizard_update_charts_accounts_account(orm.TransientModel):
|
||||
# limit for the objects in memory to let the wizard create all the items
|
||||
# at once.
|
||||
_columns = {
|
||||
'account_id': fields.many2one('account.account.template', 'Account template', required=True, ondelete='set null'),
|
||||
'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'),
|
||||
'account_id': fields.many2one(
|
||||
'account.account.template',
|
||||
'Account template',
|
||||
required=True,
|
||||
ondelete='set null'
|
||||
),
|
||||
'update_chart_wizard_id': fields.many2one(
|
||||
'wizard.update.charts.accounts',
|
||||
'Update chart wizard',
|
||||
required=True,
|
||||
ondelete='cascade'
|
||||
),
|
||||
'type': fields.selection([
|
||||
('new', 'New template'),
|
||||
('updated', 'Updated template'),
|
||||
], 'Type'),
|
||||
'update_account_id': fields.many2one('account.account', 'Account to update', required=False, ondelete='set null'),
|
||||
'update_account_id': fields.many2one(
|
||||
'account.account',
|
||||
'Account to update',
|
||||
required=False,
|
||||
ondelete='set null'
|
||||
),
|
||||
'notes': fields.text('Notes'),
|
||||
}
|
||||
|
||||
@@ -1275,13 +1427,28 @@ class wizard_update_charts_accounts_fiscal_position(orm.TransientModel):
|
||||
"""
|
||||
_name = 'wizard.update.charts.accounts.fiscal.position'
|
||||
_columns = {
|
||||
'fiscal_position_id': fields.many2one('account.fiscal.position.template', 'Fiscal position template', required=True, ondelete='set null'),
|
||||
'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'),
|
||||
'fiscal_position_id': fields.many2one(
|
||||
'account.fiscal.position.template',
|
||||
'Fiscal position template',
|
||||
required=True,
|
||||
ondelete='set null'
|
||||
),
|
||||
'update_chart_wizard_id': fields.many2one(
|
||||
'wizard.update.charts.accounts',
|
||||
'Update chart wizard',
|
||||
required=True,
|
||||
ondelete='cascade'
|
||||
),
|
||||
'type': fields.selection([
|
||||
('new', 'New template'),
|
||||
('updated', 'Updated template'),
|
||||
], 'Type'),
|
||||
'update_fiscal_position_id': fields.many2one('account.fiscal.position', 'Fiscal position to update', required=False, ondelete='set null'),
|
||||
'update_fiscal_position_id': fields.many2one(
|
||||
'account.fiscal.position',
|
||||
'Fiscal position to update',
|
||||
required=False,
|
||||
ondelete='set null'
|
||||
),
|
||||
'notes': fields.text('Notes'),
|
||||
}
|
||||
_defaults = {
|
||||
|
||||
@@ -19,5 +19,3 @@
|
||||
##############################################################################
|
||||
|
||||
from . import account_move_line
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -18,16 +18,14 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Recompute tax_amount",
|
||||
"version" : "1.0",
|
||||
"depends" : ["base",
|
||||
"account",
|
||||
],
|
||||
"author" : "Camptocamp",
|
||||
"name": "Recompute tax_amount",
|
||||
"version": "1.0",
|
||||
"depends": ["base",
|
||||
"account"],
|
||||
"author": "Camptocamp",
|
||||
"description": """Recompute tax_amount to avoid sign problem""",
|
||||
'website': 'http://www.camptocamp.com',
|
||||
'data' : [],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -56,10 +56,10 @@ class account_move_line(orm.Model):
|
||||
return result
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None, check=True, update_check=True):
|
||||
result = super(account_move_line,self).write(cr, uid, ids, vals,
|
||||
context=context,
|
||||
check=check,
|
||||
update_check=update_check)
|
||||
result = super(account_move_line, self).write(cr, uid, ids, vals,
|
||||
context=context,
|
||||
check=check,
|
||||
update_check=update_check)
|
||||
if result:
|
||||
if ('debit' in vals) or ('credit' in vals):
|
||||
move_lines = self.read(cr, uid, ids,
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import account_constraints
|
||||
import account_constraints
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
'name' : 'Account Constraints',
|
||||
'version' : '1.1',
|
||||
'depends' : [
|
||||
'account',
|
||||
],
|
||||
'author' : 'Camptocamp',
|
||||
'name': 'Account Constraints',
|
||||
'version': '1.1',
|
||||
'depends': ['account'],
|
||||
'author': 'Camptocamp',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Generic Modules/Accounting',
|
||||
'description': """
|
||||
@@ -48,12 +46,12 @@ Summary of constraints are:
|
||||
|
||||
* Add a check on entries that user cannot provide a secondary currency
|
||||
if the same than the company one.
|
||||
|
||||
|
||||
* Remove the possibility to modify or delete a move line related to an
|
||||
invoice or a bank statement, no matter what the status of the move
|
||||
(draft, validated or posted). This is useful in a standard context but
|
||||
even more if you're using `account_default_draft_move`. This way you ensure
|
||||
that the user cannot make mistakes even in draft state, he must pass through the
|
||||
that the user cannot make mistakes even in draft state, he must pass through the
|
||||
parent object to make his modification.
|
||||
|
||||
Contributors
|
||||
|
||||
@@ -82,7 +82,7 @@ class AccountMoveLine(orm.Model):
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('You cannot do this on an entry generated by an invoice. You must '
|
||||
'change the related invoice directly.\n%s.') % err_msg)
|
||||
'change the related invoice directly.\n%s.') % err_msg)
|
||||
return True
|
||||
|
||||
def _check_statement_related_move(self, cr, uid, ids, vals=None, context=None):
|
||||
@@ -95,7 +95,7 @@ class AccountMoveLine(orm.Model):
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('You cannot do this on an entry generated by a bank statement. '
|
||||
'You must change the related bank statement directly.\n%s.') % err_msg)
|
||||
'You must change the related bank statement directly.\n%s.') % err_msg)
|
||||
return True
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None, check=True):
|
||||
@@ -134,8 +134,10 @@ class AccountMoveLine(orm.Model):
|
||||
if not context.get('from_parent_object', False):
|
||||
self._check_invoice_related_move(cr, uid, ids, vals, context=context)
|
||||
self._check_statement_related_move(cr, uid, ids, vals, context=context)
|
||||
return super(AccountMoveLine, self).write(cr, uid, ids, vals,
|
||||
context=context, check=check, update_check=update_check)
|
||||
return super(AccountMoveLine, self).write(
|
||||
cr, uid, ids, vals,
|
||||
context=context, check=check, update_check=update_check
|
||||
)
|
||||
|
||||
def _check_currency_and_amount(self, cr, uid, ids, context=None):
|
||||
for l in self.browse(cr, uid, ids, context=context):
|
||||
@@ -161,23 +163,22 @@ class AccountMoveLine(orm.Model):
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_currency_and_amount,
|
||||
"You cannot create journal items with a secondary currency "
|
||||
"without recording both 'currency' and 'amount currency' field.",
|
||||
['currency_id','amount_currency']
|
||||
),
|
||||
(_check_currency_amount,
|
||||
"The amount expressed in the secondary currency must be positive "
|
||||
"when journal item are debit and negatif when journal item are "
|
||||
"credit.",
|
||||
['amount_currency']
|
||||
),
|
||||
(_check_currency_company,
|
||||
"You can't provide a secondary currency if "
|
||||
"the same than the company one.",
|
||||
['currency_id']
|
||||
),
|
||||
]
|
||||
(_check_currency_and_amount,
|
||||
"You cannot create journal items with a secondary currency "
|
||||
"without recording both 'currency' and 'amount currency' field.",
|
||||
['currency_id', 'amount_currency']),
|
||||
|
||||
(_check_currency_amount,
|
||||
"The amount expressed in the secondary currency must be positive "
|
||||
"when journal item are debit and negatif when journal item are "
|
||||
"credit.",
|
||||
['amount_currency']),
|
||||
|
||||
(_check_currency_company,
|
||||
"You can't provide a secondary currency if "
|
||||
"the same than the company one.",
|
||||
['currency_id']),
|
||||
]
|
||||
|
||||
|
||||
class AccountInvoice(orm.Model):
|
||||
@@ -203,7 +204,7 @@ class AccountInvoice(orm.Model):
|
||||
else:
|
||||
context = context.copy()
|
||||
context['from_parent_object'] = True
|
||||
return super(AccountInvoice,self).action_move_create(cr, uid, ids, context=context)
|
||||
return super(AccountInvoice, self).action_move_create(cr, uid, ids, context=context)
|
||||
|
||||
|
||||
class AccountBankStatement(orm.Model):
|
||||
@@ -232,5 +233,6 @@ class AccountBankStatement(orm.Model):
|
||||
context = context.copy()
|
||||
context['from_parent_object'] = True
|
||||
return super(AccountBankStatement, self).create_move_from_st_line(
|
||||
cr, uid, st_line_id, company_currency_id,
|
||||
st_line_number, context=context)
|
||||
cr, uid, st_line_id, company_currency_id,
|
||||
st_line_number, context=context
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ class AccountAccount(orm.Model):
|
||||
'account_id',
|
||||
string='Credit Lines',
|
||||
readonly=True),
|
||||
}
|
||||
}
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
|
||||
@@ -25,14 +25,17 @@ class ResCompany(orm.Model):
|
||||
"""Add credit control parameters"""
|
||||
_inherit = 'res.company'
|
||||
|
||||
_columns = {'credit_control_tolerance': fields.float('Credit Control Tolerance'),
|
||||
# This is not a property on the partner because we cannot search
|
||||
# on fields.property (subclass fields.function).
|
||||
'credit_policy_id': fields.many2one('credit.control.policy',
|
||||
'Credit Control Policy',
|
||||
help=("The Credit Control Policy used on partners"
|
||||
" by default. This setting can be overridden"
|
||||
" on partners or invoices.")),
|
||||
}
|
||||
_columns = {
|
||||
'credit_control_tolerance': fields.float('Credit Control Tolerance'),
|
||||
# This is not a property on the partner because we cannot search
|
||||
# on fields.property (subclass fields.function).
|
||||
'credit_policy_id': fields.many2one(
|
||||
'credit.control.policy',
|
||||
'Credit Control Policy',
|
||||
help=("The Credit Control Policy used on partners"
|
||||
" by default. This setting can be overridden"
|
||||
" on partners or invoices.")
|
||||
),
|
||||
}
|
||||
|
||||
_defaults = {"credit_control_tolerance": 0.1}
|
||||
|
||||
@@ -27,21 +27,22 @@ class AccountInvoice(orm.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
_columns = {
|
||||
'credit_policy_id':
|
||||
fields.many2one('credit.control.policy',
|
||||
'Credit Control Policy',
|
||||
help=("The Credit Control Policy used for this "
|
||||
"invoice. If nothing is defined, it will "
|
||||
"use the account setting or the partner "
|
||||
"setting."),
|
||||
readonly=True,
|
||||
),
|
||||
'credit_control_line_ids':
|
||||
fields.one2many('credit.control.line',
|
||||
'invoice_id',
|
||||
string='Credit Lines',
|
||||
readonly=True),
|
||||
}
|
||||
'credit_policy_id': fields.many2one(
|
||||
'credit.control.policy',
|
||||
'Credit Control Policy',
|
||||
help=("The Credit Control Policy used for this "
|
||||
"invoice. If nothing is defined, it will "
|
||||
"use the account setting or the partner "
|
||||
"setting."),
|
||||
readonly=True,
|
||||
),
|
||||
'credit_control_line_ids': fields.one2many(
|
||||
'credit.control.line',
|
||||
'invoice_id',
|
||||
string='Credit Lines',
|
||||
readonly=True
|
||||
),
|
||||
}
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
"""Ensure that credit lines and policy are not copied"""
|
||||
@@ -62,15 +63,17 @@ class AccountInvoice(orm.Model):
|
||||
cc_nondraft_line_ids = cc_line_obj.search(
|
||||
cr, uid,
|
||||
[('invoice_id', '=', invoice_id),
|
||||
('state', '<>', 'draft')],
|
||||
('state', '!=', 'draft')],
|
||||
context=context)
|
||||
if cc_nondraft_line_ids:
|
||||
raise orm.except_orm(_('Error!'),
|
||||
_('You cannot cancel this invoice.\n'
|
||||
'A payment reminder has already been '
|
||||
'sent to the customer.\n'
|
||||
'You must create a credit note and '
|
||||
'issue a new invoice.'))
|
||||
raise orm.except_orm(
|
||||
_('Error!'),
|
||||
_('You cannot cancel this invoice.\n'
|
||||
'A payment reminder has already been '
|
||||
'sent to the customer.\n'
|
||||
'You must create a credit note and '
|
||||
'issue a new invoice.')
|
||||
)
|
||||
cc_draft_line_ids = cc_line_obj.search(
|
||||
cr, uid,
|
||||
[('invoice_id', '=', invoice_id),
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
import logging
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.osv import osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
logger = logging.getLogger('credit.line.control')
|
||||
@@ -40,88 +39,152 @@ class CreditControlLine(orm.Model):
|
||||
_rec_name = "id"
|
||||
_order = "date DESC"
|
||||
_columns = {
|
||||
'date': fields.date('Controlling date',
|
||||
required=True,
|
||||
select=True),
|
||||
'date': fields.date(
|
||||
'Controlling date',
|
||||
required=True,
|
||||
select=True
|
||||
),
|
||||
# maturity date of related move line we do not use a related field in order to
|
||||
# allow manual changes
|
||||
'date_due': fields.date('Due date',
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}),
|
||||
'date_due': fields.date(
|
||||
'Due date',
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}
|
||||
),
|
||||
|
||||
'date_entry': fields.related('move_line_id', 'date', type='date',
|
||||
string='Entry date',
|
||||
store=True, readonly=True),
|
||||
'date_entry': fields.related(
|
||||
'move_line_id', 'date',
|
||||
type='date',
|
||||
string='Entry date',
|
||||
store=True, readonly=True
|
||||
),
|
||||
|
||||
'date_sent': fields.date('Sent date',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}),
|
||||
'date_sent': fields.date(
|
||||
'Sent date',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}
|
||||
),
|
||||
|
||||
'state': fields.selection([('draft', 'Draft'),
|
||||
('ignored', 'Ignored'),
|
||||
('to_be_sent', 'Ready To Send'),
|
||||
('sent', 'Done'),
|
||||
('error', 'Error'),
|
||||
('email_error', 'Emailing Error')],
|
||||
'State', required=True, readonly=True,
|
||||
help=("Draft lines need to be triaged.\n"
|
||||
"Ignored lines are lines for which we do "
|
||||
"not want to send something.\n"
|
||||
"Draft and ignored lines will be "
|
||||
"generated again on the next run.")),
|
||||
'state': fields.selection(
|
||||
[('draft', 'Draft'),
|
||||
('ignored', 'Ignored'),
|
||||
('to_be_sent', 'Ready To Send'),
|
||||
('sent', 'Done'),
|
||||
('error', 'Error'),
|
||||
('email_error', 'Emailing Error')],
|
||||
'State', required=True, readonly=True,
|
||||
help=("Draft lines need to be triaged.\n"
|
||||
"Ignored lines are lines for which we do "
|
||||
"not want to send something.\n"
|
||||
"Draft and ignored lines will be "
|
||||
"generated again on the next run.")
|
||||
),
|
||||
|
||||
'channel': fields.selection([('letter', 'Letter'),
|
||||
('email', 'Email')],
|
||||
'Channel', required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}),
|
||||
'channel': fields.selection(
|
||||
[('letter', 'Letter'),
|
||||
('email', 'Email')],
|
||||
'Channel', required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}
|
||||
),
|
||||
|
||||
'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
|
||||
'partner_id': fields.many2one('res.partner', "Partner", required=True),
|
||||
'amount_due': fields.float('Due Amount Tax incl.', required=True, readonly=True),
|
||||
'balance_due': fields.float('Due balance', required=True, readonly=True),
|
||||
'mail_message_id': fields.many2one('mail.mail', 'Sent Email', readonly=True),
|
||||
'invoice_id': fields.many2one(
|
||||
'account.invoice',
|
||||
'Invoice',
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'move_line_id': fields.many2one('account.move.line', 'Move line',
|
||||
required=True, readonly=True),
|
||||
'partner_id': fields.many2one(
|
||||
'res.partner',
|
||||
"Partner",
|
||||
required=True
|
||||
),
|
||||
|
||||
'account_id': fields.related('move_line_id', 'account_id', type='many2one',
|
||||
relation='account.account', string='Account',
|
||||
store=True, readonly=True),
|
||||
'amount_due': fields.float(
|
||||
'Due Amount Tax incl.',
|
||||
required=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'currency_id': fields.related('move_line_id', 'currency_id', type='many2one',
|
||||
relation='res.currency', string='Currency',
|
||||
store=True, readonly=True),
|
||||
'balance_due': fields.float(
|
||||
'Due balance', required=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'company_id': fields.related('move_line_id', 'company_id', type='many2one',
|
||||
relation='res.company', string='Company',
|
||||
store=True, readonly=True),
|
||||
'mail_message_id': fields.many2one(
|
||||
'mail.mail',
|
||||
'Sent Email',
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'move_line_id': fields.many2one(
|
||||
'account.move.line',
|
||||
'Move line',
|
||||
required=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'account_id': fields.related(
|
||||
'move_line_id',
|
||||
'account_id',
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Account',
|
||||
store=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'currency_id': fields.related(
|
||||
'move_line_id',
|
||||
'currency_id',
|
||||
type='many2one',
|
||||
relation='res.currency',
|
||||
string='Currency',
|
||||
store=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'company_id': fields.related(
|
||||
'move_line_id', 'company_id',
|
||||
type='many2one',
|
||||
relation='res.company',
|
||||
string='Company',
|
||||
store=True, readonly=True
|
||||
),
|
||||
|
||||
# we can allow a manual change of policy in draft state
|
||||
'policy_level_id':fields.many2one('credit.control.policy.level',
|
||||
'Overdue Level', required=True, readonly=True,
|
||||
states={'draft': [('readonly', False)]}),
|
||||
'policy_level_id': fields.many2one(
|
||||
'credit.control.policy.level',
|
||||
'Overdue Level',
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}
|
||||
),
|
||||
|
||||
'policy_id': fields.related('policy_level_id',
|
||||
'policy_id',
|
||||
type='many2one',
|
||||
relation='credit.control.policy',
|
||||
string='Policy',
|
||||
store=True,
|
||||
readonly=True),
|
||||
'policy_id': fields.related(
|
||||
'policy_level_id',
|
||||
'policy_id',
|
||||
type='many2one',
|
||||
relation='credit.control.policy',
|
||||
string='Policy',
|
||||
store=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'level': fields.related(
|
||||
'policy_level_id',
|
||||
'level',
|
||||
type='integer',
|
||||
relation='credit.control.policy',
|
||||
string='Level',
|
||||
store=True,
|
||||
readonly=True
|
||||
),
|
||||
|
||||
'level': fields.related('policy_level_id',
|
||||
'level',
|
||||
type='integer',
|
||||
relation='credit.control.policy',
|
||||
string='Level',
|
||||
store=True,
|
||||
readonly=True),
|
||||
'manually_overridden': fields.boolean('Manually overridden')
|
||||
}
|
||||
|
||||
|
||||
_defaults = {'state': 'draft'}
|
||||
|
||||
def _prepare_from_move_line(self, cr, uid, move_line,
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
##############################################################################
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class Mail(orm.Model):
|
||||
_inherit = 'mail.mail'
|
||||
|
||||
# use HTML fields instead of text
|
||||
_columns = {'body_html': fields.html('Rich-text Contents',
|
||||
help="Rich-text/HTML message"),}
|
||||
_columns = {
|
||||
'body_html': fields.html('Rich-text Contents',
|
||||
help="Rich-text/HTML message"),
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class ResPartner(orm.Model):
|
||||
|
||||
@@ -27,26 +27,37 @@ class CreditControlPolicy(orm.Model):
|
||||
|
||||
_name = "credit.control.policy"
|
||||
_description = """Define a reminder policy"""
|
||||
_columns = {'name': fields.char('Name', required=True, size=128),
|
||||
_columns = {
|
||||
'name': fields.char(
|
||||
'Name',
|
||||
required=True,
|
||||
size=128
|
||||
),
|
||||
|
||||
'level_ids': fields.one2many('credit.control.policy.level',
|
||||
'policy_id',
|
||||
'Policy Levels'),
|
||||
'level_ids': fields.one2many(
|
||||
'credit.control.policy.level',
|
||||
'policy_id',
|
||||
'Policy Levels'
|
||||
),
|
||||
|
||||
'do_nothing': fields.boolean('Do nothing',
|
||||
help='For policies which should not '
|
||||
'generate lines or are obsolete'),
|
||||
'do_nothing': fields.boolean(
|
||||
'Do nothing',
|
||||
help='For policies which should not '
|
||||
'generate lines or are obsolete'
|
||||
),
|
||||
|
||||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
|
||||
'account_ids': fields.many2many('account.account',
|
||||
string='Accounts',
|
||||
required=True,
|
||||
domain="[('type', '=', 'receivable')]",
|
||||
help="This policy will be active only"
|
||||
" for the selected accounts"),
|
||||
'active': fields.boolean('Active'),
|
||||
}
|
||||
'account_ids': fields.many2many(
|
||||
'account.account',
|
||||
string='Accounts',
|
||||
required=True,
|
||||
domain="[('type', '=', 'receivable')]",
|
||||
help="This policy will be active only"
|
||||
" for the selected accounts"
|
||||
),
|
||||
'active': fields.boolean('Active'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'active': True,
|
||||
|
||||
@@ -22,16 +22,19 @@ import time
|
||||
|
||||
from openerp.report import report_sxw
|
||||
|
||||
|
||||
class CreditSummaryReport(report_sxw.rml_parse):
|
||||
def __init__(self, cr, uid, name, context):
|
||||
super(CreditSummaryReport, self).__init__(cr, uid, name, context=context)
|
||||
self.localcontext.update({
|
||||
'time': time,
|
||||
'cr':cr,
|
||||
'cr': cr,
|
||||
'uid': uid,
|
||||
})
|
||||
|
||||
report_sxw.report_sxw('report.credit_control_summary',
|
||||
'credit.control.communication',
|
||||
'addons/account_credit_control/report/credit_control_summary.html.mako',
|
||||
parser=CreditSummaryReport)
|
||||
report_sxw.report_sxw(
|
||||
'report.credit_control_summary',
|
||||
'credit.control.communication',
|
||||
'addons/account_credit_control/report/credit_control_summary.html.mako',
|
||||
parser=CreditSummaryReport
|
||||
)
|
||||
|
||||
@@ -34,13 +34,14 @@ class CreditControlRun(orm.Model):
|
||||
_description = """Credit control line generator"""
|
||||
_columns = {
|
||||
'date': fields.date('Controlling Date', required=True),
|
||||
'policy_ids':
|
||||
fields.many2many('credit.control.policy',
|
||||
rel="credit_run_policy_rel",
|
||||
id1='run_id', id2='policy_id',
|
||||
string='Policies',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}),
|
||||
'policy_ids': fields.many2many(
|
||||
'credit.control.policy',
|
||||
rel="credit_run_policy_rel",
|
||||
id1='run_id', id2='policy_id',
|
||||
string='Policies',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]}
|
||||
),
|
||||
|
||||
'report': fields.text('Report', readonly=True),
|
||||
|
||||
@@ -50,16 +51,17 @@ class CreditControlRun(orm.Model):
|
||||
required=True,
|
||||
readonly=True),
|
||||
|
||||
'manual_ids':
|
||||
fields.many2many('account.move.line',
|
||||
rel="credit_runreject_rel",
|
||||
string='Lines to handle manually',
|
||||
help=('If a credit control line has been generated'
|
||||
'on a policy and the policy has been changed '
|
||||
'in the meantime, it has to be handled '
|
||||
'manually'),
|
||||
readonly=True),
|
||||
}
|
||||
'manual_ids': fields.many2many(
|
||||
'account.move.line',
|
||||
rel="credit_runreject_rel",
|
||||
string='Lines to handle manually',
|
||||
help=('If a credit control line has been generated'
|
||||
'on a policy and the policy has been changed '
|
||||
'in the meantime, it has to be handled '
|
||||
'manually'),
|
||||
readonly=True
|
||||
),
|
||||
}
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
@@ -138,11 +140,11 @@ class CreditControlRun(orm.Model):
|
||||
generated_ids.extend(policy_generated_ids)
|
||||
if policy_generated_ids:
|
||||
report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \
|
||||
(policy.name, len(policy_generated_ids))
|
||||
(policy.name, len(policy_generated_ids))
|
||||
credit_line_ids += policy_generated_ids
|
||||
else:
|
||||
report += _("Policy \"%s\" has not generated any Credit Control Lines.\n" %
|
||||
policy.name)
|
||||
policy.name)
|
||||
|
||||
vals = {'state': 'done',
|
||||
'report': report,
|
||||
@@ -159,8 +161,8 @@ class CreditControlRun(orm.Model):
|
||||
try:
|
||||
cr.execute('SELECT id FROM credit_control_run'
|
||||
' LIMIT 1 FOR UPDATE NOWAIT')
|
||||
except Exception as exc:
|
||||
# in case of exception openerp will do a rollback for us and free the lock
|
||||
except Exception:
|
||||
# In case of exception openerp will do a rollback for us and free the lock
|
||||
raise orm.except_orm(_('Error'),
|
||||
_('A credit control run is already running'
|
||||
' in background, please try later.'))
|
||||
|
||||
@@ -37,17 +37,19 @@ class CreditCommunication(TransientModel):
|
||||
'Level', required=True),
|
||||
|
||||
'credit_control_line_ids': fields.many2many('credit.control.line',
|
||||
rel='comm_credit_rel',
|
||||
string='Credit Lines'),
|
||||
rel='comm_credit_rel',
|
||||
string='Credit Lines'),
|
||||
|
||||
'company_id': fields.many2one('res.company', 'Company',
|
||||
required=True),
|
||||
|
||||
'user_id': fields.many2one('res.users', 'User')}
|
||||
|
||||
_defaults = {'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
|
||||
cr, uid, 'credit.control.policy', context=c),
|
||||
'user_id': lambda s, cr, uid, c: uid}
|
||||
_defaults = {
|
||||
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
|
||||
cr, uid, 'credit.control.policy', context=c),
|
||||
'user_id': lambda s, cr, uid, c: uid
|
||||
}
|
||||
|
||||
def get_email(self, cr, uid, com_id, context=None):
|
||||
"""Return a valid email for customer"""
|
||||
@@ -97,11 +99,16 @@ class CreditCommunication(TransientModel):
|
||||
res = cr.dictfetchall()
|
||||
for level_assoc in res:
|
||||
data = {}
|
||||
data['credit_control_line_ids'] = \
|
||||
[(6, 0, self._get_credit_lines(cr, uid, line_ids,
|
||||
level_assoc['partner_id'],
|
||||
level_assoc['policy_level_id'],
|
||||
context=context))]
|
||||
data['credit_control_line_ids'] = [
|
||||
(
|
||||
6, 0, self._get_credit_lines(
|
||||
cr, uid, line_ids,
|
||||
level_assoc['partner_id'],
|
||||
level_assoc['policy_level_id'],
|
||||
context=context
|
||||
)
|
||||
)
|
||||
]
|
||||
data['partner_id'] = level_assoc['partner_id']
|
||||
data['current_policy_level'] = level_assoc['policy_level_id']
|
||||
comm_id = self.create(cr, uid, data, context=context)
|
||||
@@ -144,10 +151,11 @@ class CreditCommunication(TransientModel):
|
||||
state = 'email_error'
|
||||
|
||||
cr_line_obj.write(
|
||||
cr, uid, cl_ids,
|
||||
{'mail_message_id': email_id,
|
||||
'state': state},
|
||||
context=context)
|
||||
cr, uid, cl_ids,
|
||||
{'mail_message_id': email_id,
|
||||
'state': state},
|
||||
context=context
|
||||
)
|
||||
att_ids = []
|
||||
for att in email_values.get('attachments', []):
|
||||
attach_fname = att[0]
|
||||
|
||||
@@ -37,9 +37,10 @@ class CreditControlEmailer(orm.TransientModel):
|
||||
if (context.get('active_model') == 'credit.control.line' and
|
||||
context.get('active_ids')):
|
||||
res = self._filter_line_ids(
|
||||
cr, uid,
|
||||
context['active_ids'],
|
||||
context=context)
|
||||
cr, uid,
|
||||
context['active_ids'],
|
||||
context=context
|
||||
)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
@@ -63,7 +64,7 @@ class CreditControlEmailer(orm.TransientModel):
|
||||
|
||||
def email_lines(self, cr, uid, wiz_id, context=None):
|
||||
assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
|
||||
"wiz_id: only one id expected"
|
||||
"wiz_id: only one id expected"
|
||||
comm_obj = self.pool.get('credit.control.communication')
|
||||
if isinstance(wiz_id, list):
|
||||
wiz_id = wiz_id[0]
|
||||
@@ -74,9 +75,10 @@ class CreditControlEmailer(orm.TransientModel):
|
||||
|
||||
line_ids = [l.id for l in form.line_ids]
|
||||
filtered_ids = self._filter_line_ids(
|
||||
cr, uid, line_ids, context)
|
||||
cr, uid, line_ids, context
|
||||
)
|
||||
comms = comm_obj._generate_comm_from_credit_line_ids(
|
||||
cr, uid, filtered_ids, context=context)
|
||||
cr, uid, filtered_ids, context=context
|
||||
)
|
||||
comm_obj._generate_emails(cr, uid, comms, context=context)
|
||||
return {}
|
||||
|
||||
|
||||
@@ -35,16 +35,18 @@ class CreditControlMarker(orm.TransientModel):
|
||||
if (context.get('active_model') == 'credit.control.line' and
|
||||
context.get('active_ids')):
|
||||
res = self._filter_line_ids(
|
||||
cr, uid,
|
||||
context['active_ids'],
|
||||
context=context)
|
||||
cr, uid,
|
||||
context['active_ids'],
|
||||
context=context
|
||||
)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.selection([('ignored', 'Ignored'),
|
||||
('to_be_sent', 'Ready To Send'),
|
||||
('sent', 'Done')],
|
||||
'Mark as', required=True),
|
||||
'Mark as',
|
||||
required=True),
|
||||
'line_ids': fields.many2many(
|
||||
'credit.control.line',
|
||||
string='Credit Control Lines',
|
||||
@@ -74,7 +76,7 @@ class CreditControlMarker(orm.TransientModel):
|
||||
"""Write state of selected credit lines to the one in entry
|
||||
done credit line will be ignored"""
|
||||
assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
|
||||
"wiz_id: only one id expected"
|
||||
"wiz_id: only one id expected"
|
||||
if isinstance(wiz_id, list):
|
||||
wiz_id = wiz_id[0]
|
||||
form = self.browse(cr, uid, wiz_id, context)
|
||||
@@ -86,15 +88,16 @@ class CreditControlMarker(orm.TransientModel):
|
||||
|
||||
filtered_ids = self._filter_line_ids(cr, uid, line_ids, context)
|
||||
if not filtered_ids:
|
||||
raise except_osv(_('Information'),
|
||||
_('No lines will be changed. All the selected lines are already done.'))
|
||||
raise orm.except_orm(
|
||||
_('Information'),
|
||||
_('No lines will be changed. All the selected lines are already done.')
|
||||
)
|
||||
|
||||
self._mark_lines(cr, uid, filtered_ids, form.name, context)
|
||||
|
||||
return {'domain': unicode([('id', 'in', filtered_ids)]),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'view_id': False,
|
||||
'res_model': 'credit.control.line',
|
||||
'type': 'ir.actions.act_window'}
|
||||
|
||||
return {'domain': unicode([('id', 'in', filtered_ids)]),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'view_id': False,
|
||||
'res_model': 'credit.control.line',
|
||||
'type': 'ir.actions.act_window'}
|
||||
|
||||
@@ -145,10 +145,12 @@ class credit_control_policy_changer(orm.TransientModel):
|
||||
uid,
|
||||
wizard.move_line_ids,
|
||||
wizard.new_policy_id)
|
||||
self._mark_as_overridden(cr,
|
||||
uid,
|
||||
wizard.move_line_ids,
|
||||
context=context)
|
||||
self._mark_as_overridden(
|
||||
cr,
|
||||
uid,
|
||||
wizard.move_line_ids,
|
||||
context=context
|
||||
)
|
||||
# As disscused with business expert
|
||||
# draft lines should be passed to ignored
|
||||
# if same level as the new one
|
||||
@@ -173,6 +175,6 @@ class credit_control_policy_changer(orm.TransientModel):
|
||||
"account_credit_control",
|
||||
"credit_control_line_action")
|
||||
assert view_id, 'No view found'
|
||||
action = ui_act_model.read(cr, uid, view_id[1], context=context)
|
||||
action = ui_act_model.read(cr, uid, view_id[1], context=context)
|
||||
action['domain'] = [('id', 'in', generated_ids)]
|
||||
return action
|
||||
|
||||
@@ -67,5 +67,4 @@ Support of fees price list
|
||||
'installable': False,
|
||||
'auto_install': False,
|
||||
'license': 'AGPL-3',
|
||||
'application': False,
|
||||
}
|
||||
'application': False}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.osv import orm
|
||||
|
||||
|
||||
class credit_control_run(orm.Model):
|
||||
@@ -26,7 +26,7 @@ class credit_control_run(orm.Model):
|
||||
|
||||
_inherit = "credit.control.run"
|
||||
|
||||
def _generate_credit_lines(self, cr, uid, run_id, context=None):
|
||||
def _generate_credit_lines(self, cr, uid, run_id, context=None):
|
||||
"""Override method to add fees computation"""
|
||||
credit_line_ids = super(credit_control_run, self)._generate_credit_lines(
|
||||
cr,
|
||||
|
||||
@@ -17,7 +17,5 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from . import account
|
||||
from . import account_bank_statement
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -17,10 +17,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Move in draft state by default",
|
||||
"version" : "1.0",
|
||||
"depends" : ["base", "account", "account_constraints"],
|
||||
"author" : "Camptocamp",
|
||||
"name": "Move in draft state by default",
|
||||
"version": "1.0",
|
||||
"depends": ["base", "account", "account_constraints"],
|
||||
"author": "Camptocamp",
|
||||
'license': 'AGPL-3',
|
||||
"description": """
|
||||
Let the generated move in draft on invoice and bank statement
|
||||
@@ -48,7 +48,7 @@ need to make a refund).
|
||||
|
||||
""",
|
||||
'website': 'http://www.camptocamp.com',
|
||||
'data' : ['account_view.xml',
|
||||
'data': ['account_view.xml',
|
||||
'invoice_view.xml'],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
|
||||
@@ -17,37 +17,42 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, orm, osv
|
||||
from openerp.osv import orm, osv
|
||||
from tools.translate import _
|
||||
|
||||
|
||||
class AccountInvoice(orm.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
|
||||
def action_move_create(self, cr, uid, ids, context=None):
|
||||
"""Set move line in draft state after creating them."""
|
||||
res = super(AccountInvoice,self).action_move_create(cr, uid, ids, context=context)
|
||||
res = super(AccountInvoice, self).action_move_create(cr, uid, ids, context=context)
|
||||
move_obj = self.pool.get('account.move')
|
||||
for inv in self.browse(cr, uid, ids, context=context):
|
||||
if inv.move_id:
|
||||
move_obj.write(cr, uid, [inv.move_id.id], {'state': 'draft'}, context=context)
|
||||
return res
|
||||
|
||||
|
||||
class AccountMove(orm.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
|
||||
def button_cancel(self, cr, uid, ids, context=None):
|
||||
""" We rewrite function button_cancel, to allow invoice or bank statement with linked draft moved
|
||||
to be canceled """
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if line.state == 'draft':
|
||||
continue
|
||||
continue
|
||||
else:
|
||||
if not line.journal_id.update_posted:
|
||||
raise osv.except_osv(_('Error!'), _('You cannot modify a posted entry of this journal.\nFirst you should set the journal to allow cancelling entries.'))
|
||||
raise osv.except_osv(
|
||||
_('Error!'),
|
||||
_('You cannot modify a posted entry of this journal.'
|
||||
'First you should set the journal to allow cancelling entries.')
|
||||
)
|
||||
if ids:
|
||||
cr.execute('UPDATE account_move '\
|
||||
'SET state=%s '\
|
||||
cr.execute('UPDATE account_move '
|
||||
'SET state=%s '
|
||||
'WHERE id IN %s', ('draft', tuple(ids),))
|
||||
return True
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class AccountBankStatement(orm.Model):
|
||||
@@ -26,9 +25,10 @@ class AccountBankStatement(orm.Model):
|
||||
|
||||
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id,
|
||||
st_line_number, context=None):
|
||||
move_ids = super(AccountBankStatement,self).create_move_from_st_line(
|
||||
cr, uid, st_line_id, company_currency_id,
|
||||
st_line_number, context)
|
||||
move_ids = super(AccountBankStatement, self).create_move_from_st_line(
|
||||
cr, uid, st_line_id, company_currency_id,
|
||||
st_line_number, context
|
||||
)
|
||||
# If a bank statement line is already linked to a voucher
|
||||
# we received boolean instead of voucher move ids in move_ids
|
||||
bank_st_line_obj = self.pool.get('account.bank.statement.line')
|
||||
|
||||
@@ -31,13 +31,21 @@
|
||||
Check that the Customer has a VAT number on invoice validation
|
||||
==============================================================
|
||||
|
||||
This module adds an option **Customer must have VAT** on fiscal positions. When a user tries to validate a customer invoice or refund with a fiscal position that have this option, OpenERP will check that the customer has a VAT number. If it doesn't, OpenERP will block the validation of the invoice and display an error message.
|
||||
This module adds an option **Customer must have VAT** on fiscal positions.
|
||||
When a user tries to validate a customer invoice or refund with a fiscal position
|
||||
that have this option, OpenERP will check that the customer has a VAT number.
|
||||
|
||||
In the European Union (EU), when an EU company sends an invoice to another EU company in another country, it can invoice without VAT (most of the time) but the VAT number of the customer must be displayed on the invoice.
|
||||
If it doesn't, OpenERP will block the validation of the invoice and display an error message.
|
||||
|
||||
This module also displays a warning when a user sets a fiscal position with the option **Customer must have VAT** on a customer and this customer doesn't have a VAT number in OpenERP yet.
|
||||
In the European Union (EU), when an EU company sends an invoice to another EU company in another country,
|
||||
|
||||
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com> for any help or question about this module.
|
||||
it can invoice without VAT (most of the time) but the VAT number of the customer must be displayed on the invoice.
|
||||
|
||||
This module also displays a warning when a user sets a fiscal position with the option
|
||||
**Customer must have VAT** on a customer and this customer doesn't have a VAT number in OpenERP yet.
|
||||
|
||||
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com>
|
||||
for any help or question about this module.
|
||||
""",
|
||||
'author': 'Akretion',
|
||||
'website': 'http://www.akretion.com',
|
||||
@@ -45,11 +53,11 @@ Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com> for
|
||||
'data': [
|
||||
'account_fiscal_position_view.xml',
|
||||
'partner_view.xml',
|
||||
],
|
||||
],
|
||||
'images': [
|
||||
'images/fiscal_position_form.jpg',
|
||||
'images/vat_check_invoice_validation.jpg',
|
||||
],
|
||||
],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
'application': True,
|
||||
|
||||
@@ -30,7 +30,9 @@ class account_fiscal_position(orm.Model):
|
||||
_columns = {
|
||||
'customer_must_have_vat': fields.boolean(
|
||||
'Customer Must Have VAT number',
|
||||
help="If enabled, OpenERP will check that the customer has a VAT number when the user validates a customer invoice/refund."),
|
||||
help="If enabled, OpenERP will check that the customer has a VAT number "
|
||||
"when the user validates a customer invoice/refund."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +53,11 @@ class account_invoice(orm.Model):
|
||||
type_label = _('a Customer Refund')
|
||||
raise orm.except_orm(
|
||||
_('Missing VAT number:'),
|
||||
_("You are trying to validate %s with the fiscal position '%s' that require the customer to have a VAT number. But the Customer '%s' doesn't have a VAT number in OpenERP. Please add the VAT number of this Customer in OpenERP and try to validate again.")
|
||||
_("You are trying to validate %s with the fiscal position '%s' "
|
||||
"that require the customer to have a VAT number. "
|
||||
"But the Customer '%s' doesn't have a VAT number in OpenERP."
|
||||
"Please add the VAT number of this Customer in OpenERP "
|
||||
" and try to validate again.")
|
||||
% (type_label, invoice.fiscal_position.name,
|
||||
invoice.partner_id.name))
|
||||
return super(account_invoice, self).action_move_create(
|
||||
|
||||
@@ -34,7 +34,14 @@ class res_partner(orm.Model):
|
||||
fp = self.pool['account.fiscal.position'].read(
|
||||
cr, uid, account_position, ['customer_must_have_vat', 'name'])
|
||||
if fp['customer_must_have_vat']:
|
||||
return {'warning': {
|
||||
'title': _('Missing VAT number:'),
|
||||
'message': _("You have set the fiscal position '%s' that require the customer to have a VAT number. You should add the VAT number of this customer in OpenERP.") % fp['name']}}
|
||||
return {
|
||||
'warning': {
|
||||
'title': _('Missing VAT number:'),
|
||||
'message': _(
|
||||
"You have set the fiscal position '%s' "
|
||||
"that require the customer to have a VAT number. "
|
||||
"You should add the VAT number of this customer in OpenERP."
|
||||
) % fp['name']
|
||||
}
|
||||
}
|
||||
return True
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
from openerp.osv import fields
|
||||
from openerp.osv import orm
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
|
||||
|
||||
@@ -36,19 +35,15 @@ class account_invoice(orm.Model):
|
||||
|
||||
def _get_invoice_line2(self, cr, uid, ids, context=None):
|
||||
result = {}
|
||||
for line in self.pool.get('account.invoice.line').browse(cr,
|
||||
uid,
|
||||
ids,
|
||||
context=context):
|
||||
for line in self.pool.get('account.invoice.line').browse(
|
||||
cr, uid, ids, context=context):
|
||||
result[line.invoice_id.id] = True
|
||||
return result.keys()
|
||||
|
||||
def _get_invoice_tax2(self, cr, uid, ids, context=None):
|
||||
result = {}
|
||||
for tax in self.pool.get('account.invoice.tax').browse(cr,
|
||||
uid,
|
||||
ids,
|
||||
context=context):
|
||||
for tax in self.pool.get('account.invoice.tax').browse(
|
||||
cr, uid, ids, context=context):
|
||||
result[tax.invoice_id.id] = True
|
||||
return result.keys()
|
||||
|
||||
@@ -68,21 +63,21 @@ class account_invoice(orm.Model):
|
||||
'cc_amount_total': 0.0,
|
||||
}
|
||||
|
||||
## It could be computed only in open or paid invoices with a generated account move
|
||||
# It could be computed only in open or paid invoices with a generated account move
|
||||
if invoice.move_id:
|
||||
## Accounts to compute amount_untaxed
|
||||
# Accounts to compute amount_untaxed
|
||||
line_account = []
|
||||
for line in invoice.invoice_line:
|
||||
if line.account_id.id not in line_account:
|
||||
line_account.append(line.account_id.id)
|
||||
|
||||
## Accounts to compute amount_tax
|
||||
# Accounts to compute amount_tax
|
||||
tax_account = []
|
||||
for line in invoice.tax_line:
|
||||
if line.account_id.id not in tax_account and line.amount != 0:
|
||||
tax_account.append(line.account_id.id)
|
||||
|
||||
## The company currency amounts are the debit-credit amounts in the account moves
|
||||
# The company currency amounts are the debit-credit amounts in the account moves
|
||||
for line in invoice.move_id.line_id:
|
||||
if line.account_id.id in line_account:
|
||||
res[invoice.id]['cc_amount_untaxed'] += line.debit - line.credit
|
||||
@@ -91,16 +86,18 @@ class account_invoice(orm.Model):
|
||||
if invoice.type in ('out_invoice', 'in_refund'):
|
||||
res[invoice.id]['cc_amount_untaxed'] = -res[invoice.id]['cc_amount_untaxed']
|
||||
res[invoice.id]['cc_amount_tax'] = -res[invoice.id]['cc_amount_tax']
|
||||
res[invoice.id]['cc_amount_total'] = res[invoice.id]['cc_amount_tax'] + res[invoice.id]['cc_amount_untaxed']
|
||||
res[invoice.id]['cc_amount_total'] = (res[invoice.id]['cc_amount_tax'] +
|
||||
res[invoice.id]['cc_amount_untaxed'])
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'cc_amount_untaxed': fields.function(_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
'cc_amount_untaxed': fields.function(
|
||||
_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
string='Company Cur. Untaxed',
|
||||
help="Invoice untaxed amount in the company currency"\
|
||||
"(useful when invoice currency is different from company currency).",
|
||||
help="Invoice untaxed amount in the company currency"
|
||||
"(useful when invoice currency is different from company currency).",
|
||||
store={
|
||||
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line',
|
||||
'currency_id',
|
||||
@@ -109,14 +106,17 @@ class account_invoice(orm.Model):
|
||||
'account.invoice.line': (_get_invoice_line2, ['price_unit',
|
||||
'invoice_line_tax_id',
|
||||
'quantity', 'discount'], 20),
|
||||
},
|
||||
multi='cc_all'),
|
||||
'cc_amount_tax': fields.function(_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
},
|
||||
multi='cc_all'
|
||||
),
|
||||
|
||||
'cc_amount_tax': fields.function(
|
||||
_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
string='Company Cur. Tax',
|
||||
help="Invoice tax amount in the company currency "\
|
||||
"(useful when invoice currency is different from company currency).",
|
||||
help="Invoice tax amount in the company currency "
|
||||
"(useful when invoice currency is different from company currency).",
|
||||
store={
|
||||
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line',
|
||||
'currency_id',
|
||||
@@ -125,13 +125,16 @@ class account_invoice(orm.Model):
|
||||
'account.invoice.line': (_get_invoice_line2, ['price_unit',
|
||||
'invoice_line_tax_id',
|
||||
'quantity', 'discount'], 20),
|
||||
},
|
||||
multi='cc_all'),
|
||||
'cc_amount_total': fields.function(_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
},
|
||||
multi='cc_all'
|
||||
),
|
||||
|
||||
'cc_amount_total': fields.function(
|
||||
_cc_amount_all,
|
||||
method=True,
|
||||
digits_compute=dp.get_precision('Account'),
|
||||
string='Company Cur. Total',
|
||||
help="Invoice total amount in the company currency "\
|
||||
help="Invoice total amount in the company currency "
|
||||
"(useful when invoice currency is different from company currency).",
|
||||
store={
|
||||
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line',
|
||||
@@ -141,8 +144,6 @@ class account_invoice(orm.Model):
|
||||
'account.invoice.line': (_get_invoice_line2, ['price_unit',
|
||||
'invoice_line_tax_id',
|
||||
'quantity', 'discount'], 20),
|
||||
},
|
||||
},
|
||||
multi='cc_all'),
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -36,7 +36,7 @@ class account_journal(orm.Model):
|
||||
|
||||
_defaults = {
|
||||
'allow_date': True,
|
||||
}
|
||||
}
|
||||
|
||||
def _allow_date_always_active(self, cr, uid, ids):
|
||||
for journal in self.browse(cr, uid, ids):
|
||||
@@ -44,8 +44,9 @@ class account_journal(orm.Model):
|
||||
raise orm.except_orm(
|
||||
_('Error:'),
|
||||
_("The option 'Check Date in Period' must be active "
|
||||
"on journal '%s'.")
|
||||
% journal.name)
|
||||
"on journal '%s'.")
|
||||
% journal.name
|
||||
)
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Move line search view - disable defaults for period and journal",
|
||||
"version" : "0.1",
|
||||
"author" : "Therp BV",
|
||||
"name": "Move line search view - disable defaults for period and journal",
|
||||
"version": "0.1",
|
||||
"author": "Therp BV",
|
||||
"category": 'Accounting & Finance',
|
||||
'description': """
|
||||
OpenERP 7.0 implements a custom javascript search view for move lines. This
|
||||
@@ -36,13 +36,7 @@ have to disable these before entering your own search queries.
|
||||
|
||||
""",
|
||||
'website': 'http://therp.nl',
|
||||
'depends' : [
|
||||
'account',
|
||||
],
|
||||
'js': [
|
||||
'static/src/js/move_line_search_view.js',
|
||||
],
|
||||
'images': [
|
||||
'static/src/img/move_line_search_view.png',
|
||||
],
|
||||
'depends': ['account'],
|
||||
'js': ['static/src/js/move_line_search_view.js'],
|
||||
'images': ['static/src/img/move_line_search_view.png'],
|
||||
}
|
||||
|
||||
@@ -19,4 +19,3 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
'version': '0.1',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Noviat',
|
||||
'category' : 'Generic Modules',
|
||||
'category': 'Generic Modules',
|
||||
'description': """
|
||||
Journal Items Search Extension
|
||||
==============================
|
||||
@@ -37,16 +37,16 @@ These fields can be used in combination with the Search window.
|
||||
|
||||
""",
|
||||
'depends': ['account'],
|
||||
'data' : [
|
||||
'data': [
|
||||
'account_view.xml',
|
||||
],
|
||||
'js': [
|
||||
'static/src/js/account_move_line_search_extension.js',
|
||||
],
|
||||
'qweb' : [
|
||||
'qweb': [
|
||||
'static/src/xml/account_move_line_search_extension.xml',
|
||||
],
|
||||
'css':[
|
||||
'css': [
|
||||
'static/src/css/account_move_line_search_extension.css',
|
||||
],
|
||||
'installable': False,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
@@ -27,21 +27,23 @@
|
||||
Templates for Journal Entries
|
||||
|
||||
User can configure journal entries templates, useful for recurring entries.
|
||||
The amount of each template line can be computed (through python code) or kept as user input. If user input, when using the template, user has to fill the amount of every input lines.
|
||||
The amount of each template line can be computed (through python code) or kept as user input.
|
||||
If user input, when using the template, user has to fill the amount of every input lines.
|
||||
The journal entry form allows lo load, through a wizard, the template to use and the amounts to fill.
|
||||
|
||||
""",
|
||||
'author': 'Agile Business Group',
|
||||
'website': 'http://www.agilebg.com',
|
||||
'license': 'AGPL-3',
|
||||
'depends' : ['account_accountant', 'analytic'],
|
||||
'data' : [
|
||||
'depends': ['account_accountant', 'analytic'],
|
||||
'data': [
|
||||
'move_template.xml',
|
||||
'wizard/select_template.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
],
|
||||
'test': [
|
||||
'test/generate_move.yml',
|
||||
],
|
||||
],
|
||||
'active': False,
|
||||
'installable': False,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
@@ -23,6 +23,7 @@ from openerp.osv import fields, orm
|
||||
from openerp.tools.translate import _
|
||||
import re
|
||||
|
||||
|
||||
class account_document_template(orm.Model):
|
||||
|
||||
_computed_lines = {}
|
||||
@@ -33,7 +34,7 @@ class account_document_template(orm.Model):
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
}
|
||||
}
|
||||
|
||||
def _input_lines(self, cr, uid, template):
|
||||
count = 0
|
||||
@@ -58,13 +59,16 @@ class account_document_template(orm.Model):
|
||||
if self._computed_lines[line_number] is not None:
|
||||
return self._computed_lines[line_number]
|
||||
line = self._get_template_line(self._cr, self._uid, self._current_template_id, line_number)
|
||||
if re.match('L\( *'+str(line_number)+' *\)',line.python_code):
|
||||
raise orm.except_orm(_('Error'),
|
||||
_('Line %s can\'t refer to itself') % str(line_number))
|
||||
if re.match('L\( *' + str(line_number) + ' *\)', line.python_code):
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('Line %s can\'t refer to itself') % str(line_number)
|
||||
)
|
||||
try:
|
||||
self._computed_lines[line_number] = eval(line.python_code.replace('L', 'self.lines'))
|
||||
except KeyError:
|
||||
raise orm.except_orm(_('Error'),
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('Code "%s" refers to non existing line') % line.python_code)
|
||||
return self._computed_lines[line_number]
|
||||
|
||||
@@ -73,8 +77,10 @@ class account_document_template(orm.Model):
|
||||
# returns all the lines (included input lines) in the form {line_number: line_amount}
|
||||
template = self.browse(cr, uid, template_id)
|
||||
if len(input_lines) != self._input_lines(cr, uid, template):
|
||||
raise orm.except_orm(_('Error'),
|
||||
_('Inconsistency between input lines and filled lines for template %s') % template.name)
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('Inconsistency between input lines and filled lines for template %s') % template.name
|
||||
)
|
||||
self._current_template_id = template.id
|
||||
self._cr = cr
|
||||
self._uid = uid
|
||||
@@ -92,6 +98,7 @@ class account_document_template(orm.Model):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class account_document_template_line(orm.Model):
|
||||
|
||||
_name = 'account.document.template.line'
|
||||
@@ -99,6 +106,6 @@ class account_document_template_line(orm.Model):
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
'sequence': fields.integer('Sequence', required=True),
|
||||
'type': fields.selection([('computed', 'Computed'),('input', 'User input')], 'Type', required=True),
|
||||
'python_code':fields.text('Python Code'),
|
||||
}
|
||||
'type': fields.selection([('computed', 'Computed'), ('input', 'User input')], 'Type', required=True),
|
||||
'python_code': fields.text('Python Code'),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
@@ -20,7 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class account_move_template(orm.Model):
|
||||
|
||||
@@ -32,50 +32,58 @@ class account_move_template(orm.Model):
|
||||
'template_line_ids': fields.one2many('account.move.template.line', 'template_id', 'Template Lines'),
|
||||
'cross_journals': fields.boolean('Cross-Journals'),
|
||||
'transitory_acc_id': fields.many2one('account.account', 'Transitory account', required=False),
|
||||
}
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.move.template', context=c),
|
||||
}
|
||||
|
||||
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(
|
||||
cr, uid, 'account.move.template', context=c
|
||||
),
|
||||
}
|
||||
|
||||
def _check_different_journal(self, cr, uid, ids, context=None):
|
||||
#Check that the journal on these lines are different/same in the case of cross journals/single journal
|
||||
journal_ids=[]
|
||||
all_journal_ids=[]
|
||||
move_template=self.pool.get('account.move.template').browse(cr,uid,ids)[0]
|
||||
# Check that the journal on these lines are different/same in the case of cross journals/single journal
|
||||
journal_ids = []
|
||||
all_journal_ids = []
|
||||
move_template = self.pool.get('account.move.template').browse(cr, uid, ids)[0]
|
||||
if not move_template.template_line_ids:
|
||||
return True
|
||||
for template_line in move_template.template_line_ids:
|
||||
all_journal_ids.append(template_line.journal_id.id)
|
||||
if template_line.journal_id.id not in journal_ids :
|
||||
if template_line.journal_id.id not in journal_ids:
|
||||
journal_ids.append(template_line.journal_id.id)
|
||||
if move_template.cross_journals:
|
||||
return len(all_journal_ids)==len(journal_ids)
|
||||
return len(all_journal_ids) == len(journal_ids)
|
||||
else:
|
||||
return len(journal_ids)==1
|
||||
return len(journal_ids) == 1
|
||||
|
||||
_constraints = [
|
||||
(_check_different_journal,
|
||||
'If the template is "cross-journals", the Journals must be different,'
|
||||
'if the template does not "cross-journals" the Journals must be the same!',
|
||||
['journal_id'])
|
||||
]
|
||||
|
||||
_constraints = [(_check_different_journal,
|
||||
'If the template is "cross-journals", the Journals must be different,' \
|
||||
'if the template does not "cross-journals" the Journals must be the same!',
|
||||
['journal_id'])]
|
||||
|
||||
class account_move_template_line(orm.Model):
|
||||
|
||||
_name = 'account.move.template.line'
|
||||
_inherit = 'account.document.template.line'
|
||||
|
||||
_columns = {
|
||||
'journal_id': fields.many2one('account.journal', 'Journal', required=True),
|
||||
'account_id': fields.many2one('account.account', 'Account', required=True, ondelete="cascade"),
|
||||
'move_line_type':fields.selection([
|
||||
('cr','Credit'),
|
||||
('dr','Debit'),
|
||||
], 'Move Line Type', required=True),
|
||||
'account_id': fields.many2one('account.account', 'Account',
|
||||
required=True, ondelete="cascade"),
|
||||
'move_line_type': fields.selection(
|
||||
[('cr', 'Credit'),
|
||||
('dr', 'Debit')],
|
||||
'Move Line Type',
|
||||
required=True
|
||||
),
|
||||
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', ondelete="cascade"),
|
||||
'template_id': fields.many2one('account.move.template', 'Template'),
|
||||
'account_tax_id':fields.many2one('account.tax', 'Tax'),
|
||||
}
|
||||
'account_tax_id': fields.many2one('account.tax', 'Tax'),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('sequence_template_uniq', 'unique (template_id,sequence)', 'The sequence of the line must be unique per template !')
|
||||
('sequence_template_uniq', 'unique (template_id,sequence)',
|
||||
'The sequence of the line must be unique per template !')
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||
#
|
||||
@@ -19,20 +19,34 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields,orm
|
||||
from openerp.osv import fields, orm
|
||||
import time
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class wizard_select_template(orm.TransientModel):
|
||||
|
||||
_name = "wizard.select.move.template"
|
||||
_columns = {
|
||||
'template_id': fields.many2one('account.move.template', 'Move Template', required=True),
|
||||
'template_id': fields.many2one(
|
||||
'account.move.template',
|
||||
'Move Template',
|
||||
required=True
|
||||
),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner'),
|
||||
'line_ids': fields.one2many('wizard.select.move.template.line', 'template_id', 'Lines'),
|
||||
'state': fields.selection([
|
||||
('template_selected','Template selected'),
|
||||
], 'State'),
|
||||
|
||||
'line_ids': fields.one2many(
|
||||
'wizard.select.move.template.line',
|
||||
'template_id',
|
||||
'Lines'
|
||||
),
|
||||
|
||||
'state': fields.selection(
|
||||
[
|
||||
('template_selected', 'Template selected'),
|
||||
],
|
||||
'State'
|
||||
),
|
||||
}
|
||||
|
||||
def on_change_template_id(self, cr, uid, ids, template_id):
|
||||
@@ -48,11 +62,11 @@ class wizard_select_template(orm.TransientModel):
|
||||
'name': line.name,
|
||||
'account_id': line.account_id.id,
|
||||
'move_line_type': line.move_line_type,
|
||||
})
|
||||
})
|
||||
return res
|
||||
|
||||
|
||||
def load_lines(self, cr, uid, ids, context=None):
|
||||
wizard = self.browse(cr, uid, ids, context=context)[0]
|
||||
wizard = self.browse(cr, uid, ids, context=context)[0]
|
||||
template_pool = self.pool.get('account.move.template')
|
||||
wizard_line_pool = self.pool.get('wizard.select.move.template.line')
|
||||
model_data_obj = self.pool.get('ir.model.data')
|
||||
@@ -60,14 +74,14 @@ class wizard_select_template(orm.TransientModel):
|
||||
template = template_pool.browse(cr, uid, wizard.template_id.id)
|
||||
for line in template.template_line_ids:
|
||||
if line.type == 'input':
|
||||
wizard_line_pool.create(cr, uid,{
|
||||
wizard_line_pool.create(cr, uid, {
|
||||
'template_id': wizard.id,
|
||||
'sequence': line.sequence,
|
||||
'name': line.name,
|
||||
'amount': 0.0,
|
||||
'account_id': line.account_id.id,
|
||||
'move_line_type': line.move_line_type,
|
||||
})
|
||||
})
|
||||
if not wizard.line_ids:
|
||||
return self.load_template(cr, uid, ids)
|
||||
wizard.write({'state': 'template_selected'})
|
||||
@@ -76,24 +90,21 @@ class wizard_select_template(orm.TransientModel):
|
||||
view_id = view_rec and view_rec[1] or False
|
||||
|
||||
return {
|
||||
'view_type': 'form',
|
||||
'view_id' : [view_id],
|
||||
'view_mode': 'form',
|
||||
'res_model': 'wizard.select.move.template',
|
||||
'res_id': wizard.id,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'context': context,
|
||||
'view_type': 'form',
|
||||
'view_id': [view_id],
|
||||
'view_mode': 'form',
|
||||
'res_model': 'wizard.select.move.template',
|
||||
'res_id': wizard.id,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'context': context,
|
||||
}
|
||||
|
||||
|
||||
def load_template(self, cr, uid, ids, context=None):
|
||||
template_obj = self.pool.get('account.move.template')
|
||||
template_line_obj = self.pool.get('account.move.template.line')
|
||||
account_period_obj = self.pool.get('account.period')
|
||||
|
||||
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
wizard = self.browse(cr, uid, ids, context=context)[0]
|
||||
|
||||
wizard = self.browse(cr, uid, ids, context=context)[0]
|
||||
if not template_obj.check_zero_lines(cr, uid, wizard):
|
||||
raise orm.except_orm(_('Error !'), _('At least one amount has to be non-zero!'))
|
||||
input_lines = {}
|
||||
@@ -108,21 +119,40 @@ class wizard_select_template(orm.TransientModel):
|
||||
|
||||
computed_lines = template_obj.compute_lines(cr, uid, wizard.template_id.id, input_lines)
|
||||
|
||||
moves={}
|
||||
moves = {}
|
||||
for line in wizard.template_id.template_line_ids:
|
||||
if line.journal_id.id not in moves:
|
||||
moves[line.journal_id.id]=self._make_move(
|
||||
cr,uid,wizard.template_id.name,period_id,line.journal_id.id, wizard.partner_id.id)
|
||||
|
||||
id_line=self._make_move_line(cr,uid,line,computed_lines,moves[line.journal_id.id],period_id,
|
||||
wizard.partner_id.id)
|
||||
if wizard.template_id.cross_journals :
|
||||
trans_account_id=wizard.template_id.transitory_acc_id.id
|
||||
id_trans_line=self._make_transitory_move_line(cr,uid,line,computed_lines,
|
||||
moves[line.journal_id.id],period_id,trans_account_id, wizard.partner_id.id)
|
||||
|
||||
moves[line.journal_id.id] = self._make_move(
|
||||
cr, uid,
|
||||
wizard.template_id.name,
|
||||
period_id,
|
||||
line.journal_id.id,
|
||||
wizard.partner_id.id
|
||||
)
|
||||
|
||||
self._make_move_line(
|
||||
cr, uid,
|
||||
line,
|
||||
computed_lines,
|
||||
moves[line.journal_id.id],
|
||||
period_id,
|
||||
wizard.partner_id.id
|
||||
)
|
||||
if wizard.template_id.cross_journals:
|
||||
trans_account_id = wizard.template_id.transitory_acc_id.id
|
||||
self._make_transitory_move_line(
|
||||
cr,
|
||||
uid,
|
||||
line,
|
||||
computed_lines,
|
||||
moves[line.journal_id.id],
|
||||
period_id,
|
||||
trans_account_id,
|
||||
wizard.partner_id.id
|
||||
)
|
||||
|
||||
return {
|
||||
'domain': "[('id','in', "+str(moves.values())+")]",
|
||||
'domain': "[('id','in', " + str(moves.values()) + ")]",
|
||||
'name': 'Entries',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
@@ -130,8 +160,8 @@ class wizard_select_template(orm.TransientModel):
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'current',
|
||||
}
|
||||
#'res_id': moves.values() or False,
|
||||
def _make_move(self, cr, uid,ref,period_id,journal_id, partner_id):
|
||||
|
||||
def _make_move(self, cr, uid, ref, period_id, journal_id, partner_id):
|
||||
account_move_obj = self.pool.get('account.move')
|
||||
move_id = account_move_obj.create(cr, uid, {
|
||||
'ref': ref,
|
||||
@@ -140,15 +170,18 @@ class wizard_select_template(orm.TransientModel):
|
||||
'partner_id': partner_id,
|
||||
})
|
||||
return move_id
|
||||
|
||||
def _make_move_line(self,cr,uid,line,computed_lines,move_id,period_id, partner_id):
|
||||
|
||||
def _make_move_line(self, cr, uid, line, computed_lines, move_id, period_id, partner_id):
|
||||
account_move_line_obj = self.pool.get('account.move.line')
|
||||
analytic_account_id = False
|
||||
if line.analytic_account_id:
|
||||
if not line.journal_id.analytic_journal_id:
|
||||
raise orm.except_orm(_('No Analytic Journal !'),
|
||||
_("You have to define an analytic journal on the '%s' journal!")
|
||||
% (line.journal_id.name,))
|
||||
raise orm.except_orm(
|
||||
_('No Analytic Journal !'),
|
||||
_("You have to dfine an analytic journal on the '%s' journal!")
|
||||
% (line.journal_id.name,)
|
||||
)
|
||||
|
||||
analytic_account_id = line.analytic_account_id.id
|
||||
val = {
|
||||
'name': line.name,
|
||||
@@ -169,16 +202,19 @@ class wizard_select_template(orm.TransientModel):
|
||||
val['debit'] = computed_lines[line.sequence]
|
||||
id_line = account_move_line_obj.create(cr, uid, val)
|
||||
return id_line
|
||||
|
||||
def _make_transitory_move_line(
|
||||
self,cr,uid,line,computed_lines,move_id,period_id,trans_account_id, partner_id):
|
||||
|
||||
def _make_transitory_move_line(self, cr, uid, line,
|
||||
computed_lines, move_id, period_id,
|
||||
trans_account_id, partner_id):
|
||||
account_move_line_obj = self.pool.get('account.move.line')
|
||||
analytic_account_id = False
|
||||
if line.analytic_account_id:
|
||||
if not line.journal_id.analytic_journal_id:
|
||||
raise orm.except_orm(_('No Analytic Journal !'),
|
||||
raise orm.except_orm(
|
||||
_('No Analytic Journal !'),
|
||||
_("You have to define an analytic journal on the '%s' journal!")
|
||||
% (wizard.template_id.journal_id.name,))
|
||||
% (line.template_id.journal_id.name,)
|
||||
)
|
||||
analytic_account_id = line.analytic_account_id.id
|
||||
val = {
|
||||
'name': 'transitory',
|
||||
@@ -197,6 +233,7 @@ class wizard_select_template(orm.TransientModel):
|
||||
id_line = account_move_line_obj.create(cr, uid, val)
|
||||
return id_line
|
||||
|
||||
|
||||
class wizard_select_template_line(orm.TransientModel):
|
||||
_description = 'Template Lines'
|
||||
_name = "wizard.select.move.template.line"
|
||||
@@ -205,9 +242,12 @@ class wizard_select_template_line(orm.TransientModel):
|
||||
'sequence': fields.integer('Number', required=True),
|
||||
'name': fields.char('Name', size=64, required=True, readonly=True),
|
||||
'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True),
|
||||
'move_line_type':fields.selection([
|
||||
('cr','Credit'),
|
||||
('dr','Debit'),
|
||||
], 'Move Line Type', required=True,readonly=True),
|
||||
'move_line_type': fields.selection(
|
||||
[('cr', 'Credit'),
|
||||
('dr', 'Debit')],
|
||||
'Move Line Type',
|
||||
required=True,
|
||||
readonly=True
|
||||
),
|
||||
'amount': fields.float('Amount', required=True),
|
||||
}
|
||||
|
||||
@@ -19,4 +19,3 @@
|
||||
##############################################################################
|
||||
|
||||
from . import wizard
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -17,10 +17,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Wizard to validate multiple moves",
|
||||
"version" : "1.0",
|
||||
"depends" : ["base", "account", "account_constraints"],
|
||||
"author" : "Camptocamp",
|
||||
"name": "Wizard to validate multiple moves",
|
||||
"version": "1.0",
|
||||
"depends": ["base", "account", "account_constraints"],
|
||||
"author": "Camptocamp",
|
||||
'license': 'AGPL-3',
|
||||
"description": """
|
||||
Re-defining a base wizard (validate all moves in a period for a journal),
|
||||
@@ -32,4 +32,3 @@ base one defined in addons/account/wizard.
|
||||
'installable': False,
|
||||
'active': False,
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -19,4 +19,3 @@
|
||||
##############################################################################
|
||||
|
||||
from . import account_validate_move
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -49,12 +49,12 @@ class ValidateAccountMove(orm.TransientModel):
|
||||
ids_move = obj_move.search(cr, uid, [('state', '=', 'draft'),
|
||||
('journal_id', 'in', journal_ids),
|
||||
('period_id', '=', period_ids)],
|
||||
context=context)
|
||||
context=context)
|
||||
if not ids_move:
|
||||
raise osv.except_osv(_('Warning!'),
|
||||
('Specified journal does not have any account move entries in draft state for this period.'))
|
||||
raise osv.except_osv(
|
||||
_('Warning!'),
|
||||
_('Specified journal does not have any account move entries '
|
||||
'in draft state for this period.')
|
||||
)
|
||||
obj_move.button_validate(cr, uid, ids_move, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ class account_account_type(orm.Model):
|
||||
_inherit = "account.account.type"
|
||||
|
||||
_columns = {
|
||||
'partner_policy': fields.selection([
|
||||
('optional', 'Optional'),
|
||||
('always', 'Always'),
|
||||
('never', 'Never')
|
||||
], 'Policy for partner field',
|
||||
'partner_policy': fields.selection(
|
||||
[('optional', 'Optional'),
|
||||
('always', 'Always'),
|
||||
('never', 'Never')],
|
||||
'Policy for partner field',
|
||||
help="Set the policy for the partner field : if you select "
|
||||
"'Optional', the accountant is free to put a partner "
|
||||
"on an account move line with this type of account ; "
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Account renumber wizard
|
||||
"""
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
'author': "Pexego",
|
||||
'website': "http://www.pexego.es",
|
||||
'category': "Enterprise Specific Modules",
|
||||
'contributors' : ['Pedro M. Baeza', 'Joaquín Gutierrez'],
|
||||
'contributors': ['Pedro M. Baeza', 'Joaquín Gutierrez'],
|
||||
'description': """
|
||||
This module adds a wizard to renumber account moves by date only for admin users.
|
||||
=================================================================================
|
||||
@@ -43,11 +43,11 @@ It will recreate the sequence number of each account move using their journal se
|
||||
- Sequences per journal are supported.
|
||||
- Sequences with prefixes and sufixes based on the move date are also supported.
|
||||
""",
|
||||
"license" : "AGPL-3",
|
||||
"depends" : [
|
||||
"license": "AGPL-3",
|
||||
"depends": [
|
||||
'account',
|
||||
],
|
||||
"demo" : [],
|
||||
"demo": [],
|
||||
"data": [
|
||||
'wizard/wizard_renumber_view.xml',
|
||||
],
|
||||
|
||||
@@ -27,9 +27,7 @@ that can be used later for testing the renumber wizard.
|
||||
__author__ = "Borja López Soilán (Pexego)"
|
||||
|
||||
import sys
|
||||
import re
|
||||
import xmlrpclib
|
||||
import socket
|
||||
import logging
|
||||
logger = logging.getLogger("create_lots_of_account_moves")
|
||||
|
||||
|
||||
@@ -19,11 +19,8 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
Account renumber wizard
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import wizard_renumber
|
||||
|
||||
@@ -24,6 +24,7 @@ from openerp.tools.translate import _
|
||||
from openerp import SUPERUSER_ID
|
||||
import logging
|
||||
|
||||
|
||||
class wizard_renumber(orm.TransientModel):
|
||||
_name = "wizard.renumber"
|
||||
_description = "Account renumber wizard"
|
||||
@@ -41,7 +42,7 @@ class wizard_renumber(orm.TransientModel):
|
||||
help='Fiscal periods to renumber',
|
||||
string="Periods", ondelete='null'),
|
||||
'number_next': fields.integer('First Number', required=True,
|
||||
help="Journal sequences will start counting on this number"),
|
||||
help="Journal sequences will start counting on this number"),
|
||||
'state': fields.selection([('init', 'Initial'),
|
||||
('renumber', 'Renumbering')], readonly=True)
|
||||
}
|
||||
@@ -94,28 +95,30 @@ class wizard_renumber(orm.TransientModel):
|
||||
[('journal_id', 'in', journal_ids),
|
||||
('period_id', '=', period),
|
||||
('state', '=', 'posted')],
|
||||
limit=0, order='date,id',
|
||||
context=context)
|
||||
limit=0, order='date,id',
|
||||
context=context)
|
||||
if not move_ids:
|
||||
continue
|
||||
logger.debug("Renumbering %d account moves." % len(move_ids))
|
||||
for move in move_obj.browse(cr, uid, move_ids, context=context):
|
||||
sequence_id = self.get_sequence_id_for_fiscalyear_id(
|
||||
cr, uid,
|
||||
sequence_id=move.journal_id.sequence_id.id,
|
||||
fiscalyear_id=move.period_id.fiscalyear_id.id)
|
||||
if not sequence_id in sequences_seen:
|
||||
cr, uid,
|
||||
sequence_id=move.journal_id.sequence_id.id,
|
||||
fiscalyear_id=move.period_id.fiscalyear_id.id
|
||||
)
|
||||
if sequence_id not in sequences_seen:
|
||||
sequence_obj.write(cr, SUPERUSER_ID, [sequence_id],
|
||||
{'number_next': number_next})
|
||||
sequences_seen.append(sequence_id)
|
||||
# Generate (using our own get_id) and write the new move number
|
||||
c = {'fiscalyear_id': move.period_id.fiscalyear_id.id}
|
||||
new_name = sequence_obj.next_by_id(cr,uid,
|
||||
move.journal_id.sequence_id.id, context=c)
|
||||
new_name = sequence_obj.next_by_id(cr, uid,
|
||||
move.journal_id.sequence_id.id,
|
||||
context=c)
|
||||
# Note: We can't just do a
|
||||
# "move_obj.write(cr, uid, [move.id], {'name': new_name})"
|
||||
# cause it might raise a
|
||||
#"You can't do this modification on a confirmed entry"
|
||||
# ``You can't do this modification on a confirmed entry``
|
||||
# exception.
|
||||
cr.execute('UPDATE account_move SET name=%s WHERE id=%s',
|
||||
(new_name, move.id))
|
||||
|
||||
@@ -20,6 +20,5 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
import account_reversal
|
||||
import wizard
|
||||
|
||||
@@ -48,7 +48,7 @@ during the Akretion-Camptocamp code sprint of June 2011.
|
||||
'data': [
|
||||
'account_view.xml',
|
||||
'wizard/account_move_reverse_view.xml'
|
||||
],
|
||||
],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class account_move(orm.Model):
|
||||
'Reversal Entry',
|
||||
ondelete='set null',
|
||||
readonly=True),
|
||||
}
|
||||
}
|
||||
|
||||
def _move_reversal(self, cr, uid, move, reversal_date,
|
||||
reversal_period_id=False, reversal_journal_id=False,
|
||||
@@ -69,7 +69,7 @@ class account_move(orm.Model):
|
||||
|
||||
if not reversal_period_id:
|
||||
reversal_period_id = period_obj.find(
|
||||
cr, uid, reversal_date, context=period_ctx)[0]
|
||||
cr, uid, reversal_date, context=period_ctx)[0]
|
||||
if not reversal_journal_id:
|
||||
reversal_journal_id = move.journal_id.id
|
||||
|
||||
@@ -93,9 +93,10 @@ class account_move(orm.Model):
|
||||
reversal_move = self.browse(cr, uid, reversal_move_id, context=context)
|
||||
for reversal_move_line in reversal_move.line_id:
|
||||
reversal_ml_name = ' '.join(
|
||||
[x for x
|
||||
in [move_line_prefix, reversal_move_line.name]
|
||||
if x])
|
||||
[x for x
|
||||
in [move_line_prefix, reversal_move_line.name]
|
||||
if x]
|
||||
)
|
||||
move_line_obj.write(
|
||||
cr,
|
||||
uid,
|
||||
@@ -137,14 +138,15 @@ class account_move(orm.Model):
|
||||
continue # skip the reversal creation if already done
|
||||
|
||||
reversal_move_id = self._move_reversal(
|
||||
cr, uid,
|
||||
src_move,
|
||||
reversal_date,
|
||||
reversal_period_id=reversal_period_id,
|
||||
reversal_journal_id=reversal_journal_id,
|
||||
move_prefix=move_prefix,
|
||||
move_line_prefix=move_line_prefix,
|
||||
context=context)
|
||||
cr, uid,
|
||||
src_move,
|
||||
reversal_date,
|
||||
reversal_period_id=reversal_period_id,
|
||||
reversal_journal_id=reversal_journal_id,
|
||||
move_prefix=move_prefix,
|
||||
move_line_prefix=move_line_prefix,
|
||||
context=context
|
||||
)
|
||||
|
||||
if reversal_move_id:
|
||||
reversed_move_ids.append(reversal_move_id)
|
||||
|
||||
@@ -59,7 +59,7 @@ class account_move_reversal(orm.TransientModel):
|
||||
help="Prefix that will be added to the name of the journal "
|
||||
"item to be reversed to create the name of the reversal "
|
||||
"journal item (a space is added after the prefix)."),
|
||||
}
|
||||
}
|
||||
|
||||
def _next_period_first_date(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
@@ -83,7 +83,7 @@ class account_move_reversal(orm.TransientModel):
|
||||
_defaults = {
|
||||
'date': _next_period_first_date,
|
||||
'move_line_prefix': 'REV -',
|
||||
}
|
||||
}
|
||||
|
||||
def action_reverse(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
@@ -110,7 +110,7 @@ class account_move_reversal(orm.TransientModel):
|
||||
context=context)
|
||||
|
||||
__, action_id = mod_obj.get_object_reference(
|
||||
cr, uid, 'account', 'action_move_journal_line')
|
||||
cr, uid, 'account', 'action_move_journal_line')
|
||||
action = act_obj.read(cr, uid, [action_id], context=context)[0]
|
||||
action['domain'] = unicode([('id', 'in', reversed_move_ids)])
|
||||
action['name'] = _('Reversal Entries')
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from . import model
|
||||
from . import model
|
||||
|
||||
@@ -41,7 +41,7 @@ default values on accounts and products on demand. Defaults for purchase
|
||||
and sales taxes can be set at independent times. During the transition,
|
||||
the old taxes can still be selected manually on invoice lines etc.
|
||||
|
||||
You can select to also duplicate linked tax code
|
||||
You can select to also duplicate linked tax code
|
||||
|
||||
After the transition, the old taxes can be made inactive.
|
||||
|
||||
|
||||
@@ -52,11 +52,9 @@ class SelectTaxes(orm.TransientModel):
|
||||
result += add_tree(child)
|
||||
return result
|
||||
|
||||
covered = [
|
||||
x.source_tax_id.id for x in
|
||||
(wiz.config_id.sale_line_ids +
|
||||
wiz.config_id.purchase_line_ids)
|
||||
]
|
||||
covered = [x.source_tax_id.id for x in
|
||||
(wiz.config_id.sale_line_ids +
|
||||
wiz.config_id.purchase_line_ids)]
|
||||
taxes = []
|
||||
for tax in list(set(map(get_root_node, wiz.tax_ids))):
|
||||
taxes += add_tree(tax)
|
||||
@@ -83,4 +81,4 @@ class SelectTaxes(orm.TransientModel):
|
||||
'account.tax', 'update_tax_select_covered_taxes_rel',
|
||||
'tax_select_id', 'tax_id',
|
||||
string='Covered taxes'),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,15 +79,15 @@ class UpdateTaxConfig(orm.Model):
|
||||
readonly=True),
|
||||
'duplicate_tax_code': fields.boolean(
|
||||
'Duplicate Tax code linked'),
|
||||
}
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'state': 'draft',
|
||||
}
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique(name)', 'Name must be unique.'),
|
||||
]
|
||||
]
|
||||
|
||||
def add_lines(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
@@ -106,14 +106,15 @@ class UpdateTaxConfig(orm.Model):
|
||||
covered_tax_ids = [
|
||||
x.source_tax_id.id
|
||||
for x in config['purchase_line_ids'] + config['sale_line_ids']
|
||||
]
|
||||
]
|
||||
|
||||
res_id = wizard_obj.create(
|
||||
cr, uid, {
|
||||
'config_id': ids[0],
|
||||
'type_tax_use': context['type_tax_use'],
|
||||
'covered_tax_ids': [(6, 0, covered_tax_ids)],
|
||||
}, context=context)
|
||||
},
|
||||
context=context)
|
||||
local_context = context.copy()
|
||||
local_context['active_id'] = res_id
|
||||
|
||||
@@ -128,7 +129,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
'target': 'new',
|
||||
'res_id': res_id,
|
||||
'nodestroy': True,
|
||||
}
|
||||
}
|
||||
|
||||
def confirm(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
@@ -149,7 +150,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
log += " - %s (%s)\n" % (
|
||||
line.source_tax_id.name,
|
||||
line.source_tax_id.description
|
||||
)
|
||||
)
|
||||
# Switch names around, not violating the uniqueness constraint
|
||||
tax_old_name = line.source_tax_id.name
|
||||
tax_pool.write(
|
||||
@@ -163,7 +164,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
# 6.0 messes up the name change with copy + write, while
|
||||
# 6.1 throws name uniqueness constraint violation
|
||||
# So jumping some hoops with rewriting the new name
|
||||
## We will check if we need to dupliace
|
||||
# We will check if we need to dupliace
|
||||
cp_base_code_id = False
|
||||
cp_ref_base_code_id = False
|
||||
cp_tax_code_id = False
|
||||
@@ -186,10 +187,10 @@ class UpdateTaxConfig(orm.Model):
|
||||
line.source_tax_id.tax_code_id.id,
|
||||
{'name': rename_old})
|
||||
if line.source_tax_id.ref_base_code_id:
|
||||
## Check if with have the same tax code for base_code_id
|
||||
# Check if with have the same tax code for base_code_id
|
||||
if line.source_tax_id.ref_base_code_id.id == line.source_tax_id.base_code_id.id:
|
||||
cp_ref_base_code_id = cp_base_code_id
|
||||
else:
|
||||
else:
|
||||
cp_ref_base_code_id = tax_code_pool.copy(cr, uid,
|
||||
line.source_tax_id.ref_base_code_id.id)
|
||||
rename_old = '[%s] %s' % (config.name,
|
||||
@@ -210,25 +211,31 @@ class UpdateTaxConfig(orm.Model):
|
||||
{'name': rename_old})
|
||||
else:
|
||||
cp_base_code_id = line.source_tax_id.base_code_id and line.source_tax_id.base_code_id.id or False
|
||||
cp_ref_base_code_id = line.source_tax_id.ref_base_code_id and line.source_tax_id.ref_base_code_id.id or False
|
||||
cp_ref_base_code_id = (line.source_tax_id.ref_base_code_id and
|
||||
line.source_tax_id.ref_base_code_id.id or False)
|
||||
cp_tax_code_id = line.source_tax_id.tax_code_id and line.source_tax_id.tax_code_id.id or False
|
||||
cp_ref_tax_code_id = line.source_tax_id.ref_tax_code_id and line.source_tax_id.ref_tax_code_id.id or False
|
||||
cp_ref_tax_code_id = (line.source_tax_id.ref_tax_code_id and
|
||||
line.source_tax_id.ref_tax_code_id.id or False)
|
||||
|
||||
target_tax_id = tax_pool.copy(
|
||||
cr, uid, line.source_tax_id.id,
|
||||
{'name': '[update, %s] %s' % (config.name, tax_old_name),
|
||||
'amount': amount_new,
|
||||
'parent_id': False,
|
||||
'child_ids': [(6, 0, [])],
|
||||
}, context=context)
|
||||
{
|
||||
'name': '[update, %s] %s' % (config.name, tax_old_name),
|
||||
'amount': amount_new,
|
||||
'parent_id': False,
|
||||
'child_ids': [(6, 0, [])],
|
||||
},
|
||||
context=context
|
||||
)
|
||||
tax_pool.write(
|
||||
cr, uid, target_tax_id, {'name': tax_old_name,
|
||||
'base_code_id': cp_base_code_id,
|
||||
'ref_base_code_id': cp_ref_base_code_id,
|
||||
'tax_code_id': cp_tax_code_id,
|
||||
'ref_tax_code_id': cp_ref_tax_code_id
|
||||
}, context=context
|
||||
)
|
||||
cr, uid, target_tax_id,
|
||||
{'name': tax_old_name,
|
||||
'base_code_id': cp_base_code_id,
|
||||
'ref_base_code_id': cp_ref_base_code_id,
|
||||
'tax_code_id': cp_tax_code_id,
|
||||
'ref_tax_code_id': cp_ref_tax_code_id},
|
||||
context=context
|
||||
)
|
||||
tax_map[line.source_tax_id.id] = target_tax_id
|
||||
line_pool.write(
|
||||
cr, uid, line.id,
|
||||
@@ -253,18 +260,18 @@ class UpdateTaxConfig(orm.Model):
|
||||
cr, uid, fp_tax.id,
|
||||
{'tax_src_id': tax_map[fp_tax.tax_src_id.id],
|
||||
'tax_dest_id': tax_map.get(
|
||||
fp_tax.tax_dest_id.id, fp_tax.tax_dest_id.id)},
|
||||
fp_tax.tax_dest_id.id, fp_tax.tax_dest_id.id)},
|
||||
context=context)
|
||||
new_fp_tax = fp_tax_pool.browse(
|
||||
cr, uid, new_fp_tax_id, context=context)
|
||||
log += ("\nCreate new tax mapping on position %s:\n"
|
||||
"%s (%s)\n"
|
||||
"=> %s (%s)\n" % (
|
||||
new_fp_tax.position_id.name,
|
||||
new_fp_tax.tax_src_id.name,
|
||||
new_fp_tax.tax_src_id.description,
|
||||
new_fp_tax.tax_dest_id.name,
|
||||
new_fp_tax.tax_dest_id.description,
|
||||
new_fp_tax.position_id.name,
|
||||
new_fp_tax.tax_src_id.name,
|
||||
new_fp_tax.tax_src_id.description,
|
||||
new_fp_tax.tax_dest_id.name,
|
||||
new_fp_tax.tax_dest_id.description,
|
||||
))
|
||||
self.write(
|
||||
cr, uid, ids[0],
|
||||
@@ -279,7 +286,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': ids[0],
|
||||
'nodestroy': True,
|
||||
}
|
||||
}
|
||||
|
||||
def set_defaults(self, cr, uid, ids, context=None):
|
||||
if not context or not context.get('type_tax_use'):
|
||||
@@ -308,7 +315,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
('name', '=', field_name)],
|
||||
context=local_context)
|
||||
for value in ir_values_pool.browse(
|
||||
cr, uid, values_ids, context=context):
|
||||
cr, uid, values_ids, context=context):
|
||||
val = False
|
||||
write = False
|
||||
try:
|
||||
@@ -337,9 +344,9 @@ class UpdateTaxConfig(orm.Model):
|
||||
model_pool = self.pool.get('ir.model')
|
||||
model_ids = model_pool.search(cr, uid, [], context=context)
|
||||
models = model_pool.read(
|
||||
cr, uid, model_ids, ['model'], context=context)
|
||||
cr, uid, model_ids, ['model'], context=context)
|
||||
pool_models_items = [(x['model'], self.pool.get(
|
||||
x['model'])) for x in models]
|
||||
x['model'])) for x in models]
|
||||
# 6.1: self.pool.models.items():
|
||||
for model_name, model in pool_models_items:
|
||||
if model:
|
||||
@@ -354,17 +361,17 @@ class UpdateTaxConfig(orm.Model):
|
||||
context['type_tax_use'])
|
||||
for (model, field) in [
|
||||
# make this a configurable list of ir_model_fields one day?
|
||||
('account.account', 'tax_ids'),
|
||||
('product.product', 'supplier_taxes_id'),
|
||||
('product.product', 'taxes_id'),
|
||||
('product.template', 'supplier_taxes_id'),
|
||||
('product.template', 'taxes_id')]:
|
||||
('account.account', 'tax_ids'),
|
||||
('product.product', 'supplier_taxes_id'),
|
||||
('product.product', 'taxes_id'),
|
||||
('product.template', 'supplier_taxes_id'),
|
||||
('product.template', 'taxes_id')]:
|
||||
pool = self.pool.get(model)
|
||||
obj_ids = pool.search(
|
||||
cr, uid, [(field, 'in', tax_map.keys())],
|
||||
context=local_context)
|
||||
for obj in pool.read(
|
||||
cr, uid, obj_ids, [field], context=context):
|
||||
cr, uid, obj_ids, [field], context=context):
|
||||
new_val = []
|
||||
write = False
|
||||
for i in obj[field]:
|
||||
@@ -393,7 +400,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': ids[0],
|
||||
'nodestroy': True,
|
||||
}
|
||||
}
|
||||
|
||||
def set_inactive(self, cr, uid, ids, context=None):
|
||||
if not context or not context.get('type_tax_use'):
|
||||
@@ -430,7 +437,7 @@ class UpdateTaxConfig(orm.Model):
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': ids[0],
|
||||
'nodestroy': True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UpdateTaxConfigLine(orm.Model):
|
||||
@@ -439,7 +446,7 @@ class UpdateTaxConfigLine(orm.Model):
|
||||
_rec_name = 'source_tax_id' # Wha'evuh
|
||||
|
||||
def _get_config_field(
|
||||
self, cr, uid, ids, field, args, context=None):
|
||||
self, cr, uid, ids, field, args, context=None):
|
||||
# Retrieve values of the associated config_id
|
||||
# either sale or purchase
|
||||
result = dict([(x, False) for x in ids or []])
|
||||
@@ -480,4 +487,4 @@ class UpdateTaxConfigLine(orm.Model):
|
||||
'state': fields.function(
|
||||
_get_config_field, 'state', method=True,
|
||||
type='char', size=16, string='State'),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ref;date;period_id;journal_id;line_id / account_id;line_id / partner_id;line_id / name;line_id / debit;line_id / credit;line_id/tax_code_id
|
||||
test_3;2013-10-01;X 01/2013;MISC;X2001;Camptocamp;TEST C2C;;1000;Tax Received
|
||||
;;;;X11003;Camptocamp;TEST C2C;;200;Tax Paid
|
||||
test_3;2014-01-01;X 01/2014;Sales Journal - (test);X2001;Camptocamp;TEST C2C1;;1000;
|
||||
;;;;X11003;Camptocamp;TEST C2C;;200;;
|
||||
;;;;X11002;Camptocamp;TEST C2C;1200;;
|
||||
test_3b;2013-10-01;X 01/2013;MISC;X2001;Camptocamp;TEST C2C;;1000;Tax Received
|
||||
test_3b;2014-01-01;X 01/2014;Sales Journal - (test);X2001;Camptocamp;TEST C2C2;;1000;
|
||||
;;;;X11003;Camptocamp;TEST C2C;;200;Faulty code
|
||||
;;;;X11002;Camptocamp;TEST C2C;1200;;
|
||||
;;;;X11002;Camptocamp;TEST C2C;1200;;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
ref;date;period_id;journal_id;line_id / account_id;line_id / partner_id;line_id / name;line_id / debit;line_id / credit;line_id/tax_code_id
|
||||
éöüàè_test_1;2013-10-01;X 01/2013;MISC;X2001;Camptocamp;TEST C2C;;1000;Tax Received
|
||||
;;;;X11003;Camptocamp;TEST C2C;;200;Tax Paid
|
||||
;;;;X11002;Camptocamp;TEST C2C;1200;;
|
||||
éöüàè_test_1;2014-01-01;X 01/2014;Sales Journal - (test);X2001;Camptocamp;TEST C2C4;;1000;
|
||||
;;;;X11003;Camptocamp;TEST C2C5;;200;;
|
||||
;;;;X11002;Camptocamp;TEST C2C6;1200;;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
ref;date;period_id;journal_id;line_id / account_id;line_id / partner_id;line_id / name;line_id / debit;line_id / credit;line_id/tax_code_id
|
||||
test_2;2013-10-01;X 01/2013;MISC;X2001;Camptocamp;TEST C2C;;1000;Tax Received
|
||||
;;;;X11003;Camptocamp;TEST C2C;;200;Tax Paid
|
||||
;;;;X11002;Camptocamp;TEST C2C;1200;;
|
||||
test_2;2014-01-01;X 01/2014;Sales Journal - (test);X2001;Camptocamp;TEST C2C8;;1000;
|
||||
;;;;X11003;Camptocamp;TEST C2C9;;200;;
|
||||
;;;;X11002;Camptocamp;TEST C2C10;1200;;
|
||||
|
||||
|
@@ -22,4 +22,3 @@
|
||||
|
||||
from . import company
|
||||
from . import currency_rate_date_check
|
||||
|
||||
|
||||
@@ -31,9 +31,14 @@
|
||||
Currency Rate Date Check
|
||||
========================
|
||||
|
||||
This module adds a check on dates when doing currency conversion in OpenERP. It checks that the currency rate used to make the conversion is not more than N days away from the date of the amount to convert. The maximum number of days of the interval can be configured on the company form.
|
||||
This module adds a check on dates when doing currency conversion in OpenERP.
|
||||
It checks that the currency rate used to make the conversion is not more than N days away
|
||||
from the date of the amount to convert.
|
||||
|
||||
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com> for any help or question about this module.
|
||||
The maximum number of days of the interval can be configured on the company form.
|
||||
|
||||
Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com>
|
||||
for any help or question about this module.
|
||||
""",
|
||||
'author': 'Akretion',
|
||||
'website': 'http://www.akretion.com',
|
||||
@@ -42,7 +47,7 @@ Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com> for
|
||||
'images': [
|
||||
'images/date_check_error_popup.jpg',
|
||||
'images/date_check_company_config.jpg',
|
||||
],
|
||||
],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
}
|
||||
|
||||
@@ -22,13 +22,16 @@
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class res_company(orm.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
_columns = {
|
||||
'currency_rate_max_delta': fields.integer(
|
||||
'Max Time Delta in Days for Currency Rates',
|
||||
help="This is the maximum interval in days between the date associated with the amount to convert and the date of the nearest currency rate available in OpenERP."),
|
||||
help="This is the maximum interval in days between the date associated "
|
||||
"with the amount to convert and the date of the nearest currency "
|
||||
"rate available in OpenERP."),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
@@ -39,4 +42,4 @@ class res_company(orm.Model):
|
||||
('currency_rate_max_delta_positive',
|
||||
'CHECK (currency_rate_max_delta >= 0)',
|
||||
"The value of the field 'Max Time Delta in Days for Currency Rates' must be positive or 0."),
|
||||
]
|
||||
]
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import fields, orm
|
||||
from datetime import datetime, timedelta
|
||||
from openerp.osv import orm
|
||||
from datetime import datetime
|
||||
from openerp.tools.translate import _
|
||||
|
||||
# Here are some explainations about the design of this module.
|
||||
@@ -29,7 +29,9 @@ from openerp.tools.translate import _
|
||||
# compute() -> _get_conversion_rate() -> _current_rate() -> _current_rate_computation()
|
||||
# The date used for the rate is the one in the context
|
||||
# compute() adds currency_rate_type_from and currency_rate_type_to to the context
|
||||
# _get_conversion_rate() adds currency_rate_type_id to context ; its value is currency_rate_type_to ; if it doesn't exist it's currency_rate_type_from ; if it doesn't exist either it's False
|
||||
# _get_conversion_rate() adds currency_rate_type_id to context ;
|
||||
# its value is currency_rate_type_to ;
|
||||
# if it doesn't exist it's currency_rate_type_from ; if it doesn't exist either it's False
|
||||
# It already contains raise "No rate found for currency ... at the date ..."
|
||||
# _current_rate() reads currency_rate_type_id from context and uses it in the SQL request
|
||||
# This is the function used for the definition of the field.function 'rate' on res_currency
|
||||
@@ -45,10 +47,12 @@ class res_currency(orm.Model):
|
||||
_inherit = 'res.currency'
|
||||
|
||||
def _current_rate_computation(self, cr, uid, ids, name, arg, raise_on_no_rate, context=None):
|
||||
if context is None: context = {}
|
||||
if context is None:
|
||||
context = {}
|
||||
# We only do the check if there is an explicit date in the context and
|
||||
# there is no specific currency_rate_type_id
|
||||
if context.get('date') and not context.get('currency_rate_type_id') and not context.get('disable_rate_date_check'):
|
||||
if context.get('date') and not context.get('currency_rate_type_id') and\
|
||||
not context.get('disable_rate_date_check'):
|
||||
for currency_id in ids:
|
||||
# We could get the company from the currency, but it's not a
|
||||
# 'required' field, so we should probably continue to get it from
|
||||
@@ -67,7 +71,7 @@ class res_currency(orm.Model):
|
||||
('currency_id', '=', currency_id),
|
||||
('name', '<=', date),
|
||||
('currency_rate_type_id', '=', None)
|
||||
], order='name desc', limit=1, context=context)
|
||||
], order='name desc', limit=1, context=context)
|
||||
if not selected_rate:
|
||||
continue
|
||||
|
||||
@@ -76,7 +80,13 @@ class res_currency(orm.Model):
|
||||
max_delta = user.company_id.currency_rate_max_delta
|
||||
if (date_datetime - rate_date_datetime).days > max_delta:
|
||||
currency_name = self.read(cr, uid, currency_id, ['name'], context=context)['name']
|
||||
raise orm.except_orm(_('Error'), _('You are requesting a rate conversion on %s for currency %s but the nearest rate before that date is dated %s and the maximum currency rate time delta for your company is %s days') % (date, currency_name, rate_date, max_delta))
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('You are requesting a rate conversion on %s for '
|
||||
'currency %s but the nearest rate before that date is '
|
||||
'dated %s and the maximum currency rate time delta for '
|
||||
'your company is %s days') % (date, currency_name, rate_date, max_delta)
|
||||
)
|
||||
# Now we call the regular function from the "base" module
|
||||
return super(res_currency, self)._current_rate_computation(cr, uid, ids, name, arg, raise_on_no_rate, context=context)
|
||||
|
||||
return super(res_currency, self)._current_rate_computation(
|
||||
cr, uid, ids, name, arg, raise_on_no_rate, context=context)
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
"name" : "Currency Rate Update",
|
||||
"version" : "0.7",
|
||||
"author" : "Camptocamp",
|
||||
"website" : "http://camptocamp.com",
|
||||
"category" : "Financial Management/Configuration",
|
||||
"name": "Currency Rate Update",
|
||||
"version": "0.7",
|
||||
"author": "Camptocamp",
|
||||
"website": "http://camptocamp.com",
|
||||
"category": "Financial Management/Configuration",
|
||||
"description": """Import exchange rates from the Internet.
|
||||
|
||||
The module is able to use 4 different sources:
|
||||
@@ -48,7 +48,7 @@ The module is able to use 4 different sources:
|
||||
You should check when rates should apply to bookkeeping. If next day you should
|
||||
change the update hour in schedule settings because in OpenERP they apply from
|
||||
date of update (date - no hours).
|
||||
|
||||
|
||||
5. Banxico for USD & MXN (created by Agustín Cruz)
|
||||
Updated daily
|
||||
|
||||
@@ -64,7 +64,7 @@ The module uses internal ir_cron feature from OpenERP, so the job is launched on
|
||||
the server starts if the 'first execute date' is before the current day.
|
||||
The module supports multi-company currency in two ways:
|
||||
|
||||
* the currencies are shared, you can set currency update only on one
|
||||
* the currencies are shared, you can set currency update only on one
|
||||
company
|
||||
* the currency are separated, you can set currency on every company
|
||||
separately
|
||||
@@ -76,16 +76,16 @@ found in database.
|
||||
|
||||
Thanks to main contributors: Grzegorz Grzelak, Alexis de Lattre
|
||||
""",
|
||||
"depends" : [
|
||||
"depends": [
|
||||
"base",
|
||||
"account", #Added to ensure account security groups are present
|
||||
],
|
||||
"data" : [
|
||||
"account", # Added to ensure account security groups are present
|
||||
],
|
||||
"data": [
|
||||
"currency_rate_update.xml",
|
||||
"company_view.xml",
|
||||
"security/security.xml",
|
||||
],
|
||||
"demo" : [],
|
||||
],
|
||||
"demo": [],
|
||||
"active": False,
|
||||
'installable': False
|
||||
}
|
||||
|
||||
@@ -18,17 +18,21 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import netsvc
|
||||
from openerp.osv import fields, orm
|
||||
|
||||
|
||||
class res_company(orm.Model):
|
||||
"""override company to add currency update"""
|
||||
|
||||
def _multi_curr_enable(self, cr, uid, ids, field_name, arg, context={}):
|
||||
"check if multi company currency is enabled"
|
||||
result = {}
|
||||
if self.pool.get('ir.model.fields').search(cr, uid, [('name', '=', 'company_id'), ('model', '=', 'res.currency')])==[]:
|
||||
fields = self.pool.get('ir.model.fields').search(
|
||||
cr, uid,
|
||||
[('name', '=', 'company_id'),
|
||||
('model', '=', 'res.currency')]
|
||||
)
|
||||
if not fields:
|
||||
enable = 0
|
||||
else:
|
||||
enable = 1
|
||||
@@ -36,18 +40,15 @@ class res_company(orm.Model):
|
||||
result[id] = enable
|
||||
return result
|
||||
|
||||
|
||||
def button_refresh_currency(self, cr, uid, ids, context=None):
|
||||
"""Refrech the currency !!for all the company
|
||||
now"""
|
||||
"""Refrech the currency for all the company now"""
|
||||
currency_updater_obj = self.pool.get('currency.rate.update')
|
||||
try:
|
||||
currency_updater_obj.run_currency_update(cr, uid)
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def on_change_auto_currency_up(self, cr, uid, id, value):
|
||||
"""handle the activation of the currecny update on compagnies.
|
||||
There are two ways of implementing multi_company currency,
|
||||
@@ -56,102 +57,100 @@ class res_company(orm.Model):
|
||||
auto update on one company, this will avoid to have unusefull cron
|
||||
object running.
|
||||
If yours currency are not share you will be able to activate the
|
||||
auto update on each separated company"""
|
||||
auto update on each separated company
|
||||
|
||||
if len(id) :
|
||||
"""
|
||||
|
||||
if len(id):
|
||||
id = id[0]
|
||||
else :
|
||||
else:
|
||||
return {}
|
||||
enable = self.browse(cr, uid, id).multi_company_currency_enable
|
||||
compagnies = self.search(cr, uid, [])
|
||||
compagnies = self.search(cr, uid, [])
|
||||
activate_cron = 'f'
|
||||
if not value :
|
||||
if not value:
|
||||
# this statement is here beacaus we do no want to save in case of error
|
||||
self.write(cr, uid, id,{'auto_currency_up':value})
|
||||
for comp in compagnies :
|
||||
self.write(cr, uid, id, {'auto_currency_up': value})
|
||||
for comp in compagnies:
|
||||
if self.browse(cr, uid, comp).auto_currency_up:
|
||||
activate_cron = 't'
|
||||
break
|
||||
self.pool.get('currency.rate.update').save_cron(
|
||||
cr,
|
||||
uid,
|
||||
{'active':activate_cron}
|
||||
)
|
||||
cr,
|
||||
uid,
|
||||
{'active': activate_cron}
|
||||
)
|
||||
return {}
|
||||
else :
|
||||
for comp in compagnies :
|
||||
else:
|
||||
for comp in compagnies:
|
||||
if comp != id and not enable:
|
||||
if self.browse(cr, uid, comp).multi_company_currency_enable:
|
||||
#we ensure taht we did not have write a true value
|
||||
self.write(cr, uid, id,{'auto_currency_up':False})
|
||||
# We ensure taht we did not have write a true value
|
||||
self.write(cr, uid, id, {'auto_currency_up': False})
|
||||
return {
|
||||
'value':{
|
||||
'auto_currency_up':False
|
||||
},
|
||||
'value': {'auto_currency_up': False},
|
||||
|
||||
'warning':{
|
||||
'title':"Warning",
|
||||
'message': 'Yon can not activate auto currency '+\
|
||||
'update on more thant one company with this '+
|
||||
'multi company configuration'
|
||||
}
|
||||
}
|
||||
self.write(cr, uid, id,{'auto_currency_up':value})
|
||||
for comp in compagnies :
|
||||
'warning': {
|
||||
'title': "Warning",
|
||||
'message': 'You can not activate auto currency '
|
||||
'update on more thant one company with this '
|
||||
'multi company configuration'
|
||||
}
|
||||
}
|
||||
self.write(cr, uid, id, {'auto_currency_up': value})
|
||||
for comp in compagnies:
|
||||
if self.browse(cr, uid, comp).auto_currency_up:
|
||||
activate_cron = 't'
|
||||
self.pool.get('currency.rate.update').save_cron(
|
||||
cr,
|
||||
uid,
|
||||
{'active':activate_cron}
|
||||
)
|
||||
cr,
|
||||
uid,
|
||||
{'active': activate_cron}
|
||||
)
|
||||
break
|
||||
return {}
|
||||
|
||||
|
||||
def on_change_intervall(self, cr, uid, id, interval) :
|
||||
###Function that will update the cron
|
||||
###freqeuence
|
||||
def on_change_intervall(self, cr, uid, id, interval):
|
||||
# Function that will update the cron freqeuence
|
||||
self.pool.get('currency.rate.update').save_cron(
|
||||
cr,
|
||||
uid,
|
||||
{'interval_type':interval}
|
||||
)
|
||||
compagnies = self.search(cr, uid, [])
|
||||
for comp in compagnies :
|
||||
self.write(cr, uid, comp,{'interval_type':interval})
|
||||
cr,
|
||||
uid,
|
||||
{'interval_type': interval}
|
||||
)
|
||||
compagnies = self.search(cr, uid, [])
|
||||
for comp in compagnies:
|
||||
self.write(cr, uid, comp, {'interval_type': interval})
|
||||
return {}
|
||||
|
||||
_inherit = "res.company"
|
||||
_columns = {
|
||||
### activate the currency update
|
||||
'auto_currency_up': fields.boolean('Automatical update of the currency this company'),
|
||||
'services_to_use' : fields.one2many(
|
||||
'currency.rate.update.service',
|
||||
'company_id',
|
||||
'Currency update services'
|
||||
),
|
||||
###predifine cron frequence
|
||||
# Activate the currency update
|
||||
'auto_currency_up': fields.boolean(
|
||||
'Automatical update of the currency this company'
|
||||
),
|
||||
'services_to_use': fields.one2many(
|
||||
'currency.rate.update.service',
|
||||
'company_id',
|
||||
'Currency update services'
|
||||
),
|
||||
# Predifine cron frequence
|
||||
'interval_type': fields.selection(
|
||||
[
|
||||
('days','Day(s)'),
|
||||
('weeks', 'Week(s)'),
|
||||
('months', 'Month(s)')
|
||||
],
|
||||
'Currency update frequence',
|
||||
help="""changing this value will
|
||||
also affect other compagnies"""
|
||||
),
|
||||
###function field that allows to know the
|
||||
###mutli company currency implementation
|
||||
'multi_company_currency_enable' : fields.function(
|
||||
_multi_curr_enable,
|
||||
method=True,
|
||||
type='boolean',
|
||||
string="Multi company currency",
|
||||
help='if this case is not check you can'+\
|
||||
' not set currency is active on two company'
|
||||
),
|
||||
[
|
||||
('days', 'Day(s)'),
|
||||
('weeks', 'Week(s)'),
|
||||
('months', 'Month(s)')
|
||||
],
|
||||
'Currency update frequence',
|
||||
help="Changing this value will "
|
||||
"also affect other compagnies"
|
||||
),
|
||||
# Function field that allows to know the
|
||||
# mutli company currency implementation
|
||||
'multi_company_currency_enable': fields.function(
|
||||
_multi_curr_enable,
|
||||
method=True,
|
||||
type='boolean',
|
||||
string="Multi company currency",
|
||||
help="If this case is not check you can"
|
||||
" not set currency is active on two company"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2009 Camptocamp SA
|
||||
# @author Nicolas Bessi
|
||||
# @source JBA and AWST inpiration
|
||||
# @contributor Grzegorz Grzelak (grzegorz.grzelak@birdglobe.com), Joel Grand-Guillaume
|
||||
# Copyright (c) 2010 Alexis de Lattre (alexis@via.ecp.fr)
|
||||
@@ -31,68 +30,73 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# TODO "nice to have" : restain the list of currencies that can be added for
|
||||
# TODO "nice to have" : restrain the list of currencies that can be added for
|
||||
# a webservice to the list of currencies supported by the Webservice
|
||||
# TODO : implement max_delta_days for Yahoo webservice
|
||||
|
||||
from openerp.osv import fields, osv, orm
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from openerp.osv import fields, osv, orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Currency_rate_update_service(osv.Model):
|
||||
"""Class thats tell for wich services wich currencies
|
||||
have to be updated"""
|
||||
"""Class that tells for wich services wich currencies have to be updated
|
||||
|
||||
"""
|
||||
_name = "currency.rate.update.service"
|
||||
_description = "Currency Rate Update"
|
||||
_columns = {
|
||||
##list of webservicies the value sould be a class name
|
||||
'service' : fields.selection(
|
||||
[
|
||||
('Admin_ch_getter','Admin.ch'),
|
||||
('ECB_getter','European Central Bank'),
|
||||
#('NYFB_getter','Federal Reserve Bank of NY'),
|
||||
#('Google_getter','Google Finance'),
|
||||
('Yahoo_getter','Yahoo Finance '),
|
||||
('PL_NBP_getter','Narodowy Bank Polski'), # Added for polish rates
|
||||
('Banxico_getter', 'Banco de México'), # Added for mexican rates
|
||||
# Bank of Canada is using RSS-CB http://www.cbwiki.net/wiki/index.php/Specification_1.1 :
|
||||
# This RSS format is used by other national banks (Thailand, Malaysia, Mexico...)
|
||||
('CA_BOC_getter','Bank of Canada - noon rates'), # Added for canadian rates
|
||||
],
|
||||
"Webservice to use",
|
||||
required = True
|
||||
),
|
||||
##list of currency to update
|
||||
'currency_to_update' : fields.many2many(
|
||||
'res.currency',
|
||||
'res_curreny_auto_udate_rel',
|
||||
'service_id',
|
||||
'currency_id',
|
||||
'currency to update with this service',
|
||||
),
|
||||
#back ref
|
||||
'company_id' : fields.many2one(
|
||||
'res.company',
|
||||
'linked company',
|
||||
),
|
||||
##note fileds that will be used as a logger
|
||||
'note':fields.text('update notice'),
|
||||
'max_delta_days': fields.integer('Max delta days', required=True, help="If the time delta between the rate date given by the webservice and the current date exeeds this value, then the currency rate is not updated in OpenERP."),
|
||||
}
|
||||
_defaults = {
|
||||
'max_delta_days': lambda *a: 4,
|
||||
# List of webservicies the value sould be a class name
|
||||
'service': fields.selection(
|
||||
[
|
||||
('Admin_ch_getter', 'Admin.ch'),
|
||||
('ECB_getter', 'European Central Bank'),
|
||||
('Yahoo_getter', 'Yahoo Finance '),
|
||||
('PL_NBP_getter', 'Narodowy Bank Polski'), # Added for polish rates
|
||||
('Banxico_getter', 'Banco de México'), # Added for mexican rates
|
||||
# Bank of Canada is using RSS-CB http://www.cbwiki.net/wiki/index.php/Specification_1.1 :
|
||||
# This RSS format is used by other national banks (Thailand, Malaysia, Mexico...)
|
||||
('CA_BOC_getter', 'Bank of Canada - noon rates'), # Added for canadian rates
|
||||
],
|
||||
"Webservice to use",
|
||||
required=True
|
||||
),
|
||||
# List of currency to update
|
||||
'currency_to_update': fields.many2many(
|
||||
'res.currency',
|
||||
'res_curreny_auto_udate_rel',
|
||||
'service_id',
|
||||
'currency_id',
|
||||
'currency to update with this service',
|
||||
),
|
||||
# Back ref
|
||||
'company_id': fields.many2one(
|
||||
'res.company',
|
||||
'linked company',
|
||||
),
|
||||
# Note fileds that will be used as a logger
|
||||
'note': fields.text('update notice'),
|
||||
'max_delta_days': fields.integer(
|
||||
'Max delta days',
|
||||
required=True,
|
||||
help="If the time delta between the "
|
||||
"rate date given by the webservice and "
|
||||
"the current date exeeds this value, "
|
||||
"then the currency rate is not updated in OpenERP."
|
||||
),
|
||||
}
|
||||
_defaults = {'max_delta_days': lambda *a: 4}
|
||||
_sql_constraints = [
|
||||
(
|
||||
'curr_service_unique',
|
||||
'unique (service, company_id)',
|
||||
_('You can use a service one time per company !')
|
||||
)
|
||||
]
|
||||
(
|
||||
'curr_service_unique',
|
||||
'unique (service, company_id)',
|
||||
_('You can use a service one time per company !')
|
||||
)
|
||||
]
|
||||
|
||||
def _check_max_delta_days(self, cr, uid, ids):
|
||||
for company in self.read(cr, uid, ids, ['max_delta_days']):
|
||||
@@ -103,7 +107,9 @@ class Currency_rate_update_service(osv.Model):
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_max_delta_days, "'Max delta days' must be >= 0", ['max_delta_days']),
|
||||
(_check_max_delta_days,
|
||||
"'Max delta days' must be >= 0",
|
||||
['max_delta_days']),
|
||||
]
|
||||
|
||||
|
||||
@@ -112,58 +118,57 @@ class Currency_rate_update(osv.Model):
|
||||
update currencies based on a web url"""
|
||||
_name = "currency.rate.update"
|
||||
_description = "Currency Rate Update"
|
||||
##dict that represent a cron object
|
||||
# Dict that represent a cron object
|
||||
cron = {
|
||||
'active' : False,
|
||||
'priority' : 1,
|
||||
'interval_number' : 1,
|
||||
'interval_type' : 'weeks',
|
||||
'nextcall' : time.strftime("%Y-%m-%d %H:%M:%S", (datetime.today() + timedelta(days=1)).timetuple() ), #tomorrow same time
|
||||
'numbercall' : -1,
|
||||
'doall' : True,
|
||||
'model' : 'currency.rate.update',
|
||||
'function' : 'run_currency_update',
|
||||
'args' : '()',
|
||||
'active': False,
|
||||
'priority': 1,
|
||||
'interval_number': 1,
|
||||
'interval_type': 'weeks',
|
||||
'nextcall': time.strftime("%Y-%m-%d %H:%M:%S",
|
||||
datetime.today() + timedelta(days=1)).timetuple(),
|
||||
'numbercall': -1,
|
||||
'doall': True,
|
||||
'model': 'currency.rate.update',
|
||||
'function': 'run_currency_update',
|
||||
'args': '()',
|
||||
}
|
||||
|
||||
LOG_NAME = 'cron-rates'
|
||||
MOD_NAME = 'currency_rate_update: '
|
||||
|
||||
def get_cron_id(self, cr, uid, context):
|
||||
"""return the updater cron's id. Create one if the cron does not exists """
|
||||
"""Returns the updater cron's id.
|
||||
Create one if the cron does not exists
|
||||
"""
|
||||
|
||||
cron_id = 0
|
||||
cron_obj = self.pool.get('ir.cron')
|
||||
try:
|
||||
#find the cron that send messages
|
||||
# Finds the cron that send messages
|
||||
cron_id = cron_obj.search(
|
||||
cr,
|
||||
uid,
|
||||
[
|
||||
('function', 'ilike', self.cron['function']),
|
||||
('model', 'ilike', self.cron['model'])
|
||||
],
|
||||
context={
|
||||
'active_test': False
|
||||
}
|
||||
)
|
||||
cr,
|
||||
uid,
|
||||
[
|
||||
('function', 'ilike', self.cron['function']),
|
||||
('model', 'ilike', self.cron['model'])
|
||||
],
|
||||
context={
|
||||
'active_test': False
|
||||
}
|
||||
)
|
||||
cron_id = int(cron_id[0])
|
||||
except Exception,e :
|
||||
except Exception:
|
||||
_logger.info('warning cron not found one will be created')
|
||||
pass # ignore if the cron is missing cause we are going to create it in db
|
||||
|
||||
#the cron does not exists
|
||||
if not cron_id :
|
||||
#translate
|
||||
pass # Ignore if the cron is missing cause we are going to create it in db
|
||||
if not cron_id:
|
||||
self.cron['name'] = _('Currency Rate Update')
|
||||
cron_id = cron_obj.create(cr, uid, self.cron, context)
|
||||
|
||||
return cron_id
|
||||
|
||||
def save_cron(self, cr, uid, datas, context={}):
|
||||
"""save the cron config data should be a dict"""
|
||||
#modify the cron
|
||||
cron_id = self.get_cron_id(cr, uid, context)
|
||||
result = self.pool.get('ir.cron').write(cr, uid, [cron_id], datas)
|
||||
return self.pool.get('ir.cron').write(cr, uid, [cron_id], datas)
|
||||
|
||||
def run_currency_update(self, cr, uid):
|
||||
"update currency at the given frequence"
|
||||
@@ -172,172 +177,192 @@ class Currency_rate_update(osv.Model):
|
||||
rate_obj = self.pool.get('res.currency.rate')
|
||||
companies = self.pool.get('res.company').search(cr, uid, [])
|
||||
for comp in self.pool.get('res.company').browse(cr, uid, companies):
|
||||
##the multi company currency can beset or no so we handle
|
||||
##the two case
|
||||
if not comp.auto_currency_up :
|
||||
# The multi company currency can beset or no so we handle
|
||||
# The two case
|
||||
if not comp.auto_currency_up:
|
||||
continue
|
||||
#we initialise the multi compnay search filter or not serach filter
|
||||
search_filter = []
|
||||
if comp.multi_company_currency_enable :
|
||||
search_filter = [('company_id','=',comp.id)]
|
||||
#we fetch the main currency looking for currency with base = true. The main rate should be set at 1.00
|
||||
main_curr_ids = curr_obj.search(cr, uid, [('base','=',True),('company_id','=',comp.id)])
|
||||
# We fetch the main currency looking for currency with base = true.
|
||||
# The main rate should be set at 1.00
|
||||
main_curr_ids = curr_obj.search(
|
||||
cr, uid,
|
||||
[('base', '=', True), ('company_id', '=', comp.id)]
|
||||
)
|
||||
if not main_curr_ids:
|
||||
# If we can not find a base currency for this company we look for one with no company set
|
||||
main_curr_ids = curr_obj.search(cr, uid, [('base','=',True),('company_id','=', False)])
|
||||
# If we can not find a base currency for this company
|
||||
# we look for one with no company set
|
||||
main_curr_ids = curr_obj.search(
|
||||
cr, uid,
|
||||
[('base', '=', True), ('company_id', '=', False)]
|
||||
)
|
||||
if main_curr_ids:
|
||||
main_curr_rec = curr_obj.browse(cr, uid, main_curr_ids[0])
|
||||
else:
|
||||
raise orm.except_orm(_('Error!'),('There is no base currency set!'))
|
||||
raise orm.except_orm(
|
||||
_('Error!'),
|
||||
('There is no base currency set!')
|
||||
)
|
||||
if main_curr_rec.rate != 1:
|
||||
raise orm.except_orm(_('Error!'),('Base currency rate should be 1.00!'))
|
||||
raise orm.except_orm(
|
||||
_('Error!'),
|
||||
('Base currency rate should be 1.00!')
|
||||
)
|
||||
main_curr = main_curr_rec.name
|
||||
for service in comp.services_to_use :
|
||||
print "comp.services_to_use =", comp.services_to_use
|
||||
for service in comp.services_to_use:
|
||||
note = service.note or ''
|
||||
try :
|
||||
## we initalize the class that will handle the request
|
||||
## and return a dict of rate
|
||||
try:
|
||||
# We initalize the class that will handle the request
|
||||
# and return a dict of rate
|
||||
getter = factory.register(service.service)
|
||||
print "getter =", getter
|
||||
curr_to_fetch = map(lambda x : x.name, service.currency_to_update)
|
||||
res, log_info = getter.get_updated_currency(curr_to_fetch, main_curr, service.max_delta_days)
|
||||
curr_to_fetch = map(lambda x: x.name, service.currency_to_update)
|
||||
res, log_info = getter.get_updated_currency(
|
||||
curr_to_fetch,
|
||||
main_curr,
|
||||
service.max_delta_days
|
||||
)
|
||||
rate_name = time.strftime('%Y-%m-%d')
|
||||
for curr in service.currency_to_update :
|
||||
if curr.name == main_curr :
|
||||
for curr in service.currency_to_update:
|
||||
if curr.name == main_curr:
|
||||
continue
|
||||
do_create = True
|
||||
for rate in curr.rate_ids :
|
||||
if rate.name == rate_name :
|
||||
rate.write({'rate':res[curr.name]})
|
||||
for rate in curr.rate_ids:
|
||||
if rate.name == rate_name:
|
||||
rate.write({'rate': res[curr.name]})
|
||||
do_create = False
|
||||
break
|
||||
if do_create :
|
||||
if do_create:
|
||||
vals = {
|
||||
'currency_id': curr.id,
|
||||
'rate':res[curr.name],
|
||||
'name': rate_name
|
||||
}
|
||||
'currency_id': curr.id,
|
||||
'rate': res[curr.name],
|
||||
'name': rate_name
|
||||
}
|
||||
rate_obj.create(
|
||||
cr,
|
||||
uid,
|
||||
vals,
|
||||
)
|
||||
cr,
|
||||
uid,
|
||||
vals,
|
||||
)
|
||||
|
||||
# show the most recent note at the top
|
||||
note = "\n%s currency updated. "\
|
||||
%(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'))\
|
||||
+ note
|
||||
note = (log_info or '') + note
|
||||
service.write({'note':note})
|
||||
except Exception, e:
|
||||
error_msg = "\n%s ERROR : %s"\
|
||||
%(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'), str(e))\
|
||||
+ note
|
||||
_logger.info(str(e))
|
||||
service.write({'note':error_msg})
|
||||
# Show the most recent note at the top
|
||||
msg = "%s \n%s currency updated. %s" % \
|
||||
(log_info or '',
|
||||
datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'),
|
||||
note)
|
||||
service.write({'note': msg})
|
||||
except Exception as exc:
|
||||
error_msg = "\n%s ERROR : %s %s" %\
|
||||
(datetime.strftime(datetime.today(), '%Y-%m-%d %H:%M:%S'),
|
||||
repr(exc),
|
||||
note)
|
||||
_logger.info(repr(exc))
|
||||
service.write({'note': error_msg})
|
||||
|
||||
|
||||
|
||||
### Error Definition as specified in python 2.6 PEP
|
||||
class AbstractClassError(Exception):
|
||||
def __str__(self):
|
||||
return 'Abstract Class'
|
||||
|
||||
def __repr__(self):
|
||||
return 'Abstract Class'
|
||||
return 'Abstract Class'
|
||||
|
||||
|
||||
class AbstractMethodError(Exception):
|
||||
def __str__(self):
|
||||
return 'Abstract Method'
|
||||
|
||||
def __repr__(self):
|
||||
return 'Abstract Method'
|
||||
|
||||
|
||||
class UnknowClassError(Exception):
|
||||
def __str__(self):
|
||||
return 'Unknown Class'
|
||||
|
||||
def __repr__(self):
|
||||
return 'Unknown Class'
|
||||
|
||||
|
||||
class UnsuportedCurrencyError(Exception):
|
||||
def __init__(self, value):
|
||||
self.curr = value
|
||||
def __str__(self):
|
||||
return 'Unsupported currency '+self.curr
|
||||
def __repr__(self):
|
||||
return 'Unsupported currency '+self.curr
|
||||
self.curr = value
|
||||
|
||||
def __str__(self):
|
||||
return 'Unsupported currency %s' % self.curr
|
||||
|
||||
def __repr__(self):
|
||||
return 'Unsupported currency %s' % self.curr
|
||||
|
||||
|
||||
### end of error definition
|
||||
class Currency_getter_factory():
|
||||
"""Factory pattern class that will return
|
||||
a currency getter class base on the name passed
|
||||
to the register method"""
|
||||
to the register method
|
||||
|
||||
"""
|
||||
def register(self, class_name):
|
||||
allowed = [
|
||||
'Admin_ch_getter',
|
||||
'PL_NBP_getter',
|
||||
'ECB_getter',
|
||||
'NYFB_getter',
|
||||
'Google_getter',
|
||||
'Yahoo_getter',
|
||||
'Banxico_getter',
|
||||
'CA_BOC_getter',
|
||||
]
|
||||
'Admin_ch_getter',
|
||||
'PL_NBP_getter',
|
||||
'ECB_getter',
|
||||
'NYFB_getter',
|
||||
'Google_getter',
|
||||
'Yahoo_getter',
|
||||
'Banxico_getter',
|
||||
'CA_BOC_getter',
|
||||
]
|
||||
if class_name in allowed:
|
||||
class_def = eval(class_name)
|
||||
return class_def()
|
||||
else :
|
||||
else:
|
||||
raise UnknowClassError
|
||||
|
||||
|
||||
class Curreny_getter_interface(object) :
|
||||
class Curreny_getter_interface(object):
|
||||
"Abstract class of currency getter"
|
||||
|
||||
#remove in order to have a dryer code
|
||||
# def __init__(self):
|
||||
# raise AbstractClassError
|
||||
|
||||
log_info = " "
|
||||
|
||||
supported_currency_array = \
|
||||
['AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS',
|
||||
'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD',
|
||||
'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM',
|
||||
'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF',
|
||||
'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP',
|
||||
'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC',
|
||||
'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR',
|
||||
'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN',
|
||||
'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF',
|
||||
'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR',
|
||||
'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG',
|
||||
'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR',
|
||||
'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP',
|
||||
'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL',
|
||||
'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA',
|
||||
'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR',
|
||||
'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT',
|
||||
'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR',
|
||||
'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR',
|
||||
'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT',
|
||||
'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG',
|
||||
'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR',
|
||||
'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR',
|
||||
'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD',
|
||||
'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR',
|
||||
'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX',
|
||||
'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB',
|
||||
'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD']
|
||||
supported_currency_array = [
|
||||
'AFN', 'ALL', 'DZD', 'USD', 'USD', 'USD', 'EUR', 'AOA', 'XCD', 'XCD', 'ARS',
|
||||
'AMD', 'AWG', 'AUD', 'EUR', 'AZN', 'EUR', 'BSD', 'BHD', 'EUR', 'BDT', 'BBD',
|
||||
'XCD', 'BYR', 'EUR', 'BZD', 'XOF', 'BMD', 'BTN', 'INR', 'BOB', 'ANG', 'BAM',
|
||||
'BWP', 'NOK', 'BRL', 'GBP', 'USD', 'USD', 'BND', 'BGN', 'XOF', 'MMK', 'BIF',
|
||||
'XOF', 'USD', 'KHR', 'XAF', 'CAD', 'EUR', 'CVE', 'KYD', 'XAF', 'XAF', 'CLP',
|
||||
'CNY', 'AUD', 'AUD', 'COP', 'XAF', 'KMF', 'XPF', 'XAF', 'CDF', 'NZD', 'CRC',
|
||||
'HRK', 'CUP', 'ANG', 'EUR', 'CYP', 'CZK', 'DKK', 'DJF', 'XCD', 'DOP', 'EUR',
|
||||
'XCD', 'IDR', 'USD', 'EGP', 'EUR', 'SVC', 'USD', 'GBP', 'XAF', 'ETB', 'ERN',
|
||||
'EEK', 'ETB', 'EUR', 'FKP', 'DKK', 'FJD', 'EUR', 'EUR', 'EUR', 'XPF', 'XPF',
|
||||
'EUR', 'XPF', 'XAF', 'GMD', 'GEL', 'EUR', 'GHS', 'GIP', 'XAU', 'GBP', 'EUR',
|
||||
'DKK', 'XCD', 'XCD', 'EUR', 'USD', 'GTQ', 'GGP', 'GNF', 'XOF', 'GYD', 'HTG',
|
||||
'USD', 'AUD', 'BAM', 'EUR', 'EUR', 'HNL', 'HKD', 'HUF', 'ISK', 'INR', 'IDR',
|
||||
'XDR', 'IRR', 'IQD', 'EUR', 'IMP', 'ILS', 'EUR', 'JMD', 'NOK', 'JPY', 'JEP',
|
||||
'JOD', 'KZT', 'AUD', 'KES', 'AUD', 'KPW', 'KRW', 'KWD', 'KGS', 'LAK', 'LVL',
|
||||
'LBP', 'LSL', 'ZAR', 'LRD', 'LYD', 'CHF', 'LTL', 'EUR', 'MOP', 'MKD', 'MGA',
|
||||
'EUR', 'MWK', 'MYR', 'MVR', 'XOF', 'EUR', 'MTL', 'FKP', 'USD', 'USD', 'EUR',
|
||||
'MRO', 'MUR', 'EUR', 'AUD', 'MXN', 'USD', 'USD', 'EUR', 'MDL', 'EUR', 'MNT',
|
||||
'EUR', 'XCD', 'MAD', 'MZN', 'MMK', 'NAD', 'ZAR', 'AUD', 'NPR', 'ANG', 'EUR',
|
||||
'XCD', 'XPF', 'NZD', 'NIO', 'XOF', 'NGN', 'NZD', 'AUD', 'USD', 'NOK', 'OMR',
|
||||
'PKR', 'USD', 'XPD', 'PAB', 'USD', 'PGK', 'PYG', 'PEN', 'PHP', 'NZD', 'XPT',
|
||||
'PLN', 'EUR', 'STD', 'USD', 'QAR', 'EUR', 'RON', 'RUB', 'RWF', 'STD', 'ANG',
|
||||
'MAD', 'XCD', 'SHP', 'XCD', 'XCD', 'EUR', 'XCD', 'EUR', 'USD', 'WST', 'EUR',
|
||||
'SAR', 'SPL', 'XOF', 'RSD', 'SCR', 'SLL', 'XAG', 'SGD', 'ANG', 'ANG', 'EUR',
|
||||
'EUR', 'SBD', 'SOS', 'ZAR', 'GBP', 'GBP', 'EUR', 'XDR', 'LKR', 'SDG', 'SRD',
|
||||
'NOK', 'SZL', 'SEK', 'CHF', 'SYP', 'TWD', 'RUB', 'TJS', 'TZS', 'THB', 'IDR',
|
||||
'TTD', 'XOF', 'NZD', 'TOP', 'TTD', 'TND', 'TRY', 'TMM', 'USD', 'TVD', 'UGX',
|
||||
'UAH', 'AED', 'GBP', 'USD', 'USD', 'UYU', 'USD', 'UZS', 'VUV', 'EUR', 'VEB',
|
||||
'VEF', 'VND', 'USD', 'USD', 'USD', 'XPF', 'MAD', 'YER', 'ZMK', 'ZWD'
|
||||
]
|
||||
|
||||
##updated currency this arry will contain the final result
|
||||
# Updated currency this arry will contain the final result
|
||||
updated_currency = {}
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days) :
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""Interface method that will retrieve the currency
|
||||
This function has to be reinplemented in child"""
|
||||
This function has to be reinplemented in child
|
||||
"""
|
||||
raise AbstractMethodError
|
||||
|
||||
def validate_cur(self, currency) :
|
||||
def validate_cur(self, currency):
|
||||
"""Validate if the currency to update is supported"""
|
||||
if currency not in self.supported_currency_array :
|
||||
if currency not in self.supported_currency_array:
|
||||
raise UnsuportedCurrencyError(currency)
|
||||
|
||||
def get_url(self, url):
|
||||
@@ -349,63 +374,81 @@ class Curreny_getter_interface(object) :
|
||||
objfile.close()
|
||||
return rawfile
|
||||
except ImportError:
|
||||
raise osv.except_osv('Error !', self.MOD_NAME+'Unable to import urllib !')
|
||||
raise osv.except_osv(
|
||||
'Error !',
|
||||
self.MOD_NAME + 'Unable to import urllib !'
|
||||
)
|
||||
except IOError:
|
||||
raise osv.except_osv('Error !', self.MOD_NAME+'Web Service does not exist !')
|
||||
raise osv.except_osv(
|
||||
'Error !',
|
||||
self.MOD_NAME + 'Web Service does not exist !'
|
||||
)
|
||||
|
||||
def check_rate_date(self, rate_date, max_delta_days):
|
||||
"""Check date constrains. WARN : rate_date must be of datetime type"""
|
||||
"""Check date constrains. rate_date must be of datetime type"""
|
||||
days_delta = (datetime.today() - rate_date).days
|
||||
if days_delta > max_delta_days:
|
||||
raise Exception('The rate timestamp (%s) is %d days away from today, which is over the limit (%d days). Rate not updated in OpenERP.'%(rate_date, days_delta, max_delta_days))
|
||||
# We always have a warning when rate_date <> today
|
||||
raise Exception(
|
||||
'The rate timestamp (%s) is %d days away from today, '
|
||||
'which is over the limit (%d days). '
|
||||
'Rate not updated in OpenERP.' % (rate_date,
|
||||
days_delta,
|
||||
max_delta_days)
|
||||
)
|
||||
|
||||
# We always have a warning when rate_date != today
|
||||
rate_date_str = datetime.strftime(rate_date, '%Y-%m-%d')
|
||||
if rate_date_str != datetime.strftime(datetime.today(), '%Y-%m-%d'):
|
||||
self.log_info = "WARNING : the rate timestamp (%s) is not today's date" % rate_date_str
|
||||
_logger.warning("the rate timestamp (%s) is not today's date", rate_date_str)
|
||||
msg = "The rate timestamp (%s) is not today's date"
|
||||
self.log_info = ("WARNING : %s %s") % (msg, rate_date_str)
|
||||
_logger.warning(msg, rate_date_str)
|
||||
|
||||
|
||||
#Yahoo ###################################################################################
|
||||
class Yahoo_getter(Curreny_getter_interface) :
|
||||
# Yahoo ########################################################################
|
||||
class Yahoo_getter(Curreny_getter_interface):
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for Yahoo finance service"""
|
||||
for Yahoo finance service
|
||||
"""
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
"""implementation of abstract method of curreny_getter_interface"""
|
||||
self.validate_cur(main_currency)
|
||||
url='http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'
|
||||
if main_currency in currency_array :
|
||||
url = 'http://download.finance.yahoo.com/d/quotes.txt?s="%s"=X&f=sl1c1abg'
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
for curr in currency_array :
|
||||
for curr in currency_array:
|
||||
self.validate_cur(curr)
|
||||
res = self.get_url(url%(main_currency+curr))
|
||||
res = self.get_url(url % (main_currency + curr))
|
||||
val = res.split(',')[1]
|
||||
if val :
|
||||
if val:
|
||||
self.updated_currency[curr] = val
|
||||
else :
|
||||
raise Exception('Could not update the %s'%(curr))
|
||||
else:
|
||||
raise Exception('Could not update the %s' % (curr))
|
||||
|
||||
return self.updated_currency, self.log_info # empty string added by polish changes
|
||||
##Admin CH ############################################################################
|
||||
class Admin_ch_getter(Curreny_getter_interface) :
|
||||
return self.updated_currency, self.log_info
|
||||
|
||||
|
||||
# Admin CH ####################################################################
|
||||
class Admin_ch_getter(Curreny_getter_interface):
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for Admin.ch service"""
|
||||
for Admin.ch service
|
||||
|
||||
def rate_retrieve(self, dom, ns, curr) :
|
||||
""" Parse a dom node to retrieve-
|
||||
currencies data"""
|
||||
"""
|
||||
|
||||
def rate_retrieve(self, dom, ns, curr):
|
||||
"""Parse a dom node to retrieve currencies data"""
|
||||
res = {}
|
||||
xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()"%(curr.lower())
|
||||
xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()"%(curr.lower())
|
||||
xpath_rate_currency = "/def:wechselkurse/def:devise[@code='%s']/def:kurs/text()" % (curr.lower())
|
||||
xpath_rate_ref = "/def:wechselkurse/def:devise[@code='%s']/def:waehrung/text()" % (curr.lower())
|
||||
res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0])
|
||||
res['rate_ref'] = float((dom.xpath(xpath_rate_ref, namespaces=ns)[0]).split(' ')[0])
|
||||
return res
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
url='http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'
|
||||
#we do not want to update the main currency
|
||||
if main_currency in currency_array :
|
||||
"""Implementation of abstract method of Curreny_getter_interface"""
|
||||
url = 'http://www.afd.admin.ch/publicdb/newdb/mwst_kurse/wechselkurse.php'
|
||||
# We do not want to update the main currency
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
# Move to new XML lib cf Launchpad bug #645263
|
||||
from lxml import etree
|
||||
@@ -417,7 +460,7 @@ class Admin_ch_getter(Curreny_getter_interface) :
|
||||
rate_date = dom.xpath('/def:wechselkurse/def:datum/text()', namespaces=adminch_ns)[0]
|
||||
rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
|
||||
self.check_rate_date(rate_date_datetime, max_delta_days)
|
||||
#we dynamically update supported currencies
|
||||
# we dynamically update supported currencies
|
||||
self.supported_currency_array = dom.xpath("/def:wechselkurse/def:devise/@code", namespaces=adminch_ns)
|
||||
self.supported_currency_array = [x.upper() for x in self.supported_currency_array]
|
||||
self.supported_currency_array.append('CHF')
|
||||
@@ -428,45 +471,50 @@ class Admin_ch_getter(Curreny_getter_interface) :
|
||||
main_curr_data = self.rate_retrieve(dom, adminch_ns, main_currency)
|
||||
# 1 MAIN_CURRENCY = main_rate CHF
|
||||
main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref']
|
||||
for curr in currency_array :
|
||||
for curr in currency_array:
|
||||
self.validate_cur(curr)
|
||||
if curr == 'CHF':
|
||||
rate = main_rate
|
||||
else:
|
||||
curr_data = self.rate_retrieve(dom, adminch_ns, curr)
|
||||
# 1 MAIN_CURRENCY = rate CURR
|
||||
if main_currency == 'CHF' :
|
||||
if main_currency == 'CHF':
|
||||
rate = curr_data['rate_ref'] / curr_data['rate_currency']
|
||||
else :
|
||||
else:
|
||||
rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
|
||||
self.updated_currency[curr] = rate
|
||||
_logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
|
||||
_logger.debug(
|
||||
"Rate retrieved : 1 %s = %s %s" % (main_currency, rate, curr)
|
||||
)
|
||||
return self.updated_currency, self.log_info
|
||||
|
||||
## ECB getter ############################################################################
|
||||
|
||||
class ECB_getter(Curreny_getter_interface) :
|
||||
# ECB getter #################################################################
|
||||
class ECB_getter(Curreny_getter_interface):
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for ECB service"""
|
||||
for ECB service
|
||||
"""
|
||||
|
||||
def rate_retrieve(self, dom, ns, curr) :
|
||||
""" Parse a dom node to retrieve-
|
||||
currencies data"""
|
||||
def rate_retrieve(self, dom, ns, curr):
|
||||
"""Parse a dom node to retrieve-
|
||||
currencies data
|
||||
|
||||
"""
|
||||
res = {}
|
||||
xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate"%(curr.upper())
|
||||
xpath_curr_rate = "/gesmes:Envelope/def:Cube/def:Cube/def:Cube[@currency='%s']/@rate" % (curr.upper())
|
||||
res['rate_currency'] = float(dom.xpath(xpath_curr_rate, namespaces=ns)[0])
|
||||
return res
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
url='http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
|
||||
url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
|
||||
# Important : as explained on the ECB web site, the currencies are
|
||||
# at the beginning of the afternoon ; so, until 3 p.m. Paris time
|
||||
# the currency rates are the ones of trading day N-1
|
||||
# see http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html
|
||||
|
||||
#we do not want to update the main currency
|
||||
if main_currency in currency_array :
|
||||
# We do not want to update the main currency
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
# Move to new XML lib cf Launchpad bug #645263
|
||||
from lxml import etree
|
||||
@@ -474,14 +522,19 @@ class ECB_getter(Curreny_getter_interface) :
|
||||
rawfile = self.get_url(url)
|
||||
dom = etree.fromstring(rawfile)
|
||||
_logger.debug("ECB sent a valid XML file")
|
||||
ecb_ns = {'gesmes': 'http://www.gesmes.org/xml/2002-08-01', 'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'}
|
||||
rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time', namespaces=ecb_ns)[0]
|
||||
ecb_ns = {
|
||||
'gesmes': 'http://www.gesmes.org/xml/2002-08-01',
|
||||
'def': 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref'
|
||||
}
|
||||
rate_date = dom.xpath('/gesmes:Envelope/def:Cube/def:Cube/@time',
|
||||
namespaces=ecb_ns)[0]
|
||||
rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
|
||||
self.check_rate_date(rate_date_datetime, max_delta_days)
|
||||
#we dynamically update supported currencies
|
||||
self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency", namespaces=ecb_ns)
|
||||
# We dynamically update supported currencies
|
||||
self.supported_currency_array = dom.xpath("/gesmes:Envelope/def:Cube/def:Cube/def:Cube/@currency",
|
||||
namespaces=ecb_ns)
|
||||
self.supported_currency_array.append('EUR')
|
||||
_logger.debug("Supported currencies = " + str(self.supported_currency_array))
|
||||
_logger.debug("Supported currencies = %s " % self.supported_currency_array)
|
||||
self.validate_cur(main_currency)
|
||||
if main_currency != 'EUR':
|
||||
main_curr_data = self.rate_retrieve(dom, ecb_ns, main_currency)
|
||||
@@ -496,56 +549,54 @@ class ECB_getter(Curreny_getter_interface) :
|
||||
else:
|
||||
rate = curr_data['rate_currency'] / main_curr_data['rate_currency']
|
||||
self.updated_currency[curr] = rate
|
||||
_logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
|
||||
_logger.debug("Rate retrieved : 1 %s = %s %s" % (main_currency, rate, curr))
|
||||
return self.updated_currency, self.log_info
|
||||
|
||||
##PL NBP ############################################################################
|
||||
class PL_NBP_getter(Curreny_getter_interface) : # class added according to polish needs = based on class Admin_ch_getter
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for PL NBP service"""
|
||||
|
||||
def rate_retrieve(self, dom, ns, curr) :
|
||||
# PL NBP ######################################################################
|
||||
class PL_NBP_getter(Curreny_getter_interface):
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for PL NBP service
|
||||
|
||||
"""
|
||||
|
||||
def rate_retrieve(self, dom, ns, curr):
|
||||
""" Parse a dom node to retrieve
|
||||
currencies data"""
|
||||
res = {}
|
||||
xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()"%(curr.upper())
|
||||
xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()"%(curr.upper())
|
||||
res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',','.'))
|
||||
xpath_rate_currency = "/tabela_kursow/pozycja[kod_waluty='%s']/kurs_sredni/text()" % (curr.upper())
|
||||
xpath_rate_ref = "/tabela_kursow/pozycja[kod_waluty='%s']/przelicznik/text()" % (curr.upper())
|
||||
res['rate_currency'] = float(dom.xpath(xpath_rate_currency, namespaces=ns)[0].replace(',', '.'))
|
||||
res['rate_ref'] = float(dom.xpath(xpath_rate_ref, namespaces=ns)[0])
|
||||
return res
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
url='http://www.nbp.pl/kursy/xml/LastA.xml' # LastA.xml is always the most recent one
|
||||
#we do not want to update the main currency
|
||||
if main_currency in currency_array :
|
||||
# LastA.xml is always the most recent one
|
||||
url = 'http://www.nbp.pl/kursy/xml/LastA.xml'
|
||||
# We do not want to update the main currency
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
# Move to new XML lib cf Launchpad bug #645263
|
||||
from lxml import etree
|
||||
_logger.debug("NBP.pl currency rate service : connecting...")
|
||||
rawfile = self.get_url(url)
|
||||
dom = etree.fromstring(rawfile) # If rawfile is not XML, it crashes here
|
||||
ns = {} # Cool, there are no namespaces !
|
||||
dom = etree.fromstring(rawfile)
|
||||
ns = {} # Cool, there are no namespaces !
|
||||
_logger.debug("NBP.pl sent a valid XML file")
|
||||
#node = xpath.Evaluate("/tabela_kursow", dom) # BEGIN Polish - rates table name
|
||||
#if isinstance(node, list) :
|
||||
# node = node[0]
|
||||
#self.log_info = node.getElementsByTagName('numer_tabeli')[0].childNodes[0].data
|
||||
#self.log_info = self.log_info + " " + node.getElementsByTagName('data_publikacji')[0].childNodes[0].data # END Polish - rates table name
|
||||
|
||||
rate_date = dom.xpath('/tabela_kursow/data_publikacji/text()', namespaces=ns)[0]
|
||||
rate_date_datetime = datetime.strptime(rate_date, '%Y-%m-%d')
|
||||
self.check_rate_date(rate_date_datetime, max_delta_days)
|
||||
#we dynamically update supported currencies
|
||||
# We dynamically update supported currencies
|
||||
self.supported_currency_array = dom.xpath('/tabela_kursow/pozycja/kod_waluty/text()', namespaces=ns)
|
||||
self.supported_currency_array.append('PLN')
|
||||
_logger.debug("Supported currencies = " + str(self.supported_currency_array))
|
||||
_logger.debug("Supported currencies = %s" % self.supported_currency_array)
|
||||
self.validate_cur(main_currency)
|
||||
if main_currency != 'PLN':
|
||||
main_curr_data = self.rate_retrieve(dom, ns, main_currency)
|
||||
# 1 MAIN_CURRENCY = main_rate PLN
|
||||
main_rate = main_curr_data['rate_currency'] / main_curr_data['rate_ref']
|
||||
for curr in currency_array :
|
||||
for curr in currency_array:
|
||||
self.validate_cur(curr)
|
||||
if curr == 'PLN':
|
||||
rate = main_rate
|
||||
@@ -557,14 +608,16 @@ class PL_NBP_getter(Curreny_getter_interface) : # class added according to pol
|
||||
else:
|
||||
rate = main_rate * curr_data['rate_ref'] / curr_data['rate_currency']
|
||||
self.updated_currency[curr] = rate
|
||||
_logger.debug("Rate retrieved : 1 " + main_currency + ' = ' + str(rate) + ' ' + curr)
|
||||
_logger.debug("Rate retrieved : %s = %s %s" % (main_currency, rate, curr))
|
||||
return self.updated_currency, self.log_info
|
||||
|
||||
|
||||
##Banco de México ############################################################################
|
||||
class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates
|
||||
# Banco de México #############################################################
|
||||
class Banxico_getter(Curreny_getter_interface):
|
||||
"""Implementation of Currency_getter_factory interface
|
||||
for Banco de México service"""
|
||||
for Banco de México service
|
||||
|
||||
"""
|
||||
|
||||
def rate_retrieve(self):
|
||||
""" Get currency exchange from Banxico.xml and proccess it
|
||||
@@ -587,17 +640,16 @@ class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates
|
||||
|
||||
return float(rate)
|
||||
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days=1):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
logger = logging.getLogger(__name__)
|
||||
# we do not want to update the main currency
|
||||
if main_currency in currency_array :
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
|
||||
# Suported currencies
|
||||
suported = ['MXN', 'USD']
|
||||
for curr in currency_array :
|
||||
for curr in currency_array:
|
||||
if curr in suported:
|
||||
# Get currency data
|
||||
main_rate = self.rate_retrieve()
|
||||
@@ -606,17 +658,19 @@ class Banxico_getter(Curreny_getter_interface) : # class added for Mexico rates
|
||||
else:
|
||||
rate = main_rate
|
||||
else:
|
||||
""" No other currency supported
|
||||
"""
|
||||
# No other currency supported
|
||||
continue
|
||||
|
||||
self.updated_currency[curr] = rate
|
||||
logger.debug("Rate retrieved : " + main_currency + ' = ' + str(rate) + ' ' + curr)
|
||||
logger.debug("Rate retrieved : %s = %s %s" % (main_currency, rate, curr))
|
||||
|
||||
|
||||
##CA BOC ##### Bank of Canada ############################################################
|
||||
class CA_BOC_getter(Curreny_getter_interface) :
|
||||
"""Implementation of Curreny_getter_factory interface for Bank of Canada RSS service"""
|
||||
# CA BOC ##### Bank of Canada #############################################
|
||||
class CA_BOC_getter(Curreny_getter_interface):
|
||||
"""Implementation of Curreny_getter_factory interface
|
||||
for Bank of Canada RSS service
|
||||
|
||||
"""
|
||||
|
||||
def get_updated_currency(self, currency_array, main_currency, max_delta_days):
|
||||
"""implementation of abstract method of Curreny_getter_interface"""
|
||||
@@ -627,7 +681,7 @@ class CA_BOC_getter(Curreny_getter_interface) :
|
||||
# currencies reported):
|
||||
# http://www.bankofcanada.ca/stats/assets/rates_rss/closing/en_%s.xml
|
||||
|
||||
#we do not want to update the main currency
|
||||
# We do not want to update the main currency
|
||||
if main_currency in currency_array:
|
||||
currency_array.remove(main_currency)
|
||||
|
||||
@@ -643,7 +697,7 @@ class CA_BOC_getter(Curreny_getter_interface) :
|
||||
self.validate_cur(curr)
|
||||
|
||||
# check if BOC service is running
|
||||
if dom.bozo and dom.status <> 404:
|
||||
if dom.bozo and dom.status != 404:
|
||||
_logger.error("Bank of Canada - service is down - try again\
|
||||
later...")
|
||||
|
||||
@@ -664,8 +718,7 @@ class CA_BOC_getter(Curreny_getter_interface) :
|
||||
.astimezone(pytz.utc).replace(tzinfo=None)
|
||||
self.check_rate_date(rate_date_datetime, max_delta_days)
|
||||
self.updated_currency[curr] = rate
|
||||
_logger.debug("BOC Rate retrieved : 1 " + main_currency +
|
||||
' = ' + str(rate) + ' ' + curr)
|
||||
_logger.debug("BOC Rate retrieved : %s = %s %s" % (main_currency, rate, curr))
|
||||
else:
|
||||
_logger.error("Exchange data format error for Bank of Canada -\
|
||||
%s. Please check provider data format and/or source code." % curr)
|
||||
|
||||
Reference in New Issue
Block a user