From 782779af6b6eab500ec19882e4b931271fb66fa9 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 11:45:18 +0100 Subject: [PATCH 01/26] Move account_credit_control to root --- .../account_credit_control => account_credit_control}/__init__.py | 0 .../__openerp__.py | 0 .../account_credit_control => account_credit_control}/account.py | 0 .../account_view.xml | 0 .../account_credit_control => account_credit_control}/company.py | 0 .../company_view.xml | 0 .../credit_control_demo.xml | 0 .../account_credit_control => account_credit_control}/data.xml | 0 .../account_credit_control => account_credit_control}/i18n/de.po | 0 .../account_credit_control => account_credit_control}/i18n/en.po | 0 .../account_credit_control => account_credit_control}/i18n/es.po | 0 .../account_credit_control => account_credit_control}/i18n/fr.po | 0 .../account_credit_control => account_credit_control}/invoice.py | 0 .../account_credit_control => account_credit_control}/line.py | 0 .../line_view.xml | 0 .../account_credit_control => account_credit_control}/mail.py | 0 .../account_credit_control => account_credit_control}/partner.py | 0 .../partner_view.xml | 0 .../account_credit_control => account_credit_control}/policy.py | 0 .../policy_view.xml | 0 .../report/__init__.py | 0 .../report/credit_control_summary.html.mako | 0 .../report/credit_control_summary.py | 0 .../report/report.xml | 0 .../account_credit_control => account_credit_control}/run.py | 0 .../run_view.xml | 0 .../scenarios/features/00_credit_control_param.feature | 0 .../scenarios/features/01_credit_control_partners.feature | 0 .../scenarios/features/02_credit_control_invoices.feature | 0 .../scenarios/features/03_credit_control_run_jan.feature | 0 .../scenarios/features/04_credit_control_run_feb.feature | 0 .../scenarios/features/05_credit_control_run_mar.feature | 0 .../scenarios/features/06_credit_control_run_apr.feature | 0 .../scenarios/features/07_credit_control_run_may.feature | 0 .../scenarios/features/08_credit_control_run_jun.feature | 0 .../scenarios/features/09_credit_control_run_jul.feature | 0 .../scenarios/features/10_credit_control_run_aug.feature | 0 .../scenarios/features/11_credit_control_manual_setting.feature | 0 .../scenarios/features/steps/account_credit_control.py | 0 .../scenarios/features/steps/account_credit_control_changer.py | 0 .../scenarios/features/steps/account_voucher.py | 0 .../security/ir.model.access.csv | 0 .../wizard/__init__.py | 0 .../wizard/credit_control_communication.py | 0 .../wizard/credit_control_emailer.py | 0 .../wizard/credit_control_emailer_view.xml | 0 .../wizard/credit_control_marker.py | 0 .../wizard/credit_control_marker_view.xml | 0 .../wizard/credit_control_policy_changer.py | 0 .../wizard/credit_control_policy_changer_view.xml | 0 .../wizard/credit_control_printer.py | 0 .../wizard/credit_control_printer_view.xml | 0 52 files changed, 0 insertions(+), 0 deletions(-) rename {__unported__/account_credit_control => account_credit_control}/__init__.py (100%) rename {__unported__/account_credit_control => account_credit_control}/__openerp__.py (100%) rename {__unported__/account_credit_control => account_credit_control}/account.py (100%) rename {__unported__/account_credit_control => account_credit_control}/account_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/company.py (100%) rename {__unported__/account_credit_control => account_credit_control}/company_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/credit_control_demo.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/data.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/i18n/de.po (100%) rename {__unported__/account_credit_control => account_credit_control}/i18n/en.po (100%) rename {__unported__/account_credit_control => account_credit_control}/i18n/es.po (100%) rename {__unported__/account_credit_control => account_credit_control}/i18n/fr.po (100%) rename {__unported__/account_credit_control => account_credit_control}/invoice.py (100%) rename {__unported__/account_credit_control => account_credit_control}/line.py (100%) rename {__unported__/account_credit_control => account_credit_control}/line_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/mail.py (100%) rename {__unported__/account_credit_control => account_credit_control}/partner.py (100%) rename {__unported__/account_credit_control => account_credit_control}/partner_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/policy.py (100%) rename {__unported__/account_credit_control => account_credit_control}/policy_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/report/__init__.py (100%) rename {__unported__/account_credit_control => account_credit_control}/report/credit_control_summary.html.mako (100%) rename {__unported__/account_credit_control => account_credit_control}/report/credit_control_summary.py (100%) rename {__unported__/account_credit_control => account_credit_control}/report/report.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/run.py (100%) rename {__unported__/account_credit_control => account_credit_control}/run_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/00_credit_control_param.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/01_credit_control_partners.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/02_credit_control_invoices.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/03_credit_control_run_jan.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/04_credit_control_run_feb.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/05_credit_control_run_mar.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/06_credit_control_run_apr.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/07_credit_control_run_may.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/08_credit_control_run_jun.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/09_credit_control_run_jul.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/10_credit_control_run_aug.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/11_credit_control_manual_setting.feature (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/steps/account_credit_control.py (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/steps/account_credit_control_changer.py (100%) rename {__unported__/account_credit_control => account_credit_control}/scenarios/features/steps/account_voucher.py (100%) rename {__unported__/account_credit_control => account_credit_control}/security/ir.model.access.csv (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/__init__.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_communication.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_emailer.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_emailer_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_marker.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_marker_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_policy_changer.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_policy_changer_view.xml (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_printer.py (100%) rename {__unported__/account_credit_control => account_credit_control}/wizard/credit_control_printer_view.xml (100%) diff --git a/__unported__/account_credit_control/__init__.py b/account_credit_control/__init__.py similarity index 100% rename from __unported__/account_credit_control/__init__.py rename to account_credit_control/__init__.py diff --git a/__unported__/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py similarity index 100% rename from __unported__/account_credit_control/__openerp__.py rename to account_credit_control/__openerp__.py diff --git a/__unported__/account_credit_control/account.py b/account_credit_control/account.py similarity index 100% rename from __unported__/account_credit_control/account.py rename to account_credit_control/account.py diff --git a/__unported__/account_credit_control/account_view.xml b/account_credit_control/account_view.xml similarity index 100% rename from __unported__/account_credit_control/account_view.xml rename to account_credit_control/account_view.xml diff --git a/__unported__/account_credit_control/company.py b/account_credit_control/company.py similarity index 100% rename from __unported__/account_credit_control/company.py rename to account_credit_control/company.py diff --git a/__unported__/account_credit_control/company_view.xml b/account_credit_control/company_view.xml similarity index 100% rename from __unported__/account_credit_control/company_view.xml rename to account_credit_control/company_view.xml diff --git a/__unported__/account_credit_control/credit_control_demo.xml b/account_credit_control/credit_control_demo.xml similarity index 100% rename from __unported__/account_credit_control/credit_control_demo.xml rename to account_credit_control/credit_control_demo.xml diff --git a/__unported__/account_credit_control/data.xml b/account_credit_control/data.xml similarity index 100% rename from __unported__/account_credit_control/data.xml rename to account_credit_control/data.xml diff --git a/__unported__/account_credit_control/i18n/de.po b/account_credit_control/i18n/de.po similarity index 100% rename from __unported__/account_credit_control/i18n/de.po rename to account_credit_control/i18n/de.po diff --git a/__unported__/account_credit_control/i18n/en.po b/account_credit_control/i18n/en.po similarity index 100% rename from __unported__/account_credit_control/i18n/en.po rename to account_credit_control/i18n/en.po diff --git a/__unported__/account_credit_control/i18n/es.po b/account_credit_control/i18n/es.po similarity index 100% rename from __unported__/account_credit_control/i18n/es.po rename to account_credit_control/i18n/es.po diff --git a/__unported__/account_credit_control/i18n/fr.po b/account_credit_control/i18n/fr.po similarity index 100% rename from __unported__/account_credit_control/i18n/fr.po rename to account_credit_control/i18n/fr.po diff --git a/__unported__/account_credit_control/invoice.py b/account_credit_control/invoice.py similarity index 100% rename from __unported__/account_credit_control/invoice.py rename to account_credit_control/invoice.py diff --git a/__unported__/account_credit_control/line.py b/account_credit_control/line.py similarity index 100% rename from __unported__/account_credit_control/line.py rename to account_credit_control/line.py diff --git a/__unported__/account_credit_control/line_view.xml b/account_credit_control/line_view.xml similarity index 100% rename from __unported__/account_credit_control/line_view.xml rename to account_credit_control/line_view.xml diff --git a/__unported__/account_credit_control/mail.py b/account_credit_control/mail.py similarity index 100% rename from __unported__/account_credit_control/mail.py rename to account_credit_control/mail.py diff --git a/__unported__/account_credit_control/partner.py b/account_credit_control/partner.py similarity index 100% rename from __unported__/account_credit_control/partner.py rename to account_credit_control/partner.py diff --git a/__unported__/account_credit_control/partner_view.xml b/account_credit_control/partner_view.xml similarity index 100% rename from __unported__/account_credit_control/partner_view.xml rename to account_credit_control/partner_view.xml diff --git a/__unported__/account_credit_control/policy.py b/account_credit_control/policy.py similarity index 100% rename from __unported__/account_credit_control/policy.py rename to account_credit_control/policy.py diff --git a/__unported__/account_credit_control/policy_view.xml b/account_credit_control/policy_view.xml similarity index 100% rename from __unported__/account_credit_control/policy_view.xml rename to account_credit_control/policy_view.xml diff --git a/__unported__/account_credit_control/report/__init__.py b/account_credit_control/report/__init__.py similarity index 100% rename from __unported__/account_credit_control/report/__init__.py rename to account_credit_control/report/__init__.py diff --git a/__unported__/account_credit_control/report/credit_control_summary.html.mako b/account_credit_control/report/credit_control_summary.html.mako similarity index 100% rename from __unported__/account_credit_control/report/credit_control_summary.html.mako rename to account_credit_control/report/credit_control_summary.html.mako diff --git a/__unported__/account_credit_control/report/credit_control_summary.py b/account_credit_control/report/credit_control_summary.py similarity index 100% rename from __unported__/account_credit_control/report/credit_control_summary.py rename to account_credit_control/report/credit_control_summary.py diff --git a/__unported__/account_credit_control/report/report.xml b/account_credit_control/report/report.xml similarity index 100% rename from __unported__/account_credit_control/report/report.xml rename to account_credit_control/report/report.xml diff --git a/__unported__/account_credit_control/run.py b/account_credit_control/run.py similarity index 100% rename from __unported__/account_credit_control/run.py rename to account_credit_control/run.py diff --git a/__unported__/account_credit_control/run_view.xml b/account_credit_control/run_view.xml similarity index 100% rename from __unported__/account_credit_control/run_view.xml rename to account_credit_control/run_view.xml diff --git a/__unported__/account_credit_control/scenarios/features/00_credit_control_param.feature b/account_credit_control/scenarios/features/00_credit_control_param.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/00_credit_control_param.feature rename to account_credit_control/scenarios/features/00_credit_control_param.feature diff --git a/__unported__/account_credit_control/scenarios/features/01_credit_control_partners.feature b/account_credit_control/scenarios/features/01_credit_control_partners.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/01_credit_control_partners.feature rename to account_credit_control/scenarios/features/01_credit_control_partners.feature diff --git a/__unported__/account_credit_control/scenarios/features/02_credit_control_invoices.feature b/account_credit_control/scenarios/features/02_credit_control_invoices.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/02_credit_control_invoices.feature rename to account_credit_control/scenarios/features/02_credit_control_invoices.feature diff --git a/__unported__/account_credit_control/scenarios/features/03_credit_control_run_jan.feature b/account_credit_control/scenarios/features/03_credit_control_run_jan.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/03_credit_control_run_jan.feature rename to account_credit_control/scenarios/features/03_credit_control_run_jan.feature diff --git a/__unported__/account_credit_control/scenarios/features/04_credit_control_run_feb.feature b/account_credit_control/scenarios/features/04_credit_control_run_feb.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/04_credit_control_run_feb.feature rename to account_credit_control/scenarios/features/04_credit_control_run_feb.feature diff --git a/__unported__/account_credit_control/scenarios/features/05_credit_control_run_mar.feature b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/05_credit_control_run_mar.feature rename to account_credit_control/scenarios/features/05_credit_control_run_mar.feature diff --git a/__unported__/account_credit_control/scenarios/features/06_credit_control_run_apr.feature b/account_credit_control/scenarios/features/06_credit_control_run_apr.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/06_credit_control_run_apr.feature rename to account_credit_control/scenarios/features/06_credit_control_run_apr.feature diff --git a/__unported__/account_credit_control/scenarios/features/07_credit_control_run_may.feature b/account_credit_control/scenarios/features/07_credit_control_run_may.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/07_credit_control_run_may.feature rename to account_credit_control/scenarios/features/07_credit_control_run_may.feature diff --git a/__unported__/account_credit_control/scenarios/features/08_credit_control_run_jun.feature b/account_credit_control/scenarios/features/08_credit_control_run_jun.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/08_credit_control_run_jun.feature rename to account_credit_control/scenarios/features/08_credit_control_run_jun.feature diff --git a/__unported__/account_credit_control/scenarios/features/09_credit_control_run_jul.feature b/account_credit_control/scenarios/features/09_credit_control_run_jul.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/09_credit_control_run_jul.feature rename to account_credit_control/scenarios/features/09_credit_control_run_jul.feature diff --git a/__unported__/account_credit_control/scenarios/features/10_credit_control_run_aug.feature b/account_credit_control/scenarios/features/10_credit_control_run_aug.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/10_credit_control_run_aug.feature rename to account_credit_control/scenarios/features/10_credit_control_run_aug.feature diff --git a/__unported__/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature b/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature similarity index 100% rename from __unported__/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature rename to account_credit_control/scenarios/features/11_credit_control_manual_setting.feature diff --git a/__unported__/account_credit_control/scenarios/features/steps/account_credit_control.py b/account_credit_control/scenarios/features/steps/account_credit_control.py similarity index 100% rename from __unported__/account_credit_control/scenarios/features/steps/account_credit_control.py rename to account_credit_control/scenarios/features/steps/account_credit_control.py diff --git a/__unported__/account_credit_control/scenarios/features/steps/account_credit_control_changer.py b/account_credit_control/scenarios/features/steps/account_credit_control_changer.py similarity index 100% rename from __unported__/account_credit_control/scenarios/features/steps/account_credit_control_changer.py rename to account_credit_control/scenarios/features/steps/account_credit_control_changer.py diff --git a/__unported__/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py similarity index 100% rename from __unported__/account_credit_control/scenarios/features/steps/account_voucher.py rename to account_credit_control/scenarios/features/steps/account_voucher.py diff --git a/__unported__/account_credit_control/security/ir.model.access.csv b/account_credit_control/security/ir.model.access.csv similarity index 100% rename from __unported__/account_credit_control/security/ir.model.access.csv rename to account_credit_control/security/ir.model.access.csv diff --git a/__unported__/account_credit_control/wizard/__init__.py b/account_credit_control/wizard/__init__.py similarity index 100% rename from __unported__/account_credit_control/wizard/__init__.py rename to account_credit_control/wizard/__init__.py diff --git a/__unported__/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_communication.py rename to account_credit_control/wizard/credit_control_communication.py diff --git a/__unported__/account_credit_control/wizard/credit_control_emailer.py b/account_credit_control/wizard/credit_control_emailer.py similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_emailer.py rename to account_credit_control/wizard/credit_control_emailer.py diff --git a/__unported__/account_credit_control/wizard/credit_control_emailer_view.xml b/account_credit_control/wizard/credit_control_emailer_view.xml similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_emailer_view.xml rename to account_credit_control/wizard/credit_control_emailer_view.xml diff --git a/__unported__/account_credit_control/wizard/credit_control_marker.py b/account_credit_control/wizard/credit_control_marker.py similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_marker.py rename to account_credit_control/wizard/credit_control_marker.py diff --git a/__unported__/account_credit_control/wizard/credit_control_marker_view.xml b/account_credit_control/wizard/credit_control_marker_view.xml similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_marker_view.xml rename to account_credit_control/wizard/credit_control_marker_view.xml diff --git a/__unported__/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_policy_changer.py rename to account_credit_control/wizard/credit_control_policy_changer.py diff --git a/__unported__/account_credit_control/wizard/credit_control_policy_changer_view.xml b/account_credit_control/wizard/credit_control_policy_changer_view.xml similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_policy_changer_view.xml rename to account_credit_control/wizard/credit_control_policy_changer_view.xml diff --git a/__unported__/account_credit_control/wizard/credit_control_printer.py b/account_credit_control/wizard/credit_control_printer.py similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_printer.py rename to account_credit_control/wizard/credit_control_printer.py diff --git a/__unported__/account_credit_control/wizard/credit_control_printer_view.xml b/account_credit_control/wizard/credit_control_printer_view.xml similarity index 100% rename from __unported__/account_credit_control/wizard/credit_control_printer_view.xml rename to account_credit_control/wizard/credit_control_printer_view.xml From dacb6fdb4c06bb73162b67097ba54be3d3d903b8 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 11:45:41 +0100 Subject: [PATCH 02/26] Set account_credit_control to installable --- account_credit_control/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py index afae1a61a..72379a7bd 100644 --- a/account_credit_control/__openerp__.py +++ b/account_credit_control/__openerp__.py @@ -76,7 +76,7 @@ On each generated line, you have many choices: "security/ir.model.access.csv"], 'demo_xml': ["credit_control_demo.xml"], 'tests': [], - 'installable': False, + 'installable': True, 'license': 'AGPL-3', 'application': True } From 630c1ab5e03eb6bb4ead247975029c8a8fedf1c4 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 11:46:31 +0100 Subject: [PATCH 03/26] Extract the description from __openerp__.py to README.rst --- account_credit_control/README.rst | 29 +++++++++++++++++++++++ account_credit_control/__openerp__.py | 34 --------------------------- 2 files changed, 29 insertions(+), 34 deletions(-) create mode 100644 account_credit_control/README.rst diff --git a/account_credit_control/README.rst b/account_credit_control/README.rst new file mode 100644 index 000000000..4294fdb56 --- /dev/null +++ b/account_credit_control/README.rst @@ -0,0 +1,29 @@ +Credit Control +============== + +Configuration +------------- + +Configure the policies and policy levels in ``Accounting > Configuration > +Credit Control > Credit Policies``. +You can define as many policy levels as you need. + +Configure a tolerance for the Credit control and a default policy +applied on all partners in each company, under the Accounting tab. + +You are able to specify a particular policy for one partner or one invoice. + +Usage +----- + +Menu entries are located in ``Accounting > Periodical Processing > Credit +Control``. + +Create a new "run" in the ``Credit Control Run`` menu with the controlling date. +Then, use the ``Compute credit lines`` button. All the credit control lines will +be generated. You can find them in the ``Credit Control Lines`` menu. + +On each generated line, you have many choices: + * Send a email + * Print a letter + * Change the state (so you can ignore or reopen lines) diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py index 72379a7bd..55b74a1d6 100644 --- a/account_credit_control/__openerp__.py +++ b/account_credit_control/__openerp__.py @@ -26,40 +26,6 @@ 'complexity': "normal", 'depends': ['base', 'account', 'email_template', 'report_webkit'], - 'description': """ -Credit Control -============== - -Configuration -------------- - -Configure the policies and policy levels in ``Accounting > Configuration > -Credit Control > Credit Policies``. -You can define as many policy levels as you need. - -Configure a tolerance for the Credit control and a default policy -applied on all partners in each company, under the Accounting tab. - -You are able to specify a particular policy for one partner or one invoice. - -Usage ------ - -Menu entries are located in ``Accounting > Periodical Processing > Credit -Control``. - -Create a new "run" in the ``Credit Control Run`` menu -with the controlling date. - -Then, use the ``Compute credit lines`` button. -All the credit control lines will be generated. -You can find them in the ``Credit Control Lines`` menu. - -On each generated line, you have many choices: - * Send a email - * Print a letter - * Change the state (so you can ignore or reopen lines) - """, 'website': 'http://www.camptocamp.com', 'data': ["report/report.xml", "data.xml", From 892b1288b89b7b0bf6605c461da6dd9382fcfbd6 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 11:48:16 +0100 Subject: [PATCH 04/26] Use 'demo' instead of the deprecated 'demo_xml' --- account_credit_control/__openerp__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py index 55b74a1d6..fa905f236 100644 --- a/account_credit_control/__openerp__.py +++ b/account_credit_control/__openerp__.py @@ -24,8 +24,10 @@ 'maintainer': 'Camptocamp', 'category': 'Finance', 'complexity': "normal", - 'depends': ['base', 'account', - 'email_template', 'report_webkit'], + 'depends': ['base', + 'account', + 'email_template', + 'report_webkit'], 'website': 'http://www.camptocamp.com', 'data': ["report/report.xml", "data.xml", @@ -40,7 +42,7 @@ "wizard/credit_control_printer_view.xml", "wizard/credit_control_policy_changer_view.xml", "security/ir.model.access.csv"], - 'demo_xml': ["credit_control_demo.xml"], + 'demo': ["credit_control_demo.xml"], 'tests': [], 'installable': True, 'license': 'AGPL-3', From 87fafe38eae2d71ec8288af17666e71919e53882 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 11:58:11 +0100 Subject: [PATCH 05/26] Port account.invoice to new API --- account_credit_control/invoice.py | 82 ++++++++++++------------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/account_credit_control/invoice.py b/account_credit_control/invoice.py index 805ae560c..361ffa630 100644 --- a/account_credit_control/invoice.py +++ b/account_credit_control/invoice.py @@ -18,69 +18,51 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields, api from openerp.tools.translate import _ -class AccountInvoice(orm.Model): +class AccountInvoice(models.Model): """Check on cancelling of an invoice""" _inherit = 'account.invoice' - _columns = { - 'credit_policy_id': fields.many2one( - 'credit.control.policy', - 'Credit Control Policy', - help=("The Credit Control Policy used for this " - "invoice. If nothing is defined, it will " - "use the account setting or the partner " - "setting."), - readonly=True, - ), - 'credit_control_line_ids': fields.one2many( - 'credit.control.line', - 'invoice_id', - string='Credit Lines', - readonly=True - ), - } + credit_policy_id = fields.Many2one( + 'credit.control.policy', + string='Credit Control Policy', + help="The Credit Control Policy used for this " + "invoice. If nothing is defined, it will " + "use the account setting or the partner " + "setting.", + readonly=True, + copy=False, + ) - def copy_data(self, cr, uid, id, default=None, context=None): - """Ensure that credit lines and policy are not copied""" - if default is None: - default = {} - else: - default = default.copy() - default['credit_control_line_ids'] = False - default['credit_policy_id'] = False - return super(AccountInvoice, self).copy_data( - cr, uid, id, default=default, context=context) + credit_control_line_ids = fields.One2many( + 'credit.control.line', 'invoice_id', + string='Credit Lines', + readonly=True, + copy=False, + ) - def action_cancel(self, cr, uid, ids, context=None): + @api.multi + def action_cancel(self): """Prevent to cancel invoice related to credit line""" # We will search if this invoice is linked with credit - cc_line_obj = self.pool.get('credit.control.line') - for invoice_id in ids: - cc_nondraft_line_ids = cc_line_obj.search( - cr, uid, - [('invoice_id', '=', invoice_id), - ('state', '!=', 'draft')], - context=context) - if cc_nondraft_line_ids: - raise orm.except_orm( - _('Error!'), + cc_line_obj = self.env['credit.control.line'] + for invoice in self: + nondraft_domain = [('invoice_id', '=', invoice.id), + ('state', '!=', 'draft')] + cc_nondraft_lines = cc_line_obj.search(nondraft_domain) + if cc_nondraft_lines: + raise api.Warning( _('You cannot cancel this invoice.\n' 'A payment reminder has already been ' 'sent to the customer.\n' 'You must create a credit note and ' 'issue a new invoice.') ) - cc_draft_line_ids = cc_line_obj.search( - cr, uid, - [('invoice_id', '=', invoice_id), - ('state', '=', 'draft')], - context=context) - cc_line_obj.unlink(cr, uid, - cc_draft_line_ids, - context=context) - return super(AccountInvoice, self).action_cancel(cr, uid, ids, - context=context) + draft_domain = [('invoice_id', '=', invoice.id), + ('state', '=', 'draft')] + cc_draft_line = cc_line_obj.search(draft_domain) + cc_draft_line.unlink() + return super(AccountInvoice, self).action_cancel() From 2538b0c55c031f95637f203799f517916af229e8 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 13:32:45 +0100 Subject: [PATCH 06/26] Changed imports no longer valid --- .../wizard/credit_control_communication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py index c33b818f5..8c72c0345 100644 --- a/account_credit_control/wizard/credit_control_communication.py +++ b/account_credit_control/wizard/credit_control_communication.py @@ -18,14 +18,14 @@ # along with this program. If not, see . # ############################################################################## -import netsvc import logging -from openerp.osv.orm import TransientModel, fields +from openerp import netsvc +from openerp.osv import orm, fields logger = logging.getLogger('credit.control.line.mailing') -class CreditCommunication(TransientModel): +class CreditCommunication(orm.TransientModel): """Shell class used to provide a base model to email template and reporting. Il use this approche in version 7 a browse record will exist even if not saved From caa628c825c60c9e785c0349e43252f57cbc2daf Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 16:19:45 +0100 Subject: [PATCH 07/26] Migrate the main models to the new API --- account_credit_control/account.py | 26 +- account_credit_control/company.py | 30 +- account_credit_control/line.py | 304 ++++++++------------ account_credit_control/mail.py | 10 +- account_credit_control/partner.py | 82 ++---- account_credit_control/policy.py | 464 +++++++++++++----------------- account_credit_control/run.py | 196 ++++++------- 7 files changed, 462 insertions(+), 650 deletions(-) diff --git a/account_credit_control/account.py b/account_credit_control/account.py index 8fe57e27b..0c00a8e23 100644 --- a/account_credit_control/account.py +++ b/account_credit_control/account.py @@ -18,27 +18,15 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class AccountAccount(orm.Model): - """Add a link to a credit control policy on account.account""" +class AccountAccount(models.Model): + """ Add a link to a credit control policy on account.account """ _inherit = "account.account" - _columns = { - 'credit_control_line_ids': fields.one2many( - 'credit.control.line', - 'account_id', - string='Credit Lines', - readonly=True), - } - - def copy_data(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - else: - default = default.copy() - default['credit_control_line_ids'] = False - return super(AccountAccount, self).copy_data( - cr, uid, id, default=default, context=context) + credit_control_line_ids = fields.One2many('credit.control.line', + 'account_id', + string='Credit Lines', + readonly=True) diff --git a/account_credit_control/company.py b/account_credit_control/company.py index 790fc05f1..cd94ef7c8 100644 --- a/account_credit_control/company.py +++ b/account_credit_control/company.py @@ -18,24 +18,20 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class ResCompany(orm.Model): - """Add credit control parameters""" +class ResCompany(models.Model): + """ Add credit control parameters """ _inherit = 'res.company' - _columns = { - 'credit_control_tolerance': fields.float('Credit Control Tolerance'), - # This is not a property on the partner because we cannot search - # on fields.property (subclass fields.function). - 'credit_policy_id': fields.many2one( - 'credit.control.policy', - 'Credit Control Policy', - help=("The Credit Control Policy used on partners" - " by default. This setting can be overridden" - " on partners or invoices.") - ), - } - - _defaults = {"credit_control_tolerance": 0.1} + credit_control_tolerance = fields.Float(string='Credit Control Tolerance', + default=0.1) + # This is not a property on the partner because we cannot search + # on fields.property (subclass fields.function). + credit_policy_id = fields.Many2one('credit.control.policy', + string='Credit Control Policy', + help="The Credit Control Policy used " + "on partners by default. " + "This setting can be overridden" + " on partners or invoices.") diff --git a/account_credit_control/line.py b/account_credit_control/line.py index 964878fa4..02d1dfbd4 100644 --- a/account_credit_control/line.py +++ b/account_credit_control/line.py @@ -20,14 +20,14 @@ ############################################################################## import logging -from openerp.osv import orm, fields +from openerp import models, fields, api from openerp.tools.translate import _ logger = logging.getLogger('credit.line.control') -class CreditControlLine(orm.Model): - """A credit control line describes an amount due by a customer for a due date. +class CreditControlLine(models.Model): + """ A credit control line describes an amount due by a customer for a due date. A line is created once the due date of the payment is exceeded. It is created in "draft" and some actions are available (send by email, @@ -38,160 +38,105 @@ class CreditControlLine(orm.Model): _description = "A credit control line" _rec_name = "id" _order = "date DESC" - _columns = { - 'date': fields.date( - 'Controlling date', - required=True, - select=True - ), - # maturity date of related move line we do not use - # a related field in order to - # allow manual changes - 'date_due': fields.date( - 'Due date', - required=True, - readonly=True, - states={'draft': [('readonly', False)]} - ), - 'date_entry': fields.related( - 'move_line_id', 'date', - type='date', - string='Entry date', - store=True, readonly=True - ), + date = fields.Date(string='Controlling date', + required=True, + select=True) + # maturity date of related move line we do not use + # a related field in order to + # allow manual changes + date_due = fields.Date(string='Due date', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}) - 'date_sent': fields.date( - 'Sent date', - readonly=True, - states={'draft': [('readonly', False)]} - ), + date_entry = fields.Date(string='Entry date', + related='move_line_id.date', + store=True, + readonly=True) - 'state': fields.selection( - [('draft', 'Draft'), - ('ignored', 'Ignored'), - ('to_be_sent', 'Ready To Send'), - ('sent', 'Done'), - ('error', 'Error'), - ('email_error', 'Emailing Error')], - 'State', required=True, readonly=True, - help=("Draft lines need to be triaged.\n" - "Ignored lines are lines for which we do " - "not want to send something.\n" - "Draft and ignored lines will be " - "generated again on the next run.") - ), + date_sent = fields.Date(string='Sent date', + readonly=True, + states={'draft': [('readonly', False)]}) - 'channel': fields.selection( - [('letter', 'Letter'), - ('email', 'Email')], - 'Channel', required=True, - readonly=True, - states={'draft': [('readonly', False)]} - ), + state = fields.Selection([('draft', 'Draft'), + ('ignored', 'Ignored'), + ('to_be_sent', 'Ready To Send'), + ('sent', 'Done'), + ('error', 'Error'), + ('email_error', 'Emailing Error')], + 'State', + required=True, + readonly=True, + default='draft', + help="Draft lines need to be triaged.\n" + "Ignored lines are lines for which we do " + "not want to send something.\n" + "Draft and ignored lines will be " + "generated again on the next run.") - 'invoice_id': fields.many2one( - 'account.invoice', - 'Invoice', - readonly=True - ), + channel = fields.Selection([('letter', 'Letter'), + ('email', 'Email')], + string='Channel', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}) - 'partner_id': fields.many2one( - 'res.partner', - "Partner", - required=True - ), + invoice_id = fields.Many2one('account.invoice', + string='Invoice', + readonly=True) - 'amount_due': fields.float( - 'Due Amount Tax incl.', - required=True, - readonly=True - ), + partner_id = fields.Many2one('res.partner', + string='Partner', + required=True) - 'balance_due': fields.float( - 'Due balance', required=True, - readonly=True - ), + amount_due = fields.Float(string='Due Amount Tax incl.', + required=True, readonly=True) - 'mail_message_id': fields.many2one( - 'mail.mail', - 'Sent Email', - readonly=True - ), + balance_due = fields.Float(string='Due balance', required=True, + readonly=True) - 'move_line_id': fields.many2one( - 'account.move.line', - 'Move line', - required=True, - readonly=True - ), + mail_message_id = fields.Many2one('mail.mail', string='Sent Email', + readonly=True) - 'account_id': fields.related( - 'move_line_id', - 'account_id', - type='many2one', - relation='account.account', - string='Account', - store=True, - readonly=True - ), + move_line_id = fields.Many2one('account.move.line', + string='Move line', + required=True, + readonly=True) - 'currency_id': fields.related( - 'move_line_id', - 'currency_id', - type='many2one', - relation='res.currency', - string='Currency', - store=True, - readonly=True - ), + account_id = fields.Many2one(related='move_line_id.account_id', + store=True, + readonly=True) - 'company_id': fields.related( - 'move_line_id', 'company_id', - type='many2one', - relation='res.company', - string='Company', - store=True, readonly=True - ), + currency_id = fields.Many2one(related='move_line_id.currency_id', + store=True, + readonly=True) - # we can allow a manual change of policy in draft state - 'policy_level_id': fields.many2one( - 'credit.control.policy.level', - 'Overdue Level', - required=True, - readonly=True, - states={'draft': [('readonly', False)]} - ), + company_id = fields.Many2one(related='move_line_id.company_id', + store=True, + readonly=True) - 'policy_id': fields.related( - 'policy_level_id', - 'policy_id', - type='many2one', - relation='credit.control.policy', - string='Policy', - store=True, - readonly=True - ), + # we can allow a manual change of policy in draft state + policy_level_id = fields.Many2one('credit.control.policy.level', + string='Overdue Level', + required=True, + readonly=True, + states={'draft': [('readonly', False)]}) - 'level': fields.related( - 'policy_level_id', - 'level', - type='integer', - relation='credit.control.policy', - string='Level', - store=True, - readonly=True - ), + policy_id = fields.Many2one(related='policy_level_id.policy_id', + store=True, + readonly=True) - 'manually_overridden': fields.boolean('Manually overridden') - } + level = fields.Integer(related='policy_level_id.level', + store=True, + readonly=True) - _defaults = {'state': 'draft'} + manually_overridden = fields.Boolean(string='Manually overridden') - def _prepare_from_move_line(self, cr, uid, move_line, - level, controlling_date, open_amount, - context=None): - """Create credit control line""" + @api.model + def _prepare_from_move_line(self, move_line, level, controlling_date, + open_amount): + """ Create credit control line """ data = {} data['date'] = controlling_date data['date_due'] = move_line.date_maturity @@ -208,17 +153,17 @@ class CreditControlLine(orm.Model): data['move_line_id'] = move_line.id return data - def create_or_update_from_mv_lines(self, cr, uid, ids, lines, - level_id, controlling_date, - check_tolerance=True, context=None): - """Create or update line based on levels + @api.model + def create_or_update_from_mv_lines(self, lines, level_id, controlling_date, + check_tolerance=True): + """ Create or update line based on levels if check_tolerance is true credit line will not be created if open amount is too small. eg. we do not want to send a letter for 10 cents of open amount. - :param lines: move.line id list + :param lines: move.line id recordset :param level_id: credit.control.policy.level id :param controlling_date: date string of the credit controlling date. Generally it should be the same @@ -228,65 +173,54 @@ class CreditControlLine(orm.Model): is smaller than company defined tolerance - :returns: list of created credit line ids + :returns: recordset of created credit lines """ - currency_obj = self.pool.get('res.currency') - level_obj = self.pool.get('credit.control.policy.level') - ml_obj = self.pool.get('account.move.line') - user = self.pool.get('res.users').browse(cr, uid, uid) - currency_ids = currency_obj.search(cr, uid, [], context=context) + currency_obj = self.env['res.currency'] + level_obj = self.env['credit.control.policy.level'] + user = self.env.user + currencies = currency_obj.search([]) tolerance = {} tolerance_base = user.company_id.credit_control_tolerance - for c_id in currency_ids: - tolerance[c_id] = currency_obj.compute( - cr, uid, - c_id, - user.company_id.currency_id.id, - tolerance_base, - context=context) + user_currency = user.company_id.currency_id + for currency in currencies: + tolerance[currency.id] = currency.compute(tolerance_base, + user_currency) - level = level_obj.browse(cr, uid, level_id, context) - line_ids = [] - for line in ml_obj.browse(cr, uid, lines, context): - - open_amount = line.amount_residual_currency - cur_tolerance = tolerance.get(line.currency_id.id, tolerance_base) + level = level_obj.browse(level_id) + new_lines = self.browse() + for move_line in lines: + open_amount = move_line.amount_residual_currency + cur_tolerance = tolerance.get(move_line.currency_id.id, + tolerance_base) if check_tolerance and open_amount < cur_tolerance: continue - vals = self._prepare_from_move_line(cr, uid, - line, + vals = self._prepare_from_move_line(move_line, level, controlling_date, - open_amount, - context=context) - line_id = self.create(cr, uid, vals, context=context) - line_ids.append(line_id) + open_amount) + line = self.create(vals) + new_lines += line # when we have lines generated earlier in draft, # on the same level, it means that we have left # them, so they are to be considered as ignored - previous_draft_ids = self.search( - cr, uid, - [('move_line_id', '=', line.id), - ('policy_level_id', '=', level.id), - ('state', '=', 'draft'), - ('id', '!=', line_id)], - context=context) - if previous_draft_ids: - self.write(cr, uid, previous_draft_ids, - {'state': 'ignored'}, context=context) + previous_drafts = self.search([('move_line_id', '=', move_line.id), + ('policy_level_id', '=', level.id), + ('state', '=', 'draft'), + ('id', '!=', line.id)]) + if previous_drafts: + previous_drafts.write({'state': 'ignored'}) - return line_ids + return new_lines - def unlink(self, cr, uid, ids, context=None, check=True): - for line in self.browse(cr, uid, ids, context=context): + @api.multi + def unlink(self): + for line in self: if line.state != 'draft': - raise orm.except_orm( - _('Error !'), + raise api.Warning( _('You are not allowed to delete a credit control ' 'line that is not in draft state.') ) - return super(CreditControlLine, self).unlink(cr, uid, ids, - context=context) + return super(CreditControlLine, self).unlink() diff --git a/account_credit_control/mail.py b/account_credit_control/mail.py index 25614913c..4636c81be 100644 --- a/account_credit_control/mail.py +++ b/account_credit_control/mail.py @@ -18,14 +18,12 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields -class Mail(orm.Model): +class Mail(models.Model): _inherit = 'mail.mail' # use HTML fields instead of text - _columns = { - 'body_html': fields.html('Rich-text Contents', - help="Rich-text/HTML message"), - } + body_html = fields.Html('Rich-text Contents', + help="Rich-text/HTML message") diff --git a/account_credit_control/partner.py b/account_credit_control/partner.py index ebfee0f9e..9255e7ff4 100644 --- a/account_credit_control/partner.py +++ b/account_credit_control/partner.py @@ -18,61 +18,41 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields, api -class ResPartner(orm.Model): - """Add a settings on the credit control policy to use on the partners, - and links to the credit control lines.""" +class ResPartner(models.Model): + """ Add a settings on the credit control policy to use on the partners, + and links to the credit control lines. + """ _inherit = "res.partner" - _columns = { - 'credit_policy_id': fields.many2one( - 'credit.control.policy', - 'Credit Control Policy', - domain="[('account_ids', 'in', property_account_receivable)]", - help=("The Credit Control Policy used for this " - "partner. This setting can be forced on the " - "invoice. If nothing is defined, it will use " - "the company setting.") - ), - 'credit_control_line_ids': fields.one2many( - 'credit.control.line', - 'invoice_id', - string='Credit Control Lines', - readonly=True - ) - } + credit_policy_id = fields.Many2one( + 'credit.control.policy', + string='Credit Control Policy', + domain="[('account_ids', 'in', property_account_receivable)]", + help="The Credit Control Policy used for this " + "partner. This setting can be forced on the " + "invoice. If nothing is defined, it will use " + "the company setting.", + ) + credit_control_line_ids = fields.One2many('credit.control.line', + 'invoice_id', + string='Credit Control Lines', + readonly=True) - def _check_credit_policy(self, cr, uid, part_ids, context=None): - """Ensure that policy on partner are limited to the account policy""" - if isinstance(part_ids, (int, long)): - part_ids = [part_ids] - policy_obj = self.pool['credit.control.policy'] - for partner in self.browse(cr, uid, part_ids, context): - if not partner.property_account_receivable or \ - not partner.credit_policy_id: - return True + @api.constrains('credit_policy_id') + def _check_credit_policy(self): + """ Ensure that policy on partner are limited to the account policy """ + for partner in self: + if (not partner.property_account_receivable or + not partner.credit_policy_id): + continue account = partner.property_account_receivable - policy_obj.check_policy_against_account( - cr, uid, - account.id, - partner.credit_policy_id.id, - context=context - ) - return True - - _constraints = [(_check_credit_policy, - 'The policy must be related to the receivable account', - ['credit_policy_id'])] - - def copy_data(self, cr, uid, id, default=None, context=None): - """Remove credit lines when copying partner""" - if default is None: - default = {} - else: - default = default.copy() - default['credit_control_line_ids'] = False - return super(ResPartner, self).copy_data( - cr, uid, id, default=default, context=context) + policy = partner.credit_policy_id + try: + policy.check_policy_against_account(account.id) + except api.Warning as err: + # constrains should raise ValidationError exceptions + raise api.ValidationError(err) diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index 0b4a5c8ca..4806cdf95 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -18,61 +18,47 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields +from openerp import models, fields, api from openerp.tools.translate import _ -class CreditControlPolicy(orm.Model): - """Define a policy of reminder""" +class CreditControlPolicy(models.Model): + """ Define a policy of reminder """ _name = "credit.control.policy" _description = """Define a reminder policy""" - _columns = { - 'name': fields.char( - 'Name', - required=True, - size=128 - ), - 'level_ids': fields.one2many( - 'credit.control.policy.level', - 'policy_id', - 'Policy Levels' - ), + name = fields.Char('Name', required=True) + level_ids = fields.One2many('credit.control.policy.level', + 'policy_id', + string='Policy Levels') + do_nothing = fields.Boolean('Do nothing', + help='For policies which should not ' + 'generate lines or are obsolete') + company_id = fields.Many2one('res.company', string='Company') + account_ids = fields.Many2many( + 'account.account', + string='Accounts', + required=True, + domain="[('type', '=', 'receivable')]", + help="This policy will be active only" + " for the selected accounts", + ) + active = fields.Boolean('Active', default=True) - 'do_nothing': fields.boolean( - 'Do nothing', - help='For policies which should not ' - 'generate lines or are obsolete' - ), - - 'company_id': fields.many2one('res.company', 'Company'), - - 'account_ids': fields.many2many( - 'account.account', - string='Accounts', - required=True, - domain="[('type', '=', 'receivable')]", - help="This policy will be active only" - " for the selected accounts" - ), - 'active': fields.boolean('Active'), - } - - _defaults = { - 'active': True, - } - - def _move_lines_domain(self, cr, uid, policy, controlling_date, - context=None): - """Build the default domain for searching move lines""" - account_ids = [a.id for a in policy.account_ids] + @api.multi + def _move_lines_domain(self, controlling_date): + """ Build the default domain for searching move lines """ + self.ensure_one() + account_ids = [a.id for a in self.account_ids] return [('account_id', 'in', account_ids), ('date_maturity', '<=', controlling_date), ('reconcile_id', '=', False), ('partner_id', '!=', False)] - def _due_move_lines(self, cr, uid, policy, controlling_date, context=None): + @api.multi + @api.returns('account.move.line') + def _due_move_lines(self, controlling_date): """ Get the due move lines for the policy of the company. The set of ids will be reduced and extended according @@ -83,18 +69,17 @@ class CreditControlPolicy(orm.Model): Assume that only the receivable lines have a maturity date and that accounts used in the policy are reconcilable. """ - move_l_obj = self.pool.get('account.move.line') - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if user.company_id.credit_policy_id.id != policy.id: - return set() + self.ensure_one() + move_l_obj = self.env['account.move.line'] + user = self.env.user + if user.company_id.credit_policy_id.id != self.id: + return move_l_obj.browse() + domain_line = self._move_lines_domain(controlling_date) + return move_l_obj.search(domain_line) - domain_line = self._move_lines_domain(cr, uid, policy, - controlling_date, - context=context) - return set(move_l_obj.search(cr, uid, domain_line, context=context)) - - def _move_lines_subset(self, cr, uid, policy, controlling_date, - model, move_relation_field, context=None): + @api.multi + @api.returns('account.move.line') + def _move_lines_subset(self, controlling_date, model, move_relation_field): """ Get the move lines related to one model for a policy. Do not use direct SQL in order to respect security rules. @@ -104,274 +89,243 @@ class CreditControlPolicy(orm.Model): The policy relation field must be named credit_policy_id. - :param browse_record policy: policy :param str controlling_date: date of credit control :param str model: name of the model where is defined a credit_policy_id :param str move_relation_field: name of the field in account.move.line which is a many2one to `model` - :return: set of ids to add in the process, set of ids to remove from + :return: recordset to add in the process, recordset to remove from the process """ + self.ensure_one() # MARK possible place for a good optimisation - my_obj = self.pool.get(model) - move_l_obj = self.pool.get('account.move.line') + my_obj = self.env[model] + move_l_obj = self.env['account.move.line'] + default_domain = self._move_lines_domain(controlling_date) - default_domain = self._move_lines_domain(cr, uid, - policy, - controlling_date, - context=context) - to_add_ids = set() - to_remove_ids = set() + to_add = move_l_obj.browse() + to_remove = move_l_obj.browse() # The lines which are linked to this policy have to be included in the # run for this policy. # If another object override the credit_policy_id (ie. invoice after - add_obj_ids = my_obj.search( - cr, uid, - [('credit_policy_id', '=', policy.id)], - context=context) - if add_obj_ids: + add_objs = my_obj.search([('credit_policy_id', '=', self.id)]) + if add_objs: domain = list(default_domain) - domain.append((move_relation_field, 'in', add_obj_ids)) - to_add_ids = set(move_l_obj.search(cr, uid, domain, - context=context)) + domain.append((move_relation_field, 'in', add_objs)) + to_add = move_l_obj.search(domain) # The lines which are linked to another policy do not have to be # included in the run for this policy. - neg_obj_ids = my_obj.search( - cr, uid, - [('credit_policy_id', '!=', policy.id), - ('credit_policy_id', '!=', False)], - context=context) - if neg_obj_ids: + neg_objs = my_obj.search([('credit_policy_id', '!=', self.id), + ('credit_policy_id', '!=', False)]) + if neg_objs: domain = list(default_domain) - domain.append((move_relation_field, 'in', neg_obj_ids)) - to_remove_ids = set(move_l_obj.search(cr, uid, domain, - context=context)) - return to_add_ids, to_remove_ids + domain.append((move_relation_field, 'in', neg_objs)) + to_remove = move_l_obj.search(domain) + return to_add, to_remove - def _get_partner_related_lines(self, cr, uid, policy, controlling_date, - context=None): + @api.multi + @api.returns('account.move.line') + def _get_partner_related_lines(self, controlling_date): """ Get the move lines for a policy related to a partner. - :param browse_record policy: policy :param str controlling_date: date of credit control :param str model: name of the model where is defined a credit_policy_id :param str move_relation_field: name of the field in account.move.line which is a many2one to `model` - :return: set of ids to add in the process, set of ids to remove from + :return: recordset to add in the process, recordset to remove from the process """ - return self._move_lines_subset(cr, uid, policy, controlling_date, - 'res.partner', 'partner_id', - context=context) + return self._move_lines_subset(controlling_date, 'res.partner', + 'partner_id') - def _get_invoice_related_lines(self, cr, uid, policy, controlling_date, - context=None): + @api.multi + @api.returns('account.move.line') + def _get_invoice_related_lines(self, controlling_date): """ Get the move lines for a policy related to an invoice. - :param browse_record policy: policy :param str controlling_date: date of credit control :param str model: name of the model where is defined a credit_policy_id :param str move_relation_field: name of the field in account.move.line which is a many2one to `model` - :return: set of ids to add in the process, set of ids to remove from + :return: recordset to add in the process, recordset to remove from the process """ - return self._move_lines_subset(cr, uid, policy, controlling_date, - 'account.invoice', 'invoice', - context=context) + return self._move_lines_subset(controlling_date, 'account.invoice', + 'invoice') - def _get_move_lines_to_process(self, cr, uid, policy_id, controlling_date, - context=None): - """Build a list of move lines ids to include in a run + @api.multi + @api.returns('account.move.line') + def _get_move_lines_to_process(self, controlling_date): + """ Build a list of move lines ids to include in a run for a policy at a given date. - :param int/long policy: id of the policy :param str controlling_date: date of credit control - :return: set of ids to include in the run - """ - assert not (isinstance(policy_id, list) and len(policy_id) > 1), \ - "policy_id: only one id expected" - if isinstance(policy_id, list): - policy_id = policy_id[0] - - policy = self.browse(cr, uid, policy_id, context=context) + :return: recordset to include in the run + """ + self.ensure_one() # there is a priority between the lines, depicted by the calls below - # warning, side effect method called on lines - lines = self._due_move_lines(cr, uid, policy, controlling_date, - context=context) - add_ids, remove_ids = self._get_partner_related_lines(cr, uid, policy, - controlling_date, - context=context) - lines = lines.union(add_ids).difference(remove_ids) - add_ids, remove_ids = self._get_invoice_related_lines(cr, uid, policy, - controlling_date, - context=context) - lines = lines.union(add_ids).difference(remove_ids) + lines = self._due_move_lines(controlling_date) + to_add, to_remove = self._get_partner_related_lines(controlling_date) + lines = (lines | to_add) - to_remove + to_add, to_remove = self._get_invoice_related_lines(controlling_date) + lines = (lines | to_add) - to_remove return lines - def _lines_different_policy(self, cr, uid, policy_id, lines, context=None): - """ Return a set of move lines ids for which there is an existing credit line - but with a different policy. + @api.multi + @api.returns('account.move.line') + def _lines_different_policy(self, lines): + """ Return a set of move lines ids for which there is an + existing credit line but with a different policy. """ - different_lines = set() + self.ensure_one() + move_line_obj = self.env['account.move.line'] + different_lines = move_line_obj.browse() if not lines: return different_lines - assert not (isinstance(policy_id, list) and len(policy_id) > 1), \ - "policy_id: only one id expected" - if isinstance(policy_id, list): - policy_id = policy_id[0] + cr = self._cr cr.execute("SELECT move_line_id FROM credit_control_line" " WHERE policy_id != %s and move_line_id in %s" " AND manually_overridden IS false", - (policy_id, tuple(lines))) + (self.id, tuple(line.id for line in lines))) res = cr.fetchall() if res: - different_lines.update([x[0] for x in res]) + return move_line_obj.browse([row[0] for row in res]) return different_lines - def check_policy_against_account(self, cr, uid, account_id, policy_id, - context=None): - """Ensure that the policy corresponds to account relation""" - policy = self.browse(cr, uid, policy_id, context=context) - account = self.pool['account.account'].browse(cr, uid, account_id, - context=context) - policies_id = self.search(cr, uid, [], - context=context) - policies = self.browse(cr, uid, policies_id, context=context) + @api.multi + def check_policy_against_account(self, account_id): + """ Ensure that the policy corresponds to account relation """ + account = self.env['account.account'].browse(account_id) + policies = self.search([]) allowed = [x for x in policies if account in x.account_ids or x.do_nothing] - if policy not in allowed: + if self not in allowed: allowed_names = u"\n".join(x.name for x in allowed) - raise orm.except_orm( + raise api.Warning( _('You can only use a policy set on ' - 'account %s') % account.name, - _("Please choose one of the following " - "policies:\n %s") % allowed_names + 'account %s.\n' + 'Please choose one of the following ' + 'policies:\n %s') % (account.name, allowed_names) ) return True -class CreditControlPolicyLevel(orm.Model): +class CreditControlPolicyLevel(models.Model): """Define a policy level. A level allows to determine if a move line is due and the level of overdue of the line""" _name = "credit.control.policy.level" _order = 'level' _description = """A credit control policy level""" - _columns = { - 'policy_id': fields.many2one('credit.control.policy', - 'Related Policy', required=True), - 'name': fields.char('Name', size=128, required=True, - translate=True), - 'level': fields.integer('Level', required=True), - 'computation_mode': fields.selection( - [('net_days', 'Due Date'), - ('end_of_month', 'Due Date, End Of Month'), - ('previous_date', 'Previous Reminder')], - 'Compute Mode', - required=True - ), - - 'delay_days': fields.integer('Delay (in days)', required='True'), - 'email_template_id': fields.many2one('email.template', - 'Email Template', - required=True), - 'channel': fields.selection([('letter', 'Letter'), - ('email', 'Email')], - 'Channel', required=True), - 'custom_text': fields.text('Custom Message', - required=True, - translate=True), - 'custom_mail_text': fields.text('Custom Mail Message', - required=True, translate=True), - - } - - def _check_level_mode(self, cr, uid, rids, context=None): - """ The smallest level of a policy cannot be computed on the - "previous_date". Return False if this happens. """ - if isinstance(rids, (int, long)): - rids = [rids] - for level in self.browse(cr, uid, rids, context): - smallest_level_id = self.search( - cr, uid, - [('policy_id', '=', level.policy_id.id)], - order='level asc', limit=1, context=context) - smallest_level = self.browse(cr, uid, smallest_level_id[0], - context) - if smallest_level.computation_mode == 'previous_date': - return False - return True + name = fields.Char(string='Name', required=True, translate=True) + policy_id = fields.Many2one('credit.control.policy', + string='Related Policy', + required=True) + level = fields.Integer(string='Level', required=True) + computation_mode = fields.Selection( + [('net_days', 'Due Date'), + ('end_of_month', 'Due Date, End Of Month'), + ('previous_date', 'Previous Reminder')], + string='Compute Mode', + required=True + ) + delay_days = fields.Integer(string='Delay (in days)', required=True) + email_template_id = fields.Many2one('email.template', + string='Email Template', + required=True) + channel = fields.Selection([('letter', 'Letter'), + ('email', 'Email')], + string='Channel', + required=True) + custom_text = fields.Text(string='Custom Message', + required=True, + translate=True) + custom_mail_text = fields.Text(string='Custom Mail Message', + required=True, translate=True) _sql_constraint = [('unique level', 'UNIQUE (policy_id, level)', 'Level must be unique per policy')] - _constraints = [(_check_level_mode, - 'The smallest level can not be of type Previous Reminder', - ['level'])] + @api.one + @api.constrains('level', 'computation_mode') + def _check_level_mode(self): + """ The smallest level of a policy cannot be computed on the + "previous_date". + """ + smallest_level = self.search([('policy_id', '=', self.policy_id.id)], + order='level asc', limit=1) + if smallest_level.computation_mode == 'previous_date': + return api.ValidationError(_('The smallest level can not be of ' + 'type Previous Reminder')) - def _previous_level(self, cr, uid, policy_level, context=None): + @api.multi + def _previous_level(self): """ For one policy level, returns the id of the previous level If there is no previous level, it returns None, it means that's the first policy level - :param browse_record policy_level: policy level - :return: previous level id or None if there is no previous level + :return: previous level or None if there is no previous level """ - previous_level_ids = self.search( - cr, - uid, - [('policy_id', '=', policy_level.policy_id.id), - ('level', '<', policy_level.level)], - order='level desc', - limit=1, - context=context) - return previous_level_ids[0] if previous_level_ids else None + self.ensure_one() + previous_levels = self.search([('policy_id', '=', self.policy_id.id), + ('level', '<', self.level)], + order='level desc', + limit=1) + if not previous_levels: + return None + return previous_levels # ----- sql time related methods --------- - def _net_days_get_boundary(self): + @staticmethod + def _net_days_get_boundary(): return (" (mv_line.date_maturity + %(delay)s)::date <= " "date(%(controlling_date)s)") - def _end_of_month_get_boundary(self): + @staticmethod + def _end_of_month_get_boundary(): return ("(date_trunc('MONTH', (mv_line.date_maturity + %(delay)s))+" "INTERVAL '1 MONTH - 1 day')::date" "<= date(%(controlling_date)s)") - def _previous_date_get_boundary(self): + @staticmethod + def _previous_date_get_boundary(): return "(cr_line.date + %(delay)s)::date <= date(%(controlling_date)s)" - def _get_sql_date_boundary_for_computation_mode(self, cr, uid, level, - controlling_date, - context=None): - """Return a where clauses statement for the given - controlling date and computation mode of the level""" - fname = "_%s_get_boundary" % (level.computation_mode,) + @api.multi + def _get_sql_date_boundary_for_computation_mode(self, controlling_date): + """ Return a where clauses statement for the given controlling + date and computation mode of the level + """ + self.ensure_one() + fname = "_%s_get_boundary" % (self.computation_mode, ) if hasattr(self, fname): fnc = getattr(self, fname) return fnc() else: raise NotImplementedError( _('Can not get function for computation mode: ' - '%s is not implemented') % (fname,) + '%s is not implemented') % (fname, ) ) # ----------------------------------------- - def _get_first_level_move_line_ids(self, cr, uid, level, controlling_date, - lines, context=None): - """Retrieve all the move lines that are linked to a first level. - We use Raw SQL for performance. Security rule where applied in - policy object when the first set of lines were retrieved""" - level_lines = set() + @api.multi + @api.returns('account.move.line') + def _get_first_level_move_lines(self, controlling_date, lines): + """ Retrieve all the move lines that are linked to a first level. + We use Raw SQL for performance. Security rule where applied in + policy object when the first set of lines were retrieved + """ + self.ensure_one() + move_line_obj = self.env['account.move.line'] if not lines: - return level_lines + return move_line_obj.browse() + cr = self._cr sql = ("SELECT DISTINCT mv_line.id\n" " FROM account_move_line mv_line\n" " WHERE mv_line.id in %(line_ids)s\n" @@ -385,26 +339,28 @@ class CreditControlPolicyLevel(orm.Model): " AND state NOT IN ('draft', 'ignored'))" " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n") sql += " AND" - sql += self._get_sql_date_boundary_for_computation_mode( - cr, uid, level, - controlling_date, context - ) + _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode + sql += _get_sql_date_part(controlling_date) data_dict = {'controlling_date': controlling_date, - 'line_ids': tuple(lines), - 'delay': level.delay_days} + 'line_ids': tuple(line.id for line in lines), + 'delay': self.delay_days} + print cr.mogrify(sql, data_dict) cr.execute(sql, data_dict) res = cr.fetchall() if res: - level_lines.update([x[0] for x in res]) - return level_lines + return move_line_obj.browse([row[0] for row in res]) + return move_line_obj.browse() - def _get_other_level_move_line_ids(self, cr, uid, level, controlling_date, - lines, context=None): + @api.multi + @api.returns('account.move.line') + def _get_other_level_move_lines(self, controlling_date, lines): """ Retrieve the move lines for other levels than first level. """ - level_lines = set() + self.ensure_one() + move_line_obj = self.env['account.move.line'] if not lines: - return level_lines + return move_line_obj.browse() + cr = self._cr sql = ("SELECT mv_line.id\n" " FROM account_move_line mv_line\n" " JOIN credit_control_line cr_line\n" @@ -424,41 +380,31 @@ class CreditControlPolicyLevel(orm.Model): " AND cr_line.state NOT IN ('draft', 'ignored')\n" " AND mv_line.id in %(line_ids)s\n") sql += " AND " - sql += self._get_sql_date_boundary_for_computation_mode( - cr, uid, level, - controlling_date, - context - ) - previous_level_id = self._previous_level(cr, uid, level, - context=context) - previous_level = self.browse(cr, uid, previous_level_id, - context=context) + _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode + sql += _get_sql_date_part(controlling_date) + previous_level = self._previous_level() data_dict = {'controlling_date': controlling_date, - 'line_ids': tuple(lines), - 'delay': level.delay_days, + 'line_ids': tuple(line.id for line in lines), + 'delay': self.delay_days, 'previous_level': previous_level.level} # print cr.mogrify(sql, data_dict) cr.execute(sql, data_dict) res = cr.fetchall() if res: - level_lines.update([x[0] for x in res]) - return level_lines + return move_line_obj.browse([row[0] for row in res]) + return move_line_obj.browse() - def get_level_lines(self, cr, uid, level_id, controlling_date, lines, - context=None): - """get all move lines in entry lines that match the current level""" - assert not (isinstance(level_id, list) and len(level_id) > 1), \ - "level_id: only one id expected" - if isinstance(level_id, list): - level_id = level_id[0] - matching_lines = set() - level = self.browse(cr, uid, level_id, context=context) - if self._previous_level(cr, uid, level, context=context) is None: - method = self._get_first_level_move_line_ids + @api.multi + @api.returns('account.move.line') + def get_level_lines(self, controlling_date, lines): + """ get all move lines in entry lines that match the current level """ + self.ensure_one() + move_line_obj = self.env['account.move.line'] + matching_lines = move_line_obj.browse() + if self._previous_level() is None: + method = self._get_first_level_move_lines else: - method = self._get_other_level_move_line_ids - matching_lines.update(method(cr, uid, level, - controlling_date, lines, - context=context)) + method = self._get_other_level_move_lines + matching_lines += method(controlling_date, lines) return matching_lines diff --git a/account_credit_control/run.py b/account_credit_control/run.py index fd32be1dc..e25518f1c 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -20,143 +20,113 @@ ############################################################################## import logging -from openerp.osv import orm, fields +from openerp import models, fields, api from openerp.tools.translate import _ logger = logging.getLogger('credit.control.run') -class CreditControlRun(orm.Model): - """Credit Control run generate all credit control lines and reject""" +class CreditControlRun(models.Model): + """ Credit Control run generate all credit control lines and reject """ _name = "credit.control.run" _rec_name = 'date' - _description = """Credit control line generator""" - _columns = { - 'date': fields.date('Controlling Date', required=True), - 'policy_ids': fields.many2many( - 'credit.control.policy', - rel="credit_run_policy_rel", - id1='run_id', id2='policy_id', - string='Policies', - readonly=True, - states={'draft': [('readonly', False)]} - ), + _description = "Credit control line generator" - 'report': fields.text('Report', readonly=True), + date = fields.Date(string='Controlling Date', required=True) - 'state': fields.selection([('draft', 'Draft'), - ('done', 'Done')], - string='State', - required=True, - readonly=True), + @api.model + def _get_policies(self): + return self.env['credit.control.policy'].search([]) - 'manual_ids': fields.many2many( - 'account.move.line', - rel="credit_runreject_rel", - string='Lines to handle manually', - help=('If a credit control line has been generated' - 'on a policy and the policy has been changed ' - 'in the meantime, it has to be handled ' - 'manually'), - readonly=True - ), - } + policy_ids = fields.Many2many( + 'credit.control.policy', + rel="credit_run_policy_rel", + id1='run_id', id2='policy_id', + string='Policies', + readonly=True, + states={'draft': [('readonly', False)]}, + default=_get_policies, + ) + report = fields.Text(string='Report', readonly=True, copy=False) + state = fields.Selection([('draft', 'Draft'), + ('done', 'Done')], + string='State', + required=True, + readonly=True, + default='draft') + manual_ids = fields.Many2many( + 'account.move.line', + rel="credit_runreject_rel", + string='Lines to handle manually', + help='If a credit control line has been generated' + 'on a policy and the policy has been changed ' + 'in the meantime, it has to be handled ' + 'manually', + readonly=True, + copy=False, + ) - def copy_data(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - else: - default = default.copy() - default.update({ - 'report': False, - 'manual_ids': False, - }) - return super(CreditControlRun, self).copy_data( - cr, uid, id, default=default, context=context) - - def _get_policies(self, cr, uid, context=None): - return self.pool['credit.control.policy'].search(cr, uid, [], - context=context) - - _defaults = {'state': 'draft', - 'policy_ids': _get_policies} - - def _check_run_date(self, cr, uid, ids, controlling_date, context=None): - """Ensure that there is no credit line in the future + @api.multi + def _check_run_date(self, controlling_date): + """ Ensure that there is no credit line in the future using controlling_date """ - run_obj = self.pool['credit.control.run'] - runs = run_obj.search(cr, uid, [('date', '>', controlling_date)], - order='date DESC', limit=1, context=context) + runs = self.search([('date', '>', controlling_date)], + order='date DESC', limit=1) if runs: - run = run_obj.browse(cr, uid, runs[0], context=context) - raise orm.except_orm(_('Error'), - _('A run has already been executed more ' - 'recently than %s') % (run.date)) + raise api.Warning(_('A run has already been executed more ' + 'recently than %s') % (runs.date)) - line_obj = self.pool['credit.control.line'] - lines = line_obj.search(cr, uid, [('date', '>', controlling_date)], - order='date DESC', limit=1, context=context) + line_obj = self.env['credit.control.line'] + lines = line_obj.search([('date', '>', controlling_date)], + order='date DESC', limit=1) if lines: - line = line_obj.browse(cr, uid, lines[0], context=context) - raise orm.except_orm(_('Error'), - _('A credit control line more ' - 'recent than %s exists at %s') % - (controlling_date, line.date)) - return True + raise api.Warning(_('A credit control line more ' + 'recent than %s exists at %s') % + (controlling_date, lines.date)) - def _generate_credit_lines(self, cr, uid, run_id, context=None): + @api.multi + @api.returns('credit.control.line') + def _generate_credit_lines(self): """ Generate credit control lines. """ - cr_line_obj = self.pool.get('credit.control.line') - assert not (isinstance(run_id, list) and len(run_id) > 1), \ - "run_id: only one id expected" - if isinstance(run_id, list): - run_id = run_id[0] + self.ensure_one() + cr_line_obj = self.env['credit.control.line'] + move_line_obj = self.env['account.move.line'] + manually_managed_lines = move_line_obj.browse() + self._check_run_date(self.date) - run = self.browse(cr, uid, run_id, context=context) - manually_managed_lines = set() # line who changed policy - credit_line_ids = [] # generated lines - run._check_run_date(run.date, context=context) - - policies = run.policy_ids + policies = self.policy_ids if not policies: - raise orm.except_orm(_('Error'), - _('Please select a policy')) + raise api.Warning(_('Please select a policy')) report = '' - generated_ids = [] + generated = [] for policy in policies: if policy.do_nothing: continue - lines = policy._get_move_lines_to_process(run.date, - context=context) - manual_lines = policy._lines_different_policy(lines, - context=context) - lines.difference_update(manual_lines) - manually_managed_lines.update(manual_lines) - policy_generated_ids = [] + lines = policy._get_move_lines_to_process(self.date) + manual_lines = policy._lines_different_policy(lines) + lines -= manual_lines + manually_managed_lines += manual_lines + policy_lines_generated = cr_line_obj.browse() if lines: # policy levels are sorted by level # so iteration is in the correct order for level in reversed(policy.level_ids): - level_lines = level.get_level_lines(run.date, lines, - context=context) - policy_generated_ids += \ + level_lines = level.get_level_lines(self.date, lines) + policy_lines_generated += \ cr_line_obj.create_or_update_from_mv_lines( - cr, uid, - [], - list(level_lines), + level_lines, level.id, - run.date, - context=context + self.date, ) - generated_ids.extend(policy_generated_ids) - if policy_generated_ids: - report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \ - (policy.name, len(policy_generated_ids)) - credit_line_ids += policy_generated_ids + generated += policy_lines_generated + if policy_lines_generated: + report += (_("Policy \"%s\" has generated %d Credit " + "Control Lines.\n") % + (policy.name, len(policy_lines_generated))) else: report += _( "Policy \"%s\" has not generated any " @@ -166,24 +136,24 @@ class CreditControlRun(orm.Model): vals = {'state': 'done', 'report': report, 'manual_ids': [(6, 0, manually_managed_lines)]} - run.write(vals, context=context) - return generated_ids + self.write(vals) + return generated - def generate_credit_lines(self, cr, uid, run_id, context=None): - """Generate credit control lines + @api.multi + def generate_credit_lines(self): + """ Generate credit control lines Lock the ``credit_control_run`` Postgres table to avoid concurrent calls of this method. """ try: - cr.execute('SELECT id FROM credit_control_run' - ' LIMIT 1 FOR UPDATE NOWAIT') + self._cr.execute('SELECT id FROM credit_control_run' + ' LIMIT 1 FOR UPDATE NOWAIT') except Exception: # In case of exception openerp will do a rollback # for us and free the lock - raise orm.except_orm(_('Error'), - _('A credit control run is already running' - ' in background, please try later.')) + raise api.Warning(_('A credit control run is already running' + ' in background, please try later.')) - self._generate_credit_lines(cr, uid, run_id, context) + self._generate_credit_lines() return True From 1399d758dd48f19d158efc0100673331aef79111 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 28 Oct 2014 16:25:15 +0100 Subject: [PATCH 08/26] Pass records instead of id in arguments --- account_credit_control/line.py | 5 ++--- account_credit_control/partner.py | 2 +- account_credit_control/policy.py | 3 +-- account_credit_control/run.py | 10 ++++------ 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/account_credit_control/line.py b/account_credit_control/line.py index 02d1dfbd4..16fd407e5 100644 --- a/account_credit_control/line.py +++ b/account_credit_control/line.py @@ -154,7 +154,7 @@ class CreditControlLine(models.Model): return data @api.model - def create_or_update_from_mv_lines(self, lines, level_id, controlling_date, + def create_or_update_from_mv_lines(self, lines, level, controlling_date, check_tolerance=True): """ Create or update line based on levels @@ -164,7 +164,7 @@ class CreditControlLine(models.Model): of open amount. :param lines: move.line id recordset - :param level_id: credit.control.policy.level id + :param level: credit.control.policy.level record :param controlling_date: date string of the credit controlling date. Generally it should be the same as create date @@ -187,7 +187,6 @@ class CreditControlLine(models.Model): tolerance[currency.id] = currency.compute(tolerance_base, user_currency) - level = level_obj.browse(level_id) new_lines = self.browse() for move_line in lines: open_amount = move_line.amount_residual_currency diff --git a/account_credit_control/partner.py b/account_credit_control/partner.py index 9255e7ff4..76f2c24cf 100644 --- a/account_credit_control/partner.py +++ b/account_credit_control/partner.py @@ -52,7 +52,7 @@ class ResPartner(models.Model): account = partner.property_account_receivable policy = partner.credit_policy_id try: - policy.check_policy_against_account(account.id) + policy.check_policy_against_account(account) except api.Warning as err: # constrains should raise ValidationError exceptions raise api.ValidationError(err) diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index 4806cdf95..c5a27853f 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -194,9 +194,8 @@ class CreditControlPolicy(models.Model): return different_lines @api.multi - def check_policy_against_account(self, account_id): + def check_policy_against_account(self, account): """ Ensure that the policy corresponds to account relation """ - account = self.env['account.account'].browse(account_id) policies = self.search([]) allowed = [x for x in policies if account in x.account_ids or x.do_nothing] diff --git a/account_credit_control/run.py b/account_credit_control/run.py index e25518f1c..ae64ac397 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -114,14 +114,12 @@ class CreditControlRun(models.Model): if lines: # policy levels are sorted by level # so iteration is in the correct order + create = cr_line_obj.create_or_update_from_mv_lines for level in reversed(policy.level_ids): level_lines = level.get_level_lines(self.date, lines) - policy_lines_generated += \ - cr_line_obj.create_or_update_from_mv_lines( - level_lines, - level.id, - self.date, - ) + policy_lines_generated += create(level_lines, + level, + self.date) generated += policy_lines_generated if policy_lines_generated: report += (_("Policy \"%s\" has generated %d Credit " From 0dfd7068af95b8d7b2af3c4da9acf287d02b0f99 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 29 Oct 2014 12:13:16 +0100 Subject: [PATCH 09/26] New API: wizards and corrections --- account_credit_control/invoice.py | 3 +- account_credit_control/line.py | 19 +- account_credit_control/policy.py | 13 +- account_credit_control/run.py | 5 +- .../wizard/credit_control_communication.py | 240 ++++++++---------- .../wizard/credit_control_emailer.py | 86 +++---- .../wizard/credit_control_marker.py | 117 ++++----- .../wizard/credit_control_policy_changer.py | 190 ++++++-------- .../credit_control_policy_changer_view.xml | 3 +- .../wizard/credit_control_printer.py | 108 +++----- 10 files changed, 322 insertions(+), 462 deletions(-) diff --git a/account_credit_control/invoice.py b/account_credit_control/invoice.py index 361ffa630..f74be82c0 100644 --- a/account_credit_control/invoice.py +++ b/account_credit_control/invoice.py @@ -18,8 +18,7 @@ # along with this program. If not, see . # ############################################################################## -from openerp import models, fields, api -from openerp.tools.translate import _ +from openerp import models, fields, api, _ class AccountInvoice(models.Model): diff --git a/account_credit_control/line.py b/account_credit_control/line.py index 16fd407e5..43782bb26 100644 --- a/account_credit_control/line.py +++ b/account_credit_control/line.py @@ -20,8 +20,7 @@ ############################################################################## import logging -from openerp import models, fields, api -from openerp.tools.translate import _ +from openerp import models, fields, api, _ logger = logging.getLogger('credit.line.control') @@ -104,15 +103,18 @@ class CreditControlLine(models.Model): required=True, readonly=True) - account_id = fields.Many2one(related='move_line_id.account_id', + account_id = fields.Many2one('account.account', + related='move_line_id.account_id', store=True, readonly=True) - currency_id = fields.Many2one(related='move_line_id.currency_id', + currency_id = fields.Many2one('res.currency', + related='move_line_id.currency_id', store=True, readonly=True) - company_id = fields.Many2one(related='move_line_id.company_id', + company_id = fields.Many2one('res.company', + related='move_line_id.company_id', store=True, readonly=True) @@ -123,11 +125,13 @@ class CreditControlLine(models.Model): readonly=True, states={'draft': [('readonly', False)]}) - policy_id = fields.Many2one(related='policy_level_id.policy_id', + policy_id = fields.Many2one('credit.control.policy', + related='policy_level_id.policy_id', store=True, readonly=True) - level = fields.Integer(related='policy_level_id.level', + level = fields.Integer('credit.control.policy.level', + related='policy_level_id.level', store=True, readonly=True) @@ -176,7 +180,6 @@ class CreditControlLine(models.Model): :returns: recordset of created credit lines """ currency_obj = self.env['res.currency'] - level_obj = self.env['credit.control.policy.level'] user = self.env.user currencies = currency_obj.search([]) diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index c5a27853f..59cac0612 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -18,8 +18,7 @@ # along with this program. If not, see . # ############################################################################## -from openerp import models, fields, api -from openerp.tools.translate import _ +from openerp import models, fields, api, _ class CreditControlPolicy(models.Model): @@ -50,8 +49,7 @@ class CreditControlPolicy(models.Model): def _move_lines_domain(self, controlling_date): """ Build the default domain for searching move lines """ self.ensure_one() - account_ids = [a.id for a in self.account_ids] - return [('account_id', 'in', account_ids), + return [('account_id', 'in', self.account_ids.ids), ('date_maturity', '<=', controlling_date), ('reconcile_id', '=', False), ('partner_id', '!=', False)] @@ -187,7 +185,7 @@ class CreditControlPolicy(models.Model): cr.execute("SELECT move_line_id FROM credit_control_line" " WHERE policy_id != %s and move_line_id in %s" " AND manually_overridden IS false", - (self.id, tuple(line.id for line in lines))) + (self.id, tuple(lines.ids))) res = cr.fetchall() if res: return move_line_obj.browse([row[0] for row in res]) @@ -341,9 +339,8 @@ class CreditControlPolicyLevel(models.Model): _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode sql += _get_sql_date_part(controlling_date) data_dict = {'controlling_date': controlling_date, - 'line_ids': tuple(line.id for line in lines), + 'line_ids': tuple(lines.ids), 'delay': self.delay_days} - print cr.mogrify(sql, data_dict) cr.execute(sql, data_dict) res = cr.fetchall() if res: @@ -383,7 +380,7 @@ class CreditControlPolicyLevel(models.Model): sql += _get_sql_date_part(controlling_date) previous_level = self._previous_level() data_dict = {'controlling_date': controlling_date, - 'line_ids': tuple(line.id for line in lines), + 'line_ids': tuple(lines.ids), 'delay': self.delay_days, 'previous_level': previous_level.level} diff --git a/account_credit_control/run.py b/account_credit_control/run.py index ae64ac397..740c811d4 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -20,8 +20,7 @@ ############################################################################## import logging -from openerp import models, fields, api -from openerp.tools.translate import _ +from openerp import models, fields, api, _ logger = logging.getLogger('credit.control.run') @@ -102,7 +101,7 @@ class CreditControlRun(models.Model): raise api.Warning(_('Please select a policy')) report = '' - generated = [] + generated = cr_line_obj.browse() for policy in policies: if policy.do_nothing: continue diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py index 8c72c0345..e988a229b 100644 --- a/account_credit_control/wizard/credit_control_communication.py +++ b/account_credit_control/wizard/credit_control_communication.py @@ -19,14 +19,14 @@ # ############################################################################## import logging +from openerp import models, fields, api from openerp import netsvc -from openerp.osv import orm, fields logger = logging.getLogger('credit.control.line.mailing') -class CreditCommunication(orm.TransientModel): - """Shell class used to provide a base model to email template and reporting. +class CreditCommunication(models.TransientModel): + """Shell class used to provide a base model to email template and reporting Il use this approche in version 7 a browse record will exist even if not saved @@ -34,85 +34,64 @@ class CreditCommunication(orm.TransientModel): _name = "credit.control.communication" _description = "credit control communication" _rec_name = 'partner_id' - _columns = { - 'partner_id': fields.many2one( - 'res.partner', - 'Partner', - required=True - ), - 'current_policy_level': fields.many2one( - 'credit.control.policy.level', - 'Level', - required=True - ), + partner_id = fields.Many2one('res.partner', 'Partner', required=True) - 'credit_control_line_ids': fields.many2many( - 'credit.control.line', - rel='comm_credit_rel', - string='Credit Lines' - ), + current_policy_level = fields.Many2one('credit.control.policy.level', + 'Level', + required=True) + credit_control_line_ids = fields.Many2many('credit.control.line', + rel='comm_credit_rel', + string='Credit Lines') - 'company_id': fields.many2one( - 'res.company', - 'Company', - required=True - ), + @api.model + def _get_company(self): + company_obj = self.env['res.company'] + return company_obj._company_default_get('credit.control.policy') - 'user_id': fields.many2one('res.users', 'User') - } + company_id = fields.Many2one('res.company', + string='Company', + default=_get_company, + required=True) + user_id = fields.Many2one('res.users', + default=lambda self: self.env.user, + string='User') - def _get_comp(self, cr, uid, context=None): - return self.pool.get('res.company')._company_default_get( - cr, uid, - 'credit.control.policy', - context=context - ) - - _defaults = { - 'company_id': _get_comp, - 'user_id': lambda s, cr, uid, c: uid - } - - def get_email(self, cr, uid, com_id, context=None): - """Return a valid email for customer""" - if isinstance(com_id, list): - assert len(com_id) == 1, "get_email only support one id as param." - com_id = com_id[0] - form = self.browse(cr, uid, com_id, context=context) - contact = form.get_contact_address() + @api.multi + def get_email(self): + """ Return a valid email for customer """ + self.ensure_one() + contact = self.get_contact_address() return contact.email - def get_contact_address(self, cr, uid, com_id, context=None): - pmod = self.pool['res.partner'] - if isinstance(com_id, list): - com_id = com_id[0] - form = self.browse(cr, uid, com_id, context=context) - part = form.partner_id - add_ids = part.address_get(adr_pref=['invoice']) or {} + @api.multi + @api.returns('res.partner') + def get_contact_address(self): + self.ensure_one() + partner_obj = self.env['res.partner'] + partner = self.partner_id + add_ids = partner.address_get(adr_pref=['invoice']) or {} add_id = add_ids.get('invoice', add_ids.get('default', False)) - return pmod.browse(cr, uid, add_id, context) + return partner_obj.browse(add_id) - def _get_credit_lines(self, cr, uid, line_ids, partner_id, level_id, - context=None): - """Return credit lines related to a partner and a policy level""" - cr_line_obj = self.pool.get('credit.control.line') - cr_l_ids = cr_line_obj.search(cr, - uid, - [('id', 'in', line_ids), + @api.model + @api.returns('credit.control.line') + def _get_credit_lines(self, line_ids, partner_id, level_id): + """ Return credit lines related to a partner and a policy level """ + cr_line_obj = self.env['credit.control.line'] + cr_lines = cr_line_obj.search([('id', 'in', line_ids), ('partner_id', '=', partner_id), - ('policy_level_id', '=', level_id)], - context=context) - return cr_l_ids + ('policy_level_id', '=', level_id)]) + return cr_lines - def _generate_comm_from_credit_line_ids(self, cr, uid, line_ids, - context=None): - """Aggregate credit control line by partner, level, and currency + @api.model + def _generate_comm_from_credit_lines(self, lines): + """ Aggregate credit control line by partner, level, and currency It also generate a communication object per aggregation. """ - if not line_ids: + if not lines: return [] - comms = [] + comms = self.browse() sql = ( "SELECT distinct partner_id, policy_level_id, " " credit_control_line.currency_id, " @@ -124,54 +103,46 @@ class CreditCommunication(orm.TransientModel): " ORDER by credit_control_policy_level.level, " " credit_control_line.currency_id" ) - - cr.execute(sql, (tuple(line_ids),)) + cr = self._cr + cr.execute(sql, (tuple(lines.ids), )) res = cr.dictfetchall() for level_assoc in res: data = {} - data['credit_control_line_ids'] = [ - ( - 6, 0, self._get_credit_lines( - cr, uid, line_ids, - level_assoc['partner_id'], - level_assoc['policy_level_id'], - context=context - ) - ) - ] + level_lines = self._get_credit_lines(lines.ids, + level_assoc['partner_id'], + level_assoc['policy_level_id'] + ) + + data['credit_control_line_ids'] = [(6, 0, level_lines.ids)] data['partner_id'] = level_assoc['partner_id'] data['current_policy_level'] = level_assoc['policy_level_id'] - comm_id = self.create(cr, uid, data, context=context) - comms.append(self.browse(cr, uid, comm_id, context=context)) + comm = self.create(data) + comms += comm return comms - def _generate_emails(self, cr, uid, comms, context=None): - """Generate email message using template related to level""" - cr_line_obj = self.pool.get('credit.control.line') - email_temp_obj = self.pool.get('email.template') - email_message_obj = self.pool.get('mail.mail') - att_obj = self.pool.get('ir.attachment') - email_ids = [] - essential_fields = ['subject', - 'body_html', - 'email_from', - 'email_to'] - - for comm in comms: - # we want to use a local cr in order to send the maximum - # of email - template = comm.current_policy_level.email_template_id.id - email_values = {} - cl_ids = [cl.id for cl in comm.credit_control_line_ids] - email_values = email_temp_obj.generate_email(cr, uid, - template, - comm.id, - context=context) + @api.multi + @api.returns('mail.mail') + def _generate_emails(self): + """ Generate email message using template related to level """ + email_message_obj = self.env['mail.mail'] + email_template_obj = self.pool['email.template'] + att_obj = self.env['ir.attachment'] + emails = email_message_obj.browse() + required_fields = ['subject', + 'body_html', + 'email_from', + 'email_to'] + cr, uid, context = self.env.cr, self.env.uid, self.env.context + for comm in self: + template = comm.current_policy_level.email_template_id + email_values = email_template_obj.generate_email(cr, uid, + template.id, + comm.id, + context=context) email_values['body_html'] = email_values['body'] email_values['type'] = 'email' - email_id = email_message_obj.create(cr, uid, email_values, - context=context) + email = email_message_obj.create(email_values) state = 'sent' # The mail will not be send, however it will be in the pool, in an @@ -179,16 +150,13 @@ class CreditCommunication(orm.TransientModel): # the credit control line # and put this latter in a `email_error` state we not that we have # a problem with the email - if any(not email_values.get(field) for field in essential_fields): + if not all(email_values.get(field) for field in required_fields): state = 'email_error' - cr_line_obj.write( - cr, uid, cl_ids, - {'mail_message_id': email_id, - 'state': state}, - context=context - ) - att_ids = [] + comm.credit_control_line_ids.write({'mail_message_id': email.id, + 'state': state}) + + attachments = att_obj.browse() for att in email_values.get('attachments', []): attach_fname = att[0] attach_datas = att[1] @@ -197,33 +165,35 @@ class CreditCommunication(orm.TransientModel): 'datas': attach_datas, 'datas_fname': attach_fname, 'res_model': 'mail.mail', - 'res_id': email_id, + 'res_id': email.id, 'type': 'binary', } - att_ids.append( - att_obj.create(cr, uid, data_attach, - context=context) - ) - email_message_obj.write(cr, uid, [email_id], - {'attachment_ids': [(6, 0, att_ids)]}, - context=context) - email_ids.append(email_id) - return email_ids + attachments += att_obj.create(data_attach) + email.write({'attachment_ids': [(6, 0, attachments.ids)]}) + emails += email + return emails - def _generate_report(self, cr, uid, comms, context=None): - """Will generate a report by inserting mako template + @api.multi + def _generate_report(self): + """ Will generate a report by inserting mako template of related policy template """ + return '' service = netsvc.LocalService('report.credit_control_summary') - ids = [x.id for x in comms] - result, format = service.create(cr, uid, ids, {}, {}) + cr, uid = self.env.cr, self.env.uid + result, format = service.create(cr, uid, self.ids, {}, {}) return result + # TODO + # return self.env['report'].get_pdf(self, 'credit_control_summary') - def _mark_credit_line_as_sent(self, cr, uid, comms, context=None): - line_ids = [] - for comm in comms: - line_ids += [x.id for x in comm.credit_control_line_ids] - l_obj = self.pool.get('credit.control.line') - l_obj.write(cr, uid, line_ids, {'state': 'sent'}, context=context) - return line_ids + @api.multi + @api.returns('credit.control.line') + def _mark_credit_line_as_sent(self): + line_obj = self.env['credit.control.line'] + lines = line_obj.browse() + for comm in self: + lines += comm.credit_control_line_ids + + lines.write({'state': 'sent'}) + return lines diff --git a/account_credit_control/wizard/credit_control_emailer.py b/account_credit_control/wizard/credit_control_emailer.py index 17528ce71..934c016df 100644 --- a/account_credit_control/wizard/credit_control_emailer.py +++ b/account_credit_control/wizard/credit_control_emailer.py @@ -19,71 +19,51 @@ # ############################################################################## -from openerp.osv import orm, fields -from openerp.tools.translate import _ +from openerp import models, fields, api, _ -class CreditControlEmailer(orm.TransientModel): - """Send emails for each selected credit control lines.""" +class CreditControlEmailer(models.TransientModel): + """ Send emails for each selected credit control lines. """ _name = "credit.control.emailer" _description = """Mass credit line emailer""" _rec_name = 'id' - def _get_line_ids(self, cr, uid, context=None): - if context is None: - context = {} - res = False - if (context.get('active_model') == 'credit.control.line' and + @api.model + def _get_line_ids(self): + context = self.env.context + if not (context.get('active_model') == 'credit.control.line' and context.get('active_ids')): - res = self._filter_line_ids( - cr, uid, - context['active_ids'], - context=context - ) - return res + return False + line_obj = self.env['credit.control.line'] + lines = line_obj.browse(context['active_id']) + return self._filter_lines(lines) - _columns = { - 'line_ids': fields.many2many( - 'credit.control.line', - string='Credit Control Lines', - domain=[('state', '=', 'to_be_sent'), - ('channel', '=', 'email')] - ), - } + line_ids = fields.Many2many('credit.control.line', + string='Credit Control Lines', + default=_get_line_ids, + domain=[('state', '=', 'to_be_sent'), + ('channel', '=', 'email')]) - _defaults = { - 'line_ids': _get_line_ids, - } - - def _filter_line_ids(self, cr, uid, active_ids, context=None): - """filter lines to use in the wizard""" - line_obj = self.pool.get('credit.control.line') + @api.model + @api.returns('credit.control.line') + def _filter_lines(self, lines): + """ filter lines to use in the wizard """ + line_obj = self.env['credit.control.line'] domain = [('state', '=', 'to_be_sent'), - ('id', 'in', active_ids), + ('id', 'in', lines.ids), ('channel', '=', 'email')] - return line_obj.search(cr, uid, domain, context=context) + return line_obj.search(domain) - def email_lines(self, cr, uid, wiz_id, context=None): - assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \ - "wiz_id: only one id expected" - comm_obj = self.pool.get('credit.control.communication') - if isinstance(wiz_id, list): - wiz_id = wiz_id[0] - form = self.browse(cr, uid, wiz_id, context) + @api.multi + def email_lines(self): + self.ensure_one() + if not self.line_ids: + raise api.Warning(_('No credit control lines selected.')) - if not form.line_ids: - raise orm.except_orm( - _('Error'), - _('No credit control lines selected.') - ) + comm_obj = self.env['credit.control.communication'] - line_ids = [l.id for l in form.line_ids] - filtered_ids = self._filter_line_ids( - cr, uid, line_ids, context - ) - comms = comm_obj._generate_comm_from_credit_line_ids( - cr, uid, filtered_ids, context=context - ) - comm_obj._generate_emails(cr, uid, comms, context=context) - return {} + filtered_lines = self._filter_lines(self.line_ids) + comms = comm_obj._generate_comm_from_credit_lines(filtered_lines) + comms._generate_emails() + return {'type': 'ir.actions.act_window_close'} diff --git a/account_credit_control/wizard/credit_control_marker.py b/account_credit_control/wizard/credit_control_marker.py index 525ba930e..b0b1f75aa 100644 --- a/account_credit_control/wizard/credit_control_marker.py +++ b/account_credit_control/wizard/credit_control_marker.py @@ -18,90 +18,69 @@ # along with this program. If not, see . # ############################################################################## -from openerp.osv import orm, fields -from openerp.tools.translate import _ +from openerp import models, fields, api, _ -class CreditControlMarker(orm.TransientModel): - """Change the state of lines in mass""" +class CreditControlMarker(models.TransientModel): + """ Change the state of lines in mass """ _name = 'credit.control.marker' _description = 'Mass marker' - def _get_line_ids(self, cr, uid, context=None): - if context is None: - context = {} - res = False - if (context.get('active_model') == 'credit.control.line' and + @api.model + def _get_line_ids(self): + context = self.env.context + if not (context.get('active_model') == 'credit.control.line' and context.get('active_ids')): - res = self._filter_line_ids( - cr, uid, - context['active_ids'], - context=context - ) - return res + return False + line_obj = self.env['credit.control.line'] + lines = line_obj.browse(context['active_id']) + return self._filter_lines(lines) - _columns = { - 'name': fields.selection([('ignored', 'Ignored'), - ('to_be_sent', 'Ready To Send'), - ('sent', 'Done')], - 'Mark as', - required=True), - 'line_ids': fields.many2many( - 'credit.control.line', - string='Credit Control Lines', - domain="[('state', '!=', 'sent')]"), - } + name = fields.Selection([('ignored', 'Ignored'), + ('to_be_sent', 'Ready To Send'), + ('sent', 'Done')], + string='Mark as', + default='to_be_sent', + required=True) + line_ids = fields.Many2many('credit.control.line', + string='Credit Control Lines', + default=_get_line_ids, + domain="[('state', '!=', 'sent')]") - _defaults = { - 'name': 'to_be_sent', - 'line_ids': _get_line_ids, - } + @api.model + @api.returns('credit.control.line') + def _filter_lines(self, lines): + """ get line to be marked filter done lines """ + line_obj = self.env['credit.control.line'] + domain = [('state', '!=', 'sent'), ('id', 'in', lines.ids)] + return line_obj.search(domain) - def _filter_line_ids(self, cr, uid, active_ids, context=None): - """get line to be marked filter done lines""" - line_obj = self.pool.get('credit.control.line') - domain = [('state', '!=', 'sent'), ('id', 'in', active_ids)] - return line_obj.search(cr, uid, domain, context=context) + @api.model + @api.returns('credit.control.line') + def _mark_lines(self, filtered_lines, state): + """ write hook """ + assert state + filtered_lines.write({'state': state}) + return filtered_lines - def _mark_lines(self, cr, uid, filtered_ids, state, context=None): - """write hook""" - line_obj = self.pool.get('credit.control.line') - if not state: - raise ValueError(_('state can not be empty')) - line_obj.write(cr, uid, filtered_ids, - {'state': state}, - context=context) - return filtered_ids + @api.multi + def mark_lines(self): + """ Write state of selected credit lines to the one in entry + done credit line will be ignored """ + self.ensure_one() - def mark_lines(self, cr, uid, wiz_id, context=None): - """Write state of selected credit lines to the one in entry - done credit line will be ignored""" - assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \ - "wiz_id: only one id expected" - if isinstance(wiz_id, list): - wiz_id = wiz_id[0] - form = self.browse(cr, uid, wiz_id, context) + if not self.line_ids: + raise api.Warning(_('No credit control lines selected.')) - if not form.line_ids: - raise orm.except_orm( - _('Error'), - _('No credit control lines selected.') - ) + filtered_lines = self._filter_lines(self.line_ids) + if not filtered_lines: + raise api.Warning(_('No lines will be changed. ' + 'All the selected lines are already done.')) - line_ids = [l.id for l in form.line_ids] + self._mark_lines(filtered_lines, self.name) - filtered_ids = self._filter_line_ids(cr, uid, line_ids, context) - if not filtered_ids: - raise orm.except_orm( - _('Information'), - _('No lines will be changed. ' - 'All the selected lines are already done.') - ) - - self._mark_lines(cr, uid, filtered_ids, form.name, context) - - return {'domain': unicode([('id', 'in', filtered_ids)]), + return {'domain': unicode([('id', 'in', filtered_lines.ids)]), 'view_type': 'form', 'view_mode': 'tree,form', 'view_id': False, diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py index c4f2416e9..d8bded736 100644 --- a/account_credit_control/wizard/credit_control_policy_changer.py +++ b/account_credit_control/wizard/credit_control_policy_changer.py @@ -19,113 +19,99 @@ # ############################################################################## import logging -from openerp.tools.translate import _ -from openerp.osv import orm, fields +from openerp import models, fields, api, _ logger = logging.getLogger(__name__) -class credit_control_policy_changer(orm.TransientModel): - """Wizard that is run from invoices and allows to set manually a policy +class credit_control_policy_changer(models.TransientModel): + """ Wizard that is run from invoices and allows to set manually a policy Policy are actually apply to related move lines availabe in selection widget """ _name = "credit.control.policy.changer" - _columns = { - 'new_policy_id': fields.many2one('credit.control.policy', - 'New Policy to Apply', - required=True), - 'new_policy_level_id': fields.many2one('credit.control.policy.level', - 'New level to apply', - required=True), - # Only used to provide dynamic filtering on form - 'do_nothing': fields.boolean('No follow policy'), - 'move_line_ids': fields.many2many('account.move.line', - rel='credit_changer_ml_rel', - string='Move line to change'), - } - def _get_default_lines(self, cr, uid, context=None): - """Get default lines for fields move_line_ids + new_policy_id = fields.Many2one('credit.control.policy', + string='New Policy to Apply', + required=True) + new_policy_level_id = fields.Many2one('credit.control.policy.level', + string='New level to apply', + required=True) + # Only used to provide dynamic filtering on form + do_nothing = fields.Boolean(string='No follow policy') + + @api.model + def _get_default_lines(self): + """ Get default lines for fields move_line_ids of wizard. Only take lines that are on the same account and move of the invoice and not reconciled - :return: list of compliant move line ids + :return: list of compliant move lines """ - if context is None: - context = {} + context = self.env.context active_ids = context.get('active_ids') - selected_line_ids = [] - inv_model = self.pool['account.invoice'] - move_line_model = self.pool['account.move.line'] + invoice_obj = self.env['account.invoice'] + move_line_obj = self.env['account.move.line'] if not active_ids: return False - # raise ValueError('No active_ids passed in context') - for invoice in inv_model.browse(cr, uid, active_ids, context=context): + selected_lines = move_line_obj.browse() + for invoice in invoice_obj.browse(active_ids): if invoice.type in ('in_invoice', 'in_refund', 'out_refund'): - raise orm.except_orm( - _('User error'), - _('Please use wizard on cutomer invoices') - ) + raise api.Warning(_('Please use wizard on customer invoices')) domain = [('account_id', '=', invoice.account_id.id), ('move_id', '=', invoice.move_id.id), ('reconcile_id', '=', False)] - move_ids = move_line_model.search(cr, uid, domain, context=context) - selected_line_ids.extend(move_ids) - return selected_line_ids + move_lines = move_line_obj.search(domain) + selected_lines += move_lines + return selected_lines - _defaults = {'move_line_ids': _get_default_lines} + move_line_ids = fields.Many2many('account.move.line', + rel='credit_changer_ml_rel', + string='Move line to change', + default=_get_default_lines) - def onchange_policy_id(self, cr, uid, ids, new_policy_id, context=None): - if not new_policy_id: - return {} - policy = self.pool['credit.control.policy'].browse(cr, uid, - new_policy_id, - context=context) - return {'value': {'do_nothing': policy.do_nothing}} + @api.onchange('new_policy_level_id') + def onchange_policy_id(self): + if not self.new_policy_id: + return + self.do_nothing = self.new_policy_id.do_nothing - def _mark_as_overridden(self, cr, uid, move_lines, context=None): - """Mark `move_lines` related credit control line as overridden + @api.model + @api.returns('credit.control.line') + def _mark_as_overridden(self, move_lines): + """ Mark `move_lines` related credit control line as overridden This is done by setting manually_overridden fields to True :param move_lines: move line to mark as overridden - - :retun: list of credit line ids that where marked as overridden - + :return: list of credit lines that where marked as overridden """ - credit_model = self.pool['credit.control.line'] - domain = [('move_line_id', 'in', [x.id for x in move_lines])] - credits_ids = credit_model.search(cr, uid, domain, context=context) - credit_model.write(cr, uid, - credits_ids, - {'manually_overridden': True}, - context) - return credits_ids + credit_obj = self.env['credit.control.line'] + domain = [('move_line_id', 'in', move_lines.ids)] + credit_lines = credit_obj.search(domain) + credit_lines.write({'manually_overridden': True}) + return credit_lines - def _set_invoice_policy(self, cr, uid, move_line_ids, policy, - context=None): - """Force policy on invoice""" - invoice_model = self.pool['account.invoice'] - invoice_ids = set([x.invoice.id for x in move_line_ids if x.invoice]) - invoice_model.write(cr, uid, list(invoice_ids), - {'credit_policy_id': policy.id}, - context=context) + @api.model + def _set_invoice_policy(self, move_lines, policy): + """ Force policy on invoice """ + invoice_obj = self.env['account.invoice'] + invoice_ids = set(line.invoice.id for line in move_lines + if line.invoice) + invoices = invoice_obj.browse(invoice_ids) + invoices.write({'credit_policy_id': policy.id}) - def _check_accounts_policies(self, cr, uid, lines, policy, context=None): - policy_obj = self.pool['credit.control.policy'] - for line in lines: - policy_obj.check_policy_against_account( - cr, uid, - line.account_id.id, - policy.id, - context=context - ) + @api.model + def _check_accounts_policies(self, lines, policy): + accounts = set(line.account_id for line in lines) + for account in accounts: + policy.check_policy_against_account(account) return True - def set_new_policy(self, cr, uid, wizard_id, context=None): - """Set new policy on an invoice. + @api.multi + def set_new_policy(self): + """ Set new policy on an invoice. This is done by creating a new credit control line related to the move line and the policy setted in @@ -134,49 +120,29 @@ class credit_control_policy_changer(orm.TransientModel): :return: ir.actions.act_windows dict """ - assert len(wizard_id) == 1, "Only one id expected" - wizard_id = wizard_id[0] + self.ensure_one() + credit_line_obj = self.env['credit.control.line'] - credit_line_model = self.pool['credit.control.line'] - ir_model = self.pool['ir.model.data'] - ui_act_model = self.pool['ir.actions.act_window'] - wizard = self.browse(cr, uid, wizard_id, context=context) controlling_date = fields.date.today() - self._check_accounts_policies( - cr, - uid, - wizard.move_line_ids, - wizard.new_policy_id) - self._mark_as_overridden( - cr, - uid, - wizard.move_line_ids, - context=context - ) + self._check_accounts_policies(self.move_line_ids, self.new_policy_id) + self._mark_as_overridden(self.move_line_ids) # As disscused with business expert # draft lines should be passed to ignored # if same level as the new one # As it is a manual action # We also ignore rounding tolerance - generated_ids = None - generated_ids = credit_line_model.create_or_update_from_mv_lines( - cr, uid, [], - [x.id for x in wizard.move_line_ids], - wizard.new_policy_level_id.id, - controlling_date, - check_tolerance=False, - context=None - ) - self._set_invoice_policy(cr, uid, - wizard.move_line_ids, - wizard.new_policy_id, - context=context) - if not generated_ids: - return {} - view_id = ir_model.get_object_reference(cr, uid, - "account_credit_control", - "credit_control_line_action") - assert view_id, 'No view found' - action = ui_act_model.read(cr, uid, view_id[1], context=context) - action['domain'] = [('id', 'in', generated_ids)] + create = credit_line_obj.create_or_update_from_mv_lines + generated_lines = create(self.move_line_ids, + self.new_policy_level_id, + controlling_date, + check_tolerance=False) + self._set_invoice_policy(self.move_line_ids, self.new_policy_id) + + if not generated_lines: + return {'type': 'ir.actions.act_window_close'} + + action_ref = 'account_credit_control.credit_control_line_action' + action = self.env.ref(action_ref) + action = action.read()[0] + action['domain'] = [('id', 'in', generated_lines.ids)] return action diff --git a/account_credit_control/wizard/credit_control_policy_changer_view.xml b/account_credit_control/wizard/credit_control_policy_changer_view.xml index f65356ea0..cee46799d 100644 --- a/account_credit_control/wizard/credit_control_policy_changer_view.xml +++ b/account_credit_control/wizard/credit_control_policy_changer_view.xml @@ -11,8 +11,7 @@ - + 1), \ - "wiz_id: only one id expected" - comm_obj = self.pool.get('credit.control.communication') - if isinstance(wiz_id, list): - wiz_id = wiz_id[0] - form = self.browse(cr, uid, wiz_id, context) + @api.multi + def print_lines(self): + self.ensure_one() + comm_obj = self.env['credit.control.communication'] + if not self.line_ids and not self.print_all: + raise api.Warning(_('No credit control lines selected.')) - if not form.line_ids and not form.print_all: - raise orm.except_orm(_('Error'), - _('No credit control lines selected.')) + lines = self._get_lines(self.line_ids, self._credit_line_predicate) - line_ids = self._get_line_ids(cr, - uid, - form.line_ids, - self._credit_line_predicate, - context=context) + comms = comm_obj._generate_comm_from_credit_lines(lines) - comms = comm_obj._generate_comm_from_credit_line_ids(cr, uid, line_ids, - context=context) - report_file = comm_obj._generate_report(cr, uid, comms, - context=context) + report_content = comms._generate_report() - form.write({'report_file': base64.b64encode(report_file), + self.write({'report_file': base64.b64encode(report_content), 'report_name': ('credit_control_esr_bvr_%s.pdf' % fields.datetime.now()), 'state': 'done'}) - if form.mark_as_sent: - comm_obj._mark_credit_line_as_sent(cr, uid, comms, context=context) - - return {'type': 'ir.actions.act_window', - 'res_model': 'credit.control.printer', - 'view_mode': 'form', - 'view_type': 'form', - 'res_id': form.id, - 'views': [(False, 'form')], - 'target': 'new', - } + if self.mark_as_sent: + comms._mark_credit_line_as_sent() + action = self.get_formview_action()[0] + action['target'] = 'new' + return action From c89896146ca77b0fd172bc3cc521cd0c39d21cd0 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 29 Oct 2014 14:52:33 +0100 Subject: [PATCH 10/26] Replace the webkit report by a qweb one --- account_credit_control/__init__.py | 1 - account_credit_control/__openerp__.py | 3 +- account_credit_control/data.xml | 4 +- account_credit_control/policy.py | 6 +- account_credit_control/report/__init__.py | 1 - .../report/credit_control_summary.html.mako | 244 ------------------ .../report/credit_control_summary.py | 41 --- account_credit_control/report/report.xml | 21 +- .../report/report_credit_control_summary.xml | 78 ++++++ account_credit_control/run.py | 4 +- .../wizard/credit_control_communication.py | 44 ++-- .../wizard/credit_control_printer.py | 18 +- .../wizard/credit_control_printer_view.xml | 21 +- 13 files changed, 135 insertions(+), 351 deletions(-) delete mode 100644 account_credit_control/report/__init__.py delete mode 100644 account_credit_control/report/credit_control_summary.html.mako delete mode 100644 account_credit_control/report/credit_control_summary.py create mode 100644 account_credit_control/report/report_credit_control_summary.xml diff --git a/account_credit_control/__init__.py b/account_credit_control/__init__.py index 27a4fabc2..b195eabad 100644 --- a/account_credit_control/__init__.py +++ b/account_credit_control/__init__.py @@ -26,5 +26,4 @@ from . import partner from . import policy from . import company from . import wizard -from . import report from . import invoice diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py index fa905f236..2cee51f35 100644 --- a/account_credit_control/__openerp__.py +++ b/account_credit_control/__openerp__.py @@ -27,9 +27,10 @@ 'depends': ['base', 'account', 'email_template', - 'report_webkit'], + ], 'website': 'http://www.camptocamp.com', 'data': ["report/report.xml", + "report/report_credit_control_summary.xml", "data.xml", "line_view.xml", "account_view.xml", diff --git a/account_credit_control/data.xml b/account_credit_control/data.xml index 09fc3cced..2ff738563 100644 --- a/account_credit_control/data.xml +++ b/account_credit_control/data.xml @@ -9,9 +9,9 @@ ${object.get_contact_address().lang or 'en_US'} - +
${object.current_policy_level.custom_mail_text} diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index 59cac0612..5680d8929 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -181,7 +181,7 @@ class CreditControlPolicy(models.Model): different_lines = move_line_obj.browse() if not lines: return different_lines - cr = self._cr + cr = self.env.cr cr.execute("SELECT move_line_id FROM credit_control_line" " WHERE policy_id != %s and move_line_id in %s" " AND manually_overridden IS false", @@ -322,7 +322,7 @@ class CreditControlPolicyLevel(models.Model): move_line_obj = self.env['account.move.line'] if not lines: return move_line_obj.browse() - cr = self._cr + cr = self.env.cr sql = ("SELECT DISTINCT mv_line.id\n" " FROM account_move_line mv_line\n" " WHERE mv_line.id in %(line_ids)s\n" @@ -356,7 +356,7 @@ class CreditControlPolicyLevel(models.Model): move_line_obj = self.env['account.move.line'] if not lines: return move_line_obj.browse() - cr = self._cr + cr = self.env.cr sql = ("SELECT mv_line.id\n" " FROM account_move_line mv_line\n" " JOIN credit_control_line cr_line\n" diff --git a/account_credit_control/report/__init__.py b/account_credit_control/report/__init__.py deleted file mode 100644 index b7e5edadc..000000000 --- a/account_credit_control/report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import credit_control_summary diff --git a/account_credit_control/report/credit_control_summary.html.mako b/account_credit_control/report/credit_control_summary.html.mako deleted file mode 100644 index 323838aa2..000000000 --- a/account_credit_control/report/credit_control_summary.html.mako +++ /dev/null @@ -1,244 +0,0 @@ -## -*- coding: utf-8 -*- - - - - - - - %for comm in objects : - <% setLang(comm.get_contact_address().lang) %> -
- - <% - add = comm.get_contact_address() - %> - %if comm.partner_id.id == add.id: - - <% address_lines = comm.partner_id.contact_address.split("\n") %> - - %else: - - - <% address_lines = add.contact_address.split("\n")[1:] %> - %endif - %for part in address_lines: - %if part: - - %endif - %endfor -
${comm.partner_id.title and comm.partner_id.title.name or ''} ${comm.partner_id.name }
${comm.partner_id.name or ''}
${add.title and add.title.name or ''} ${add.name}
${part}
-
-
-
-
- -
-
-
-
-
- -

- ${_('Reminder')}: ${comm.current_policy_level.name or '' } -

- -

${_('Dear')},

-

${comm.current_policy_level.custom_text.replace('\n', '
')}

- -
-
-

${_('Summary')}

- - - - - - - - - - -%for line in comm.credit_control_line_ids: - - %if line.invoice_id: - - %else: - - %endif - - - - - - -%endfor -
${_('Invoice number')}${_('Invoice date')}${_('Date due')}${_('Invoiced amount')}${_('Open amount')}${_('Currency')}
${line.invoice_id.number} - %if line.invoice_id.name: -
- ${line.invoice_id.name} - %endif -
${line.move_line_id.name}${line.date_entry}${line.date_due}${line.amount_due}${line.balance_due}${line.currency_id.name or comm.company_id.currency_id.name}
-
-
-<%doc> - -

${_('If you have any question, do not hesitate to contact us.')}

- -

${comm.user_id.name} ${comm.user_id.email and '<%s>'%(comm.user_id.email) or ''}
- ${comm.company_id.name}
- % if comm.company_id.street: - ${comm.company_id.street or ''}
- - % endif - - % if comm.company_id.street2: - ${comm.company_id.street2}
- % endif - % if comm.company_id.city or comm.company_id.zip: - ${comm.company_id.zip or ''} ${comm.company_id.city or ''}
- % endif - % if comm.company_id.country_id: - ${comm.company_id.state_id and ('%s, ' % comm.company_id.state_id.name) or ''} ${comm.company_id.country_id.name or ''}
- % endif - % if comm.company_id.phone: - Phone: ${comm.company_id.phone}
- % endif - % if comm.company_id.website: - ${comm.company_id.website or ''}
- % endif - - -

- %endfor - - - diff --git a/account_credit_control/report/credit_control_summary.py b/account_credit_control/report/credit_control_summary.py deleted file mode 100644 index ffbc65116..000000000 --- a/account_credit_control/report/credit_control_summary.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -import time - -from openerp.report import report_sxw - - -class CreditSummaryReport(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(CreditSummaryReport, self).__init__(cr, uid, name, - context=context) - self.localcontext.update({ - 'time': time, - 'cr': cr, - 'uid': uid, - }) - -report_sxw.report_sxw( - 'report.credit_control_summary', - 'credit.control.communication', - 'addons/account_credit_control/report/credit_control_summary.html.mako', - parser=CreditSummaryReport -) diff --git a/account_credit_control/report/report.xml b/account_credit_control/report/report.xml index dee64b896..2d24c0862 100644 --- a/account_credit_control/report/report.xml +++ b/account_credit_control/report/report.xml @@ -1,16 +1,15 @@ - - - - + + diff --git a/account_credit_control/report/report_credit_control_summary.xml b/account_credit_control/report/report_credit_control_summary.xml new file mode 100644 index 000000000..da5d38f90 --- /dev/null +++ b/account_credit_control/report/report_credit_control_summary.xml @@ -0,0 +1,78 @@ + + + + + + + + diff --git a/account_credit_control/run.py b/account_credit_control/run.py index 740c811d4..ff2bca35a 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -144,8 +144,8 @@ class CreditControlRun(models.Model): calls of this method. """ try: - self._cr.execute('SELECT id FROM credit_control_run' - ' LIMIT 1 FOR UPDATE NOWAIT') + self.env.cr.execute('SELECT id FROM credit_control_run' + ' LIMIT 1 FOR UPDATE NOWAIT') except Exception: # In case of exception openerp will do a rollback # for us and free the lock diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py index e988a229b..b2307898a 100644 --- a/account_credit_control/wizard/credit_control_communication.py +++ b/account_credit_control/wizard/credit_control_communication.py @@ -20,7 +20,6 @@ ############################################################################## import logging from openerp import models, fields, api -from openerp import netsvc logger = logging.getLogger('credit.control.line.mailing') @@ -40,10 +39,15 @@ class CreditCommunication(models.TransientModel): current_policy_level = fields.Many2one('credit.control.policy.level', 'Level', required=True) + credit_control_line_ids = fields.Many2many('credit.control.line', rel='comm_credit_rel', string='Credit Lines') + contact_address = fields.Many2one('res.partner', + string='Contact Address', + readonly=True) + @api.model def _get_company(self): company_obj = self.env['res.company'] @@ -57,21 +61,37 @@ class CreditCommunication(models.TransientModel): default=lambda self: self.env.user, string='User') + @api.model + @api.returns('self', lambda value: value.id) + def create(self, vals): + if vals.get('partner_id'): + # the computed field does not work in TransientModel, + # just set a value on creation + partner_id = vals['partner_id'] + vals['contact_address'] = self._get_contact_address(partner_id).id + return super(CreditCommunication, self).create(vals) + @api.multi def get_email(self): """ Return a valid email for customer """ self.ensure_one() - contact = self.get_contact_address() + contact = self.contact_address return contact.email @api.multi @api.returns('res.partner') def get_contact_address(self): + """ Compatibility method, please use the contact_address field """ self.ensure_one() + return self.contact_address + + @api.model + @api.returns('res.partner') + def _get_contact_address(self, partner_id): partner_obj = self.env['res.partner'] - partner = self.partner_id + partner = partner_obj.browse(partner_id) add_ids = partner.address_get(adr_pref=['invoice']) or {} - add_id = add_ids.get('invoice', add_ids.get('default', False)) + add_id = add_ids['invoice'] return partner_obj.browse(add_id) @api.model @@ -89,9 +109,9 @@ class CreditCommunication(models.TransientModel): """ Aggregate credit control line by partner, level, and currency It also generate a communication object per aggregation. """ - if not lines: - return [] comms = self.browse() + if not lines: + return comms sql = ( "SELECT distinct partner_id, policy_level_id, " " credit_control_line.currency_id, " @@ -103,7 +123,7 @@ class CreditCommunication(models.TransientModel): " ORDER by credit_control_policy_level.level, " " credit_control_line.currency_id" ) - cr = self._cr + cr = self.env.cr cr.execute(sql, (tuple(lines.ids), )) res = cr.dictfetchall() for level_assoc in res: @@ -139,7 +159,6 @@ class CreditCommunication(models.TransientModel): template.id, comm.id, context=context) - email_values['body_html'] = email_values['body'] email_values['type'] = 'email' email = email_message_obj.create(email_values) @@ -179,13 +198,8 @@ class CreditCommunication(models.TransientModel): of related policy template """ - return '' - service = netsvc.LocalService('report.credit_control_summary') - cr, uid = self.env.cr, self.env.uid - result, format = service.create(cr, uid, self.ids, {}, {}) - return result - # TODO - # return self.env['report'].get_pdf(self, 'credit_control_summary') + report_name = 'account_credit_control.report_credit_control_summary' + return self.env['report'].get_pdf(self, report_name) @api.multi @api.returns('credit.control.line') diff --git a/account_credit_control/wizard/credit_control_printer.py b/account_credit_control/wizard/credit_control_printer.py index b7c2ba8f2..b5796b271 100644 --- a/account_credit_control/wizard/credit_control_printer.py +++ b/account_credit_control/wizard/credit_control_printer.py @@ -18,7 +18,6 @@ # along with this program. If not, see . # ############################################################################## -import base64 from openerp import models, fields, api, _ @@ -40,9 +39,6 @@ class CreditControlPrinter(models.TransientModel): mark_as_sent = fields.Boolean(string='Mark letter lines as sent', default=True, help="Only letter lines will be marked.") - report_file = fields.Binary(string='Generated Report', readonly=True) - report_name = fields.Char(string='Report name') - state = fields.Char(string='state') line_ids = fields.Many2many('credit.control.line', string='Credit Control Lines', default=_get_line_ids) @@ -67,15 +63,9 @@ class CreditControlPrinter(models.TransientModel): comms = comm_obj._generate_comm_from_credit_lines(lines) - report_content = comms._generate_report() - - self.write({'report_file': base64.b64encode(report_content), - 'report_name': ('credit_control_esr_bvr_%s.pdf' % - fields.datetime.now()), - 'state': 'done'}) - if self.mark_as_sent: comms._mark_credit_line_as_sent() - action = self.get_formview_action()[0] - action['target'] = 'new' - return action + + report_name = 'account_credit_control.report_credit_control_summary' + report_obj = self.env['report'].with_context(active_ids=comms.ids) + return report_obj.get_action(comms, report_name) diff --git a/account_credit_control/wizard/credit_control_printer_view.xml b/account_credit_control/wizard/credit_control_printer_view.xml index 68b67aa31..224e5fc8e 100644 --- a/account_credit_control/wizard/credit_control_printer_view.xml +++ b/account_credit_control/wizard/credit_control_printer_view.xml @@ -11,28 +11,17 @@ + colspan="4"/> - - + + - - - -
-
From 074cf4afdbcb8a24a7a759ffeac096b30d7092ba Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 29 Oct 2014 14:56:42 +0100 Subject: [PATCH 11/26] Invoices are not mentioned above the text in the emails. --- account_credit_control/data.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/account_credit_control/data.xml b/account_credit_control/data.xml index 2ff738563..c629232d4 100644 --- a/account_credit_control/data.xml +++ b/account_credit_control/data.xml @@ -63,7 +63,7 @@ Thank you in advance for your anticipated cooperation in this matter. Best regards
- Our records indicate that we have not received the payment of the above mentioned invoice (copy attached for your convenience). + Our records indicate that we have not received the payment of the invoices mentioned in the attached document. If it has already been sent, please disregard this notice. If not, please proceed with payment within 10 days. @@ -89,7 +89,7 @@ Thank you in advance for your anticipated cooperation in this matter. Best regards - Our records indicate that we have not yet received the payment of the above mentioned invoice (copy attached for your convenience) despite our first reminder. + Our records indicate that we have not yet received the payment of the invoices mentioned in the attached document despite our first reminder. If it has already been sent, please disregard this notice. If not, please proceed with payment within 5 days. Thank you in advance for your anticipated cooperation in this matter. @@ -120,7 +120,7 @@ Best regards Best regards -Our records indicate that we still have not received the payment of the above mentioned invoice (copy attached) despite our two reminders. +Our records indicate that we still have not received the payment of the invoices mentioned in the attached document despite our two reminders. If payment have already been sent, please disregard this notice. If not, please proceed with payment. If your payment has not been received in the next 5 days, your file will be transfered to our debt collection agency. @@ -155,7 +155,7 @@ Best regards Best regards - Our records indicate that we have not received the payment of the above mentioned invoice (copy attached for your convenience). + Our records indicate that we have not received the payment of the invoices mentioned in the attached document. If it has already been sent, please disregard this notice. If not, please proceed with payment within 10 days. Thank you in advance for your anticipated cooperation in this matter. @@ -186,7 +186,7 @@ Best regards Best regards - Our records indicate that we still have not received the payment of the above mentioned invoice (copy attached) despite our reminder. + Our records indicate that we still have not received the payment of the invoices mentioned in the attached document despite our reminder. If payment have already been sent, please disregard this notice. If not, please proceed with payment. If your payment has not been received in the next 5 days, your file will be transfered to our debt From 2cca24070d234c18d20fb0bc0541d972fb6e76c3 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 29 Oct 2014 15:03:26 +0100 Subject: [PATCH 12/26] Update copyright dates --- account_credit_control/__init__.py | 2 +- account_credit_control/__openerp__.py | 2 +- account_credit_control/account.py | 2 +- account_credit_control/company.py | 2 +- account_credit_control/line.py | 2 +- account_credit_control/mail.py | 2 +- account_credit_control/partner.py | 2 +- account_credit_control/policy.py | 2 +- account_credit_control/run.py | 2 +- .../scenarios/features/02_credit_control_invoices.feature | 2 +- .../scenarios/features/03_credit_control_run_jan.feature | 2 +- .../scenarios/features/04_credit_control_run_feb.feature | 2 +- .../scenarios/features/05_credit_control_run_mar.feature | 2 +- .../scenarios/features/06_credit_control_run_apr.feature | 2 +- .../scenarios/features/07_credit_control_run_may.feature | 2 +- .../scenarios/features/08_credit_control_run_jun.feature | 2 +- .../scenarios/features/09_credit_control_run_jul.feature | 2 +- .../scenarios/features/10_credit_control_run_aug.feature | 2 +- .../scenarios/features/11_credit_control_manual_setting.feature | 2 +- account_credit_control/wizard/__init__.py | 2 +- account_credit_control/wizard/credit_control_communication.py | 2 +- account_credit_control/wizard/credit_control_emailer.py | 2 +- account_credit_control/wizard/credit_control_marker.py | 2 +- account_credit_control/wizard/credit_control_policy_changer.py | 2 +- account_credit_control/wizard/credit_control_printer.py | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/account_credit_control/__init__.py b/account_credit_control/__init__.py index b195eabad..531c9f6d3 100644 --- a/account_credit_control/__init__.py +++ b/account_credit_control/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py index 2cee51f35..7239177b9 100644 --- a/account_credit_control/__openerp__.py +++ b/account_credit_control/__openerp__.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/account.py b/account_credit_control/account.py index 0c00a8e23..de4758c5f 100644 --- a/account_credit_control/account.py +++ b/account_credit_control/account.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/company.py b/account_credit_control/company.py index cd94ef7c8..8deb708fa 100644 --- a/account_credit_control/company.py +++ b/account_credit_control/company.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/line.py b/account_credit_control/line.py index 43782bb26..d6a0d1d80 100644 --- a/account_credit_control/line.py +++ b/account_credit_control/line.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/mail.py b/account_credit_control/mail.py index 4636c81be..7ec9be86d 100644 --- a/account_credit_control/mail.py +++ b/account_credit_control/mail.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/partner.py b/account_credit_control/partner.py index 76f2c24cf..d7669c101 100644 --- a/account_credit_control/partner.py +++ b/account_credit_control/partner.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index 5680d8929..4119740ee 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/run.py b/account_credit_control/run.py index ff2bca35a..2b11788a6 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/scenarios/features/02_credit_control_invoices.feature b/account_credit_control/scenarios/features/02_credit_control_invoices.feature index 4e80f7695..a153d39f7 100644 --- a/account_credit_control/scenarios/features/02_credit_control_invoices.feature +++ b/account_credit_control/scenarios/features/02_credit_control_invoices.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/03_credit_control_run_jan.feature b/account_credit_control/scenarios/features/03_credit_control_run_jan.feature index 5a9e66fec..9839f9bf1 100644 --- a/account_credit_control/scenarios/features/03_credit_control_run_jan.feature +++ b/account_credit_control/scenarios/features/03_credit_control_run_jan.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/04_credit_control_run_feb.feature b/account_credit_control/scenarios/features/04_credit_control_run_feb.feature index 154cf2600..f9b1865ca 100644 --- a/account_credit_control/scenarios/features/04_credit_control_run_feb.feature +++ b/account_credit_control/scenarios/features/04_credit_control_run_feb.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/05_credit_control_run_mar.feature b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature index 0885fa71e..33a53b5b2 100644 --- a/account_credit_control/scenarios/features/05_credit_control_run_mar.feature +++ b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/06_credit_control_run_apr.feature b/account_credit_control/scenarios/features/06_credit_control_run_apr.feature index 0d3d7f82f..635bfeee7 100644 --- a/account_credit_control/scenarios/features/06_credit_control_run_apr.feature +++ b/account_credit_control/scenarios/features/06_credit_control_run_apr.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/07_credit_control_run_may.feature b/account_credit_control/scenarios/features/07_credit_control_run_may.feature index e046c12e3..6028cbe19 100644 --- a/account_credit_control/scenarios/features/07_credit_control_run_may.feature +++ b/account_credit_control/scenarios/features/07_credit_control_run_may.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/08_credit_control_run_jun.feature b/account_credit_control/scenarios/features/08_credit_control_run_jun.feature index 556c779df..75c7df7f5 100644 --- a/account_credit_control/scenarios/features/08_credit_control_run_jun.feature +++ b/account_credit_control/scenarios/features/08_credit_control_run_jun.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/09_credit_control_run_jul.feature b/account_credit_control/scenarios/features/09_credit_control_run_jul.feature index 9bd9df52d..df0aa4c55 100644 --- a/account_credit_control/scenarios/features/09_credit_control_run_jul.feature +++ b/account_credit_control/scenarios/features/09_credit_control_run_jul.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/10_credit_control_run_aug.feature b/account_credit_control/scenarios/features/10_credit_control_run_aug.feature index 12b27b8de..5351b4b9c 100644 --- a/account_credit_control/scenarios/features/10_credit_control_run_aug.feature +++ b/account_credit_control/scenarios/features/10_credit_control_run_aug.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature b/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature index 1a34ac71a..6c30d67b1 100644 --- a/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature +++ b/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature @@ -1,7 +1,7 @@ ############################################################################### # # OERPScenario, OpenERP Functional Tests -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # Author Nicolas Bessi ############################################################################## diff --git a/account_credit_control/wizard/__init__.py b/account_credit_control/wizard/__init__.py index f12dea14d..82938e856 100644 --- a/account_credit_control/wizard/__init__.py +++ b/account_credit_control/wizard/__init__.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py index b2307898a..4ed345572 100644 --- a/account_credit_control/wizard/credit_control_communication.py +++ b/account_credit_control/wizard/credit_control_communication.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/wizard/credit_control_emailer.py b/account_credit_control/wizard/credit_control_emailer.py index 934c016df..981892e64 100644 --- a/account_credit_control/wizard/credit_control_emailer.py +++ b/account_credit_control/wizard/credit_control_emailer.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/wizard/credit_control_marker.py b/account_credit_control/wizard/credit_control_marker.py index b0b1f75aa..558b0c755 100644 --- a/account_credit_control/wizard/credit_control_marker.py +++ b/account_credit_control/wizard/credit_control_marker.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py index d8bded736..61bf0a340 100644 --- a/account_credit_control/wizard/credit_control_policy_changer.py +++ b/account_credit_control/wizard/credit_control_policy_changer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Author: Nicolas Bessi +# Author: Nicolas Bessi, Guewen Baconnier # Copyright 2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify diff --git a/account_credit_control/wizard/credit_control_printer.py b/account_credit_control/wizard/credit_control_printer.py index b5796b271..3611045eb 100644 --- a/account_credit_control/wizard/credit_control_printer.py +++ b/account_credit_control/wizard/credit_control_printer.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Nicolas Bessi, Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2014 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From 20e9113568586f161bb5482ef14fb1fae6abdfe2 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 29 Oct 2014 16:03:38 +0100 Subject: [PATCH 13/26] Recordset is not a set, need to use |= instead of += to have unique ids --- account_credit_control/policy.py | 2 +- account_credit_control/run.py | 4 ++-- account_credit_control/wizard/credit_control_communication.py | 2 +- .../wizard/credit_control_policy_changer.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index 4119740ee..dae28cbb1 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -402,5 +402,5 @@ class CreditControlPolicyLevel(models.Model): method = self._get_first_level_move_lines else: method = self._get_other_level_move_lines - matching_lines += method(controlling_date, lines) + matching_lines |= method(controlling_date, lines) return matching_lines diff --git a/account_credit_control/run.py b/account_credit_control/run.py index 2b11788a6..780057581 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -108,7 +108,7 @@ class CreditControlRun(models.Model): lines = policy._get_move_lines_to_process(self.date) manual_lines = policy._lines_different_policy(lines) lines -= manual_lines - manually_managed_lines += manual_lines + manually_managed_lines |= manual_lines policy_lines_generated = cr_line_obj.browse() if lines: # policy levels are sorted by level @@ -119,7 +119,7 @@ class CreditControlRun(models.Model): policy_lines_generated += create(level_lines, level, self.date) - generated += policy_lines_generated + generated |= policy_lines_generated if policy_lines_generated: report += (_("Policy \"%s\" has generated %d Credit " "Control Lines.\n") % diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py index 4ed345572..4ebbf31db 100644 --- a/account_credit_control/wizard/credit_control_communication.py +++ b/account_credit_control/wizard/credit_control_communication.py @@ -207,7 +207,7 @@ class CreditCommunication(models.TransientModel): line_obj = self.env['credit.control.line'] lines = line_obj.browse() for comm in self: - lines += comm.credit_control_line_ids + lines |= comm.credit_control_line_ids lines.write({'state': 'sent'}) return lines diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py index 61bf0a340..6829d7aff 100644 --- a/account_credit_control/wizard/credit_control_policy_changer.py +++ b/account_credit_control/wizard/credit_control_policy_changer.py @@ -64,7 +64,7 @@ class credit_control_policy_changer(models.TransientModel): ('move_id', '=', invoice.move_id.id), ('reconcile_id', '=', False)] move_lines = move_line_obj.search(domain) - selected_lines += move_lines + selected_lines |= move_lines return selected_lines move_line_ids = fields.Many2many('account.move.line', From 18574ddb7651a77194605b470486678f11326009 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 09:41:42 +0100 Subject: [PATCH 14/26] search returns a recordset --- account_credit_control/policy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py index dae28cbb1..0e2b03dfe 100644 --- a/account_credit_control/policy.py +++ b/account_credit_control/policy.py @@ -109,7 +109,7 @@ class CreditControlPolicy(models.Model): add_objs = my_obj.search([('credit_policy_id', '=', self.id)]) if add_objs: domain = list(default_domain) - domain.append((move_relation_field, 'in', add_objs)) + domain.append((move_relation_field, 'in', add_objs.ids)) to_add = move_l_obj.search(domain) # The lines which are linked to another policy do not have to be @@ -118,7 +118,7 @@ class CreditControlPolicy(models.Model): ('credit_policy_id', '!=', False)]) if neg_objs: domain = list(default_domain) - domain.append((move_relation_field, 'in', neg_objs)) + domain.append((move_relation_field, 'in', neg_objs.ids)) to_remove = move_l_obj.search(domain) return to_add, to_remove From 267c9bbd53343507b8562d49dfbdccd0b54d8cef Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 09:42:09 +0100 Subject: [PATCH 15/26] Add a notice in the scenario, needing to run @base_finance before, change the mailtrap url --- .../scenarios/features/00_credit_control_param.feature | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/account_credit_control/scenarios/features/00_credit_control_param.feature b/account_credit_control/scenarios/features/00_credit_control_param.feature index c189a55fb..c02bb6384 100644 --- a/account_credit_control/scenarios/features/00_credit_control_param.feature +++ b/account_credit_control/scenarios/features/00_credit_control_param.feature @@ -3,6 +3,12 @@ # OERPScenario, OpenERP Functional Tests # Copyright 2009 Camptocamp SA # +# +# The base scenario for the finance data must be executed before this +# one. The finance scenario is included in the oerpscenario base and +# the tag to run it is: @base_finance +# +# ############################################################################## ############################################################################## # Branch # Module # Processes # System @@ -30,11 +36,11 @@ Feature: General parameters in order to test the credit control module """ @email_params_mailtrap - Scenario: E-MAIL PARAMS WITH EMAIL EATER (http://mailtrap.railsware.com/) + Scenario: E-MAIL PARAMS WITH EMAIL EATER (http://mailtrap.io) Given I need a "ir.mail_server" with name: mailstrap_testings And having: | name | value | - | smtp_host | mailtrap.railsware.com | + | smtp_host | mailtrap.io | | sequence | 1 | | smtp_port | 2525 | | smtp_user | camptocamp1 | From e006b82b7bb6833c42dde5697a1916b734cf764b Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 11:39:35 +0100 Subject: [PATCH 16/26] The voucher is no longer linked with the bank statement, use a new phrase to generate the payment for the invoice using solely the voucher --- .../05_credit_control_run_mar.feature | 20 +---- .../features/steps/account_voucher.py | 76 ++++++++++++++++++- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/account_credit_control/scenarios/features/05_credit_control_run_mar.feature b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature index 33a53b5b2..af5199b10 100644 --- a/account_credit_control/scenarios/features/05_credit_control_run_mar.feature +++ b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature @@ -20,25 +20,7 @@ Feature: Ensure that email credit line generation first pass is correct @pay_invoice_si_19_part1 Scenario: I pay a part of the first part of the invoice SI 19, - Given I need a "account.bank.statement" with oid: scen.state_control_eur_1 - And having: - | name | value | - | name | Bk.St.si_19_part1 | - | date | 2013-03-31 | - | journal_id | by oid: scen.eur_journal | - | period_id | by name: 03/2013 | - - And I import invoice "SI_19" using import invoice button - And I should have a "account.bank.statement.line" with name: "SI_19" and amount: "450" - And I set the voucher paid amount to "300" - And I save the voucher - And I should have a "account.bank.statement.line" with name: "SI_19" and amount: "1050" - And I set the voucher paid amount to "0" - And I save the voucher - Then I modify the line amount to "0" - Given I need a "account.bank.statement" with oid: scen.state_control_eur_1 - And I set bank statement end-balance - When I confirm bank statement + Given I pay 300.0 on the invoice "SI_19" Then My invoice "SI_19" is in state "open" reconciled with a residual amount of "1200.0" @account_credit_control_run_month_mar diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py index a73c78fef..e0d32f36e 100644 --- a/account_credit_control/scenarios/features/steps/account_voucher.py +++ b/account_credit_control/scenarios/features/steps/account_voucher.py @@ -3,6 +3,76 @@ from support import * import datetime +@step('I pay {amount:f} on the invoice "{inv_name}"') +def impl(ctx, amount, inv_name): + Partner = model('res.partner') + Invoice = model('account.invoice') + Voucher = model('account.voucher') + VoucherLine = model('account.voucher.line') + Journal = model('account.journal') + invoice = Invoice.get([('name', '=', inv_name)]) + assert invoice + journal = Journal.get('scen.eur_journal') + values = { + 'partner_id': invoice.partner_id.commercial_partner_id.id, + 'reference': invoice.name, + 'amount': amount, + 'date': invoice.date_invoice, + 'currency_id': invoice.currency_id.id, + 'company_id': invoice.company_id.id, + 'journal_id': journal.id, + } + + if invoice.type in ('out_invoice','out_refund'): + values['type'] = 'receipt' + else: + values['type'] = 'payment' + + onchange = Voucher.onchange_partner_id([], values['partner_id'], + values['journal_id'], + values['amount'], + values['currency_id'], + values['type'], + values['date']) + values.update(onchange['value']) + + onchange = Voucher.onchange_date([], values['date'], + values['currency_id'], + False, + values['amount'], + values['company_id']) + values.update(onchange['value']) + + onchange = Voucher.onchange_amount([], values['amount'], + False, + values['partner_id'], + values['journal_id'], + values['currency_id'], + values['type'], + values['date'], + False, + values['company_id']) + values.update(onchange['value']) + values['line_cr_ids'] = False + + voucher = Voucher.create(values) + + vals = voucher.recompute_voucher_lines(voucher.partner_id.id, + voucher.journal_id.id, + voucher.amount, + voucher.currency_id.id, + voucher.type, + voucher.date) + for line in vals['value']['line_cr_ids']: + line['voucher_id'] = voucher.id + VoucherLine.create(line) + + for line in vals['value']['line_dr_ids']: + line['voucher_id'] = voucher.id + VoucherLine.create(line) + + voucher.button_proforma_voucher() + @step('I import invoice "{inv_name}" using import invoice button') def impl(ctx, inv_name): invoice = model('account.invoice').get([('name', '=', inv_name)]) @@ -68,9 +138,9 @@ def impl(ctx): def impl(ctx, amount): assert ctx.line # we have to change voucher amount before chaning statement line amount - if ctx.line.voucher_id: - model('account.voucher').write([ctx.line.voucher_id.id], - {'amount': float(amount)}) + # if ctx.line.voucher_id: + # model('account.voucher').write([ctx.line.voucher_id.id], + # {'amount': float(amount)}) ctx.line.write({'amount': float(amount)}) @step('My invoice "{inv_name}" is in state "{state}" reconciled with a residual amount of "{amount:f}"') From 688934e9a3f1ff8dc93e98a2b2c997858aaed867 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 14:53:58 +0100 Subject: [PATCH 17/26] New phrase to pay fully an invoice --- .../scenarios/features/steps/account_voucher.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py index e0d32f36e..921278ceb 100644 --- a/account_credit_control/scenarios/features/steps/account_voucher.py +++ b/account_credit_control/scenarios/features/steps/account_voucher.py @@ -3,6 +3,16 @@ from support import * import datetime + +@step('I pay the full amount on the invoice "{inv_name}"') +def impl(ctx, inv_name): + Invoice = model('account.invoice') + invoice = Invoice.get([('name', '=', inv_name)]) + assert invoice + ctx.execute_steps(""" + When I pay %f on the invoice "%s" + """ % (invoice.residual, inv_name)) + @step('I pay {amount:f} on the invoice "{inv_name}"') def impl(ctx, amount, inv_name): Partner = model('res.partner') From dedb50bfa26cd230adf4a6507d58a9a0d01c39a1 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 14:54:16 +0100 Subject: [PATCH 18/26] Workaround to force recomputation of residual, cf. https://github.com/odoo/odoo/issues/3395 --- .../scenarios/features/steps/account_voucher.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py index 921278ceb..b7943461b 100644 --- a/account_credit_control/scenarios/features/steps/account_voucher.py +++ b/account_credit_control/scenarios/features/steps/account_voucher.py @@ -82,6 +82,10 @@ def impl(ctx, amount, inv_name): VoucherLine.create(line) voucher.button_proforma_voucher() + # Workaround to force recomputation of the residual. + # Must be removed once this bug is fixed: + # https://github.com/odoo/odoo/issues/3395 + invoice.write({'currency_id': invoice.currency_id.id}) @step('I import invoice "{inv_name}" using import invoice button') def impl(ctx, inv_name): From eacce6df1b879ff334e1f5581a9ff956688e4e47 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 14:54:45 +0100 Subject: [PATCH 19/26] Use the new phrase to pay the May invoices --- .../07_credit_control_run_may.feature | 50 ++----------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/account_credit_control/scenarios/features/07_credit_control_run_may.feature b/account_credit_control/scenarios/features/07_credit_control_run_may.feature index 6028cbe19..2b73fa660 100644 --- a/account_credit_control/scenarios/features/07_credit_control_run_may.feature +++ b/account_credit_control/scenarios/features/07_credit_control_run_may.feature @@ -20,61 +20,17 @@ Feature: Ensure that email credit line generation first pass is correct @pay_invoice_si_16 Scenario: I pay entirely the invoice SI 16, so it should no longer appear in the credit control lines - Given I need a "account.bank.statement" with oid: scen.voucher_statement_si_16 - And having: - | name | value | - | name | Bk.St.si_16 | - | date | 2013-05-30 | - | journal_id | by oid: scen.eur_journal | - | period_id | by name: 05/2013 | - - And I import invoice "SI_16" using import invoice button - And I set bank statement end-balance - When I confirm bank statement + Given I pay the full amount on the invoice "SI_16" Then My invoice "SI_16" is in state "paid" reconciled with a residual amount of "0.0" @pay_invoice_si_17 Scenario: I pay entirely the invoice SI 17, so it should no longer appear in the credit control lines - Given I need a "account.bank.statement" with oid: scen.voucher_statement_si_17 - And having: - | name | value | - | name | Bk.St.si_17 | - | date | 2013-05-30 | - | journal_id | by oid: scen.eur_journal | - | period_id | by name: 05/2013 | - - And I import invoice "SI_17" using import invoice button - And I should have a "account.bank.statement.line" with name: "SI_17" and amount: "1500" - And I set the voucher paid amount to "1000" - And I save the voucher - And I modify the line amount to "1000" - And I need a "account.bank.statement" with oid: scen.voucher_statement_si_17 - And I set bank statement end-balance - When I confirm bank statement + Given I pay 1000.0 on the invoice "SI_17" Then My invoice "SI_17" is in state "open" reconciled with a residual amount of "500.0" @pay_invoice_si_18_part1 Scenario: I pay the first part of the invoice SI 18, so it should no longer appear in the credit control lines however, the second move lines should still appears - Given I need a "account.bank.statement" with oid: scen.voucher_statement_si_18 - And having: - | name | value | - | name | Bk.St.si_18_part1 | - | date | 2013-05-30 | - | journal_id | by oid: scen.eur_journal | - | period_id | by name: 05/2013 | - - And I import invoice "SI_18" using import invoice button - And I should have a "account.bank.statement.line" with name: "SI_18" and amount: "450" - And I set the voucher paid amount to "450" - And I save the voucher - And I modify the line amount to "450" - And I should have a "account.bank.statement.line" with name: "SI_18" and amount: "1050" - And I set the voucher paid amount to "0" - And I save the voucher - Then I modify the line amount to "0" - And I need a "account.bank.statement" with oid: scen.voucher_statement_si_18 - And I set bank statement end-balance - When I confirm bank statement + Given I pay 450.0 on the invoice "SI_18" Then My invoice "SI_18" is in state "open" reconciled with a residual amount of "1050.0" @account_credit_control_run_month From ba48a2a08635a08c8d416fa4bc178d5acf999fe0 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 15:11:28 +0100 Subject: [PATCH 20/26] Explicit import in scenario steps --- .../scenarios/features/steps/account_credit_control.py | 2 +- .../scenarios/features/steps/account_credit_control_changer.py | 2 ++ .../scenarios/features/steps/account_voucher.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/account_credit_control/scenarios/features/steps/account_credit_control.py b/account_credit_control/scenarios/features/steps/account_credit_control.py index 5ec7fd3ce..2a292d0a5 100644 --- a/account_credit_control/scenarios/features/steps/account_credit_control.py +++ b/account_credit_control/scenarios/features/steps/account_credit_control.py @@ -2,7 +2,7 @@ # flake8: noqa import time from behave import given, when -from support import model +from support import model, assert_equal @given(u'I configure the following accounts on the credit control policy with oid: "{policy_oid}"') def impl(ctx, policy_oid): diff --git a/account_credit_control/scenarios/features/steps/account_credit_control_changer.py b/account_credit_control/scenarios/features/steps/account_credit_control_changer.py index ce9d944d7..6f46d11fc 100644 --- a/account_credit_control/scenarios/features/steps/account_credit_control_changer.py +++ b/account_credit_control/scenarios/features/steps/account_credit_control_changer.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from support import model, assert_equal, assert_in, assert_true + # flake8: noqa @given(u'I change level for invoice "{invoice_name}" to "{level_name}" of policy "{policy_name}"') def impl(ctx, invoice_name, level_name, policy_name): diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py index b7943461b..e49ca679c 100644 --- a/account_credit_control/scenarios/features/steps/account_voucher.py +++ b/account_credit_control/scenarios/features/steps/account_voucher.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # flake8: noqa -from support import * +from support import model, assert_equal, assert_almost_equal import datetime From 0115b7625dc4bede9fda54738e5eb2b7292b3b93 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 15:13:28 +0100 Subject: [PATCH 21/26] Remove steps no longer used (replaced by simpler steps using only the voucher and not the bank statement) --- .../features/steps/account_voucher.py | 89 +------------------ 1 file changed, 1 insertion(+), 88 deletions(-) diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py index e49ca679c..6d314316f 100644 --- a/account_credit_control/scenarios/features/steps/account_voucher.py +++ b/account_credit_control/scenarios/features/steps/account_voucher.py @@ -13,6 +13,7 @@ def impl(ctx, inv_name): When I pay %f on the invoice "%s" """ % (invoice.residual, inv_name)) + @step('I pay {amount:f} on the invoice "{inv_name}"') def impl(ctx, amount, inv_name): Partner = model('res.partner') @@ -87,97 +88,9 @@ def impl(ctx, amount, inv_name): # https://github.com/odoo/odoo/issues/3395 invoice.write({'currency_id': invoice.currency_id.id}) -@step('I import invoice "{inv_name}" using import invoice button') -def impl(ctx, inv_name): - invoice = model('account.invoice').get([('name', '=', inv_name)]) - assert invoice - bank_statement = ctx.found_item - for line in bank_statement.line_ids: - line.unlink() - lines = model('account.move.line').browse([('move_id', '=', invoice.move_id.id), - ('account_id', '=', invoice.account_id.id)]) - - wizard = model('account.statement.from.invoice.lines').create({'line_ids': lines}) - wizard.populate_statement({'statement_id': bank_statement.id}) - -@given(u'I should have a "account.bank.statement.line" with name: "{name}" and amount: "{amount}"') -def impl(ctx, name, amount): - assert ctx.found_item - line = model('account.bank.statement.line').get([('name', '=', name), - ('amount', '=', amount), - ('statement_id', '=', ctx.found_item.id)]) - assert line - ctx.line = line - -@given(u'I set the voucher paid amount to "{amount}"') -def impl(ctx, amount): - assert ctx.line - voucher = model('account.voucher').get(ctx.line.voucher_id.id) - assert voucher - - vals = voucher.onchange_amount(float(amount), - voucher.payment_rate, - voucher.partner_id.id, - voucher.journal_id.id if voucher.journal_id else False, - voucher.currency_id.id if voucher.currency_id else False, - voucher.type, - voucher.date, - voucher.payment_rate, - voucher.company_id.id if voucher.company_id else false) - vals = vals['value'] - vals.update({'amount': ctx.line.voucher_id.amount}) - voucher_line_ids = [] - voucher_line_dr_ids = [] - v_l_obj = model('account.voucher.line') - for v_line_vals in vals.get('line_cr_ids', []) or []: - v_line_vals['voucher_id'] = voucher.id - voucher_line_ids.append(v_l_obj.create(v_line_vals).id) - vals['line_cr_ids'] = voucher_line_ids - - for v_line_vals in vals.get('line_dr_ids', []) or []: - v_line_vals['voucher_id'] = voucher.id - voucher_line_dr_ids.append(v_l_obj.create(v_line_vals).id) - vals['line_dr_ids'] = voucher_line_ids - - voucher.write(vals) - ctx.vals = vals - ctx.voucher = voucher - -@given(u'I save the voucher') -def impl(ctx): - assert True - -@given(u'I modify the line amount to "{amount}"') -@then(u'I modify the line amount to "{amount}"') -def impl(ctx, amount): - assert ctx.line - # we have to change voucher amount before chaning statement line amount - # if ctx.line.voucher_id: - # model('account.voucher').write([ctx.line.voucher_id.id], - # {'amount': float(amount)}) - ctx.line.write({'amount': float(amount)}) @step('My invoice "{inv_name}" is in state "{state}" reconciled with a residual amount of "{amount:f}"') def impl(ctx, inv_name, state, amount): invoice = model('account.invoice').get([('name', '=', inv_name)]) assert_almost_equal(invoice.residual, amount) assert_equal(invoice.state, state) - -@step('I modify the bank statement line amount to {amount:f}') -def impl(ctx, amount): - line = ctx.found_item.voucher_id.line_cr_ids[0] - #ctx.voucher = model('account.voucher').get(ctx.found_item.voucher_id.id) - ctx.found_item.on_change('onchange_amount', 'amount', (), amount) - -@then(u'I set bank statement end-balance') -@given(u'I set bank statement end-balance') -def impl(ctx): - assert ctx.found_item, "No statement found" - ctx.found_item.write({'balance_end_real': ctx.found_item.balance_end}) - assert ctx.found_item.balance_end == ctx.found_item.balance_end_real - -@when(u'I confirm bank statement') -def impl(ctx): - assert ctx.found_item - assert_equal(ctx.found_item._model._name, 'account.bank.statement') - ctx.found_item.button_confirm_bank() From 9aa1d0c6458a68903a915db06b48aac0fa9abacd Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 15:32:53 +0100 Subject: [PATCH 22/26] The form view of credit control lines is hardly readable (no labels, ...) --- account_credit_control/line_view.xml | 52 +++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/account_credit_control/line_view.xml b/account_credit_control/line_view.xml index 370a9a136..767f44921 100644 --- a/account_credit_control/line_view.xml +++ b/account_credit_control/line_view.xml @@ -6,23 +6,41 @@ form
- - - - - - - - - - - - - - - - - +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
From a782c1a0680200a632ef7a6a73edcebe3dfbb028 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 15:35:10 +0100 Subject: [PATCH 23/26] Fields not in a group display no label --- account_credit_control/policy_view.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/account_credit_control/policy_view.xml b/account_credit_control/policy_view.xml index 0dfdf1de8..cdab56d34 100644 --- a/account_credit_control/policy_view.xml +++ b/account_credit_control/policy_view.xml @@ -7,10 +7,12 @@ form
- - - - + + + + + + From fe28c5c95955c420142a0431750ecc0fba8997cd Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 30 Oct 2014 15:43:58 +0100 Subject: [PATCH 24/26] Better view for credit control runs --- account_credit_control/run.py | 4 ++- account_credit_control/run_view.xml | 50 ++++++++++++++++------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/account_credit_control/run.py b/account_credit_control/run.py index 780057581..aaa443d17 100644 --- a/account_credit_control/run.py +++ b/account_credit_control/run.py @@ -32,7 +32,9 @@ class CreditControlRun(models.Model): _rec_name = 'date' _description = "Credit control line generator" - date = fields.Date(string='Controlling Date', required=True) + date = fields.Date(string='Controlling Date', required=True, + readonly=True, + states={'draft': [('readonly', False)]}) @api.model def _get_policies(self): diff --git a/account_credit_control/run_view.xml b/account_credit_control/run_view.xml index 59c99b9a5..949eeb0a4 100644 --- a/account_credit_control/run_view.xml +++ b/account_credit_control/run_view.xml @@ -6,7 +6,7 @@ credit.control.run tree - + @@ -19,27 +19,33 @@ form - - - - - - - - - - - - - -