Manage write off, some refactore to manage better currency, partial reconciliation, and add some tests
Also add an advanced reconciliation method based on name. Could be very useful to actually match account move line based on payment_reference field on invoices, as it is passed to account move line on name fieldpull/517/head
parent
b95dd38e50
commit
47108f791c
|
@ -14,13 +14,13 @@ Account Mass Reconcile
|
|||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/account-reconcile/tree/13.0/account_mass_reconcile
|
||||
:target: https://github.com/OCA/account-reconcile/tree/14.0/account_mass_reconcile
|
||||
:alt: OCA/account-reconcile
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/account-reconcile-13-0/account-reconcile-13-0-account_mass_reconcile
|
||||
:target: https://translation.odoo-community.org/projects/account-reconcile-14-0/account-reconcile-14-0-account_mass_reconcile
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/98/13.0
|
||||
:target: https://runbot.odoo-community.org/runbot/98/14.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
@ -62,7 +62,7 @@ Bug Tracker
|
|||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-reconcile/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/account-reconcile/issues/new?body=module:%20account_mass_reconcile%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
`feedback <https://github.com/OCA/account-reconcile/issues/new?body=module:%20account_mass_reconcile%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
|
@ -111,6 +111,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
|
|||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
This module is part of the `OCA/account-reconcile <https://github.com/OCA/account-reconcile/tree/13.0/account_mass_reconcile>`_ project on GitHub.
|
||||
This module is part of the `OCA/account-reconcile <https://github.com/OCA/account-reconcile/tree/14.0/account_mass_reconcile>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 13.0\n"
|
||||
"Project-Id-Version: Odoo Server 14.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
|
@ -13,6 +13,14 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: code:addons/account_mass_reconcile/models/mass_reconcile.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"A mass reconcile is already ongoing for this account, please try again "
|
||||
"later."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: code:addons/account_mass_reconcile/models/base_advanced_reconciliation.py:0
|
||||
#, python-format
|
||||
|
@ -33,6 +41,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__account
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple__account_id
|
||||
|
@ -45,6 +54,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__account_lost_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__account_lost_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__account_lost_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__account_lost_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__account_lost_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__account_lost_id
|
||||
|
@ -63,6 +73,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__account_profit_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__account_profit_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__account_profit_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__account_profit_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__account_profit_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__account_profit_id
|
||||
|
@ -113,6 +124,12 @@ msgstr ""
|
|||
msgid "Automatic Write Off"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: code:addons/account_mass_reconcile/models/base_reconciliation.py:0
|
||||
#, python-format
|
||||
msgid "Automatic writeoff"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model_terms:ir.actions.act_window,help:account_mass_reconcile.action_account_mass_reconcile
|
||||
msgid "Click to add a reconciliation profile."
|
||||
|
@ -148,6 +165,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__create_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__create_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__create_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__create_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__create_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__create_uid
|
||||
|
@ -159,6 +177,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__create_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__create_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__create_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__create_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__create_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__create_date
|
||||
|
@ -170,6 +189,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__date_base_on
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__date_base_on
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__date_base_on
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__date_base_on
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__date_base_on
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__date_base_on
|
||||
|
@ -184,6 +204,7 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__display_name
|
||||
|
@ -192,6 +213,8 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_partner__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_reference__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_company__display_name
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_config_settings__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -211,6 +234,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method___filter
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced___filter
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name___filter
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref___filter
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base___filter
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options___filter
|
||||
|
@ -241,19 +265,6 @@ msgstr ""
|
|||
msgid "Full Reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_partner__income_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_reference__income_exchange_account_id
|
||||
msgid "Gain Exchange Rate Account"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model_terms:ir.ui.view,arch_db:account_mass_reconcile.account_mass_reconcile_form
|
||||
#: model_terms:ir.ui.view,arch_db:account_mass_reconcile.mass_reconcile_history_form
|
||||
|
@ -277,11 +288,6 @@ msgstr ""
|
|||
msgid "History"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.actions.act_window,name:account_mass_reconcile.act_mass_reconcile_to_history
|
||||
msgid "History Details"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_company__reconciliation_commit_every
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_config_settings__reconciliation_commit_every
|
||||
|
@ -299,6 +305,7 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__id
|
||||
|
@ -307,6 +314,8 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_partner__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_reference__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_company__id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_config_settings__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
|
@ -335,6 +344,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__journal_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__journal_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__journal_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__journal_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__journal_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__journal_id
|
||||
|
@ -349,6 +359,7 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history____last_update
|
||||
|
@ -357,12 +368,15 @@ msgstr ""
|
|||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_partner____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_reference____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_company____last_update
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_res_config_settings____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__write_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__write_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__write_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__write_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__write_uid
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__write_uid
|
||||
|
@ -374,6 +388,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__write_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__write_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__write_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__write_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_history__write_date
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__write_date
|
||||
|
@ -393,19 +408,6 @@ msgstr ""
|
|||
msgid "Leave zero to commit only at the end of the process."
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_name__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_partner__expense_exchange_account_id
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple_reference__expense_exchange_account_id
|
||||
msgid "Loss Exchange Rate Account"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile__message_main_attachment_id
|
||||
msgid "Main Attachment"
|
||||
|
@ -427,6 +429,11 @@ msgstr ""
|
|||
msgid "Mass Reconcile Advanced"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model,name:account_mass_reconcile.model_mass_reconcile_advanced_name
|
||||
msgid "Mass Reconcile Advanced Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model,name:account_mass_reconcile.model_mass_reconcile_advanced_ref
|
||||
msgid "Mass Reconcile Advanced Ref"
|
||||
|
@ -578,6 +585,7 @@ msgstr ""
|
|||
|
||||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__partner_ids
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__partner_ids
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__partner_ids
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__partner_ids
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_simple__partner_ids
|
||||
|
@ -698,6 +706,7 @@ msgstr ""
|
|||
#. module: account_mass_reconcile
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_account_mass_reconcile_method__write_off
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced__write_off
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_name__write_off
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_advanced_ref__write_off
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_base__write_off
|
||||
#: model:ir.model.fields,field_description:account_mass_reconcile.field_mass_reconcile_options__write_off
|
||||
|
|
|
@ -110,3 +110,107 @@ class MassReconcileAdvancedRef(models.TransientModel):
|
|||
move_line["name"].lower().strip(),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class MassReconcileAdvancedName(models.TransientModel):
|
||||
|
||||
_name = "mass.reconcile.advanced.name"
|
||||
_inherit = "mass.reconcile.advanced"
|
||||
_description = "Mass Reconcile Advanced Name"
|
||||
|
||||
@staticmethod
|
||||
def _skip_line(move_line):
|
||||
"""
|
||||
When True is returned on some conditions, the credit move line
|
||||
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("name", "/") != "/" and move_line.get("partner_id"))
|
||||
|
||||
@staticmethod
|
||||
def _matchers(move_line):
|
||||
"""
|
||||
Return the values used as matchers to find the opposite lines
|
||||
|
||||
All the matcher keys in the dict must have their equivalent in
|
||||
the `_opposite_matchers`.
|
||||
|
||||
The values of each matcher key will be searched in the
|
||||
one returned by the `_opposite_matchers`
|
||||
|
||||
Must be inherited to implement the matchers for one method
|
||||
|
||||
For instance, it can return:
|
||||
return ('ref', move_line['rec'])
|
||||
|
||||
or
|
||||
return (('partner_id', move_line['partner_id']),
|
||||
('ref', "prefix_%s" % move_line['rec']))
|
||||
|
||||
All the matchers have to be found in the opposite lines
|
||||
to consider them as "opposite"
|
||||
|
||||
The matchers will be evaluated in the same order as declared
|
||||
vs the the opposite matchers, so you can gain performance by
|
||||
declaring first the partners with the less computation.
|
||||
|
||||
All matchers should match with their opposite to be considered
|
||||
as "matching".
|
||||
So with the previous example, partner_id and ref have to be
|
||||
equals on the opposite line matchers.
|
||||
|
||||
:return: tuple of tuples (key, value) where the keys are
|
||||
the matchers keys
|
||||
(must be the same than `_opposite_matchers` returns,
|
||||
and their values to match in the opposite lines.
|
||||
A matching key can have multiples values.
|
||||
"""
|
||||
return (
|
||||
("partner_id", move_line["partner_id"]),
|
||||
("name", move_line["name"].lower().strip()),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _opposite_matchers(move_line):
|
||||
"""
|
||||
Return the values of the opposite line used as matchers
|
||||
so the line is matched
|
||||
|
||||
Must be inherited to implement the matchers for one method
|
||||
It can be inherited to apply some formatting of fields
|
||||
(strip(), lower() and so on)
|
||||
|
||||
This method is the counterpart of the `_matchers()` method.
|
||||
|
||||
Each matcher has to yield its value respecting the order
|
||||
of the `_matchers()`.
|
||||
|
||||
When a matcher does not correspond, the next matchers won't
|
||||
be evaluated so the ones which need the less computation
|
||||
have to be executed first.
|
||||
|
||||
If the `_matchers()` returns:
|
||||
(('partner_id', move_line['partner_id']),
|
||||
('ref', move_line['ref']))
|
||||
|
||||
Here, you should yield :
|
||||
yield ('partner_id', move_line['partner_id'])
|
||||
yield ('ref', move_line['ref'])
|
||||
|
||||
Note that a matcher can contain multiple values, as instance,
|
||||
if for a move line, you want to search from its `ref` in the
|
||||
`ref` or `name` fields of the opposite move lines, you have to
|
||||
yield ('partner_id', move_line['partner_id'])
|
||||
yield ('ref', (move_line['ref'], move_line['name'])
|
||||
|
||||
An OR is used between the values for the same key.
|
||||
An AND is used between the differents keys.
|
||||
|
||||
: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 (
|
||||
"name",
|
||||
(move_line["name"].lower().strip(),),
|
||||
)
|
||||
|
|
|
@ -205,6 +205,7 @@ class MassReconcileAdvanced(models.AbstractModel):
|
|||
]
|
||||
|
||||
def _action_rec(self):
|
||||
self.flush()
|
||||
credit_lines = self._query_credit()
|
||||
debit_lines = self._query_debit()
|
||||
result = self._rec_auto_lines_advanced(credit_lines, debit_lines)
|
||||
|
@ -222,57 +223,51 @@ class MassReconcileAdvanced(models.AbstractModel):
|
|||
""" Advanced reconciliation main loop """
|
||||
# pylint: disable=invalid-commit
|
||||
reconciled_ids = []
|
||||
for rec in self:
|
||||
reconcile_groups = []
|
||||
ctx = self.env.context.copy()
|
||||
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)
|
||||
reconcile_groups = []
|
||||
_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)
|
||||
)
|
||||
if self._skip_line(credit_line):
|
||||
continue
|
||||
opposite_lines = self._search_opposites(credit_line, debit_lines)
|
||||
if not opposite_lines:
|
||||
continue
|
||||
opposite_ids = [line["id"] for line 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,
|
||||
)
|
||||
if self._skip_line(credit_line):
|
||||
continue
|
||||
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"]]
|
||||
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,
|
||||
)
|
||||
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 = {l["id"]: l for l in credit_lines + debit_lines}
|
||||
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 = {line["id"]: line for line 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
|
||||
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:
|
||||
self.env.cr.commit()
|
||||
_logger.info(
|
||||
"Commit the reconciliations after %d groups", group_count
|
||||
)
|
||||
if (
|
||||
self.env.context.get("commit_every", 0)
|
||||
and group_count % self.env.context["commit_every"] == 0
|
||||
):
|
||||
self.env.cr.commit()
|
||||
_logger.info("Commit the reconciliations after %d groups", group_count)
|
||||
_logger.info("Reconciliation is over")
|
||||
return reconciled_ids
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# Copyright 2010 Sébastien Beau
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from functools import reduce
|
||||
from operator import itemgetter
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
@ -46,6 +45,9 @@ class MassReconcileBase(models.AbstractModel):
|
|||
"id",
|
||||
"debit",
|
||||
"credit",
|
||||
"currency_id",
|
||||
"amount_residual",
|
||||
"amount_residual_currency",
|
||||
"date",
|
||||
"ref",
|
||||
"name",
|
||||
|
@ -69,7 +71,8 @@ class MassReconcileBase(models.AbstractModel):
|
|||
self.ensure_one()
|
||||
where = (
|
||||
"WHERE account_move_line.account_id = %s "
|
||||
"AND NOT account_move_line.reconciled"
|
||||
"AND NOT account_move_line.reconciled "
|
||||
"AND parent_state = 'posted'"
|
||||
)
|
||||
# it would be great to use dict for params
|
||||
# but as we use _where_calc in _get_filter
|
||||
|
@ -78,7 +81,7 @@ class MassReconcileBase(models.AbstractModel):
|
|||
params = [self.account_id.id]
|
||||
if self.partner_ids:
|
||||
where += " AND account_move_line.partner_id IN %s"
|
||||
params.append(tuple([l.id for l in self.partner_ids]))
|
||||
params.append(tuple([line.id for line in self.partner_ids]))
|
||||
return where, params
|
||||
|
||||
def _get_filter(self):
|
||||
|
@ -95,16 +98,29 @@ class MassReconcileBase(models.AbstractModel):
|
|||
def _below_writeoff_limit(self, lines, writeoff_limit):
|
||||
self.ensure_one()
|
||||
precision = self.env["decimal.precision"].precision_get("Account")
|
||||
keys = ("debit", "credit")
|
||||
sums = reduce(
|
||||
lambda line, memo: {
|
||||
key: value + memo[key] for key, value in line.items() if key in keys
|
||||
},
|
||||
lines,
|
||||
|
||||
writeoff_amount = round(
|
||||
sum([line["amount_residual"] for line in lines]), precision
|
||||
)
|
||||
writeoff_amount_curr = round(
|
||||
sum([line["amount_residual_currency"] for line in lines]), precision
|
||||
)
|
||||
|
||||
first_currency = lines[0]["currency_id"]
|
||||
if all([line["currency_id"] == first_currency for line in lines]):
|
||||
ref_amount = writeoff_amount_curr
|
||||
same_curr = True
|
||||
# TODO if currency != company currency compute writeoff_limit in currency
|
||||
else:
|
||||
ref_amount = writeoff_amount
|
||||
same_curr = False
|
||||
|
||||
return (
|
||||
bool(writeoff_limit >= abs(ref_amount)),
|
||||
writeoff_amount,
|
||||
writeoff_amount_curr,
|
||||
same_curr,
|
||||
)
|
||||
debit, credit = sums["debit"], sums["credit"]
|
||||
writeoff_amount = round(debit - credit, precision)
|
||||
return bool(writeoff_limit >= abs(writeoff_amount)), debit, credit
|
||||
|
||||
def _get_rec_date(self, lines, based_on="end_period_last_credit"):
|
||||
self.ensure_one()
|
||||
|
@ -113,10 +129,10 @@ class MassReconcileBase(models.AbstractModel):
|
|||
return max(mlines, key=itemgetter("date"))
|
||||
|
||||
def credit(mlines):
|
||||
return [l for l in mlines if l["credit"] > 0]
|
||||
return [line for line in mlines if line["credit"] > 0]
|
||||
|
||||
def debit(mlines):
|
||||
return [l for l in mlines if l["debit"] > 0]
|
||||
return [line for line in mlines if line["debit"] > 0]
|
||||
|
||||
if based_on == "newest":
|
||||
return last_date(lines)["date"]
|
||||
|
@ -128,11 +144,51 @@ class MassReconcileBase(models.AbstractModel):
|
|||
# when date is None
|
||||
return None
|
||||
|
||||
def create_write_off(self, lines, amount, amount_curr, same_curr):
|
||||
self.ensure_one()
|
||||
if amount < 0:
|
||||
account = self.account_profit_id
|
||||
else:
|
||||
account = self.account_lost_id
|
||||
currency = same_curr and lines[0].currency_id or lines[0].company_id.currency_id
|
||||
journal = self.journal_id
|
||||
partners = lines.mapped("partner_id")
|
||||
write_off_vals = {
|
||||
"name": _("Automatic writeoff"),
|
||||
"amount_currency": same_curr and amount_curr or amount,
|
||||
"debit": amount > 0.0 and amount or 0.0,
|
||||
"credit": amount < 0.0 and -amount or 0.0,
|
||||
"partner_id": len(partners) == 1 and partners.id or False,
|
||||
"account_id": account.id,
|
||||
"journal_id": journal.id,
|
||||
"currency_id": currency.id,
|
||||
}
|
||||
counterpart_account = lines.mapped("account_id")
|
||||
counter_part = write_off_vals.copy()
|
||||
counter_part["debit"] = write_off_vals["credit"]
|
||||
counter_part["credit"] = write_off_vals["debit"]
|
||||
counter_part["amount_currency"] = -write_off_vals["amount_currency"]
|
||||
counter_part["account_id"] = (counterpart_account.id,)
|
||||
|
||||
move = self.env["account.move"].create(
|
||||
{
|
||||
"date": lines.env.context.get("date_p"),
|
||||
"journal_id": journal.id,
|
||||
"currency_id": currency.id,
|
||||
"line_ids": [(0, 0, write_off_vals), (0, 0, counter_part)],
|
||||
}
|
||||
)
|
||||
move.action_post()
|
||||
return move.line_ids.filtered(
|
||||
lambda l: l.account_id.id == counterpart_account.id
|
||||
)
|
||||
|
||||
def _reconcile_lines(self, lines, allow_partial=False):
|
||||
"""Try to reconcile given lines
|
||||
|
||||
:param list lines: list of dict of move lines, they must at least
|
||||
contain values for : id, debit, credit
|
||||
contain values for : id, debit, credit, amount_residual and
|
||||
amount_residual_currency
|
||||
:param boolean allow_partial: if True, partial reconciliation will be
|
||||
created, otherwise only Full
|
||||
reconciliation will be created
|
||||
|
@ -143,36 +199,26 @@ class MassReconcileBase(models.AbstractModel):
|
|||
"""
|
||||
self.ensure_one()
|
||||
ml_obj = self.env["account.move.line"]
|
||||
below_writeoff, sum_debit, sum_credit = self._below_writeoff_limit(
|
||||
lines, self.write_off
|
||||
)
|
||||
(
|
||||
below_writeoff,
|
||||
amount_writeoff,
|
||||
amount_writeoff_curr,
|
||||
same_curr,
|
||||
) = 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(
|
||||
line_rs = ml_obj.browse([line["id"] for line 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
|
||||
)
|
||||
balance = amount_writeoff_curr if same_curr else amount_writeoff
|
||||
if abs(balance) != 0.0:
|
||||
writeoff_line = self.create_write_off(
|
||||
line_rs, amount_writeoff, amount_writeoff_curr, same_curr
|
||||
)
|
||||
line_rs |= writeoff_line
|
||||
line_rs.reconcile()
|
||||
return True, True
|
||||
elif allow_partial:
|
||||
# We need to give a writeoff_acc_id
|
||||
# in case we have a multi currency lines
|
||||
# to reconcile.
|
||||
# If amount in currency is equal between
|
||||
# lines to reconcile
|
||||
# it will do a full reconcile instead of a partial reconcile
|
||||
# and make a write-off for exchange
|
||||
if sum_credit > sum_debit:
|
||||
writeoff_account = self.income_exchange_account_id
|
||||
else:
|
||||
writeoff_account = self.expense_exchange_account_id
|
||||
line_rs.reconcile(
|
||||
writeoff_acc_id=writeoff_account, writeoff_journal_id=self.journal_id
|
||||
)
|
||||
line_rs.reconcile()
|
||||
return True, False
|
||||
return False, False
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extensions import AsIs
|
||||
|
||||
from odoo import _, api, fields, models, sql_db
|
||||
from odoo import _, api, exceptions, fields, models, sql_db
|
||||
from odoo.exceptions import Warning as UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
@ -40,12 +41,6 @@ class MassReconcileOptions(models.AbstractModel):
|
|||
default="newest",
|
||||
)
|
||||
_filter = fields.Char(string="Filter")
|
||||
income_exchange_account_id = fields.Many2one(
|
||||
"account.account", string="Gain Exchange Rate Account"
|
||||
)
|
||||
expense_exchange_account_id = fields.Many2one(
|
||||
"account.account", string="Loss Exchange Rate Account"
|
||||
)
|
||||
|
||||
|
||||
class AccountMassReconcileMethod(models.Model):
|
||||
|
@ -61,6 +56,7 @@ class AccountMassReconcileMethod(models.Model):
|
|||
("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.advanced.name", "Advanced. Partner and Name."),
|
||||
]
|
||||
|
||||
def _selection_name(self):
|
||||
|
@ -136,13 +132,16 @@ class AccountMassReconcile(models.Model):
|
|||
"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,
|
||||
}
|
||||
|
||||
def _run_reconcile_method(self, reconcile_method):
|
||||
rec_model = self.env[reconcile_method.name]
|
||||
auto_rec_id = rec_model.create(self._prepare_run_transient(reconcile_method))
|
||||
return auto_rec_id.automatic_reconcile()
|
||||
|
||||
def run_reconcile(self):
|
||||
def find_reconcile_ids(fieldname, move_line_ids):
|
||||
if not move_line_ids:
|
||||
|
@ -163,21 +162,36 @@ class AccountMassReconcile(models.Model):
|
|||
# does not.
|
||||
|
||||
for rec in self:
|
||||
# SELECT FOR UPDATE the mass reconcile row ; this is done in order
|
||||
# to avoid 2 processes on the same mass reconcile method.
|
||||
try:
|
||||
self.env.cr.execute(
|
||||
"SELECT id FROM account_mass_reconcile"
|
||||
" WHERE id = %s"
|
||||
" FOR UPDATE NOWAIT",
|
||||
(rec.id,),
|
||||
)
|
||||
except psycopg2.OperationalError:
|
||||
raise exceptions.UserError(
|
||||
_(
|
||||
"A mass reconcile is already ongoing for this account, "
|
||||
"please try again later."
|
||||
)
|
||||
)
|
||||
ctx = self.env.context.copy()
|
||||
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()
|
||||
new_env = api.Environment(new_cr, self.env.uid, ctx)
|
||||
else:
|
||||
new_cr = self.env.cr
|
||||
new_env = self.env
|
||||
|
||||
try:
|
||||
all_ml_rec_ids = []
|
||||
|
||||
for method in rec.reconcile_method:
|
||||
rec_model = self.env[method.name]
|
||||
auto_rec_id = rec_model.create(self._prepare_run_transient(method))
|
||||
|
||||
ml_rec_ids = auto_rec_id.automatic_reconcile()
|
||||
ml_rec_ids = self.with_env(new_env)._run_reconcile_method(method)
|
||||
|
||||
all_ml_rec_ids += ml_rec_ids
|
||||
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
# Copyright 2010 Sébastien Beau
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MassReconcileSimple(models.AbstractModel):
|
||||
_name = "mass.reconcile.simple"
|
||||
|
@ -40,6 +44,15 @@ class MassReconcileSimple(models.AbstractModel):
|
|||
if reconciled:
|
||||
res += [credit_line["id"], debit_line["id"]]
|
||||
del lines[i]
|
||||
if (
|
||||
self.env.context.get("commit_every", 0)
|
||||
and len(res) % self.env.context["commit_every"] == 0
|
||||
):
|
||||
# new cursor is already open in cron
|
||||
self.env.cr.commit() # pylint: disable=invalid-commit
|
||||
_logger.info(
|
||||
"Commit the reconciliations after %d groups", len(res)
|
||||
)
|
||||
break
|
||||
count += 1
|
||||
return res
|
||||
|
@ -58,7 +71,7 @@ class MassReconcileSimple(models.AbstractModel):
|
|||
query = " ".join(
|
||||
(select, self._from_query(), where, where2, self._simple_order())
|
||||
)
|
||||
|
||||
self.flush()
|
||||
self.env.cr.execute(query, params + params2)
|
||||
lines = self.env.cr.dictfetchall()
|
||||
return self.rec_auto_lines_simple(lines)
|
||||
|
|
|
@ -10,3 +10,5 @@ access_mass_reconcile_history_acc_mgr,mass.reconcile.history,model_mass_reconcil
|
|||
access_mass_reconcile_simple_name,mass.reconcile.simple.name,model_mass_reconcile_simple_name,account.group_account_user,1,1,1,1
|
||||
access_mass_reconcile_simple_partner,mass.reconcile.simple.partner,model_mass_reconcile_simple_partner,account.group_account_user,1,1,1,1
|
||||
access_mass_reconcile_simple_reference,mass.reconcile.simple.reference,model_mass_reconcile_simple_reference,account.group_account_user,1,1,1,1
|
||||
access_mass_reconcile_advanced_ref_acc_user,mass.reconcile.advanced.ref,model_mass_reconcile_advanced_ref,account.group_account_user,1,1,1,1
|
||||
access_mass_reconcile_advanced_name_acc_user,mass.reconcile.advanced.name,model_mass_reconcile_advanced_name,account.group_account_user,1,1,1,1
|
||||
|
|
|
|
@ -367,7 +367,7 @@ ul.auto-toc {
|
|||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/account-reconcile/tree/13.0/account_mass_reconcile"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/account-reconcile-13-0/account-reconcile-13-0-account_mass_reconcile"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/98/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/account-reconcile/tree/14.0/account_mass_reconcile"><img alt="OCA/account-reconcile" src="https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/account-reconcile-14-0/account-reconcile-14-0-account_mass_reconcile"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/98/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This is a shared work between Akretion and Camptocamp
|
||||
in order to provide:</p>
|
||||
<ul class="simple">
|
||||
|
@ -410,7 +410,7 @@ reconcile.</p>
|
|||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-reconcile/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/account-reconcile/issues/new?body=module:%20account_mass_reconcile%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<a class="reference external" href="https://github.com/OCA/account-reconcile/issues/new?body=module:%20account_mass_reconcile%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
|
@ -453,7 +453,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-reconcile/tree/13.0/account_mass_reconcile">OCA/account-reconcile</a> project on GitHub.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-reconcile/tree/14.0/account_mass_reconcile">OCA/account-reconcile</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,5 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_onchange_company
|
||||
from . import test_reconcile_history
|
||||
from . import test_reconcile
|
||||
from . import test_scenario_reconcile
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
|
@ -10,15 +8,6 @@ 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"]
|
||||
cls.acc_setting_obj = acc_setting.create({})
|
||||
cls.company_obj = cls.env["res.company"]
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import exceptions, fields, tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
import odoo.tests
|
||||
from odoo import exceptions, fields
|
||||
|
||||
from odoo.addons.account.tests.common import TestAccountReconciliationCommon
|
||||
|
||||
|
||||
class TestReconcile(common.SavepointCase):
|
||||
@odoo.tests.tagged("post_install", "-at_install")
|
||||
class TestReconcile(TestAccountReconciliationCommon):
|
||||
@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"]
|
||||
|
||||
cls.sale_journal = cls.company_data["default_journal_sale"]
|
||||
cls.mass_rec = cls.mass_rec_obj.create(
|
||||
{"name": "AER2", "account": cls.env.ref("account.a_salary_expense").id}
|
||||
{"name": "Sale Account", "account": cls.sale_journal.default_account_id.id}
|
||||
)
|
||||
cls.mass_rec_method = cls.mass_rec_method_obj.create(
|
||||
{
|
||||
|
@ -33,7 +28,7 @@ class TestReconcile(common.SavepointCase):
|
|||
}
|
||||
)
|
||||
cls.mass_rec_no_history = cls.mass_rec_obj.create(
|
||||
{"name": "AER3", "account": cls.env.ref("account.a_salary_expense").id}
|
||||
{"name": "AER3", "account": cls.sale_journal.default_account_id.id}
|
||||
)
|
||||
cls.rec_history = cls.rec_history_obj.create(
|
||||
{"mass_reconcile_id": cls.mass_rec.id, "date": fields.Datetime.now()}
|
||||
|
@ -57,4 +52,14 @@ class TestReconcile(common.SavepointCase):
|
|||
|
||||
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.sale_journal.default_account_id.id, res.get("account_id", 0)
|
||||
)
|
||||
|
||||
def test_open_full_empty(self):
|
||||
res = self.rec_history._open_move_lines()
|
||||
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", []))
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
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"]
|
||||
cls.mass_rec = cls.mass_rec_obj.create(
|
||||
{"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()}
|
||||
)
|
||||
|
||||
def test_open_full_empty(self):
|
||||
res = self.rec_history._open_move_lines()
|
||||
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", []))
|
|
@ -1,24 +1,19 @@
|
|||
# © 2014-2016 Camptocamp SA (Damien Crier)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, tools
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests import common
|
||||
from datetime import timedelta
|
||||
|
||||
import odoo.tests
|
||||
from odoo import fields
|
||||
|
||||
from odoo.addons.account.tests.common import TestAccountReconciliationCommon
|
||||
|
||||
|
||||
class TestScenarioReconcile(common.SavepointCase):
|
||||
@odoo.tests.tagged("post_install", "-at_install")
|
||||
class TestScenarioReconcile(TestAccountReconciliationCommon):
|
||||
@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.move"]
|
||||
|
@ -26,13 +21,12 @@ class TestScenarioReconcile(common.SavepointCase):
|
|||
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"]
|
||||
|
||||
acs_ids = cls.acs_model.search(
|
||||
[("company_id", "=", cls.env.ref("base.main_company").id)]
|
||||
)
|
||||
cls.company = cls.company_data["company"]
|
||||
cls.bank_journal = cls.company_data["default_journal_bank"]
|
||||
cls.sale_journal = cls.company_data["default_journal_sale"]
|
||||
acs_ids = cls.acs_model.search([("company_id", "=", cls.company.id)])
|
||||
|
||||
values = {"group_multi_currency": True}
|
||||
|
||||
|
@ -44,185 +38,193 @@ class TestScenarioReconcile(common.SavepointCase):
|
|||
acs_ids = cls.acs_model.create(default_vals)
|
||||
|
||||
def test_scenario_reconcile(self):
|
||||
# create invoice
|
||||
invoice = self.invoice_obj.with_context(default_type="out_invoice").create(
|
||||
{
|
||||
"type": "out_invoice",
|
||||
"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": "[FURN_7800] Desk Combination",
|
||||
"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.post()
|
||||
invoice = self.create_invoice()
|
||||
self.assertEqual("posted", invoice.state)
|
||||
|
||||
# create bank_statement
|
||||
statement = self.bk_stmt_obj.create(
|
||||
receivalble_account_id = invoice.partner_id.property_account_receivable_id.id
|
||||
# create payment
|
||||
payment = self.env["account.payment"].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.name,
|
||||
"ref": invoice.name,
|
||||
},
|
||||
)
|
||||
],
|
||||
"partner_type": "customer",
|
||||
"payment_type": "inbound",
|
||||
"partner_id": invoice.partner_id.id,
|
||||
"destination_account_id": receivalble_account_id,
|
||||
"amount": 50.0,
|
||||
"journal_id": self.bank_journal.id,
|
||||
}
|
||||
)
|
||||
|
||||
# reconcile
|
||||
line_id = None
|
||||
for l in invoice.line_ids:
|
||||
if l.account_id.internal_type == "receivable":
|
||||
line_id = l
|
||||
break
|
||||
|
||||
for statement_line in statement.line_ids:
|
||||
statement_line.process_reconciliation(
|
||||
[
|
||||
{
|
||||
"move_line": line_id,
|
||||
"credit": 1000.0,
|
||||
"debit": 0.0,
|
||||
"name": invoice.name,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
# unreconcile journal item created by previous reconciliation
|
||||
lines_to_unreconcile = self.acc_move_line_obj.search(
|
||||
[("reconciled", "=", True), ("statement_id", "=", statement.id)]
|
||||
)
|
||||
lines_to_unreconcile.remove_move_reconcile()
|
||||
payment.action_post()
|
||||
|
||||
# create the mass reconcile record
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
"name": "mass_reconcile_1",
|
||||
"account": line_id.account_id.id,
|
||||
"account": invoice.partner_id.property_account_receivable_id.id,
|
||||
"reconcile_method": [(0, 0, {"name": "mass.reconcile.simple.partner"})],
|
||||
}
|
||||
)
|
||||
# call the automatic reconcilation method
|
||||
mass_rec.run_reconcile()
|
||||
invoice.invalidate_cache()
|
||||
self.assertEqual("paid", invoice.invoice_payment_state)
|
||||
self.assertEqual("paid", invoice.payment_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",
|
||||
"name": fields.Date.today(),
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"rate": 1.5,
|
||||
"rate": 1.25,
|
||||
}
|
||||
)
|
||||
# create invoice
|
||||
invoice = self.invoice_obj.with_context(default_type="out_invoice").create(
|
||||
{
|
||||
"type": "out_invoice",
|
||||
"company_id": self.ref("base.main_company"),
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"journal_id": self.ref("account.sales_journal"),
|
||||
"partner_id": self.ref("base.res_partner_12"),
|
||||
"invoice_line_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "[FURN_7800] Desk Combination",
|
||||
"account_id": self.ref("account.a_sale"),
|
||||
"price_unit": 1000.0,
|
||||
"quantity": 1.0,
|
||||
"product_id": self.ref("product.product_product_3"),
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
invoice = self._create_invoice(
|
||||
currency_id=self.ref("base.USD"),
|
||||
date_invoice=fields.Date.today(),
|
||||
auto_validate=True,
|
||||
)
|
||||
# validate invoice
|
||||
invoice.post()
|
||||
self.assertEqual("posted", invoice.state)
|
||||
|
||||
# create bank_statement
|
||||
statement = self.bk_stmt_obj.create(
|
||||
self.env["res.currency.rate"].create(
|
||||
{
|
||||
"balance_end_real": 0.0,
|
||||
"balance_start": 0.0,
|
||||
"date": fields.Date.today(),
|
||||
"journal_id": self.ref("account.bank_journal_usd"),
|
||||
"name": fields.Date.today() - timedelta(days=3),
|
||||
"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.name,
|
||||
"ref": invoice.name,
|
||||
},
|
||||
)
|
||||
],
|
||||
"rate": 2,
|
||||
}
|
||||
)
|
||||
|
||||
# reconcile
|
||||
line_id = None
|
||||
for l in invoice.line_ids:
|
||||
if l.account_id.internal_type == "receivable":
|
||||
line_id = l
|
||||
break
|
||||
|
||||
for statement_line in statement.line_ids:
|
||||
statement_line.process_reconciliation(
|
||||
[
|
||||
{
|
||||
"move_line": line_id,
|
||||
"credit": 1000.0,
|
||||
"debit": 0.0,
|
||||
"name": invoice.name,
|
||||
}
|
||||
]
|
||||
)
|
||||
# unreconcile journal item created by previous reconciliation
|
||||
lines_to_unreconcile = self.acc_move_line_obj.search(
|
||||
[("reconciled", "=", True), ("statement_id", "=", statement.id)]
|
||||
receivable_account_id = invoice.partner_id.property_account_receivable_id.id
|
||||
# create payment
|
||||
payment = self.env["account.payment"].create(
|
||||
{
|
||||
"partner_type": "customer",
|
||||
"payment_type": "inbound",
|
||||
"partner_id": invoice.partner_id.id,
|
||||
"destination_account_id": receivable_account_id,
|
||||
"amount": 50.0,
|
||||
"currency_id": self.ref("base.USD"),
|
||||
"journal_id": self.bank_journal.id,
|
||||
"date": fields.Date.today() - timedelta(days=2),
|
||||
}
|
||||
)
|
||||
lines_to_unreconcile.remove_move_reconcile()
|
||||
payment.action_post()
|
||||
|
||||
# create the mass reconcile record
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
"name": "mass_reconcile_1",
|
||||
"account": line_id.account_id.id,
|
||||
"account": invoice.partner_id.property_account_receivable_id.id,
|
||||
"reconcile_method": [(0, 0, {"name": "mass.reconcile.simple.partner"})],
|
||||
}
|
||||
)
|
||||
# call the automatic reconcilation method
|
||||
mass_rec.run_reconcile()
|
||||
invoice.invalidate_cache()
|
||||
self.assertEqual("paid", invoice.invoice_payment_state)
|
||||
self.assertEqual("paid", invoice.payment_state)
|
||||
|
||||
def test_scenario_reconcile_partial(self):
|
||||
invoice1 = self.create_invoice()
|
||||
invoice1.ref = "test ref"
|
||||
# create payment
|
||||
receivable_account_id = invoice1.partner_id.property_account_receivable_id.id
|
||||
payment = self.env["account.payment"].create(
|
||||
{
|
||||
"partner_type": "customer",
|
||||
"payment_type": "inbound",
|
||||
"partner_id": invoice1.partner_id.id,
|
||||
"destination_account_id": receivable_account_id,
|
||||
"amount": 500.0,
|
||||
"journal_id": self.bank_journal.id,
|
||||
"ref": "test ref",
|
||||
}
|
||||
)
|
||||
payment.action_post()
|
||||
line_payment = payment.line_ids.filtered(
|
||||
lambda l: l.account_id.id == receivable_account_id
|
||||
)
|
||||
self.assertEqual(line_payment.reconciled, False)
|
||||
invoice1_line = invoice1.line_ids.filtered(
|
||||
lambda l: l.account_id.id == receivable_account_id
|
||||
)
|
||||
self.assertEqual(invoice1_line.reconciled, False)
|
||||
|
||||
# Create the mass reconcile record
|
||||
reconcile_method_vals = {
|
||||
"name": "mass.reconcile.advanced.ref",
|
||||
"write_off": 0.1,
|
||||
}
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
"name": "mass_reconcile_1",
|
||||
"account": receivable_account_id,
|
||||
"reconcile_method": [(0, 0, reconcile_method_vals)],
|
||||
}
|
||||
)
|
||||
mass_rec.run_reconcile()
|
||||
|
||||
self.assertEqual(line_payment.amount_residual, -450.0)
|
||||
self.assertEqual(invoice1_line.reconciled, True)
|
||||
invoice2 = self._create_invoice(invoice_amount=500, auto_validate=True)
|
||||
invoice2.ref = "test ref"
|
||||
invoice2_line = invoice2.line_ids.filtered(
|
||||
lambda l: l.account_id.id == receivable_account_id
|
||||
)
|
||||
mass_rec.run_reconcile()
|
||||
self.assertEqual(line_payment.reconciled, True)
|
||||
self.assertEqual(invoice2_line.reconciled, False)
|
||||
|
||||
self.assertEqual(invoice2_line.amount_residual, 50.0)
|
||||
|
||||
def test_reconcile_with_writeoff(self):
|
||||
invoice = self.create_invoice()
|
||||
|
||||
receivable_account_id = invoice.partner_id.property_account_receivable_id.id
|
||||
# create payment
|
||||
payment = self.env["account.payment"].create(
|
||||
{
|
||||
"partner_type": "customer",
|
||||
"payment_type": "inbound",
|
||||
"partner_id": invoice.partner_id.id,
|
||||
"destination_account_id": receivable_account_id,
|
||||
"amount": 50.1,
|
||||
"journal_id": self.bank_journal.id,
|
||||
}
|
||||
)
|
||||
payment.action_post()
|
||||
|
||||
# create the mass reconcile record
|
||||
mass_rec = self.mass_rec_obj.create(
|
||||
{
|
||||
"name": "mass_reconcile_1",
|
||||
"account": invoice.partner_id.property_account_receivable_id.id,
|
||||
"reconcile_method": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": "mass.reconcile.simple.partner",
|
||||
"account_lost_id": self.company_data[
|
||||
"default_account_expense"
|
||||
].id,
|
||||
"account_profit_id": self.company_data[
|
||||
"default_account_revenue"
|
||||
].id,
|
||||
"journal_id": self.company_data["default_journal_misc"].id,
|
||||
"write_off": 0.05,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
# call the automatic reconcilation method
|
||||
mass_rec.run_reconcile()
|
||||
self.assertEqual("not_paid", invoice.payment_state)
|
||||
mass_rec.reconcile_method.write_off = 0.11
|
||||
mass_rec.run_reconcile()
|
||||
self.assertEqual("paid", invoice.payment_state)
|
||||
full_reconcile = invoice.line_ids.mapped("full_reconcile_id")
|
||||
writeoff_line = full_reconcile.reconciled_line_ids.filtered(
|
||||
lambda l: l.debit == 0.1
|
||||
)
|
||||
self.assertEqual(len(writeoff_line), 1)
|
||||
self.assertEqual(
|
||||
writeoff_line.move_id.journal_id.id,
|
||||
self.company_data["default_journal_misc"].id,
|
||||
)
|
||||
|
|
|
@ -173,14 +173,6 @@ The lines should have the same partner, and the credit entry ref. is matched wit
|
|||
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>
|
||||
|
|
Loading…
Reference in New Issue