mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
[IMP] account_mass_reconcile: black, isort
This commit is contained in:
committed by
hveficent
parent
4a1d8eacfc
commit
1b5a442d92
0
account_mass_reconcile/__init__.py
Executable file → Normal file
0
account_mass_reconcile/__init__.py
Executable file → Normal file
@@ -5,9 +5,7 @@
|
||||
{
|
||||
"name": "Account Mass Reconcile",
|
||||
"version": "12.0.1.0.0",
|
||||
"depends": [
|
||||
"account",
|
||||
],
|
||||
"depends": ["account"],
|
||||
"author": "Akretion,Camptocamp,Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/account-reconcile",
|
||||
"category": "Finance",
|
||||
@@ -18,5 +16,5 @@
|
||||
"views/mass_reconcile_history_view.xml",
|
||||
"views/res_config_view.xml",
|
||||
],
|
||||
'license': 'AGPL-3',
|
||||
"license": "AGPL-3",
|
||||
}
|
||||
|
||||
0
account_mass_reconcile/models/__init__.py
Executable file → Normal file
0
account_mass_reconcile/models/__init__.py
Executable file → Normal file
@@ -7,8 +7,8 @@ from odoo import models
|
||||
|
||||
class MassReconcileAdvancedRef(models.TransientModel):
|
||||
|
||||
_name = 'mass.reconcile.advanced.ref'
|
||||
_inherit = 'mass.reconcile.advanced'
|
||||
_name = "mass.reconcile.advanced.ref"
|
||||
_inherit = "mass.reconcile.advanced"
|
||||
|
||||
@staticmethod
|
||||
def _skip_line(move_line):
|
||||
@@ -17,7 +17,7 @@ class MassReconcileAdvancedRef(models.TransientModel):
|
||||
will be skipped for reconciliation. Can be inherited to
|
||||
skip on some conditions. ie: ref or partner_id is empty.
|
||||
"""
|
||||
return not (move_line.get('ref') and move_line.get('partner_id'))
|
||||
return not (move_line.get("ref") and move_line.get("partner_id"))
|
||||
|
||||
@staticmethod
|
||||
def _matchers(move_line):
|
||||
@@ -57,8 +57,10 @@ class MassReconcileAdvancedRef(models.TransientModel):
|
||||
and their values to match in the opposite lines.
|
||||
A matching key can have multiples values.
|
||||
"""
|
||||
return (('partner_id', move_line['partner_id']),
|
||||
('ref', move_line['ref'].lower().strip()))
|
||||
return (
|
||||
("partner_id", move_line["partner_id"]),
|
||||
("ref", move_line["ref"].lower().strip()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _opposite_matchers(move_line):
|
||||
@@ -99,6 +101,11 @@ class MassReconcileAdvancedRef(models.TransientModel):
|
||||
:param dict move_line: values of the move_line
|
||||
:yield: matchers as tuple ('matcher key', value(s))
|
||||
"""
|
||||
yield ('partner_id', move_line['partner_id'])
|
||||
yield ('ref', ((move_line['ref'] or '').lower().strip(),
|
||||
move_line['name'].lower().strip()))
|
||||
yield ("partner_id", move_line["partner_id"])
|
||||
yield (
|
||||
"ref",
|
||||
(
|
||||
(move_line["ref"] or "").lower().strip(),
|
||||
move_line["name"].lower().strip(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
import logging
|
||||
from itertools import product
|
||||
|
||||
from odoo import models, api
|
||||
from odoo import api, models
|
||||
from odoo.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MassReconcileAdvanced(models.AbstractModel):
|
||||
_name = 'mass.reconcile.advanced'
|
||||
_inherit = 'mass.reconcile.base'
|
||||
_name = "mass.reconcile.advanced"
|
||||
_inherit = "mass.reconcile.base"
|
||||
|
||||
def _query_debit(self):
|
||||
"""Select all move (debit>0) as candidate. """
|
||||
@@ -22,7 +22,7 @@ class MassReconcileAdvanced(models.AbstractModel):
|
||||
where, params = self._where_query()
|
||||
where += " AND account_move_line.debit > 0 "
|
||||
where2, params2 = self._get_filter()
|
||||
query = ' '.join((select, sql_from, where, where2))
|
||||
query = " ".join((select, sql_from, where, where2))
|
||||
self.env.cr.execute(query, params + params2)
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
@@ -33,7 +33,7 @@ class MassReconcileAdvanced(models.AbstractModel):
|
||||
where, params = self._where_query()
|
||||
where += " AND account_move_line.credit > 0 "
|
||||
where2, params2 = self._get_filter()
|
||||
query = ' '.join((select, sql_from, where, where2))
|
||||
query = " ".join((select, sql_from, where, where2))
|
||||
self.env.cr.execute(query, params + params2)
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
@@ -153,15 +153,15 @@ class MassReconcileAdvanced(models.AbstractModel):
|
||||
"""
|
||||
mkey, mvalue = matcher
|
||||
omkey, omvalue = opposite_matcher
|
||||
assert mkey == omkey, \
|
||||
(_("A matcher %s is compared with a matcher %s, the _matchers and "
|
||||
"_opposite_matchers are probably wrong") % (mkey, omkey))
|
||||
assert mkey == omkey, _(
|
||||
"A matcher %s is compared with a matcher %s, the _matchers and "
|
||||
"_opposite_matchers are probably wrong"
|
||||
) % (mkey, omkey)
|
||||
if not isinstance(mvalue, (list, tuple)):
|
||||
mvalue = mvalue,
|
||||
mvalue = (mvalue,)
|
||||
if not isinstance(omvalue, (list, tuple)):
|
||||
omvalue = omvalue,
|
||||
return MassReconcileAdvanced._compare_matcher_values(mkey, mvalue,
|
||||
omvalue)
|
||||
omvalue = (omvalue,)
|
||||
return MassReconcileAdvanced._compare_matcher_values(mkey, mvalue, omvalue)
|
||||
|
||||
def _compare_opposite(self, move_line, opposite_move_line, matchers):
|
||||
""" Iterate over the matchers of the move lines vs opposite move lines
|
||||
@@ -197,8 +197,11 @@ class MassReconcileAdvanced(models.AbstractModel):
|
||||
:return: list of matching lines
|
||||
"""
|
||||
matchers = self._matchers(move_line)
|
||||
return [op for op in opposite_move_lines if
|
||||
self._compare_opposite(move_line, op, matchers)]
|
||||
return [
|
||||
op
|
||||
for op in opposite_move_lines
|
||||
if self._compare_opposite(move_line, op, matchers)
|
||||
]
|
||||
|
||||
def _action_rec(self):
|
||||
credit_lines = self._query_credit()
|
||||
@@ -221,51 +224,54 @@ class MassReconcileAdvanced(models.AbstractModel):
|
||||
for rec in self:
|
||||
reconcile_groups = []
|
||||
ctx = self.env.context.copy()
|
||||
ctx['commit_every'] = (
|
||||
rec.account_id.company_id.reconciliation_commit_every
|
||||
)
|
||||
ctx["commit_every"] = rec.account_id.company_id.reconciliation_commit_every
|
||||
_logger.info("%d credit lines to reconcile", len(credit_lines))
|
||||
for idx, credit_line in enumerate(credit_lines, start=1):
|
||||
if idx % 50 == 0:
|
||||
_logger.info("... %d/%d credit lines inspected ...", idx,
|
||||
len(credit_lines))
|
||||
_logger.info(
|
||||
"... %d/%d credit lines inspected ...", idx, len(credit_lines)
|
||||
)
|
||||
if self._skip_line(credit_line):
|
||||
continue
|
||||
opposite_lines = self._search_opposites(credit_line,
|
||||
debit_lines)
|
||||
opposite_lines = self._search_opposites(credit_line, debit_lines)
|
||||
if not opposite_lines:
|
||||
continue
|
||||
opposite_ids = [l['id'] for l in opposite_lines]
|
||||
line_ids = opposite_ids + [credit_line['id']]
|
||||
opposite_ids = [l["id"] for l in opposite_lines]
|
||||
line_ids = opposite_ids + [credit_line["id"]]
|
||||
for group in reconcile_groups:
|
||||
if any([lid in group for lid in opposite_ids]):
|
||||
_logger.debug("New lines %s matched with an existing "
|
||||
"group %s", line_ids, group)
|
||||
_logger.debug(
|
||||
"New lines %s matched with an existing " "group %s",
|
||||
line_ids,
|
||||
group,
|
||||
)
|
||||
group.update(line_ids)
|
||||
break
|
||||
else:
|
||||
_logger.debug("New group of lines matched %s", line_ids)
|
||||
reconcile_groups.append(set(line_ids))
|
||||
lines_by_id = dict([(l['id'], l)
|
||||
for l in credit_lines + debit_lines])
|
||||
_logger.info("Found %d groups to reconcile",
|
||||
len(reconcile_groups))
|
||||
for group_count, reconcile_group_ids \
|
||||
in enumerate(reconcile_groups, start=1):
|
||||
_logger.debug("Reconciling group %d/%d with ids %s",
|
||||
group_count, len(reconcile_groups),
|
||||
reconcile_group_ids)
|
||||
group_lines = [lines_by_id[lid]
|
||||
for lid in reconcile_group_ids]
|
||||
reconciled, full = self._reconcile_lines(group_lines,
|
||||
allow_partial=True)
|
||||
lines_by_id = {l["id"]: l for l in credit_lines + debit_lines}
|
||||
_logger.info("Found %d groups to reconcile", len(reconcile_groups))
|
||||
for group_count, reconcile_group_ids in enumerate(
|
||||
reconcile_groups, start=1
|
||||
):
|
||||
_logger.debug(
|
||||
"Reconciling group %d/%d with ids %s",
|
||||
group_count,
|
||||
len(reconcile_groups),
|
||||
reconcile_group_ids,
|
||||
)
|
||||
group_lines = [lines_by_id[lid] for lid in reconcile_group_ids]
|
||||
reconciled, full = self._reconcile_lines(
|
||||
group_lines, allow_partial=True
|
||||
)
|
||||
if reconciled and full:
|
||||
reconciled_ids += reconcile_group_ids
|
||||
|
||||
if (ctx['commit_every'] and
|
||||
group_count % ctx['commit_every'] == 0):
|
||||
if ctx["commit_every"] and group_count % ctx["commit_every"] == 0:
|
||||
self.env.cr.commit()
|
||||
_logger.info("Commit the reconciliations after %d groups",
|
||||
group_count)
|
||||
_logger.info(
|
||||
"Commit the reconciliations after %d groups", group_count
|
||||
)
|
||||
_logger.info("Reconciliation is over")
|
||||
return reconciled_ids
|
||||
|
||||
@@ -11,17 +11,13 @@ from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
class MassReconcileBase(models.AbstractModel):
|
||||
"""Abstract Model for reconciliation methods"""
|
||||
_name = 'mass.reconcile.base'
|
||||
_inherit = 'mass.reconcile.options'
|
||||
|
||||
account_id = fields.Many2one(
|
||||
'account.account',
|
||||
string='Account',
|
||||
required=True
|
||||
)
|
||||
_name = "mass.reconcile.base"
|
||||
_inherit = "mass.reconcile.options"
|
||||
|
||||
account_id = fields.Many2one("account.account", string="Account", required=True)
|
||||
partner_ids = fields.Many2many(
|
||||
comodel_name='res.partner',
|
||||
string='Restrict on partners',
|
||||
comodel_name="res.partner", string="Restrict on partners"
|
||||
)
|
||||
# other fields are inherited from mass.reconcile.options
|
||||
|
||||
@@ -47,16 +43,16 @@ class MassReconcileBase(models.AbstractModel):
|
||||
An extra column aliased as ``key`` should be defined
|
||||
in each query."""
|
||||
aml_cols = (
|
||||
'id',
|
||||
'debit',
|
||||
'credit',
|
||||
'date',
|
||||
'ref',
|
||||
'name',
|
||||
'partner_id',
|
||||
'account_id',
|
||||
'reconciled',
|
||||
'move_id',
|
||||
"id",
|
||||
"debit",
|
||||
"credit",
|
||||
"date",
|
||||
"ref",
|
||||
"name",
|
||||
"partner_id",
|
||||
"account_id",
|
||||
"reconciled",
|
||||
"move_id",
|
||||
)
|
||||
return ["account_move_line.{}".format(col) for col in aml_cols]
|
||||
|
||||
@@ -64,16 +60,18 @@ class MassReconcileBase(models.AbstractModel):
|
||||
return self._base_columns()
|
||||
|
||||
def _select_query(self, *args, **kwargs):
|
||||
return "SELECT %s" % ', '.join(self._selection_columns())
|
||||
return "SELECT %s" % ", ".join(self._selection_columns())
|
||||
|
||||
def _from_query(self, *args, **kwargs):
|
||||
return ("FROM account_move_line ")
|
||||
return "FROM account_move_line "
|
||||
|
||||
@api.multi
|
||||
def _where_query(self, *args, **kwargs):
|
||||
self.ensure_one()
|
||||
where = ("WHERE account_move_line.account_id = %s "
|
||||
"AND NOT account_move_line.reconciled")
|
||||
where = (
|
||||
"WHERE account_move_line.account_id = %s "
|
||||
"AND NOT account_move_line.reconciled"
|
||||
)
|
||||
# it would be great to use dict for params
|
||||
# but as we use _where_calc in _get_filter
|
||||
# which returns a list, we have to
|
||||
@@ -87,12 +85,11 @@ class MassReconcileBase(models.AbstractModel):
|
||||
@api.multi
|
||||
def _get_filter(self):
|
||||
self.ensure_one()
|
||||
ml_obj = self.env['account.move.line']
|
||||
where = ''
|
||||
ml_obj = self.env["account.move.line"]
|
||||
where = ""
|
||||
params = []
|
||||
if self._filter:
|
||||
dummy, where, params = ml_obj._where_calc(
|
||||
safe_eval(self._filter)).get_sql()
|
||||
dummy, where, params = ml_obj._where_calc(safe_eval(self._filter)).get_sql()
|
||||
if where:
|
||||
where = " AND %s" % where
|
||||
return where, params
|
||||
@@ -100,37 +97,37 @@ class MassReconcileBase(models.AbstractModel):
|
||||
@api.multi
|
||||
def _below_writeoff_limit(self, lines, writeoff_limit):
|
||||
self.ensure_one()
|
||||
precision = self.env['decimal.precision'].precision_get('Account')
|
||||
keys = ('debit', 'credit')
|
||||
precision = self.env["decimal.precision"].precision_get("Account")
|
||||
keys = ("debit", "credit")
|
||||
sums = reduce(
|
||||
lambda line, memo:
|
||||
dict((key, value + memo[key])
|
||||
for key, value
|
||||
in line.items()
|
||||
if key in keys), lines)
|
||||
debit, credit = sums['debit'], sums['credit']
|
||||
lambda line, memo: {
|
||||
key: value + memo[key] for key, value in line.items() if key in keys
|
||||
},
|
||||
lines,
|
||||
)
|
||||
debit, credit = sums["debit"], sums["credit"]
|
||||
writeoff_amount = round(debit - credit, precision)
|
||||
return bool(writeoff_limit >= abs(writeoff_amount)), debit, credit
|
||||
|
||||
@api.multi
|
||||
def _get_rec_date(self, lines, based_on='end_period_last_credit'):
|
||||
def _get_rec_date(self, lines, based_on="end_period_last_credit"):
|
||||
self.ensure_one()
|
||||
|
||||
def last_date(mlines):
|
||||
return max(mlines, key=itemgetter('date'))
|
||||
return max(mlines, key=itemgetter("date"))
|
||||
|
||||
def credit(mlines):
|
||||
return [l for l in mlines if l['credit'] > 0]
|
||||
return [l for l in mlines if l["credit"] > 0]
|
||||
|
||||
def debit(mlines):
|
||||
return [l for l in mlines if l['debit'] > 0]
|
||||
return [l for l in mlines if l["debit"] > 0]
|
||||
|
||||
if based_on == 'newest':
|
||||
return last_date(lines)['date']
|
||||
elif based_on == 'newest_credit':
|
||||
return last_date(credit(lines))['date']
|
||||
elif based_on == 'newest_debit':
|
||||
return last_date(debit(lines))['date']
|
||||
if based_on == "newest":
|
||||
return last_date(lines)["date"]
|
||||
elif based_on == "newest_credit":
|
||||
return last_date(credit(lines))["date"]
|
||||
elif based_on == "newest_debit":
|
||||
return last_date(debit(lines))["date"]
|
||||
# reconcilation date will be today
|
||||
# when date is None
|
||||
return None
|
||||
@@ -150,22 +147,21 @@ class MassReconcileBase(models.AbstractModel):
|
||||
or partial (False)
|
||||
"""
|
||||
self.ensure_one()
|
||||
ml_obj = self.env['account.move.line']
|
||||
ml_obj = self.env["account.move.line"]
|
||||
below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
|
||||
lines, self.write_off
|
||||
)
|
||||
rec_date = self._get_rec_date(lines, self.date_base_on)
|
||||
line_rs = ml_obj.browse([l['id'] for l in lines]).with_context(
|
||||
date_p=rec_date,
|
||||
comment=_('Automatic Write Off'))
|
||||
line_rs = ml_obj.browse([l["id"] for l in lines]).with_context(
|
||||
date_p=rec_date, comment=_("Automatic Write Off")
|
||||
)
|
||||
if below_writeoff:
|
||||
if sum_credit > sum_debit:
|
||||
writeoff_account = self.account_profit_id
|
||||
else:
|
||||
writeoff_account = self.account_lost_id
|
||||
line_rs.reconcile(
|
||||
writeoff_acc_id=writeoff_account,
|
||||
writeoff_journal_id=self.journal_id
|
||||
writeoff_acc_id=writeoff_account, writeoff_journal_id=self.journal_id
|
||||
)
|
||||
return True, True
|
||||
elif allow_partial:
|
||||
@@ -181,8 +177,7 @@ class MassReconcileBase(models.AbstractModel):
|
||||
else:
|
||||
writeoff_account = self.expense_exchange_account_id
|
||||
line_rs.reconcile(
|
||||
writeoff_acc_id=writeoff_account,
|
||||
writeoff_journal_id=self.journal_id
|
||||
writeoff_acc_id=writeoff_account, writeoff_journal_id=self.journal_id
|
||||
)
|
||||
return True, False
|
||||
return False, False
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
# Copyright 2010 Sébastien Beau
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import models, api, fields, _
|
||||
from odoo import _, api, fields, models, sql_db
|
||||
from odoo.exceptions import Warning as UserError
|
||||
from odoo import sql_db
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,91 +19,64 @@ class MassReconcileOptions(models.AbstractModel):
|
||||
This allows decoupling of the methods and the
|
||||
wizards and allows to launch the wizards alone
|
||||
"""
|
||||
_name = 'mass.reconcile.options'
|
||||
_description = 'Options of a reconciliation profile'
|
||||
|
||||
_name = "mass.reconcile.options"
|
||||
_description = "Options of a reconciliation profile"
|
||||
|
||||
@api.model
|
||||
def _get_rec_base_date(self):
|
||||
return [
|
||||
('newest', 'Most recent move line'),
|
||||
('actual', 'Today'),
|
||||
]
|
||||
return [("newest", "Most recent move line"), ("actual", "Today")]
|
||||
|
||||
write_off = fields.Float(
|
||||
'Write off allowed',
|
||||
default=0.,
|
||||
)
|
||||
account_lost_id = fields.Many2one(
|
||||
'account.account',
|
||||
string="Account Lost",
|
||||
)
|
||||
account_profit_id = fields.Many2one(
|
||||
'account.account',
|
||||
string="Account Profit",
|
||||
)
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal',
|
||||
string="Journal",
|
||||
)
|
||||
write_off = fields.Float("Write off allowed", default=0.0)
|
||||
account_lost_id = fields.Many2one("account.account", string="Account Lost")
|
||||
account_profit_id = fields.Many2one("account.account", string="Account Profit")
|
||||
journal_id = fields.Many2one("account.journal", string="Journal")
|
||||
date_base_on = fields.Selection(
|
||||
'_get_rec_base_date',
|
||||
"_get_rec_base_date",
|
||||
required=True,
|
||||
string='Date of reconciliation',
|
||||
default='newest',
|
||||
)
|
||||
_filter = fields.Char(
|
||||
string='Filter', oldname='filter',
|
||||
string="Date of reconciliation",
|
||||
default="newest",
|
||||
)
|
||||
_filter = fields.Char(string="Filter", oldname="filter")
|
||||
income_exchange_account_id = fields.Many2one(
|
||||
'account.account',
|
||||
string='Gain Exchange Rate Account',
|
||||
"account.account", string="Gain Exchange Rate Account"
|
||||
)
|
||||
expense_exchange_account_id = fields.Many2one(
|
||||
'account.account',
|
||||
string='Loss Exchange Rate Account',
|
||||
"account.account", string="Loss Exchange Rate Account"
|
||||
)
|
||||
|
||||
|
||||
class AccountMassReconcileMethod(models.Model):
|
||||
_name = 'account.mass.reconcile.method'
|
||||
_description = 'reconcile method for account_mass_reconcile'
|
||||
_inherit = 'mass.reconcile.options'
|
||||
_order = 'sequence'
|
||||
_name = "account.mass.reconcile.method"
|
||||
_description = "reconcile method for account_mass_reconcile"
|
||||
_inherit = "mass.reconcile.options"
|
||||
_order = "sequence"
|
||||
|
||||
@staticmethod
|
||||
def _get_reconcilation_methods():
|
||||
return [
|
||||
('mass.reconcile.simple.name', 'Simple. Amount and Name'),
|
||||
('mass.reconcile.simple.partner', 'Simple. Amount and Partner'),
|
||||
('mass.reconcile.simple.reference',
|
||||
'Simple. Amount and Reference'),
|
||||
('mass.reconcile.advanced.ref',
|
||||
'Advanced. Partner and Ref.'),
|
||||
("mass.reconcile.simple.name", "Simple. Amount and Name"),
|
||||
("mass.reconcile.simple.partner", "Simple. Amount and Partner"),
|
||||
("mass.reconcile.simple.reference", "Simple. Amount and Reference"),
|
||||
("mass.reconcile.advanced.ref", "Advanced. Partner and Ref."),
|
||||
]
|
||||
|
||||
def _selection_name(self):
|
||||
return self._get_reconcilation_methods()
|
||||
|
||||
name = fields.Selection(
|
||||
'_selection_name',
|
||||
string='Type',
|
||||
required=True,
|
||||
)
|
||||
name = fields.Selection("_selection_name", string="Type", required=True)
|
||||
sequence = fields.Integer(
|
||||
string='Sequence',
|
||||
string="Sequence",
|
||||
default=1,
|
||||
required=True,
|
||||
help="The sequence field is used to order the reconcile method",
|
||||
)
|
||||
task_id = fields.Many2one(
|
||||
'account.mass.reconcile',
|
||||
string='Task',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
"account.mass.reconcile", string="Task", required=True, ondelete="cascade"
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
"res.company",
|
||||
string="Company",
|
||||
related="task_id.company_id",
|
||||
store=True,
|
||||
readonly=True,
|
||||
@@ -112,90 +84,75 @@ class AccountMassReconcileMethod(models.Model):
|
||||
|
||||
|
||||
class AccountMassReconcile(models.Model):
|
||||
_name = 'account.mass.reconcile'
|
||||
_inherit = ['mail.thread']
|
||||
_description = 'account mass reconcile'
|
||||
_name = "account.mass.reconcile"
|
||||
_inherit = ["mail.thread"]
|
||||
_description = "account mass reconcile"
|
||||
|
||||
@api.multi
|
||||
@api.depends('account')
|
||||
@api.depends("account")
|
||||
def _get_total_unrec(self):
|
||||
obj_move_line = self.env['account.move.line']
|
||||
obj_move_line = self.env["account.move.line"]
|
||||
for rec in self:
|
||||
rec.unreconciled_count = obj_move_line.search_count(
|
||||
[('account_id', '=', rec.account.id),
|
||||
('reconciled', '=', False)])
|
||||
[("account_id", "=", rec.account.id), ("reconciled", "=", False)]
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.depends('history_ids')
|
||||
@api.depends("history_ids")
|
||||
def _last_history(self):
|
||||
# do a search() for retrieving the latest history line,
|
||||
# as a read() will badly split the list of ids with 'date desc'
|
||||
# and return the wrong result.
|
||||
history_obj = self.env['mass.reconcile.history']
|
||||
history_obj = self.env["mass.reconcile.history"]
|
||||
for rec in self:
|
||||
last_history_rs = history_obj.search(
|
||||
[('mass_reconcile_id', '=', rec.id)],
|
||||
limit=1, order='date desc'
|
||||
)
|
||||
[("mass_reconcile_id", "=", rec.id)], limit=1, order="date desc"
|
||||
)
|
||||
rec.last_history = last_history_rs or False
|
||||
|
||||
name = fields.Char(
|
||||
string='Name',
|
||||
required=True,
|
||||
)
|
||||
account = fields.Many2one(
|
||||
'account.account',
|
||||
string='Account',
|
||||
required=True,
|
||||
)
|
||||
name = fields.Char(string="Name", required=True)
|
||||
account = fields.Many2one("account.account", string="Account", required=True)
|
||||
reconcile_method = fields.One2many(
|
||||
'account.mass.reconcile.method',
|
||||
'task_id',
|
||||
string='Method',
|
||||
"account.mass.reconcile.method", "task_id", string="Method"
|
||||
)
|
||||
unreconciled_count = fields.Integer(
|
||||
string='Unreconciled Items',
|
||||
compute='_get_total_unrec',
|
||||
string="Unreconciled Items", compute="_get_total_unrec"
|
||||
)
|
||||
history_ids = fields.One2many(
|
||||
'mass.reconcile.history',
|
||||
'mass_reconcile_id',
|
||||
string='History',
|
||||
readonly=True,
|
||||
"mass.reconcile.history", "mass_reconcile_id", string="History", readonly=True
|
||||
)
|
||||
last_history = fields.Many2one(
|
||||
'mass.reconcile.history',
|
||||
string='Last history', readonly=True,
|
||||
compute='_last_history',
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
"mass.reconcile.history",
|
||||
string="Last history",
|
||||
readonly=True,
|
||||
compute="_last_history",
|
||||
)
|
||||
company_id = fields.Many2one("res.company", string="Company")
|
||||
|
||||
@staticmethod
|
||||
def _prepare_run_transient(rec_method):
|
||||
return {'account_id': rec_method.task_id.account.id,
|
||||
'write_off': rec_method.write_off,
|
||||
'account_lost_id': (rec_method.account_lost_id.id),
|
||||
'account_profit_id': (rec_method.account_profit_id.id),
|
||||
'income_exchange_account_id':
|
||||
(rec_method.income_exchange_account_id.id),
|
||||
'expense_exchange_account_id':
|
||||
(rec_method.income_exchange_account_id.id),
|
||||
'journal_id': (rec_method.journal_id.id),
|
||||
'date_base_on': rec_method.date_base_on,
|
||||
'_filter': rec_method._filter}
|
||||
return {
|
||||
"account_id": rec_method.task_id.account.id,
|
||||
"write_off": rec_method.write_off,
|
||||
"account_lost_id": (rec_method.account_lost_id.id),
|
||||
"account_profit_id": (rec_method.account_profit_id.id),
|
||||
"income_exchange_account_id": (rec_method.income_exchange_account_id.id),
|
||||
"expense_exchange_account_id": (rec_method.income_exchange_account_id.id),
|
||||
"journal_id": (rec_method.journal_id.id),
|
||||
"date_base_on": rec_method.date_base_on,
|
||||
"_filter": rec_method._filter,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def run_reconcile(self):
|
||||
def find_reconcile_ids(fieldname, move_line_ids):
|
||||
if not move_line_ids:
|
||||
return []
|
||||
sql = ("SELECT DISTINCT " + fieldname +
|
||||
" FROM account_move_line "
|
||||
" WHERE id in %s "
|
||||
" AND " + fieldname + " IS NOT NULL")
|
||||
sql = (
|
||||
"SELECT DISTINCT " + fieldname + " FROM account_move_line "
|
||||
" WHERE id in %s "
|
||||
" AND " + fieldname + " IS NOT NULL"
|
||||
)
|
||||
self.env.cr.execute(sql, (tuple(move_line_ids),))
|
||||
res = self.env.cr.fetchall()
|
||||
return [row[0] for row in res]
|
||||
@@ -207,10 +164,8 @@ class AccountMassReconcile(models.Model):
|
||||
|
||||
for rec in self:
|
||||
ctx = self.env.context.copy()
|
||||
ctx['commit_every'] = (
|
||||
rec.account.company_id.reconciliation_commit_every
|
||||
)
|
||||
if ctx['commit_every']:
|
||||
ctx["commit_every"] = rec.account.company_id.reconciliation_commit_every
|
||||
if ctx["commit_every"]:
|
||||
new_cr = sql_db.db_connect(self.env.cr.dbname).cursor()
|
||||
else:
|
||||
new_cr = self.env.cr
|
||||
@@ -220,46 +175,38 @@ class AccountMassReconcile(models.Model):
|
||||
|
||||
for method in rec.reconcile_method:
|
||||
rec_model = self.env[method.name]
|
||||
auto_rec_id = rec_model.create(
|
||||
self._prepare_run_transient(method)
|
||||
)
|
||||
auto_rec_id = rec_model.create(self._prepare_run_transient(method))
|
||||
|
||||
ml_rec_ids = auto_rec_id.automatic_reconcile()
|
||||
|
||||
all_ml_rec_ids += ml_rec_ids
|
||||
|
||||
reconcile_ids = find_reconcile_ids(
|
||||
'full_reconcile_id',
|
||||
all_ml_rec_ids
|
||||
)
|
||||
self.env['mass.reconcile.history'].create(
|
||||
reconcile_ids = find_reconcile_ids("full_reconcile_id", all_ml_rec_ids)
|
||||
self.env["mass.reconcile.history"].create(
|
||||
{
|
||||
'mass_reconcile_id': rec.id,
|
||||
'date': fields.Datetime.now(),
|
||||
'reconcile_ids': [
|
||||
(4, rid) for rid in reconcile_ids
|
||||
],
|
||||
})
|
||||
"mass_reconcile_id": rec.id,
|
||||
"date": fields.Datetime.now(),
|
||||
"reconcile_ids": [(4, rid) for rid in reconcile_ids],
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
# In case of error, we log it in the mail thread, log the
|
||||
# stack trace and create an empty history line; otherwise,
|
||||
# the cron will just loop on this reconcile task.
|
||||
_logger.exception(
|
||||
"The reconcile task %s had an exception: %s",
|
||||
rec.name, str(e)
|
||||
"The reconcile task %s had an exception: %s", rec.name, str(e)
|
||||
)
|
||||
message = _("There was an error during reconciliation : %s") \
|
||||
% str(e)
|
||||
message = _("There was an error during reconciliation : %s") % str(e)
|
||||
rec.message_post(body=message)
|
||||
self.env['mass.reconcile.history'].create(
|
||||
self.env["mass.reconcile.history"].create(
|
||||
{
|
||||
'mass_reconcile_id': rec.id,
|
||||
'date': fields.Datetime.now(),
|
||||
'reconcile_ids': [],
|
||||
"mass_reconcile_id": rec.id,
|
||||
"date": fields.Datetime.now(),
|
||||
"reconcile_ids": [],
|
||||
}
|
||||
)
|
||||
finally:
|
||||
if ctx['commit_every']:
|
||||
if ctx["commit_every"]:
|
||||
new_cr.commit()
|
||||
new_cr.close()
|
||||
|
||||
@@ -271,33 +218,32 @@ class AccountMassReconcile(models.Model):
|
||||
task.
|
||||
"""
|
||||
raise UserError(
|
||||
_('There is no history of reconciled '
|
||||
'items on the task: %s.') % self.name
|
||||
_("There is no history of reconciled " "items on the task: %s.") % self.name
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _open_move_line_list(move_line_ids, name):
|
||||
return {
|
||||
'name': name,
|
||||
'view_mode': 'tree,form',
|
||||
'view_id': False,
|
||||
'view_type': 'form',
|
||||
'res_model': 'account.move.line',
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy': True,
|
||||
'target': 'current',
|
||||
'domain': [('id', 'in', move_line_ids)],
|
||||
"name": name,
|
||||
"view_mode": "tree,form",
|
||||
"view_id": False,
|
||||
"view_type": "form",
|
||||
"res_model": "account.move.line",
|
||||
"type": "ir.actions.act_window",
|
||||
"nodestroy": True,
|
||||
"target": "current",
|
||||
"domain": [("id", "in", move_line_ids)],
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def open_unreconcile(self):
|
||||
""" Open the view of move line with the unreconciled move lines"""
|
||||
self.ensure_one()
|
||||
obj_move_line = self.env['account.move.line']
|
||||
obj_move_line = self.env["account.move.line"]
|
||||
lines = obj_move_line.search(
|
||||
[('account_id', '=', self.account.id),
|
||||
('reconciled', '=', False)])
|
||||
name = _('Unreconciled items')
|
||||
[("account_id", "=", self.account.id), ("reconciled", "=", False)]
|
||||
)
|
||||
name = _("Unreconciled items")
|
||||
return self._open_move_line_list(lines.ids or [], name)
|
||||
|
||||
def last_history_reconcile(self):
|
||||
@@ -318,6 +264,7 @@ class AccountMassReconcile(models.Model):
|
||||
:returns: True in case of success or raises an exception
|
||||
|
||||
"""
|
||||
|
||||
def _get_date(reconcile):
|
||||
if reconcile.last_history.date:
|
||||
return fields.Datetime.to_datetime(reconcile.last_history.date)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2012-2016 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, api, fields, _
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class MassReconcileHistory(models.Model):
|
||||
@@ -9,47 +9,40 @@ class MassReconcileHistory(models.Model):
|
||||
|
||||
Each history stores the list of reconciliations done
|
||||
"""
|
||||
_name = 'mass.reconcile.history'
|
||||
_description = 'Store an history of the runs per profile'
|
||||
_rec_name = 'mass_reconcile_id'
|
||||
_order = 'date DESC'
|
||||
|
||||
_name = "mass.reconcile.history"
|
||||
_description = "Store an history of the runs per profile"
|
||||
_rec_name = "mass_reconcile_id"
|
||||
_order = "date DESC"
|
||||
|
||||
@api.multi
|
||||
@api.depends('reconcile_ids')
|
||||
@api.depends("reconcile_ids")
|
||||
def _get_reconcile_line_ids(self):
|
||||
for rec in self:
|
||||
rec.reconcile_line_ids = rec.mapped(
|
||||
'reconcile_ids.reconciled_line_ids'
|
||||
).ids
|
||||
rec.reconcile_line_ids = rec.mapped("reconcile_ids.reconciled_line_ids").ids
|
||||
|
||||
mass_reconcile_id = fields.Many2one(
|
||||
'account.mass.reconcile',
|
||||
string='Reconcile Profile',
|
||||
readonly=True,
|
||||
)
|
||||
date = fields.Datetime(
|
||||
string='Run date',
|
||||
readonly=True,
|
||||
required=True,
|
||||
"account.mass.reconcile", string="Reconcile Profile", readonly=True
|
||||
)
|
||||
date = fields.Datetime(string="Run date", readonly=True, required=True)
|
||||
reconcile_ids = fields.Many2many(
|
||||
comodel_name='account.full.reconcile',
|
||||
relation='account_full_reconcile_history_rel',
|
||||
string='Full Reconciliations',
|
||||
comodel_name="account.full.reconcile",
|
||||
relation="account_full_reconcile_history_rel",
|
||||
string="Full Reconciliations",
|
||||
readonly=True,
|
||||
)
|
||||
reconcile_line_ids = fields.Many2many(
|
||||
comodel_name='account.move.line',
|
||||
relation='account_move_line_history_rel',
|
||||
string='Reconciled Items',
|
||||
compute='_get_reconcile_line_ids',
|
||||
comodel_name="account.move.line",
|
||||
relation="account_move_line_history_rel",
|
||||
string="Reconciled Items",
|
||||
compute="_get_reconcile_line_ids",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
"res.company",
|
||||
string="Company",
|
||||
store=True,
|
||||
readonly=True,
|
||||
related='mass_reconcile_id.company_id',
|
||||
related="mass_reconcile_id.company_id",
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@@ -60,18 +53,18 @@ class MassReconcileHistory(models.Model):
|
||||
:param history_id: id of the history
|
||||
:return: action to open the move lines
|
||||
"""
|
||||
move_line_ids = self.mapped('reconcile_ids.reconciled_line_ids').ids
|
||||
name = _('Reconciliations')
|
||||
move_line_ids = self.mapped("reconcile_ids.reconciled_line_ids").ids
|
||||
name = _("Reconciliations")
|
||||
return {
|
||||
'name': name,
|
||||
'view_mode': 'tree,form',
|
||||
'view_id': False,
|
||||
'view_type': 'form',
|
||||
'res_model': 'account.move.line',
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy': True,
|
||||
'target': 'current',
|
||||
'domain': [('id', 'in', move_line_ids)],
|
||||
"name": name,
|
||||
"view_mode": "tree,form",
|
||||
"view_id": False,
|
||||
"view_type": "form",
|
||||
"res_model": "account.move.line",
|
||||
"type": "ir.actions.act_window",
|
||||
"nodestroy": True,
|
||||
"target": "current",
|
||||
"domain": [("id", "in", move_line_ids)],
|
||||
}
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Copyright 2014-2016 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class AccountConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
reconciliation_commit_every = fields.Integer(
|
||||
related="company_id.reconciliation_commit_every",
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
# Copyright 2010 Sébastien Beau
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, api
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class MassReconcileSimple(models.AbstractModel):
|
||||
_name = 'mass.reconcile.simple'
|
||||
_inherit = 'mass.reconcile.base'
|
||||
_name = "mass.reconcile.simple"
|
||||
_inherit = "mass.reconcile.base"
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
@@ -19,27 +19,26 @@ class MassReconcileSimple(models.AbstractModel):
|
||||
raise ValueError("_key_field has to be defined")
|
||||
count = 0
|
||||
res = []
|
||||
while (count < len(lines)):
|
||||
while count < len(lines):
|
||||
for i in range(count + 1, len(lines)):
|
||||
if lines[count][self._key_field] != lines[i][self._key_field]:
|
||||
break
|
||||
check = False
|
||||
if lines[count]['credit'] > 0 and lines[i]['debit'] > 0:
|
||||
if lines[count]["credit"] > 0 and lines[i]["debit"] > 0:
|
||||
credit_line = lines[count]
|
||||
debit_line = lines[i]
|
||||
check = True
|
||||
elif lines[i]['credit'] > 0 and lines[count]['debit'] > 0:
|
||||
elif lines[i]["credit"] > 0 and lines[count]["debit"] > 0:
|
||||
credit_line = lines[i]
|
||||
debit_line = lines[count]
|
||||
check = True
|
||||
if not check:
|
||||
continue
|
||||
reconciled, dummy = self._reconcile_lines(
|
||||
[credit_line, debit_line],
|
||||
allow_partial=False
|
||||
)
|
||||
[credit_line, debit_line], allow_partial=False
|
||||
)
|
||||
if reconciled:
|
||||
res += [credit_line['id'], debit_line['id']]
|
||||
res += [credit_line["id"], debit_line["id"]]
|
||||
del lines[i]
|
||||
break
|
||||
count += 1
|
||||
@@ -56,11 +55,9 @@ class MassReconcileSimple(models.AbstractModel):
|
||||
where += " AND account_move_line.%s IS NOT NULL " % self._key_field
|
||||
|
||||
where2, params2 = self._get_filter()
|
||||
query = ' '.join((
|
||||
select,
|
||||
self._from_query(),
|
||||
where, where2,
|
||||
self._simple_order()))
|
||||
query = " ".join(
|
||||
(select, self._from_query(), where, where2, self._simple_order())
|
||||
)
|
||||
|
||||
self.env.cr.execute(query, params + params2)
|
||||
lines = self.env.cr.dictfetchall()
|
||||
@@ -68,27 +65,27 @@ class MassReconcileSimple(models.AbstractModel):
|
||||
|
||||
|
||||
class MassReconcileSimpleName(models.TransientModel):
|
||||
_name = 'mass.reconcile.simple.name'
|
||||
_inherit = 'mass.reconcile.simple'
|
||||
_name = "mass.reconcile.simple.name"
|
||||
_inherit = "mass.reconcile.simple"
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'name'
|
||||
_key_field = "name"
|
||||
|
||||
|
||||
class MassReconcileSimplePartner(models.TransientModel):
|
||||
_name = 'mass.reconcile.simple.partner'
|
||||
_inherit = 'mass.reconcile.simple'
|
||||
_name = "mass.reconcile.simple.partner"
|
||||
_inherit = "mass.reconcile.simple"
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'partner_id'
|
||||
_key_field = "partner_id"
|
||||
|
||||
|
||||
class MassReconcileSimpleReference(models.TransientModel):
|
||||
_name = 'mass.reconcile.simple.reference'
|
||||
_inherit = 'mass.reconcile.simple'
|
||||
_name = "mass.reconcile.simple.reference"
|
||||
_inherit = "mass.reconcile.simple"
|
||||
|
||||
# has to be subclassed
|
||||
# field name used as key for matching the move lines
|
||||
_key_field = 'ref'
|
||||
_key_field = "ref"
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
<record id="mass_reconcile_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile multi-company</field>
|
||||
<field name="model_id" ref="model_account_mass_reconcile"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_reconcile_history_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile history multi-company</field>
|
||||
<field name="model_id" ref="model_mass_reconcile_history"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_reconcile_method_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile method multi-company</field>
|
||||
<field name="model_id" ref="model_account_mass_reconcile_method"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_reconcile_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile multi-company</field>
|
||||
<field name="model_id" ref="model_account_mass_reconcile" />
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
<record id="mass_reconcile_history_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile history multi-company</field>
|
||||
<field name="model_id" ref="model_mass_reconcile_history" />
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
<record id="mass_reconcile_method_rule" model="ir.rule">
|
||||
<field name="name">Mass reconcile method multi-company</field>
|
||||
<field name="model_id" ref="model_account_mass_reconcile_method" />
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo import tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestOnChange(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestOnChange, cls).setUpClass()
|
||||
tools.convert_file(cls.cr, 'account',
|
||||
get_module_resource('account', 'test',
|
||||
'account_minimal_test.xml'),
|
||||
{}, 'init', False, 'test')
|
||||
acc_setting = cls.env['res.config.settings']
|
||||
tools.convert_file(
|
||||
cls.cr,
|
||||
"account",
|
||||
get_module_resource("account", "test", "account_minimal_test.xml"),
|
||||
{},
|
||||
"init",
|
||||
False,
|
||||
"test",
|
||||
)
|
||||
acc_setting = cls.env["res.config.settings"]
|
||||
cls.acc_setting_obj = acc_setting.create({})
|
||||
cls.company_obj = cls.env['res.company']
|
||||
cls.company_obj = cls.env["res.company"]
|
||||
# analytic defaults account creation
|
||||
cls.main_company = cls.env.ref('base.main_company')
|
||||
cls.main_company = cls.env.ref("base.main_company")
|
||||
cls.sec_company = cls.company_obj.create(
|
||||
{
|
||||
'name': 'Second company',
|
||||
'reconciliation_commit_every': 80
|
||||
}
|
||||
{"name": "Second company", "reconciliation_commit_every": 80}
|
||||
)
|
||||
|
||||
def test_retrieve_analytic_account(self):
|
||||
@@ -33,12 +34,12 @@ class TestOnChange(common.SavepointCase):
|
||||
|
||||
self.acc_setting_obj.company_id = self.sec_company
|
||||
|
||||
self.assertEqual(sec_company_commit,
|
||||
self.acc_setting_obj.reconciliation_commit_every,
|
||||
False)
|
||||
self.assertEqual(
|
||||
sec_company_commit, self.acc_setting_obj.reconciliation_commit_every, False
|
||||
)
|
||||
|
||||
self.acc_setting_obj.company_id = self.main_company
|
||||
|
||||
self.assertEqual(main_company_commit,
|
||||
self.acc_setting_obj.reconciliation_commit_every,
|
||||
False)
|
||||
self.assertEqual(
|
||||
main_company_commit, self.acc_setting_obj.reconciliation_commit_every, False
|
||||
)
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo import fields, exceptions, tools
|
||||
from odoo import exceptions, fields, tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestReconcile(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestReconcile, cls).setUpClass()
|
||||
tools.convert_file(cls.cr, 'account',
|
||||
get_module_resource('account', 'test',
|
||||
'account_minimal_test.xml'),
|
||||
{}, 'init', False, 'test')
|
||||
cls.rec_history_obj = cls.env['mass.reconcile.history']
|
||||
cls.mass_rec_obj = cls.env['account.mass.reconcile']
|
||||
cls.mass_rec_method_obj = (
|
||||
cls.env['account.mass.reconcile.method']
|
||||
tools.convert_file(
|
||||
cls.cr,
|
||||
"account",
|
||||
get_module_resource("account", "test", "account_minimal_test.xml"),
|
||||
{},
|
||||
"init",
|
||||
False,
|
||||
"test",
|
||||
)
|
||||
cls.rec_history_obj = cls.env["mass.reconcile.history"]
|
||||
cls.mass_rec_obj = cls.env["account.mass.reconcile"]
|
||||
cls.mass_rec_method_obj = cls.env["account.mass.reconcile.method"]
|
||||
cls.mass_rec = cls.mass_rec_obj.create(
|
||||
{"name": "AER2", "account": cls.env.ref("account.a_salary_expense").id}
|
||||
)
|
||||
cls.mass_rec_method = cls.mass_rec_method_obj.create(
|
||||
{
|
||||
"name": "mass.reconcile.simple.name",
|
||||
"sequence": "10",
|
||||
"task_id": cls.mass_rec.id,
|
||||
}
|
||||
)
|
||||
cls.mass_rec_no_history = cls.mass_rec_obj.create(
|
||||
{"name": "AER3", "account": cls.env.ref("account.a_salary_expense").id}
|
||||
)
|
||||
cls.rec_history = cls.rec_history_obj.create(
|
||||
{"mass_reconcile_id": cls.mass_rec.id, "date": fields.Datetime.now()}
|
||||
)
|
||||
cls.mass_rec = cls.mass_rec_obj.create({
|
||||
'name': 'AER2',
|
||||
'account': cls.env.ref('account.a_salary_expense').id,
|
||||
})
|
||||
cls.mass_rec_method = cls.mass_rec_method_obj.create({
|
||||
'name': 'mass.reconcile.simple.name',
|
||||
'sequence': '10',
|
||||
'task_id': cls.mass_rec.id,
|
||||
})
|
||||
cls.mass_rec_no_history = cls.mass_rec_obj.create({
|
||||
'name': 'AER3',
|
||||
'account': cls.env.ref('account.a_salary_expense').id,
|
||||
})
|
||||
cls.rec_history = cls.rec_history_obj.create({
|
||||
'mass_reconcile_id': cls.mass_rec.id,
|
||||
'date': fields.Datetime.now(),
|
||||
})
|
||||
|
||||
def test_last_history(self):
|
||||
mass_rec_last_hist = self.mass_rec.last_history
|
||||
@@ -52,9 +53,8 @@ class TestReconcile(common.SavepointCase):
|
||||
|
||||
def test_open_unreconcile(self):
|
||||
res = self.mass_rec.open_unreconcile()
|
||||
self.assertEqual([('id', 'in', [])], res.get('domain', []))
|
||||
self.assertEqual([("id", "in", [])], res.get("domain", []))
|
||||
|
||||
def test_prepare_run_transient(self):
|
||||
res = self.mass_rec._prepare_run_transient(self.mass_rec_method)
|
||||
self.assertEqual(self.ref('account.a_salary_expense'),
|
||||
res.get('account_id', 0))
|
||||
self.assertEqual(self.ref("account.a_salary_expense"), res.get("account_id", 0))
|
||||
|
||||
@@ -1,42 +1,37 @@
|
||||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo import fields, tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestReconcileHistory(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestReconcileHistory, cls).setUpClass()
|
||||
tools.convert_file(cls.cr, 'account',
|
||||
get_module_resource('account', 'test',
|
||||
'account_minimal_test.xml'),
|
||||
{}, 'init', False, 'test')
|
||||
cls.rec_history_obj = cls.env['mass.reconcile.history']
|
||||
cls.mass_rec_obj = cls.env['account.mass.reconcile']
|
||||
tools.convert_file(
|
||||
cls.cr,
|
||||
"account",
|
||||
get_module_resource("account", "test", "account_minimal_test.xml"),
|
||||
{},
|
||||
"init",
|
||||
False,
|
||||
"test",
|
||||
)
|
||||
cls.rec_history_obj = cls.env["mass.reconcile.history"]
|
||||
cls.mass_rec_obj = cls.env["account.mass.reconcile"]
|
||||
cls.mass_rec = cls.mass_rec_obj.create(
|
||||
{
|
||||
'name': 'AER1',
|
||||
'account': cls.env.ref('account.a_expense').id,
|
||||
|
||||
}
|
||||
)
|
||||
{"name": "AER1", "account": cls.env.ref("account.a_expense").id}
|
||||
)
|
||||
cls.rec_history = cls.rec_history_obj.create(
|
||||
{
|
||||
'mass_reconcile_id': cls.mass_rec.id,
|
||||
'date': fields.Datetime.now(),
|
||||
}
|
||||
)
|
||||
{"mass_reconcile_id": cls.mass_rec.id, "date": fields.Datetime.now()}
|
||||
)
|
||||
|
||||
def test_open_full_empty(self):
|
||||
res = self.rec_history._open_move_lines()
|
||||
self.assertEqual([('id', 'in', [])], res.get(
|
||||
'domain', []))
|
||||
self.assertEqual([("id", "in", [])], res.get("domain", []))
|
||||
|
||||
def test_open_full_empty_from_method(self):
|
||||
res = self.rec_history.open_reconcile()
|
||||
self.assertEqual([('id', 'in', [])], res.get(
|
||||
'domain', []))
|
||||
self.assertEqual([("id", "in", [])], res.get("domain", []))
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo import fields, tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestScenarioReconcile(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestScenarioReconcile, cls).setUpClass()
|
||||
tools.convert_file(cls.cr, 'account',
|
||||
get_module_resource('account', 'test',
|
||||
'account_minimal_test.xml'),
|
||||
{}, 'init', False, 'test')
|
||||
cls.rec_history_obj = cls.env['mass.reconcile.history']
|
||||
cls.mass_rec_obj = cls.env['account.mass.reconcile']
|
||||
cls.invoice_obj = cls.env['account.invoice']
|
||||
cls.bk_stmt_obj = cls.env['account.bank.statement']
|
||||
cls.bk_stmt_line_obj = cls.env['account.bank.statement.line']
|
||||
cls.acc_move_line_obj = cls.env['account.move.line']
|
||||
cls.mass_rec_method_obj = (
|
||||
cls.env['account.mass.reconcile.method']
|
||||
tools.convert_file(
|
||||
cls.cr,
|
||||
"account",
|
||||
get_module_resource("account", "test", "account_minimal_test.xml"),
|
||||
{},
|
||||
"init",
|
||||
False,
|
||||
"test",
|
||||
)
|
||||
cls.rec_history_obj = cls.env["mass.reconcile.history"]
|
||||
cls.mass_rec_obj = cls.env["account.mass.reconcile"]
|
||||
cls.invoice_obj = cls.env["account.invoice"]
|
||||
cls.bk_stmt_obj = cls.env["account.bank.statement"]
|
||||
cls.bk_stmt_line_obj = cls.env["account.bank.statement.line"]
|
||||
cls.acc_move_line_obj = cls.env["account.move.line"]
|
||||
cls.mass_rec_method_obj = cls.env["account.mass.reconcile.method"]
|
||||
cls.account_fx_income_id = cls.env.ref("account.income_fx_income").id
|
||||
cls.account_fx_expense_id = cls.env.ref("account.income_fx_expense").id
|
||||
cls.acs_model = cls.env['res.config.settings']
|
||||
cls.acs_model = cls.env["res.config.settings"]
|
||||
|
||||
acs_ids = cls.acs_model.search(
|
||||
[('company_id', '=', cls.env.ref("base.main_company").id)]
|
||||
)
|
||||
[("company_id", "=", cls.env.ref("base.main_company").id)]
|
||||
)
|
||||
|
||||
values = {
|
||||
'group_multi_currency': True,
|
||||
}
|
||||
values = {"group_multi_currency": True}
|
||||
|
||||
if acs_ids:
|
||||
acs_ids.write(values)
|
||||
@@ -47,49 +47,56 @@ class TestScenarioReconcile(common.SavepointCase):
|
||||
# create invoice
|
||||
invoice = self.invoice_obj.create(
|
||||
{
|
||||
'type': 'out_invoice',
|
||||
'account_id': self.ref('account.a_recv'),
|
||||
'company_id': self.ref('base.main_company'),
|
||||
'journal_id': self.ref('account.sales_journal'),
|
||||
'partner_id': self.ref('base.res_partner_12'),
|
||||
'invoice_line_ids': [
|
||||
(0, 0, {
|
||||
'name': '[PCSC234] PC Assemble SC234',
|
||||
'account_id': self.ref('account.a_sale'),
|
||||
'price_unit': 1000.0,
|
||||
'quantity': 1.0,
|
||||
'product_id': self.ref('product.product_product_3'),
|
||||
}
|
||||
"type": "out_invoice",
|
||||
"account_id": self.ref("account.a_recv"),
|
||||
"company_id": self.ref("base.main_company"),
|
||||
"journal_id": self.ref("account.sales_journal"),
|
||||
"partner_id": self.ref("base.res_partner_12"),
|
||||
"invoice_line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "[PCSC234] PC Assemble SC234",
|
||||
"account_id": self.ref("account.a_sale"),
|
||||
"price_unit": 1000.0,
|
||||
"quantity": 1.0,
|
||||
"product_id": self.ref("product.product_product_3"),
|
||||
},
|
||||
)
|
||||
]
|
||||
],
|
||||
}
|
||||
)
|
||||
# validate invoice
|
||||
invoice.action_invoice_open()
|
||||
self.assertEqual('open', invoice.state)
|
||||
self.assertEqual("open", invoice.state)
|
||||
|
||||
# create bank_statement
|
||||
statement = self.bk_stmt_obj.create(
|
||||
{
|
||||
'balance_end_real': 0.0,
|
||||
'balance_start': 0.0,
|
||||
'date': fields.Date.today(),
|
||||
'journal_id': self.ref('account.bank_journal'),
|
||||
'line_ids': [
|
||||
(0, 0, {
|
||||
'amount': 1000.0,
|
||||
'partner_id': self.ref('base.res_partner_12'),
|
||||
'name': invoice.number,
|
||||
'ref': invoice.number,
|
||||
})
|
||||
]
|
||||
"balance_end_real": 0.0,
|
||||
"balance_start": 0.0,
|
||||
"date": fields.Date.today(),
|
||||
"journal_id": self.ref("account.bank_journal"),
|
||||
"line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"amount": 1000.0,
|
||||
"partner_id": self.ref("base.res_partner_12"),
|
||||
"name": invoice.number,
|
||||
"ref": invoice.number,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# reconcile
|
||||
line_id = None
|
||||
for l in invoice.move_id.line_ids:
|
||||
if l.account_id.id == self.ref('account.a_recv'):
|
||||
if l.account_id.id == self.ref("account.a_recv"):
|
||||
line_id = l
|
||||
break
|
||||
|
||||
@@ -97,95 +104,97 @@ class TestScenarioReconcile(common.SavepointCase):
|
||||
statement_line.process_reconciliation(
|
||||
[
|
||||
{
|
||||
'move_line': line_id,
|
||||
'credit': 1000.0,
|
||||
'debit': 0.0,
|
||||
'name': invoice.number,
|
||||
"move_line": line_id,
|
||||
"credit": 1000.0,
|
||||
"debit": 0.0,
|
||||
"name": invoice.number,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
# unreconcile journal item created by previous reconciliation
|
||||
lines_to_unreconcile = self.acc_move_line_obj.search(
|
||||
[('reconciled', '=', True),
|
||||
('statement_id', '=', statement.id)]
|
||||
[("reconciled", "=", True), ("statement_id", "=", statement.id)]
|
||||
)
|
||||
lines_to_unreconcile.remove_move_reconcile()
|
||||
|
||||
# create the mass reconcile record
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
'name': 'mass_reconcile_1',
|
||||
'account': self.ref('account.a_recv'),
|
||||
'reconcile_method': [
|
||||
(0, 0, {
|
||||
'name': 'mass.reconcile.simple.partner',
|
||||
})
|
||||
]
|
||||
"name": "mass_reconcile_1",
|
||||
"account": self.ref("account.a_recv"),
|
||||
"reconcile_method": [(0, 0, {"name": "mass.reconcile.simple.partner"})],
|
||||
}
|
||||
)
|
||||
# call the automatic reconcilation method
|
||||
mass_rec.run_reconcile()
|
||||
self.assertEqual(
|
||||
'paid',
|
||||
invoice.state
|
||||
)
|
||||
self.assertEqual("paid", invoice.state)
|
||||
|
||||
def test_scenario_reconcile_currency(self):
|
||||
# create currency rate
|
||||
self.env['res.currency.rate'].create({
|
||||
'name': fields.Date.today().strftime('%Y-%m-%d') + ' 00:00:00',
|
||||
'currency_id': self.ref('base.USD'),
|
||||
'rate': 1.5,
|
||||
})
|
||||
self.env["res.currency.rate"].create(
|
||||
{
|
||||
"name": fields.Date.today().strftime("%Y-%m-%d") + " 00:00:00",
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"rate": 1.5,
|
||||
}
|
||||
)
|
||||
# create invoice
|
||||
invoice = self.invoice_obj.create(
|
||||
{
|
||||
'type': 'out_invoice',
|
||||
'account_id': self.ref('account.a_recv'),
|
||||
'company_id': self.ref('base.main_company'),
|
||||
'currency_id': self.ref('base.USD'),
|
||||
'journal_id': self.ref('account.bank_journal_usd'),
|
||||
'partner_id': self.ref('base.res_partner_12'),
|
||||
'invoice_line_ids': [
|
||||
(0, 0, {
|
||||
'name': '[PCSC234] PC Assemble SC234',
|
||||
'account_id': self.ref('account.a_sale'),
|
||||
'price_unit': 1000.0,
|
||||
'quantity': 1.0,
|
||||
'product_id': self.ref('product.product_product_3'),
|
||||
})
|
||||
]
|
||||
"type": "out_invoice",
|
||||
"account_id": self.ref("account.a_recv"),
|
||||
"company_id": self.ref("base.main_company"),
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"journal_id": self.ref("account.bank_journal_usd"),
|
||||
"partner_id": self.ref("base.res_partner_12"),
|
||||
"invoice_line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "[PCSC234] PC Assemble SC234",
|
||||
"account_id": self.ref("account.a_sale"),
|
||||
"price_unit": 1000.0,
|
||||
"quantity": 1.0,
|
||||
"product_id": self.ref("product.product_product_3"),
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
# validate invoice
|
||||
invoice.action_invoice_open()
|
||||
self.assertEqual('open', invoice.state)
|
||||
self.assertEqual("open", invoice.state)
|
||||
|
||||
# create bank_statement
|
||||
statement = self.bk_stmt_obj.create(
|
||||
{
|
||||
'balance_end_real': 0.0,
|
||||
'balance_start': 0.0,
|
||||
'date': fields.Date.today(),
|
||||
'journal_id': self.ref('account.bank_journal_usd'),
|
||||
'currency_id': self.ref('base.USD'),
|
||||
'line_ids': [
|
||||
(0, 0, {
|
||||
'amount': 1000.0,
|
||||
'amount_currency': 1500.0,
|
||||
'partner_id': self.ref('base.res_partner_12'),
|
||||
'name': invoice.number,
|
||||
'ref': invoice.number,
|
||||
})
|
||||
]
|
||||
"balance_end_real": 0.0,
|
||||
"balance_start": 0.0,
|
||||
"date": fields.Date.today(),
|
||||
"journal_id": self.ref("account.bank_journal_usd"),
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"amount": 1000.0,
|
||||
"amount_currency": 1500.0,
|
||||
"partner_id": self.ref("base.res_partner_12"),
|
||||
"name": invoice.number,
|
||||
"ref": invoice.number,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
# reconcile
|
||||
line_id = None
|
||||
for l in invoice.move_id.line_ids:
|
||||
if l.account_id.id == self.ref('account.a_recv'):
|
||||
if l.account_id.id == self.ref("account.a_recv"):
|
||||
line_id = l
|
||||
break
|
||||
|
||||
@@ -193,35 +202,27 @@ class TestScenarioReconcile(common.SavepointCase):
|
||||
statement_line.process_reconciliation(
|
||||
[
|
||||
{
|
||||
'move_line': line_id,
|
||||
'credit': 1000.0,
|
||||
'debit': 0.0,
|
||||
'name': invoice.number,
|
||||
"move_line": line_id,
|
||||
"credit": 1000.0,
|
||||
"debit": 0.0,
|
||||
"name": invoice.number,
|
||||
}
|
||||
]
|
||||
)
|
||||
# unreconcile journal item created by previous reconciliation
|
||||
lines_to_unreconcile = self.acc_move_line_obj.search(
|
||||
[('reconciled', '=', True),
|
||||
('statement_id', '=', statement.id)]
|
||||
[("reconciled", "=", True), ("statement_id", "=", statement.id)]
|
||||
)
|
||||
lines_to_unreconcile.remove_move_reconcile()
|
||||
|
||||
# create the mass reconcile record
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
'name': 'mass_reconcile_1',
|
||||
'account': self.ref('account.a_recv'),
|
||||
'reconcile_method': [
|
||||
(0, 0, {
|
||||
'name': 'mass.reconcile.simple.partner',
|
||||
})
|
||||
]
|
||||
"name": "mass_reconcile_1",
|
||||
"account": self.ref("account.a_recv"),
|
||||
"reconcile_method": [(0, 0, {"name": "mass.reconcile.simple.partner"})],
|
||||
}
|
||||
)
|
||||
# call the automatic reconcilation method
|
||||
mass_rec.run_reconcile()
|
||||
self.assertEqual(
|
||||
'paid',
|
||||
invoice.state
|
||||
)
|
||||
self.assertEqual("paid", invoice.state)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- account.mass.reconcile view -->
|
||||
<record id="account_mass_reconcile_form" model="ir.ui.view">
|
||||
@@ -8,87 +8,136 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Automatic Mass Reconcile" version="7.0">
|
||||
<header>
|
||||
<button name="run_reconcile" class="oe_highlight"
|
||||
string="Start Auto Reconciliation" type="object"/>
|
||||
<button icon="fa-share" name="last_history_reconcile"
|
||||
<button
|
||||
name="run_reconcile"
|
||||
class="oe_highlight"
|
||||
string="Start Auto Reconciliation"
|
||||
type="object"
|
||||
/>
|
||||
<button
|
||||
icon="fa-share"
|
||||
name="last_history_reconcile"
|
||||
string="Display items reconciled on the last run"
|
||||
type="object"/>
|
||||
type="object"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<separator colspan="4" string="Profile Information" />
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" select="1"/>
|
||||
<field name="account"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="name" select="1" />
|
||||
<field name="account" />
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="unreconciled_count"/>
|
||||
<button icon="fa-share" name="open_unreconcile"
|
||||
string="Go to unreconciled items" type="object"/>
|
||||
<field name="unreconciled_count" />
|
||||
<button
|
||||
icon="fa-share"
|
||||
name="open_unreconcile"
|
||||
string="Go to unreconciled items"
|
||||
type="object"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page name="methods" string="Configuration">
|
||||
<field name="reconcile_method" colspan = "4" nolabel="1"/>
|
||||
<field name="reconcile_method" colspan="4" nolabel="1" />
|
||||
</page>
|
||||
<page name="history" string="History">
|
||||
<field name="history_ids" nolabel="1">
|
||||
<tree string="Automatic Mass Reconcile History">
|
||||
<field name="date"/>
|
||||
<button icon="fa-share" name="open_reconcile"
|
||||
string="Go to reconciled items" type="object"/>
|
||||
<field name="date" />
|
||||
<button
|
||||
icon="fa-share"
|
||||
name="open_reconcile"
|
||||
string="Go to reconciled items"
|
||||
type="object"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page name="information" string="Information">
|
||||
<separator colspan="4" string="Simple. Amount and Name"/>
|
||||
<label for="reconcile_method" string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same name to be reconciled." colspan="4"/>
|
||||
|
||||
<separator colspan="4" string="Simple. Amount and Partner"/>
|
||||
<label for="reconcile_method" string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same partner to be reconciled." colspan="4"/>
|
||||
|
||||
<separator colspan="4" string="Simple. Amount and Reference"/>
|
||||
<label for="reconcile_method" string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same reference to be reconciled." colspan="4"/>
|
||||
<group colspan="2" col="2">
|
||||
<separator colspan="4" string="Advanced. Partner and Ref"/>
|
||||
<label for="reconcile_method" string="Match multiple debit vs multiple credit entries. Allow partial reconciliation.
|
||||
The lines should have the same partner, and the credit entry ref. is matched with the debit entry ref. or name." colspan="4"/>
|
||||
</group>
|
||||
<separator colspan="4" string="Simple. Amount and Name" />
|
||||
<label
|
||||
for="reconcile_method"
|
||||
string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same name to be reconciled."
|
||||
colspan="4"
|
||||
/>
|
||||
<separator
|
||||
colspan="4"
|
||||
string="Simple. Amount and Partner"
|
||||
/>
|
||||
<label
|
||||
for="reconcile_method"
|
||||
string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same partner to be reconciled."
|
||||
colspan="4"
|
||||
/>
|
||||
<separator
|
||||
colspan="4"
|
||||
string="Simple. Amount and Reference"
|
||||
/>
|
||||
<label
|
||||
for="reconcile_method"
|
||||
string="Match one debit line vs one credit line. Do not allow partial reconciliation.
|
||||
The lines should have the same amount (with the write-off) and the same reference to be reconciled."
|
||||
colspan="4"
|
||||
/>
|
||||
<group colspan="2" col="2">
|
||||
<separator
|
||||
colspan="4"
|
||||
string="Advanced. Partner and Ref"
|
||||
/>
|
||||
<label
|
||||
for="reconcile_method"
|
||||
string="Match multiple debit vs multiple credit entries. Allow partial reconciliation.
|
||||
The lines should have the same partner, and the credit entry ref. is matched with the debit entry ref. or name."
|
||||
colspan="4"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account_mass_reconcile_tree" model="ir.ui.view">
|
||||
<field name="name">account.mass.reconcile.tree</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.mass.reconcile</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Automatic Mass Reconcile">
|
||||
<field name="name"/>
|
||||
<field name="account"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="unreconciled_count"/>
|
||||
<button icon="fa-cogs" name="run_reconcile" colspan="4"
|
||||
string="Start Auto Reconcilation" type="object"/>
|
||||
<button icon="fa-share" name="last_history_reconcile" colspan="2"
|
||||
string="Display items reconciled on the last run" type="object"/>
|
||||
<field name="name" />
|
||||
<field name="account" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<field name="unreconciled_count" />
|
||||
<button
|
||||
icon="fa-cogs"
|
||||
name="run_reconcile"
|
||||
colspan="4"
|
||||
string="Start Auto Reconcilation"
|
||||
type="object"
|
||||
/>
|
||||
<button
|
||||
icon="fa-share"
|
||||
name="last_history_reconcile"
|
||||
colspan="2"
|
||||
string="Display items reconciled on the last run"
|
||||
type="object"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_account_mass_reconcile" model="ir.actions.act_window">
|
||||
<field name="name">Mass Automatic Reconcile</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -96,9 +145,10 @@ The lines should have the same partner, and the credit entry ref. is matched wit
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a reconciliation profile.
|
||||
</p><p>
|
||||
</p>
|
||||
<p>
|
||||
A reconciliation profile specifies, for one account, how
|
||||
the entries should be reconciled.
|
||||
You can select one or many reconciliation methods which will
|
||||
@@ -106,50 +156,58 @@ The lines should have the same partner, and the credit entry ref. is matched wit
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<!-- account.mass.reconcile.method view -->
|
||||
|
||||
<record id="account_mass_reconcile_method_tree" model="ir.ui.view">
|
||||
<field name="name">account.mass.reconcile.method.tree</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="model">account.mass.reconcile.method</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="top" string="Automatic Mass Reconcile Method">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="write_off"/>
|
||||
<field name="account_lost_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="account_profit_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="income_exchange_account_id" groups="base.group_multi_currency"/>
|
||||
<field name="expense_exchange_account_id" groups="base.group_multi_currency"/>
|
||||
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}"/>
|
||||
<field name="date_base_on"/>
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="name" />
|
||||
<field name="write_off" />
|
||||
<field
|
||||
name="account_lost_id"
|
||||
attrs="{'required':[('write_off','>',0)]}"
|
||||
/>
|
||||
<field
|
||||
name="account_profit_id"
|
||||
attrs="{'required':[('write_off','>',0)]}"
|
||||
/>
|
||||
<field
|
||||
name="income_exchange_account_id"
|
||||
groups="base.group_multi_currency"
|
||||
/>
|
||||
<field
|
||||
name="expense_exchange_account_id"
|
||||
groups="base.group_multi_currency"
|
||||
/>
|
||||
<field name="journal_id" attrs="{'required':[('write_off','>',0)]}" />
|
||||
<field name="date_base_on" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- menu item -->
|
||||
|
||||
<menuitem action="action_account_mass_reconcile"
|
||||
id="menu_mass_reconcile" sequence="30"
|
||||
parent="account.menu_finance_entries_actions"/>
|
||||
|
||||
|
||||
<data noupdate="1">
|
||||
|
||||
<record forcecreate="True" id="ir_cron_run_reconciliations" model="ir.cron">
|
||||
<field name="name">Do Automatic Reconciliations</field>
|
||||
<field name="model_id" ref="account_mass_reconcile.model_account_mass_reconcile"/>
|
||||
<field name="active" eval="False"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">3</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="code">model.run_scheduler()</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
||||
<menuitem
|
||||
action="action_account_mass_reconcile"
|
||||
id="menu_mass_reconcile"
|
||||
sequence="30"
|
||||
parent="account.menu_finance_entries_actions"
|
||||
/>
|
||||
<data noupdate="1">
|
||||
<record forcecreate="True" id="ir_cron_run_reconciliations" model="ir.cron">
|
||||
<field name="name">Do Automatic Reconciliations</field>
|
||||
<field
|
||||
name="model_id"
|
||||
ref="account_mass_reconcile.model_account_mass_reconcile"
|
||||
/>
|
||||
<field name="active" eval="False" />
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_number">3</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="code">model.run_scheduler()</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,73 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_mass_reconcile_history_search" model="ir.ui.view">
|
||||
<field name="name">mass.reconcile.history.search</field>
|
||||
<field name="model">mass.reconcile.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Automatic Mass Reconcile History">
|
||||
<filter icon="terp-go-today" name="Today"
|
||||
<filter
|
||||
icon="terp-go-today"
|
||||
name="Today"
|
||||
domain="[('date','<', time.strftime('%%Y-%%m-%%d 23:59:59')), ('date','>=', time.strftime('%%Y-%%m-%%d 00:00:00'))]"
|
||||
help="Todays' Reconcilations" />
|
||||
<filter icon="terp-go-week" name="7 Days"
|
||||
help="Todays' Reconcilations"
|
||||
/>
|
||||
<filter
|
||||
icon="terp-go-week"
|
||||
name="7 Days"
|
||||
help="Reconciliations of last 7 days"
|
||||
domain="[('date','<', time.strftime('%%Y-%%m-%%d 23:59:59')),('date','>=',(datetime.date.today()-datetime.timedelta(days=7)).strftime('%%Y-%%m-%%d 00:00:00'))]"
|
||||
/>
|
||||
|
||||
<separator orientation="vertical"/>
|
||||
<field name="mass_reconcile_id"/>
|
||||
<field name="date"/>
|
||||
|
||||
<newline/>
|
||||
<separator orientation="vertical" />
|
||||
<field name="mass_reconcile_id" />
|
||||
<field name="date" />
|
||||
<newline />
|
||||
<group expand="0" string="Group By...">
|
||||
<filter name="Reconciliation Profile"
|
||||
<filter
|
||||
name="Reconciliation Profile"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
domain="[]" context="{'group_by': 'mass_reconcile_id'}"/>
|
||||
<filter name="Date" icon="terp-go-month" domain="[]"
|
||||
context="{'group_by': 'date'}"/>
|
||||
domain="[]"
|
||||
context="{'group_by': 'mass_reconcile_id'}"
|
||||
/>
|
||||
<filter
|
||||
name="Date"
|
||||
icon="terp-go-month"
|
||||
domain="[]"
|
||||
context="{'group_by': 'date'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_reconcile_history_form" model="ir.ui.view">
|
||||
<field name="name">mass.reconcile.history.form</field>
|
||||
<field name="model">mass.reconcile.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Automatic Mass Reconcile History" version="7.0">
|
||||
<header>
|
||||
<button name="open_reconcile"
|
||||
<button
|
||||
name="open_reconcile"
|
||||
string="Go to reconciled items"
|
||||
icon="fa-share" type="object"/>
|
||||
icon="fa-share"
|
||||
type="object"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="mass_reconcile_id"/>
|
||||
<field name="date"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="mass_reconcile_id" />
|
||||
<field name="date" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
</group>
|
||||
<group col="2">
|
||||
<separator colspan="2" string="Reconciliations"/>
|
||||
<field name="reconcile_ids" nolabel="1"/>
|
||||
<separator colspan="2" string="Reconciliations" />
|
||||
<field name="reconcile_ids" nolabel="1" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_reconcile_history_tree" model="ir.ui.view">
|
||||
<field name="name">mass.reconcile.history.tree</field>
|
||||
<field name="model">mass.reconcile.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Automatic Mass Reconcile History">
|
||||
<field name="mass_reconcile_id"/>
|
||||
<field name="date"/>
|
||||
<button icon="fa-share" name="open_reconcile"
|
||||
string="Go to reconciled items" type="object"/>
|
||||
<field name="mass_reconcile_id" />
|
||||
<field name="date" />
|
||||
<button
|
||||
icon="fa-share"
|
||||
name="open_reconcile"
|
||||
string="Go to reconciled items"
|
||||
type="object"
|
||||
/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_mass_reconcile_history" model="ir.actions.act_window">
|
||||
<field name="name">Mass Automatic Reconcile History</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -75,13 +88,12 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_mass_reconcile_id': [active_id], 'default_mass_reconcile_id': active_id}"
|
||||
id="act_mass_reconcile_to_history"
|
||||
name="History Details"
|
||||
groups=""
|
||||
res_model="mass.reconcile.history"
|
||||
src_model="account.mass.reconcile"/>
|
||||
|
||||
context="{'search_default_mass_reconcile_id': [active_id], 'default_mass_reconcile_id': active_id}"
|
||||
id="act_mass_reconcile_to_history"
|
||||
name="History Details"
|
||||
groups=""
|
||||
res_model="mass.reconcile.history"
|
||||
binding_model="account.mass.reconcile"
|
||||
/>
|
||||
</odoo>
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_account_config" model="ir.ui.view">
|
||||
<field name="name">account settings</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='invoicing_settings']" position="after">
|
||||
<h2>Reconciliation</h2>
|
||||
<div class="row mt16 o_settings_container" id="reconciliation_settings">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="reconciliation_commit_every" string="Commit frequency"/>
|
||||
<div class="text-muted">
|
||||
<field name="name">account settings</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="account.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='invoicing_settings']" position="after">
|
||||
<h2>Reconciliation</h2>
|
||||
<div class="row mt16 o_settings_container" id="reconciliation_settings">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane" />
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
for="reconciliation_commit_every"
|
||||
string="Commit frequency"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
How often to commit when performing automatic reconciliation.
|
||||
Leave zero to commit only at the end of the process.
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<field name="reconciliation_commit_every" class="oe_inline"/>
|
||||
<div class="content-group">
|
||||
<field
|
||||
name="reconciliation_commit_every"
|
||||
class="oe_inline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
1
setup/account_mass_reconcile/odoo/addons/account_mass_reconcile
Symbolic link
1
setup/account_mass_reconcile/odoo/addons/account_mass_reconcile
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../account_mass_reconcile
|
||||
6
setup/account_mass_reconcile/setup.py
Normal file
6
setup/account_mass_reconcile/setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
Reference in New Issue
Block a user