mirror of
https://github.com/OCA/account-financial-tools.git
synced 2025-02-02 12:47:26 +02:00
[MIG] account_credit_control: Migration to 8.0
This commit is contained in:
committed by
Pedro M. Baeza
parent
3e36773432
commit
70e7966266
29
account_credit_control/README.rst
Normal file
29
account_credit_control/README.rst
Normal file
@@ -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)
|
||||
29
account_credit_control/__init__.py
Normal file
29
account_credit_control/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from . import mail
|
||||
from . import run
|
||||
from . import line
|
||||
from . import account
|
||||
from . import partner
|
||||
from . import policy
|
||||
from . import company
|
||||
from . import wizard
|
||||
from . import invoice
|
||||
@@ -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
|
||||
@@ -20,45 +20,17 @@
|
||||
##############################################################################
|
||||
{'name': 'Account Credit Control',
|
||||
'version': '0.2.0',
|
||||
'author': 'Camptocamp',
|
||||
'author': "Camptocamp,Odoo Community Association (OCA)",
|
||||
'maintainer': 'Camptocamp',
|
||||
'category': 'Finance',
|
||||
'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)
|
||||
""",
|
||||
'depends': ['base',
|
||||
'account',
|
||||
'email_template',
|
||||
],
|
||||
'website': 'http://www.camptocamp.com',
|
||||
'data': ["report/report.xml",
|
||||
"report/report_credit_control_summary.xml",
|
||||
"data.xml",
|
||||
"line_view.xml",
|
||||
"account_view.xml",
|
||||
@@ -71,7 +43,7 @@ On each generated line, you have many choices:
|
||||
"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',
|
||||
|
||||
32
account_credit_control/account.py
Normal file
32
account_credit_control/account.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class AccountAccount(models.Model):
|
||||
""" Add a link to a credit control policy on account.account """
|
||||
|
||||
_inherit = "account.account"
|
||||
|
||||
credit_control_line_ids = fields.One2many('credit.control.line',
|
||||
'account_id',
|
||||
string='Credit Lines',
|
||||
readonly=True)
|
||||
45
account_credit_control/account_view.xml
Normal file
45
account_credit_control/account_view.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<act_window
|
||||
id="act_account_credit_relation_relation"
|
||||
name="Credit Lines"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_user"
|
||||
domain="[('account_id', '=', active_id)]"
|
||||
res_model="credit.control.line"
|
||||
src_model="account.account"/>
|
||||
|
||||
<record id="invoice_followup_form_view" model="ir.ui.view">
|
||||
<field name="name">invoice.followup.form.view</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form" />
|
||||
<field name="arch" type="xml">
|
||||
<notebook position="inside">
|
||||
<page string="Credit Control"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_user,account_credit_control.group_account_credit_control_info">
|
||||
<group>
|
||||
<field name="credit_policy_id" widget="selection"
|
||||
string="Manual Credit Control Policy"
|
||||
attrs="{'invisible': [('credit_policy_id', '=', False)]}"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_user,account_credit_control.group_account_credit_control_info"/>
|
||||
</group>
|
||||
<separator string="Issued Lines" colspan="4"/>
|
||||
<field name="credit_control_line_ids" colspan="4" nolabel="1"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_user,account_credit_control.group_account_credit_control_info" >
|
||||
<tree string="Credit Control Lines">
|
||||
<field name="date"/>
|
||||
<field name="level"/>
|
||||
<field name="state"/>
|
||||
<field name="channel"/>
|
||||
<field name="balance_due"/>
|
||||
<field name="policy_level_id"/>
|
||||
<field name="policy_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
37
account_credit_control/company.py
Normal file
37
account_credit_control/company.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
""" Add credit control parameters """
|
||||
_inherit = 'res.company'
|
||||
|
||||
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.")
|
||||
15
account_credit_control/company_view.xml
Normal file
15
account_credit_control/company_view.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="credit_control_company_form" model="ir.ui.view">
|
||||
<field name="name">credit.control.company.form</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="currency_id" position="after">
|
||||
<field name="credit_policy_id" widget="selection"/>
|
||||
<field name="credit_control_tolerance"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
30
account_credit_control/credit_control_demo.xml
Normal file
30
account_credit_control/credit_control_demo.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="a_recv_1" model="account.account">
|
||||
<field name="code">X11002-a</field>
|
||||
<field name="name">B2B Debtors - (test)</field>
|
||||
<field ref="account.cas" name="parent_id"/>
|
||||
<field name="type">receivable</field>
|
||||
<field eval="True" name="reconcile"/>
|
||||
<field name="user_type" ref="account.data_account_type_receivable"/>
|
||||
</record>
|
||||
|
||||
<record id="a_recv_2" model="account.account">
|
||||
<field name="code">X11002-b</field>
|
||||
<field name="name">B2C Debtors - (test)</field>
|
||||
<field ref="account.cas" name="parent_id"/>
|
||||
<field name="type">receivable</field>
|
||||
<field eval="True" name="reconcile"/>
|
||||
<field name="user_type" ref="account.data_account_type_receivable"/>
|
||||
</record>
|
||||
|
||||
<record id="a_recv_3" model="account.account">
|
||||
<field name="code">X11002-c</field>
|
||||
<field name="name">New Debtors - (test)</field>
|
||||
<field ref="account.cas" name="parent_id"/>
|
||||
<field name="type">receivable</field>
|
||||
<field eval="True" name="reconcile"/>
|
||||
<field name="user_type" ref="account.data_account_type_receivable"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
224
account_credit_control/data.xml
Normal file
224
account_credit_control/data.xml
Normal file
@@ -0,0 +1,224 @@
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<!--Email template -->
|
||||
<record id="email_template_credit_control_base" model="email.template">
|
||||
<field name="name">Credit Control Email</field>
|
||||
<field name="email_from">noreply@localhost</field>
|
||||
<field name="subject">Credit Control: (${object.current_policy_level.name or 'n/a'})</field>
|
||||
<field name="email_to">${object.get_email() or ''}</field>
|
||||
<field name="model_id" ref="model_credit_control_communication"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="lang">${object.get_contact_address().lang or 'en_US'}</field>
|
||||
<field name="report_template" ref="credit_control_summary"/>
|
||||
<field name="body_html"><![CDATA[
|
||||
Dear ${object.contact_address.name or ''}
|
||||
<br/>
|
||||
<br/>
|
||||
${object.current_policy_level.custom_mail_text}
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<!-- policy no follow -->
|
||||
<record model="credit.control.policy"
|
||||
id="credit_control_no_follow">
|
||||
<field name="name">No follow</field>
|
||||
<field name="do_nothing" eval="1"/>
|
||||
</record>
|
||||
|
||||
<!-- no follow policy -->
|
||||
<record model="credit.control.policy.level"
|
||||
id="no_follow_1">
|
||||
<field name="name">No follow</field>
|
||||
<field name="level" eval="1"/>
|
||||
<field name="computation_mode">net_days</field>
|
||||
<field name="delay_days" eval="0"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_no_follow"/>
|
||||
<field name="channel">email</field>
|
||||
<field name="custom_text">Manual no follow</field>
|
||||
|
||||
<field name="custom_mail_text">Manual no follow</field>
|
||||
</record>
|
||||
|
||||
<!-- policy 1 -->
|
||||
<record model="credit.control.policy"
|
||||
id="credit_control_3_time">
|
||||
<field name="name">3 time policy</field>
|
||||
</record>
|
||||
|
||||
<record model="credit.control.policy.level"
|
||||
id="3_time_1">
|
||||
<field name="name">10 days net</field>
|
||||
<field name="level" eval="1"/>
|
||||
<field name="computation_mode">net_days</field>
|
||||
<field name="delay_days" eval="10"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_3_time"/>
|
||||
<field name="channel">email</field>
|
||||
<field name="custom_text">Our records indicate that we have not received the payment of the above mentioned invoice.
|
||||
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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
|
||||
<field name="custom_mail_text">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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="credit.control.policy.level"
|
||||
id="3_time_2">
|
||||
<field name="name">30 days end of month</field>
|
||||
<field name="level" eval="2"/>
|
||||
<field name="computation_mode">end_of_month</field>
|
||||
<field name="delay_days" eval="30"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_3_time"/>
|
||||
<field name="channel">email</field>
|
||||
<field name="custom_text">Our records indicate that we have not yet received the payment of the above mentioned invoice 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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
<field name="custom_mail_text">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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="credit.control.policy.level"
|
||||
id="3_time_3">
|
||||
<field name="name">10 days last reminder</field>
|
||||
<field name="level" eval="3"/>
|
||||
<field name="computation_mode">previous_date</field>
|
||||
<field name="delay_days" eval="10"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_3_time"/>
|
||||
<field name="channel">letter</field>
|
||||
<field name="custom_text">
|
||||
Our records indicate that we still have not received the payment of the above mentioned invoice 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.
|
||||
|
||||
Should you need us to arrange a payment plan for you, please advise.
|
||||
A customer account statement is enclosed for you convenience.
|
||||
|
||||
Thank you in advance for your anticipated cooperation in this matter.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
|
||||
<field name="custom_mail_text">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.
|
||||
|
||||
Should you need us to arrange a payment plan for you, please advise.
|
||||
A customer account statement is enclosed for you convenience.
|
||||
|
||||
Thank you in advance for your anticipated cooperation in this matter.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- policy 2 -->
|
||||
<record model="credit.control.policy"
|
||||
id="credit_control_2_time">
|
||||
<field name="name">2 time policy</field>
|
||||
</record>
|
||||
|
||||
<record model="credit.control.policy.level"
|
||||
id="2_time_1">
|
||||
<field name="name">30 days end of month</field>
|
||||
<field name="level" eval="1"/>
|
||||
<field name="computation_mode">end_of_month</field>
|
||||
<field name="delay_days" eval="30"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_2_time"/>
|
||||
<field name="channel">email</field>
|
||||
<field name="custom_text">Our records indicate that we have not received the payment of the above mentioned invoice.
|
||||
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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
<field name="custom_mail_text">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.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="credit.control.policy.level"
|
||||
id="2_time_2">
|
||||
<field name="name">60 days last reminder</field>
|
||||
<field name="level" eval="2"/>
|
||||
<field name="computation_mode">previous_date</field>
|
||||
<field name="delay_days" eval="60"/>
|
||||
<field name="email_template_id" ref="email_template_credit_control_base"/>
|
||||
<field name="policy_id" ref="credit_control_2_time"/>
|
||||
<field name="channel">letter</field>
|
||||
<field name="custom_text">Our records indicate that we still have not received the payment of the above mentioned invoice 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
|
||||
collection agency.
|
||||
|
||||
Should you need us to arrange a payment plan for you, please advise.
|
||||
A customer account statement is enclosed for you convenience.
|
||||
|
||||
Thank you in advance for your anticipated cooperation in this matter.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
<field name="custom_mail_text">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
|
||||
collection agency.
|
||||
|
||||
Should you need us to arrange a payment plan for you, please advise.
|
||||
A customer account statement is enclosed for you convenience.
|
||||
|
||||
Thank you in advance for your anticipated cooperation in this matter.
|
||||
|
||||
Best regards
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="group_account_credit_control_manager" model="res.groups">
|
||||
<field name="name">Credit Control Manager</field>
|
||||
<field name="category_id" ref="base.module_category_accounting_and_finance"/>
|
||||
</record>
|
||||
|
||||
<record id="group_account_credit_control_user" model="res.groups" context="{'noadmin':True}">
|
||||
<field name="name">Credit Control User</field>
|
||||
<field name="category_id" ref="base.module_category_accounting_and_finance"/>
|
||||
</record>
|
||||
|
||||
<record id="group_account_credit_control_info" model="res.groups" context="{'noadmin':True}">
|
||||
<field name="name">Credit Control Info</field>
|
||||
<field name="category_id" ref="base.module_category_accounting_and_finance"/>
|
||||
</record>
|
||||
|
||||
<record id="base.main_company" model="res.company">
|
||||
<field name="credit_policy_id" ref="credit_control_3_time"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
1160
account_credit_control/i18n/account_credit_control.pot
Normal file
1160
account_credit_control/i18n/account_credit_control.pot
Normal file
File diff suppressed because it is too large
Load Diff
1190
account_credit_control/i18n/de.po
Normal file
1190
account_credit_control/i18n/de.po
Normal file
File diff suppressed because it is too large
Load Diff
1357
account_credit_control/i18n/fr.po
Normal file
1357
account_credit_control/i18n/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
67
account_credit_control/invoice.py
Normal file
67
account_credit_control/invoice.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Vincent Renaville
|
||||
# Copyright 2013 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
"""Check on cancelling of an invoice"""
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
credit_control_line_ids = fields.One2many(
|
||||
'credit.control.line', 'invoice_id',
|
||||
string='Credit Lines',
|
||||
readonly=True,
|
||||
copy=False,
|
||||
)
|
||||
|
||||
@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.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.')
|
||||
)
|
||||
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()
|
||||
230
account_credit_control/line.py
Normal file
230
account_credit_control/line.py
Normal file
@@ -0,0 +1,230 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
logger = logging.getLogger('credit.line.control')
|
||||
|
||||
|
||||
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,
|
||||
print, ...)
|
||||
"""
|
||||
|
||||
_name = "credit.control.line"
|
||||
_description = "A credit control line"
|
||||
_rec_name = "id"
|
||||
_order = "date DESC"
|
||||
|
||||
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_entry = fields.Date(string='Entry date',
|
||||
related='move_line_id.date',
|
||||
store=True,
|
||||
readonly=True)
|
||||
|
||||
date_sent = fields.Date(string='Sent date',
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
|
||||
state = fields.Selection([('draft', 'Draft'),
|
||||
('ignored', 'Ignored'),
|
||||
('to_be_sent', 'Ready To Send'),
|
||||
('sent', 'Done'),
|
||||
('error', 'Error'),
|
||||
('email_error', 'Emailing Error')],
|
||||
'State',
|
||||
required=True,
|
||||
readonly=True,
|
||||
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.")
|
||||
|
||||
channel = fields.Selection([('letter', 'Letter'),
|
||||
('email', 'Email')],
|
||||
string='Channel',
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
|
||||
invoice_id = fields.Many2one('account.invoice',
|
||||
string='Invoice',
|
||||
readonly=True)
|
||||
|
||||
partner_id = fields.Many2one('res.partner',
|
||||
string='Partner',
|
||||
required=True)
|
||||
|
||||
amount_due = fields.Float(string='Due Amount Tax incl.',
|
||||
required=True, readonly=True)
|
||||
|
||||
balance_due = fields.Float(string='Due balance', required=True,
|
||||
readonly=True)
|
||||
|
||||
mail_message_id = fields.Many2one('mail.mail', string='Sent Email',
|
||||
readonly=True)
|
||||
|
||||
move_line_id = fields.Many2one('account.move.line',
|
||||
string='Move line',
|
||||
required=True,
|
||||
readonly=True)
|
||||
|
||||
account_id = fields.Many2one('account.account',
|
||||
related='move_line_id.account_id',
|
||||
store=True,
|
||||
readonly=True)
|
||||
|
||||
currency_id = fields.Many2one('res.currency',
|
||||
related='move_line_id.currency_id',
|
||||
store=True,
|
||||
readonly=True)
|
||||
|
||||
company_id = fields.Many2one('res.company',
|
||||
related='move_line_id.company_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',
|
||||
string='Overdue Level',
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
|
||||
policy_id = fields.Many2one('credit.control.policy',
|
||||
related='policy_level_id.policy_id',
|
||||
store=True,
|
||||
readonly=True)
|
||||
|
||||
level = fields.Integer('credit.control.policy.level',
|
||||
related='policy_level_id.level',
|
||||
store=True,
|
||||
readonly=True)
|
||||
|
||||
manually_overridden = fields.Boolean(string='Manually overridden')
|
||||
|
||||
run_id = fields.Many2one(comodel_name='credit.control.run',
|
||||
string='Source')
|
||||
|
||||
@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
|
||||
data['state'] = 'draft'
|
||||
data['channel'] = level.channel
|
||||
data['invoice_id'] = (move_line.invoice.id if
|
||||
move_line.invoice else False)
|
||||
data['partner_id'] = move_line.partner_id.id
|
||||
data['amount_due'] = (move_line.amount_currency or move_line.debit or
|
||||
move_line.credit)
|
||||
data['balance_due'] = open_amount
|
||||
data['policy_level_id'] = level.id
|
||||
data['move_line_id'] = move_line.id
|
||||
return data
|
||||
|
||||
@api.model
|
||||
def create_or_update_from_mv_lines(self, lines, level, 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 recordset
|
||||
: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
|
||||
:param check_tolerance: boolean if True credit line
|
||||
will not be generated if open amount
|
||||
is smaller than company defined
|
||||
tolerance
|
||||
|
||||
:returns: recordset of created credit lines
|
||||
"""
|
||||
currency_obj = self.env['res.currency']
|
||||
user = self.env.user
|
||||
currencies = currency_obj.search([])
|
||||
|
||||
tolerance = {}
|
||||
tolerance_base = user.company_id.credit_control_tolerance
|
||||
user_currency = user.company_id.currency_id
|
||||
for currency in currencies:
|
||||
tolerance[currency.id] = currency.compute(tolerance_base,
|
||||
user_currency)
|
||||
|
||||
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(move_line,
|
||||
level,
|
||||
controlling_date,
|
||||
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_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 new_lines
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
for line in self:
|
||||
if line.state != 'draft':
|
||||
raise api.Warning(
|
||||
_('You are not allowed to delete a credit control '
|
||||
'line that is not in draft state.')
|
||||
)
|
||||
|
||||
return super(CreditControlLine, self).unlink()
|
||||
171
account_credit_control/line_view.xml
Normal file
171
account_credit_control/line_view.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="credit_control_line_form" model="ir.ui.view">
|
||||
<field name="name">credit.control.line.form</field>
|
||||
<field name="model">credit.control.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Control Credit Lines">
|
||||
<header>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,to_be_sent,sent"
|
||||
statusbar_colors='{"error":"red","email_error":"red"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="policy_level_id"/>
|
||||
<field name="policy_id"/>
|
||||
<field name="level"/>
|
||||
<field name="manually_overridden"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="date_due"/>
|
||||
<field name="channel"/>
|
||||
<field name="date_sent"/>
|
||||
<field name="mail_message_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="invoice_id"/>
|
||||
<field name="move_line_id"/>
|
||||
<field name="account_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="amount_due"/>
|
||||
<field name="balance_due"/>
|
||||
<field name="currency_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="credit_control_line_search" model="ir.ui.view">
|
||||
<field name="name">Credit Control Lines</field>
|
||||
<field name="model">credit.control.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Control Credit Lines">
|
||||
<group string="Filters">
|
||||
<filter name="filter_draft" icon="terp-mail-message-new"
|
||||
string="Draft" domain="[('state', '=', 'draft')]"
|
||||
help="Draft lines have to be triaged."/>
|
||||
<filter name="filter_to_be_sent" icon="terp-mail-forward"
|
||||
string="Ready To Send" domain="[('state', '=', 'to_be_sent')]"
|
||||
help="These lines are ready to send by email or by letter using the Actions."/>
|
||||
<filter name="filter_ignored" icon="terp-mail_delete"
|
||||
string="Ignored" domain="[('state', '=', 'ignored')]"
|
||||
help="Lines which have been ignored from previous runs."/>
|
||||
<filter name="filter_sent" icon="terp-mail-replied"
|
||||
string="Sent" domain="[('state', '=', 'sent')]"
|
||||
help="Lines already sent."/>
|
||||
<filter name="filter_error" icon="terp-gtk-stop" string="Error"
|
||||
domain="[('state', 'in', ('error', 'email_error'))]"
|
||||
help="An error has occured during the sending of the email."/>
|
||||
<filter name="filter_manual" icon="terp-gtk-stop" string="Manual change"
|
||||
domain="[('manually_overridden', '=', True)]"
|
||||
help="The line was deprecated by a manual change of policy on invoice."/>
|
||||
<separator orientation="vertical"/>
|
||||
|
||||
<field name="date"/>
|
||||
<field name="channel" />
|
||||
<field name="policy_id"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="0" string="More...">
|
||||
<field name="partner_id"/>
|
||||
<field name="account_id"/>
|
||||
<field name="invoice_id"/>
|
||||
<field name="level"/>
|
||||
<field name="policy_level_id"/>
|
||||
</group>
|
||||
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'date'}"
|
||||
icon="terp-go-month" string="Run date"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'level'}"
|
||||
icon="terp-gtk-jump-to-rtl" string="Level"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'partner_id'}"
|
||||
icon="terp-partner" string="Partner"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'account_id'}"
|
||||
icon="terp-folder-green" string="Account"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'invoice_id'}"
|
||||
icon="terp-document-new" string="Invoice"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'policy_id'}"
|
||||
icon="terp-document-new" string="Credit policy"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'policy_level_id'}"
|
||||
icon="terp-document-new" string="Credit policy level"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter domain='[]' context="{'group_by': 'channel'}"
|
||||
icon="terp-document-new" string="Channel"/>
|
||||
<filter domain='[]' context="{'group_by': 'manually_overridden'}"
|
||||
icon="terp-document-new" string="Manual change"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="credit_control_line_tree" model="ir.ui.view">
|
||||
<field name="name">credit.control.line.tree</field>
|
||||
<field name="model">credit.control.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" string="Control Credit Lines" colors="green:state == 'sent';red:state in ('error', 'email_error');">
|
||||
<field name="date"/>
|
||||
<field name="date_due"/>
|
||||
<field name="level"/>
|
||||
<field name="manually_overridden"/>
|
||||
<field name="state"/>
|
||||
<field name="channel"/>
|
||||
<field name="invoice_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="amount_due"/>
|
||||
<field name="balance_due"/>
|
||||
<field name="currency_id"/>
|
||||
<field name="move_line_id"/>
|
||||
<field name="account_id"/>
|
||||
<field name="policy_level_id"/>
|
||||
<field name="policy_id"/>
|
||||
<field name="mail_message_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Credit Control"
|
||||
parent="account.menu_finance_periodical_processing"
|
||||
id="base_credit_control_menu"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="credit_control_line_action">
|
||||
<field name="name">Credit Control Lines</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">credit.control.line</field>
|
||||
<field name="domain"></field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="credit_control_line_tree"/>
|
||||
<field name="context">{'search_default_filter_draft': 1, 'search_default_filter_to_be_sent': 1}</field>
|
||||
<field name="search_view_id" ref="credit_control_line_search"/>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem
|
||||
name="Credit Control Lines"
|
||||
parent="base_credit_control_menu"
|
||||
action="credit_control_line_action"
|
||||
sequence="20"
|
||||
id="credit_control_line_action_menu"/>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
29
account_credit_control/mail.py
Normal file
29
account_credit_control/mail.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class Mail(models.Model):
|
||||
_inherit = 'mail.mail'
|
||||
|
||||
# use HTML fields instead of text
|
||||
body_html = fields.Html('Rich-text Contents',
|
||||
help="Rich-text/HTML message")
|
||||
58
account_credit_control/partner.py
Normal file
58
account_credit_control/partner.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields, api
|
||||
|
||||
|
||||
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"
|
||||
|
||||
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)
|
||||
|
||||
@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 = partner.credit_policy_id
|
||||
try:
|
||||
policy.check_policy_against_account(account)
|
||||
except api.Warning as err:
|
||||
# constrains should raise ValidationError exceptions
|
||||
raise api.ValidationError(err)
|
||||
24
account_credit_control/partner_view.xml
Normal file
24
account_credit_control/partner_view.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="partner_followup_form_view" model="ir.ui.view">
|
||||
<field name="name">partner.credit_control.form.view</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="credit_limit" position="after">
|
||||
<field name="credit_policy_id" widget="selection"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_use"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
id="act_partner_credit_relation_relation"
|
||||
name="Credit Lines"
|
||||
groups="account_credit_control.group_account_credit_control_manager,account_credit_control.group_account_credit_control_user"
|
||||
domain="[('partner_id', '=', active_id)]"
|
||||
res_model="credit.control.line"
|
||||
src_model="res.partner"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
406
account_credit_control/policy.py
Normal file
406
account_credit_control/policy.py
Normal file
@@ -0,0 +1,406 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class CreditControlPolicy(models.Model):
|
||||
""" Define a policy of reminder """
|
||||
|
||||
_name = "credit.control.policy"
|
||||
_description = """Define a reminder policy"""
|
||||
|
||||
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)
|
||||
|
||||
@api.multi
|
||||
def _move_lines_domain(self, controlling_date):
|
||||
""" Build the default domain for searching move lines """
|
||||
self.ensure_one()
|
||||
return [('account_id', 'in', self.account_ids.ids),
|
||||
('date_maturity', '<=', controlling_date),
|
||||
('reconcile_id', '=', False),
|
||||
('partner_id', '!=', False)]
|
||||
|
||||
@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
|
||||
to the specific policies defined on partners and invoices.
|
||||
|
||||
Do not use direct SQL in order to respect security rules.
|
||||
|
||||
Assume that only the receivable lines have a maturity date and that
|
||||
accounts used in the policy are reconcilable.
|
||||
"""
|
||||
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)
|
||||
|
||||
@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.
|
||||
|
||||
Assume that only the receivable lines have a maturity date and that
|
||||
accounts used in the policy are reconcilable.
|
||||
|
||||
The policy relation field must be named credit_policy_id.
|
||||
|
||||
: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: 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.env[model]
|
||||
move_l_obj = self.env['account.move.line']
|
||||
default_domain = self._move_lines_domain(controlling_date)
|
||||
|
||||
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_objs = my_obj.search([('credit_policy_id', '=', self.id)])
|
||||
if add_objs:
|
||||
domain = list(default_domain)
|
||||
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
|
||||
# included in the run for this policy.
|
||||
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_objs.ids))
|
||||
to_remove = move_l_obj.search(domain)
|
||||
return to_add, to_remove
|
||||
|
||||
@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 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: recordset to add in the process, recordset to remove from
|
||||
the process
|
||||
"""
|
||||
return self._move_lines_subset(controlling_date, 'res.partner',
|
||||
'partner_id')
|
||||
|
||||
@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 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: recordset to add in the process, recordset to remove from
|
||||
the process
|
||||
"""
|
||||
return self._move_lines_subset(controlling_date, 'account.invoice',
|
||||
'invoice')
|
||||
|
||||
@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 str controlling_date: date of credit control
|
||||
:return: recordset to include in the run
|
||||
"""
|
||||
self.ensure_one()
|
||||
# there is a priority between the lines, depicted by the calls below
|
||||
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
|
||||
|
||||
@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.
|
||||
"""
|
||||
self.ensure_one()
|
||||
move_line_obj = self.env['account.move.line']
|
||||
different_lines = move_line_obj.browse()
|
||||
if not lines:
|
||||
return different_lines
|
||||
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",
|
||||
(self.id, tuple(lines.ids)))
|
||||
res = cr.fetchall()
|
||||
if res:
|
||||
return move_line_obj.browse([row[0] for row in res])
|
||||
return different_lines
|
||||
|
||||
@api.multi
|
||||
def check_policy_against_account(self, account):
|
||||
""" Ensure that the policy corresponds to account relation """
|
||||
policies = self.search([])
|
||||
allowed = [x for x in policies
|
||||
if account in x.account_ids or x.do_nothing]
|
||||
if self not in allowed:
|
||||
allowed_names = u"\n".join(x.name for x in allowed)
|
||||
raise api.Warning(
|
||||
_('You can only use a policy set on '
|
||||
'account %s.\n'
|
||||
'Please choose one of the following '
|
||||
'policies:\n %s') % (account.name, allowed_names)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
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"""
|
||||
|
||||
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')]
|
||||
|
||||
@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'))
|
||||
|
||||
@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
|
||||
|
||||
:return: previous level or None if there is no previous level
|
||||
"""
|
||||
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 ---------
|
||||
|
||||
@staticmethod
|
||||
def _net_days_get_boundary():
|
||||
return (" (mv_line.date_maturity + %(delay)s)::date <= "
|
||||
"date(%(controlling_date)s)")
|
||||
|
||||
@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)")
|
||||
|
||||
@staticmethod
|
||||
def _previous_date_get_boundary():
|
||||
return "(cr_line.date + %(delay)s)::date <= date(%(controlling_date)s)"
|
||||
|
||||
@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, )
|
||||
)
|
||||
|
||||
# -----------------------------------------
|
||||
|
||||
@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 move_line_obj.browse()
|
||||
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"
|
||||
" AND NOT EXISTS (SELECT id\n"
|
||||
" FROM credit_control_line\n"
|
||||
" WHERE move_line_id = mv_line.id\n"
|
||||
# lines from a previous level with a draft or ignored state
|
||||
# or manually overridden
|
||||
# have to be generated again for the previous level
|
||||
" AND NOT manually_overridden\n"
|
||||
" AND state NOT IN ('draft', 'ignored'))"
|
||||
" AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n")
|
||||
sql += " AND"
|
||||
_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.ids),
|
||||
'delay': self.delay_days}
|
||||
cr.execute(sql, data_dict)
|
||||
res = cr.fetchall()
|
||||
if res:
|
||||
return move_line_obj.browse([row[0] for row in res])
|
||||
return move_line_obj.browse()
|
||||
|
||||
@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.
|
||||
"""
|
||||
self.ensure_one()
|
||||
move_line_obj = self.env['account.move.line']
|
||||
if not lines:
|
||||
return move_line_obj.browse()
|
||||
cr = self.env.cr
|
||||
sql = ("SELECT mv_line.id\n"
|
||||
" FROM account_move_line mv_line\n"
|
||||
" JOIN credit_control_line cr_line\n"
|
||||
" ON (mv_line.id = cr_line.move_line_id)\n"
|
||||
" WHERE cr_line.id = (SELECT credit_control_line.id "
|
||||
" FROM credit_control_line\n"
|
||||
" WHERE credit_control_line.move_line_id = mv_line.id\n"
|
||||
" AND state != 'ignored'"
|
||||
" AND NOT manually_overridden"
|
||||
" ORDER BY credit_control_line.level desc limit 1)\n"
|
||||
" AND cr_line.level = %(previous_level)s\n"
|
||||
" AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n"
|
||||
# lines from a previous level with a draft or ignored state
|
||||
# or manually overridden
|
||||
# have to be generated again for the previous level
|
||||
" AND NOT manually_overridden\n"
|
||||
" AND cr_line.state NOT IN ('draft', 'ignored')\n"
|
||||
" AND mv_line.id in %(line_ids)s\n")
|
||||
sql += " AND "
|
||||
_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.ids),
|
||||
'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:
|
||||
return move_line_obj.browse([row[0] for row in res])
|
||||
return move_line_obj.browse()
|
||||
|
||||
@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_lines
|
||||
matching_lines |= method(controlling_date, lines)
|
||||
return matching_lines
|
||||
131
account_credit_control/policy_view.xml
Normal file
131
account_credit_control/policy_view.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="credit_control_policy_form" model="ir.ui.view">
|
||||
<field name="name">credit.control.policy.form</field>
|
||||
<field name="model">credit.control.policy</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Credit control policy">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="do_nothing"/>
|
||||
<field name="company_id"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Policy levels">
|
||||
<field name="level_ids" nolabel="1" colspan="4" >
|
||||
<tree string="Credit control policy Level">
|
||||
<field name="name"/>
|
||||
<field name="level"/>
|
||||
<field name="channel"/>
|
||||
<field name="delay_days"/>
|
||||
<field name="computation_mode"/>
|
||||
<field name="email_template_id"/>
|
||||
</tree>
|
||||
<form string="Policy level">
|
||||
<field name="name"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Delay Setting">
|
||||
<group>
|
||||
<field name="level"/>
|
||||
<field name="channel"/>
|
||||
<field name="delay_days"/>
|
||||
<field name="computation_mode"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Mail and reporting">
|
||||
<group>
|
||||
<field name="email_template_id"/>
|
||||
<newline/>
|
||||
<field name="custom_text" colspan="4"/>
|
||||
<newline/>
|
||||
<field name="custom_mail_text" colspan="4"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Accounts">
|
||||
<field name="account_ids" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="credit_control_policy_tree" model="ir.ui.view">
|
||||
<field name="name">credit.control.policy.tree</field>
|
||||
<field name="model">credit.control.policy</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Credit control policy">
|
||||
<field name="name"/>
|
||||
<field name="do_nothing"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Credit Control"
|
||||
parent="account.menu_finance_configuration"
|
||||
id="base_credit_control_configuration_menu"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="credit_policy_configuration_action">
|
||||
<field name="name">Credit Control Policies</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">credit.control.policy</field>
|
||||
<field name="domain"></field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="credit_control_policy_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Credit Control Policies"
|
||||
parent="base_credit_control_configuration_menu"
|
||||
action="credit_policy_configuration_action"
|
||||
id="credit_policy_configuration_action_menu"/>
|
||||
|
||||
<record id="credit_mangement_policy_level_form" model="ir.ui.view">
|
||||
<field name="name">credit.mangement.policy.level.form</field>
|
||||
<field name="model">credit.control.policy.level</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Policy level">
|
||||
<field name="name"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Delay Setting">
|
||||
<group>
|
||||
<field name="level"/>
|
||||
<field name="channel"/>
|
||||
<field name="delay_days"/>
|
||||
<field name="computation_mode"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Mail and reporting">
|
||||
<group>
|
||||
<field name="email_template_id"/>
|
||||
<field name="custom_text"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="credit_control_policy_level_tree" model="ir.ui.view">
|
||||
<field name="name">credit.control.policy.level.tree</field>
|
||||
<field name="model">credit.control.policy.level</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Credit control policy level">
|
||||
<field name="name"/>
|
||||
<field name="level"/>
|
||||
<field name="channel"/>
|
||||
<field name="delay_days"/>
|
||||
<field name="computation_mode"/>
|
||||
<field name="email_template_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
15
account_credit_control/report/report.xml
Normal file
15
account_credit_control/report/report.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<report
|
||||
id="credit_control_summary"
|
||||
model="credit.control.communication"
|
||||
string="Credit Control Summary"
|
||||
report_type="qweb-pdf"
|
||||
name="account_credit_control.report_credit_control_summary"
|
||||
file="account_credit_control.report_credit_control_summary"
|
||||
auto="False"
|
||||
/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="report_credit_control_summary_document">
|
||||
<t t-call="report.external_layout">
|
||||
<div class="page">
|
||||
<div class="row" id="address">
|
||||
<div class="col-xs-5 col-xs-offset-7">
|
||||
<address t-field="o.contact_address"
|
||||
t-field-options='{"widget": "contact",
|
||||
"fields": ["address", "name"],
|
||||
"no_marker": true}' />
|
||||
</div>
|
||||
<div class="col-xs-5 col-xs-offset-7">
|
||||
<span t-field="o.report_date"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 id="policy_level">
|
||||
<span t-field="o.current_policy_level.name"/>
|
||||
</h2>
|
||||
|
||||
<p class="mt32 mb32" t-field="o.current_policy_level.custom_text"/>
|
||||
|
||||
<h3>Summary</h3>
|
||||
|
||||
<table class="table table-condensed" id="summary_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice number</th>
|
||||
<th>Invoice date</th>
|
||||
<th>Date due</th>
|
||||
<th class="text-right">Invoiced amount</th>
|
||||
<th class="text-right">Open amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="o.credit_control_line_ids" t-as="l">
|
||||
<t t-if="l.invoice_id">
|
||||
<td><span t-field="l.invoice_id.number"/>
|
||||
<t t-if="l.invoice_id.name">
|
||||
<span t-field="l.invoice_id.name"/>
|
||||
</t>
|
||||
</td>
|
||||
</t>
|
||||
<t t-if="not l.invoice_id">
|
||||
<td><span t-field="l.move_line_id.name"/></td>
|
||||
</t>
|
||||
<td>
|
||||
<span t-field="l.date_entry"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-field="l.date_due"/>
|
||||
</td>
|
||||
|
||||
<td class="text-right">
|
||||
<span t-field="l.amount_due"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.balance_due"
|
||||
t-field-options='{"widget": "monetary",
|
||||
"display_currency": "l.currency_id or l.company_id.currency_id"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-4 pull-right">
|
||||
<table class="table table-condensed">
|
||||
<tr>
|
||||
<td><strong>Total Invoiced</strong></td>
|
||||
<td class="text-right">
|
||||
<span t-field="o.total_invoiced" t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Total Due</strong></td>
|
||||
<td class="text-right">
|
||||
<span t-field="o.total_due" t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_credit_control_summary">
|
||||
<t t-call="report.html_container">
|
||||
<t t-foreach="doc_ids" t-as="doc_id">
|
||||
<t t-raw="translate_doc(doc_id, doc_model, 'partner_id.lang', 'account_credit_control.report_credit_control_summary_document')"/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
175
account_credit_control/run.py
Normal file
175
account_credit_control/run.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
logger = logging.getLogger('credit.control.run')
|
||||
|
||||
|
||||
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"
|
||||
|
||||
date = fields.Date(string='Controlling Date', required=True,
|
||||
readonly=True,
|
||||
states={'draft': [('readonly', False)]})
|
||||
|
||||
@api.model
|
||||
def _get_policies(self):
|
||||
return self.env['credit.control.policy'].search([])
|
||||
|
||||
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.Html(string='Report', readonly=True, copy=False)
|
||||
state = fields.Selection([('draft', 'Draft'),
|
||||
('done', 'Done')],
|
||||
string='State',
|
||||
required=True,
|
||||
readonly=True,
|
||||
default='draft')
|
||||
|
||||
line_ids = fields.One2many(
|
||||
comodel_name='credit.control.line',
|
||||
inverse_name='run_id',
|
||||
string='Generated lines')
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _check_run_date(self, controlling_date):
|
||||
""" Ensure that there is no credit line in the future
|
||||
using controlling_date
|
||||
|
||||
"""
|
||||
runs = self.search([('date', '>', controlling_date)],
|
||||
order='date DESC', limit=1)
|
||||
if runs:
|
||||
raise api.Warning(_('A run has already been executed more '
|
||||
'recently than %s') % (runs.date))
|
||||
|
||||
line_obj = self.env['credit.control.line']
|
||||
lines = line_obj.search([('date', '>', controlling_date)],
|
||||
order='date DESC', limit=1)
|
||||
if lines:
|
||||
raise api.Warning(_('A credit control line more '
|
||||
'recent than %s exists at %s') %
|
||||
(controlling_date, lines.date))
|
||||
|
||||
@api.multi
|
||||
@api.returns('credit.control.line')
|
||||
def _generate_credit_lines(self):
|
||||
""" Generate credit control lines. """
|
||||
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)
|
||||
|
||||
policies = self.policy_ids
|
||||
if not policies:
|
||||
raise api.Warning(_('Please select a policy'))
|
||||
|
||||
report = ''
|
||||
generated = cr_line_obj.browse()
|
||||
for policy in policies:
|
||||
if policy.do_nothing:
|
||||
continue
|
||||
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
|
||||
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 += create(level_lines,
|
||||
level,
|
||||
self.date)
|
||||
generated |= policy_lines_generated
|
||||
if policy_lines_generated:
|
||||
report += (_("Policy \"<b>%s</b>\" has generated <b>%d Credit "
|
||||
"Control Lines.</b><br/>") %
|
||||
(policy.name, len(policy_lines_generated)))
|
||||
else:
|
||||
report += _(
|
||||
"Policy \"<b>%s</b>\" has not generated any "
|
||||
"Credit Control Lines.<br/>" % policy.name
|
||||
)
|
||||
|
||||
vals = {'state': 'done',
|
||||
'report': report,
|
||||
'manual_ids': [(6, 0, manually_managed_lines.ids)],
|
||||
'line_ids': [(6, 0, generated.ids)]}
|
||||
self.write(vals)
|
||||
return generated
|
||||
|
||||
@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:
|
||||
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
|
||||
raise api.Warning(_('A credit control run is already running'
|
||||
' in background, please try later.'))
|
||||
|
||||
self._generate_credit_lines()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def open_credit_lines(self):
|
||||
""" Open the generated lines """
|
||||
self.ensure_one()
|
||||
action_name = 'account_credit_control.credit_control_line_action'
|
||||
action = self.env.ref(action_name)
|
||||
action = action.read()[0]
|
||||
action['domain'] = [('id', 'in', self.line_ids.ids)]
|
||||
return action
|
||||
75
account_credit_control/run_view.xml
Normal file
75
account_credit_control/run_view.xml
Normal file
@@ -0,0 +1,75 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="credit_control_run_tree" model="ir.ui.view">
|
||||
<field name="name">credit.control.run.tree</field>
|
||||
<field name="model">credit.control.run</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Credit control run">
|
||||
<field name="date"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="credit_control_run_form" model="ir.ui.view">
|
||||
<field name="name">credit.control.run.form</field>
|
||||
<field name="model">credit.control.run</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Credit control run">
|
||||
<header>
|
||||
<button name="generate_credit_lines"
|
||||
string="Compute Credit Control Lines"
|
||||
class="oe_highlight"
|
||||
type="object" icon="gtk-execute"
|
||||
attrs="{'invisible': [('state', '!=', 'draft')]}"/>
|
||||
<button name="open_credit_lines"
|
||||
string="Open Credit Control Lines"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,done"
|
||||
statusbar_colors='{}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Policies">
|
||||
<field name="policy_ids" colspan="4" nolabel="1"/>
|
||||
<separator string="Report"
|
||||
attrs="{'invisible': [('report', '=', False)]}"/>
|
||||
<field name="report" colspan="4" nolabel="1"
|
||||
attrs="{'invisible': [('report', '=', False)]}"/>
|
||||
</page>
|
||||
<page string="Manual Lines" groups="base.group_no_one">
|
||||
<field name="manual_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="credit_control_run">
|
||||
<field name="name">Credit Control Run</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">credit.control.run</field>
|
||||
<field name="domain"></field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="credit_control_run_tree"/>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem
|
||||
name="Credit Control Run"
|
||||
parent="base_credit_control_menu"
|
||||
action="credit_control_run"
|
||||
sequence="10"
|
||||
id="credit_control_run_menu"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,61 @@
|
||||
###############################################################################
|
||||
#
|
||||
# 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
|
||||
@account_credit_control @account_credit_control_setup
|
||||
|
||||
Feature: General parameters in order to test the credit control module
|
||||
|
||||
|
||||
@account_credit_control_setup_install_modules
|
||||
Scenario: MODULES INSTALLATION
|
||||
|
||||
Given I do not want all demo data to be loaded on install
|
||||
Given I install the required modules with dependencies:
|
||||
| name |
|
||||
| account_credit_control |
|
||||
|
||||
Then my modules should have been installed and models reloaded
|
||||
|
||||
|
||||
@deactivate_journal_control
|
||||
Scenario: Journal setup to avoid unfixed voucher bug
|
||||
Given I execute the SQL commands
|
||||
"""
|
||||
UPDATE account_journal SET allow_date = false;
|
||||
"""
|
||||
|
||||
@email_params_mailtrap
|
||||
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.io |
|
||||
| sequence | 1 |
|
||||
| smtp_port | 2525 |
|
||||
| smtp_user | camptocamp1 |
|
||||
| smtp_pass | 20468fa2f2879cb9 |
|
||||
|
||||
@account_credit_control_policy_2_times
|
||||
Scenario: Configure the credit control policy in 2 times
|
||||
Given I configure the following accounts on the credit control policy with oid: "account_credit_control.credit_control_2_time":
|
||||
| account code |
|
||||
| 4111 |
|
||||
| 4112 |
|
||||
|
||||
@account_credit_control_policy_3_times
|
||||
Scenario: Configure the credit control policy in 3 times
|
||||
Given I configure the following accounts on the credit control policy with oid: "account_credit_control.credit_control_3_time":
|
||||
| account code |
|
||||
| 4111 |
|
||||
| 4112 |
|
||||
@@ -0,0 +1,105 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2009 Camptocamp SA
|
||||
#
|
||||
##############################################################################
|
||||
##############################################################################
|
||||
# Branch # Module # Processes # System
|
||||
@account_credit_control @account_credit_control_add_policy @account_credit_control_setup
|
||||
|
||||
Feature: I add policy to partners already created
|
||||
@account_credit_control_partner_1
|
||||
Scenario: Partner_1
|
||||
Given I need a "res.partner" with oid: scen.partner_1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | partner_1 |
|
||||
| credit_policy_id | by name: No follow |
|
||||
|
||||
@account_credit_control_customer_1
|
||||
Scenario: Customer_1
|
||||
Given I need a "res.partner" with oid: scen.customer_1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_1 |
|
||||
| credit_policy_id | by name: 2 time policy |
|
||||
|
||||
@account_credit_control_customer_2
|
||||
Scenario: Customer_2
|
||||
Given I need a "res.partner" with oid: scen.customer_2
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_2 |
|
||||
| credit_policy_id | by name: 2 time policy |
|
||||
|
||||
@account_credit_control_customer_3
|
||||
Scenario: Customer_3
|
||||
Given I need a "res.partner" with oid: scen.customer_3
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_3 |
|
||||
| credit_policy_id | by name: 2 time policy |
|
||||
|
||||
@account_credit_control_customer_4
|
||||
Scenario: Customer_4
|
||||
Given I need a "res.partner" with oid: scen.customer_4
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_4 |
|
||||
# the credit policy must be 3 time policy (inherited from company)
|
||||
|
||||
@account_credit_control_customer_5
|
||||
Scenario: Customer_5
|
||||
Given I need a "res.partner" with oid: scen.customer_5
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_5_usd |
|
||||
| credit_policy_id | by name: 3 time policy |
|
||||
|
||||
@account_credit_control_customer_6
|
||||
Scenario: Customer_6
|
||||
Given I need a "res.partner" with oid: scen.customer_6
|
||||
And having:
|
||||
| name | value |
|
||||
| name | customer_6 |
|
||||
| credit_policy_id | by name: 3 time policy |
|
||||
|
||||
@account_credit_control_customer_partial_pay
|
||||
Scenario: A customer who like to do partial payments
|
||||
Given I need a "res.partner" with oid: scen.customer_partial_pay
|
||||
And having:
|
||||
| name | value |
|
||||
| name | Scrooge McDuck |
|
||||
| zip | 1000 |
|
||||
| city | Duckburg |
|
||||
| email | openerp@locahost.dummy |
|
||||
| phone | |
|
||||
| street | Duckstreet |
|
||||
|
||||
|
||||
@account_credit_control_customer_multiple_payterm
|
||||
Scenario: A customer who use payment terms in 2 times
|
||||
Given I need a "res.partner" with oid: scen.customer_multiple_payterm
|
||||
And having:
|
||||
| name | value |
|
||||
| name | Donald Duck |
|
||||
| zip | 1100 |
|
||||
| city | Duckburg |
|
||||
| email | openerp@locahost.dummy |
|
||||
| phone | |
|
||||
| street | Duckstreet |
|
||||
|
||||
@account_credit_control_customer_multiple_payterm2
|
||||
Scenario: A customer who use payment terms in 2 times
|
||||
Given I need a "res.partner" with oid: scen.customer_multiple_payterm2
|
||||
And having:
|
||||
| name | value |
|
||||
| name | Gus Goose |
|
||||
| type | default |
|
||||
| name | Gus Goose |
|
||||
| zip | 1100 |
|
||||
| city | Duckburg |
|
||||
| email | openerp@locahost.dummy |
|
||||
| phone | |
|
||||
| street | Duckstreet |
|
||||
@@ -0,0 +1,588 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
|
||||
@account_credit_control @account_credit_control_setup @account_credit_control_base_data @account_credit_control_invoices
|
||||
|
||||
Feature: Invoices creation
|
||||
|
||||
##################### Partner 1 ##########################################################
|
||||
|
||||
@inv_1
|
||||
Scenario: Create invoice 1
|
||||
|
||||
Given I need a "account.invoice" with oid: scen._inv_1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_1 |
|
||||
| date_invoice | 2013-01-15 |
|
||||
| partner_id | by oid: scen.partner_1 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv1_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_1 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_1
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv1_line2
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 2 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_1 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_1
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_2
|
||||
Scenario: Create invoice 2
|
||||
Given I need a "account.invoice" with oid: scen._inv_2
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_2 |
|
||||
| date_invoice | 2013-02-15 |
|
||||
| partner_id | by oid: scen.partner_1 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv2_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1200 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_2 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_2
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
@inv_3
|
||||
Scenario: Create invoice 3
|
||||
Given I need a "account.invoice" with oid: scen._inv_3
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_3 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.partner_1 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv3_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_3 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_3
|
||||
And I open the credit invoice
|
||||
|
||||
##################### Customer 2 ##########################################################
|
||||
|
||||
@inv_4
|
||||
Scenario: Create invoice 4
|
||||
|
||||
Given I need a "account.invoice" with oid: scen._inv_4
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_4 |
|
||||
| date_invoice | 2013-01-18 |
|
||||
| partner_id | by oid: scen.customer_2 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv4_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_4 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_4
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
|
||||
@inv_5
|
||||
Scenario: Create invoice 5
|
||||
Given I need a "account.invoice" with oid: scen._inv_5
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_5 |
|
||||
| date_invoice | 2013-02-15 |
|
||||
| partner_id | by oid: scen.customer_2 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv5_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1200 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_5 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_5
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
@inv_6
|
||||
Scenario: Create invoice 6
|
||||
Given I need a "account.invoice" with oid: scen._inv_6
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_6 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_2 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Days End of Month |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv6_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_6 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_6
|
||||
And I open the credit invoice
|
||||
|
||||
##################### Customer 3 ##########################################################
|
||||
|
||||
@inv_7
|
||||
Scenario: Create invoice 7
|
||||
|
||||
Given I need a "account.invoice" with oid: scen._inv_7
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_7 |
|
||||
| date_invoice | 2013-01-18 |
|
||||
| partner_id | by oid: scen.customer_3 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv7_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_7 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_7
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
|
||||
@inv_8
|
||||
Scenario: Create invoice 8
|
||||
Given I need a "account.invoice" with oid: scen._inv_8
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_8 |
|
||||
| date_invoice | 2013-02-15 |
|
||||
| partner_id | by oid: scen.customer_3 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv8_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1200 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_8 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_8
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
@inv_9
|
||||
Scenario: Create invoice 9
|
||||
Given I need a "account.invoice" with oid: scen._inv_9
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_9 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_3 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv9_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_9 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_9
|
||||
And I open the credit invoice
|
||||
|
||||
##################### Customer 4 ##########################################################
|
||||
|
||||
@inv_10
|
||||
Scenario: Create invoice 10
|
||||
|
||||
Given I need a "account.invoice" with oid: scen._inv_10
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_10 |
|
||||
| date_invoice | 2013-01-18 |
|
||||
| partner_id | by oid: scen.customer_4 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30% Advance End 30 Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv10_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_10 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_10
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
|
||||
@inv_11
|
||||
Scenario: Create invoice 11
|
||||
Given I need a "account.invoice" with oid: scen._inv_11
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_11 |
|
||||
| date_invoice | 2013-02-15 |
|
||||
| partner_id | by oid: scen.customer_4 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30% Advance End 30 Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv11_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1200 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_11 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_11
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
@inv_12
|
||||
Scenario: Create invoice 12
|
||||
Given I need a "account.invoice" with oid: scen._inv_12
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_12 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_4 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30% Advance End 30 Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv12_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_12 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_12
|
||||
And I open the credit invoice
|
||||
|
||||
##################### Customer 5 ##########################################################
|
||||
|
||||
@inv_13
|
||||
Scenario: Create invoice 13
|
||||
|
||||
Given I need a "account.invoice" with oid: scen._inv_13
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_13 |
|
||||
| date_invoice | 2013-01-18 |
|
||||
| partner_id | by oid: scen.customer_5 |
|
||||
| account_id | by name: Debtors USD |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv13_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1000 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_13 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_13
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
|
||||
@inv_14
|
||||
Scenario: Create invoice 14
|
||||
Given I need a "account.invoice" with oid: scen._inv_14
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_14 |
|
||||
| date_invoice | 2013-02-15 |
|
||||
| partner_id | by oid: scen.customer_5 |
|
||||
| account_id | by name: Debtors USD |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv14_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1200 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_14 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_14
|
||||
And I open the credit invoice
|
||||
|
||||
|
||||
@inv_15
|
||||
Scenario: Create invoice 15
|
||||
Given I need a "account.invoice" with oid: scen._inv_15
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_15 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_5 |
|
||||
| account_id | by name: Debtors USD |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: USD |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
|
||||
Given I need a "account.invoice.line" with oid: scen._inv15_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_15 |
|
||||
Given I find a "account.invoice" with oid: scen._inv_15
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_16
|
||||
Scenario: Create invoice 16
|
||||
Given I need a "account.invoice" with oid: scen._inv_16
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_16 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_4 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
And I need a "account.invoice.line" with oid: scen._inv16_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_16 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_16
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_17
|
||||
Scenario: Create invoice 17
|
||||
Given I need a "account.invoice" with oid: scen._inv_17
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_17 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_partial_pay |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
And I need a "account.invoice.line" with oid: scen._inv17_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_17 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_17
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_18
|
||||
Scenario: Create invoice 18
|
||||
Given I need a "account.invoice" with oid: scen._inv_18
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_18 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_multiple_payterm |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30% Advance End 30 Days |
|
||||
| type | out_invoice |
|
||||
|
||||
And I need a "account.invoice.line" with oid: scen._inv18_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_18 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_18
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_19
|
||||
Scenario: Create invoice 19
|
||||
Given I need a "account.invoice" with oid: scen._inv_19
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_19 |
|
||||
| date_invoice | 2013-03-15 |
|
||||
| partner_id | by oid: scen.customer_multiple_payterm2 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30% Advance End 30 Days |
|
||||
| type | out_invoice |
|
||||
And I need a "account.invoice.line" with oid: scen._inv19_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 1500 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_19 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_19
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_20
|
||||
Scenario: Create invoice 20
|
||||
Given I need a "account.invoice" with oid: scen._inv_20
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_20_test_tolerance |
|
||||
| date_invoice | 2013-03-23 |
|
||||
| partner_id | by oid: scen.customer_6 |
|
||||
| account_id | by name: Debtors |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
And I need a "account.invoice.line" with oid: scen._inv20_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 0.09 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_20 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_20
|
||||
And I open the credit invoice
|
||||
|
||||
@inv_20
|
||||
Scenario: Create invoice 21 (this receivable account must not be chased-> no credit line creation)
|
||||
Given I need a "account.invoice" with oid: scen._inv_21
|
||||
And having:
|
||||
| name | value |
|
||||
| name | SI_21_test_receivable_account_excluded |
|
||||
| date_invoice | 2013-03-25 |
|
||||
| partner_id | by oid: scen.customer_6 |
|
||||
| account_id | by name: Debtors GBP |
|
||||
| journal_id | by name: Sales |
|
||||
| currency_id | by name: EUR |
|
||||
| payment_term | by name: 30 Net Days |
|
||||
| type | out_invoice |
|
||||
|
||||
And I need a "account.invoice.line" with oid: scen._inv21_line1
|
||||
And having:
|
||||
| name | value |
|
||||
| name | invoice line 1 |
|
||||
| quantity | 1 |
|
||||
| price_unit | 6666 |
|
||||
| account_id | by name: Sales |
|
||||
| invoice_id | by oid: scen._inv_21 |
|
||||
Then I find a "account.invoice" with oid: scen._inv_21
|
||||
And I open the credit invoice
|
||||
@@ -0,0 +1,29 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_jan
|
||||
|
||||
Feature: Ensure that mail credit line generation first pass is correct
|
||||
|
||||
Scenario: clean data
|
||||
Given I clean all the credit lines
|
||||
#Given I unreconcile and clean all move line
|
||||
|
||||
@account_credit_control_run_month
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run1
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-01-31 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 300 | 2013-01-18 | Debtors | 3 time policy | 2013-01-31 | customer_4 | email | 1 | SI_10 | 10 days net | draft | 300 | |
|
||||
@@ -0,0 +1,33 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_feb
|
||||
|
||||
Feature: Ensure that mail credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@account_credit_control_run_month
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run2
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-02-28 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 360 | 2013-02-15 | Debtors | 3 time policy | 2013-02-28 | customer_4 | email | 1 | SI_11 | 10 days net | draft | 360 | USD |
|
||||
| 1000 | 2013-02-17 | Debtors USD | 3 time policy | 2013-02-28 | customer_5_usd | email | 1 | SI_13 | 10 days net | draft | 1000 | USD |
|
||||
| 300 | 2013-01-18 | Debtors | 3 time policy | 2013-02-28 | customer_4 | email | 2 | SI_10 | 30 days end of month | draft | 300 | |
|
||||
@@ -0,0 +1,45 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_mar
|
||||
|
||||
Feature: Ensure that email credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@pay_invoice_si_19_part1
|
||||
Scenario: I pay a part of the first part of the invoice SI 19,
|
||||
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
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run3
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-03-31 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 1000 | 2013-02-28 | Debtors | 2 time policy | 2013-03-31 | customer_2 | email | 1 | SI_4 | 30 days end of month | draft | 1000 | |
|
||||
| 1000 | 2013-02-17 | Debtors | 2 time policy | 2013-03-31 | customer_3 | email | 1 | SI_7 | 30 days end of month | draft | 1000 | |
|
||||
| 700 | 2013-02-28 | Debtors | 3 time policy | 2013-03-31 | customer_4 | email | 1 | SI_10 | 10 days net | draft | 700 | |
|
||||
| 449.99 | 2013-03-15 | Debtors | 3 time policy | 2013-03-31 | customer_4 | email | 1 | SI_12 | 10 days net | draft | 449.99 | USD |
|
||||
| 1200 | 2013-03-17 | Debtors USD | 3 time policy | 2013-03-31 | customer_5_usd | email | 1 | SI_14 | 10 days net | draft | 1200 | USD |
|
||||
| 360 | 2013-02-15 | Debtors | 3 time policy | 2013-03-31 | customer_4 | email | 2 | SI_11 | 30 days end of month | draft | 360 | USD |
|
||||
| 1000 | 2013-02-17 | Debtors USD | 3 time policy | 2013-03-31 | customer_5_usd | email | 2 | SI_13 | 30 days end of month | draft | 1000 | USD |
|
||||
| 300 | 2013-01-18 | Debtors | 3 time policy | 2013-03-31 | customer_4 | letter | 3 | SI_10 | 10 days last reminder | draft | 300 | |
|
||||
| 450 | 2013-03-15 | Debtors | 3 time policy | 2013-03-31 | Donald Duck | email | 1 | SI_18 | 10 days net | draft | 450 | |
|
||||
| 150 | 2013-03-15 | Debtors | 3 time policy | 2013-03-31 | Gus Goose | email | 1 | SI_19 | 10 days net | draft | 450 | |
|
||||
@@ -0,0 +1,44 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_apr
|
||||
|
||||
Feature: Ensure that email credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@account_credit_control_run_month
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run4
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-04-30 |
|
||||
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 360 | 2013-02-15 | Debtors | 3 time policy | 2013-04-30 | customer_4 | letter | 3 | SI_11 | 10 days last reminder | draft | 360 | USD |
|
||||
| 1200 | 2013-03-31 | Debtors | 2 time policy | 2013-04-30 | customer_2 | email | 1 | SI_5 | 30 days end of month | draft | 1200 | USD |
|
||||
| 1200 | 2013-03-17 | Debtors | 2 time policy | 2013-04-30 | customer_3 | email | 1 | SI_8 | 30 days end of month | draft | 1200 | USD |
|
||||
| 700 | 2013-02-28 | Debtors | 3 time policy | 2013-04-30 | customer_4 | email | 2 | SI_10 | 30 days end of month | draft | 700 | |
|
||||
| 840 | 2013-03-31 | Debtors | 3 time policy | 2013-04-30 | customer_4 | email | 1 | SI_11 | 10 days net | draft | 840 | USD |
|
||||
| 449.99 | 2013-03-15 | Debtors | 3 time policy | 2013-04-30 | customer_4 | email | 2 | SI_12 | 30 days end of month | draft | 449.99 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors USD | 3 time policy | 2013-04-30 | customer_5_usd | email | 1 | SI_15 | 10 days net | draft | 1500 | USD |
|
||||
| 1200 | 2013-03-17 | Debtors USD | 3 time policy | 2013-04-30 | customer_5_usd | email | 2 | SI_14 | 30 days end of month | draft | 1200 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors USD | 3 time policy | 2013-04-30 | customer_5_usd | email | 1 | SI_15 | 10 days net | draft | 1500 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors | 3 time policy | 2013-04-30 | customer_4 | email | 1 | SI_16 | 10 days net | draft | 1500 | |
|
||||
| 1500 | 2013-04-14 | Debtors | 3 time policy | 2013-04-30 | Scrooge McDuck | email | 1 | SI_17 | 10 days net | draft | 1500 | |
|
||||
| 450 | 2013-03-15 | Debtors | 3 time policy | 2013-04-30 | Donald Duck | email | 2 | SI_18 | 30 days end of month | draft | 450 | |
|
||||
| 150 | 2013-03-15 | Debtors | 3 time policy | 2013-04-30 | Gus Goose | email | 2 | SI_19 | 30 days end of month | draft | 450 | |
|
||||
@@ -0,0 +1,59 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_may
|
||||
|
||||
Feature: Ensure that email credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@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 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 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 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
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run5
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-05-31 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 1500 | 2013-04-30 | Debtors | 2 time policy | 2013-05-31 | customer_2 | email | 1 | SI_6 | 30 days end of month | draft | 1500 | USD |
|
||||
| 1000 | 2013-02-28 | Debtors | 2 time policy | 2013-05-31 | customer_2 | letter | 2 | SI_4 | 60 days last reminder | draft | 1000 | |
|
||||
| 1000 | 2013-02-17 | Debtors | 2 time policy | 2013-05-31 | customer_3 | letter | 2 | SI_7 | 60 days last reminder | draft | 1000 | |
|
||||
| 1500 | 2013-04-14 | Debtors | 2 time policy | 2013-05-31 | customer_3 | email | 1 | SI_9 | 30 days end of month | draft | 1500 | |
|
||||
| 840 | 2013-03-31 | Debtors | 3 time policy | 2013-05-31 | customer_4 | email | 2 | SI_11 | 30 days end of month | draft | 840 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors USD | 3 time policy | 2013-05-31 | customer_5_usd | email | 2 | SI_15 | 30 days end of month | draft | 1500 | USD |
|
||||
| 700 | 2013-02-28 | Debtors | 3 time policy | 2013-05-31 | customer_4 | letter | 3 | SI_10 | 10 days last reminder | draft | 700 | |
|
||||
| 449.99 | 2013-03-15 | Debtors | 3 time policy | 2013-05-31 | customer_4 | letter | 3 | SI_12 | 10 days last reminder | draft | 449.99 | USD |
|
||||
| 1050.01 | 2013-04-30 | Debtors | 3 time policy | 2013-05-31 | customer_4 | email | 1 | SI_12 | 10 days net | draft | 1050.01 | USD |
|
||||
| 1200 | 2013-03-17 | Debtors USD | 3 time policy | 2013-05-31 | customer_5_usd | letter | 3 | SI_14 | 10 days last reminder | draft | 1200 | USD |
|
||||
| 500 | 2013-04-14 | Debtors | 3 time policy | 2013-05-31 | Scrooge McDuck | email | 2 | SI_17 | 30 days end of month | draft | 1500 | |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-05-31 | Donald Duck | email | 1 | SI_18 | 10 days net | draft | 1050 | |
|
||||
| 150 | 2013-03-15 | Debtors | 3 time policy | 2013-05-31 | Gus Goose | letter | 3 | SI_19 | 10 days last reminder | draft | 450 | |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-05-31 | Gus Goose | email | 1 | SI_19 | 10 days net | draft | 1050 | |
|
||||
@@ -0,0 +1,38 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_jun
|
||||
|
||||
Feature: Ensure that email credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@account_credit_control_run_month
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run6
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-06-30 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 1200 | 2013-03-31 | Debtors | 2 time policy | 2013-06-30 | customer_2 | letter | 2 | SI_5 | 60 days last reminder | draft | 1200 | USD |
|
||||
| 1200 | 2013-03-17 | Debtors | 2 time policy | 2013-06-30 | customer_3 | letter | 2 | SI_8 | 60 days last reminder | draft | 1200 | USD |
|
||||
| 1050.01 | 2013-04-30 | Debtors | 3 time policy | 2013-06-30 | customer_4 | email | 2 | SI_12 | 30 days end of month | draft | 1050.01 | USD |
|
||||
| 840 | 2013-03-31 | Debtors | 3 time policy | 2013-06-30 | customer_4 | letter | 3 | SI_11 | 10 days last reminder | draft | 840 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors USD | 3 time policy | 2013-06-30 | customer_5_usd | letter | 3 | SI_15 | 10 days last reminder | draft | 1500 | USD |
|
||||
| 500 | 2013-04-14 | Debtors | 3 time policy | 2013-06-30 | Scrooge McDuck | letter | 3 | SI_17 | 10 days last reminder | draft | 1500 | |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-06-30 | Donald Duck | email | 2 | SI_18 | 30 days end of month | draft | 1050 | |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-06-30 | Gus Goose | email | 2 | SI_19 | 30 days end of month | draft | 1050 | |
|
||||
@@ -0,0 +1,35 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_jul
|
||||
|
||||
Feature: Ensure that email credit line generation first pass is correct
|
||||
|
||||
@account_credit_control_mark
|
||||
Scenario: mark lines
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
|
||||
@account_credit_control_run_month
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.run7
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-07-31 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
And the generated credit lines should have the following values:
|
||||
| balance | date due | account | policy | date | partner | channel | level | move line | policy level | state | amount due | currency |
|
||||
| 1500 | 2013-04-30 | Debtors | 2 time policy | 2013-07-31 | customer_2 | letter | 2 | SI_6 | 60 days last reminder | draft | 1500 | USD |
|
||||
| 1500 | 2013-04-14 | Debtors | 2 time policy | 2013-07-31 | customer_3 | letter | 2 | SI_9 | 60 days last reminder | draft | 1500 | USD |
|
||||
| 1050.01 | 2013-04-30 | Debtors | 3 time policy | 2013-07-31 | customer_4 | letter | 3 | SI_12 | 10 days last reminder | draft | 1050.01 | USD |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-07-31 | Donald Duck | letter | 3 | SI_18 | 10 days last reminder | draft | 1050 | |
|
||||
| 1050 | 2013-04-30 | Debtors | 3 time policy | 2013-07-31 | Gus Goose | letter | 3 | SI_19 | 10 days last reminder | draft | 1050 | |
|
||||
@@ -0,0 +1,30 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_aug
|
||||
|
||||
Feature: Ensure that ignore feature works as expected
|
||||
|
||||
@account_credit_control_mark_as_ignore
|
||||
Scenario: mark last line as ignore
|
||||
Given I ignore the "Gus Goose" credit line at level "3" for move line "SI_19" with amount "1050.0"
|
||||
|
||||
@account_credit_control_run_month_aug
|
||||
Scenario: Create run
|
||||
Given I need a "credit.control.run" with oid: credit_control.runignored
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-08-30 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
|
||||
@check_ignored_line
|
||||
Scenario: Check ignored lines
|
||||
Given I have for "Gus Goose" "2" credit lines at level "3" for move line "SI_19" with amount "1050.0" respectively in state "draft" and "ignored"
|
||||
@@ -0,0 +1,42 @@
|
||||
###############################################################################
|
||||
#
|
||||
# OERPScenario, OpenERP Functional Tests
|
||||
# Copyright 2012-2014 Camptocamp SA
|
||||
# Author Nicolas Bessi
|
||||
##############################################################################
|
||||
|
||||
# Features Generic tags (none for all)
|
||||
##############################################################################
|
||||
|
||||
@account_credit_control @account_credit_control_run @account_credit_control_run_change_level
|
||||
|
||||
Feature: Ensure that manually changing an invoice level feature works as expected
|
||||
|
||||
@account_credit_control_change_level
|
||||
Scenario: Change level
|
||||
Given I change level for invoice "SAJ/2014/0004" to "10 days net" of policy "3 time policy"
|
||||
Then wizard selected move lines should be:
|
||||
| name |
|
||||
| SI_4 |
|
||||
When I confirm the level change
|
||||
And I should have "3" credit control lines overridden
|
||||
And one new credit control line of level "10 days net" related to invoice "SAJ/2014/0004"
|
||||
Then I force date of generated credit line to "2013-09-15"
|
||||
|
||||
@account_credit_control_run_month_sept
|
||||
Scenario: Create run
|
||||
Given there is "draft" credit lines
|
||||
And I mark all draft email to state "to_be_sent"
|
||||
Then the draft line should be in state "to_be_sent"
|
||||
Given I need a "credit.control.run" with oid: credit_control.manual_change
|
||||
And having:
|
||||
| name | value |
|
||||
| date | 2013-09-30 |
|
||||
When I launch the credit run
|
||||
Then my credit run should be in state "done"
|
||||
|
||||
@account_credit_control_manual_next_step
|
||||
Scenario: Check manually managed line on run
|
||||
Given the invoice "SAJ/2014/0004" with manual changes
|
||||
And the invoice has "1" line of level "1" for policy "3 time policy"
|
||||
And the invoice has "1" line of level "2" for policy "3 time policy"
|
||||
@@ -0,0 +1,147 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# flake8: noqa
|
||||
import time
|
||||
from behave import given, when
|
||||
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):
|
||||
policy = model('credit.control.policy').get(policy_oid)
|
||||
assert policy, 'No policy % found' % policy_oid
|
||||
acc_obj = model('account.account')
|
||||
accounts = []
|
||||
for row in ctx.table:
|
||||
acc = acc_obj.get(['code = %s' % row['account code']])
|
||||
assert acc, "Account with code %s not found" % row['account code']
|
||||
accounts.append(acc)
|
||||
policy.write({'account_ids': [x.id for x in accounts]})
|
||||
|
||||
|
||||
@when(u'I launch the credit run')
|
||||
def impl(ctx):
|
||||
assert ctx.found_item
|
||||
# Must be a cleaner way to do it
|
||||
assert 'credit.control.run' == ctx.found_item._model._name
|
||||
ctx.found_item.generate_credit_lines()
|
||||
|
||||
@given(u'I clean all the credit lines')
|
||||
def impl(ctx):
|
||||
model('credit.control.line').browse([]).unlink()
|
||||
|
||||
@then(u'my credit run should be in state "done"')
|
||||
def impl(ctx):
|
||||
assert ctx.found_item
|
||||
# Must be a cleaner way to do it
|
||||
assert model("credit.control.run").get(ctx.found_item.id).state == 'done'
|
||||
|
||||
@then(u'the generated credit lines should have the following values')
|
||||
def impl(ctx):
|
||||
def _row_to_dict(row):
|
||||
return dict((name, row[name]) for name in row.headings if row[name])
|
||||
rows = map(_row_to_dict, ctx.table)
|
||||
|
||||
def _parse_date(value):
|
||||
return time.strftime(value) if '%' in value else value
|
||||
|
||||
for row in rows:
|
||||
account = model('account.account').get(['name = %s' % row['account']])
|
||||
assert account, "no account named %s found" % row['account']
|
||||
|
||||
policy = model('credit.control.policy').get(['name = %s' % row['policy']])
|
||||
assert policy, "No policy %s found" % row['policy']
|
||||
|
||||
partner = model('res.partner').get(['name = %s' % row['partner']])
|
||||
assert partner, "No partner %s found" % row['partner']
|
||||
|
||||
maturity_date = _parse_date(row['date due'])
|
||||
move_line = model('account.move.line').get(['name = %s' % row['move line'],
|
||||
'date_maturity = %s' % maturity_date])
|
||||
assert move_line, "No move line %s found" % row['move line']
|
||||
|
||||
level = model('credit.control.policy.level').get(['name = %s' % row['policy level'],
|
||||
'policy_id = %s' % policy.id])
|
||||
assert level, "No level % found" % row['policy level']
|
||||
|
||||
domain = [['account_id', '=', account.id],
|
||||
['policy_id', '=', policy.id],
|
||||
['partner_id', '=', partner.id],
|
||||
['policy_level_id', '=', level.id],
|
||||
['amount_due', '=', row.get('amount due', 0.0)],
|
||||
['state', '=', row['state']],
|
||||
['level', '=', row.get('level', 0.0)],
|
||||
['channel', '=', row['channel']],
|
||||
['balance_due', '=', row.get('balance', 0.0)],
|
||||
['date_due', '=', _parse_date(row['date due'])],
|
||||
['date', '=', _parse_date(row['date'])],
|
||||
['move_line_id', '=', move_line.id],
|
||||
]
|
||||
if row.get('currency'):
|
||||
curreny = model('res.currency').get(['name = %s' % row['currency']])
|
||||
assert curreny, "No currency %s found" % row['currency']
|
||||
domain.append(('currency_id', '=', curreny.id))
|
||||
|
||||
lines = model('credit.control.line').search(domain)
|
||||
assert lines, "no line found for %s" % repr(row)
|
||||
assert len(lines) == 1, "Too many lines found for %s" % repr(row)
|
||||
date_lines = model('credit.control.line').search([('date', '=', ctx.found_item.date)])
|
||||
assert len(date_lines) == len(ctx.table.rows), "Too many lines generated"
|
||||
|
||||
|
||||
def open_invoice(ctx):
|
||||
assert ctx.found_item
|
||||
ctx.found_item._send('invoice_open')
|
||||
# _send refresh object
|
||||
assert ctx.found_item.state == 'open'
|
||||
|
||||
@then(u'I open the credit invoice')
|
||||
def impl(ctx):
|
||||
open_invoice(ctx)
|
||||
|
||||
@given(u'I open the credit invoice')
|
||||
def impl(ctx):
|
||||
open_invoice(ctx)
|
||||
|
||||
@given(u'there is "{state}" credit lines')
|
||||
def impl(ctx, state):
|
||||
assert model('credit.control.line').search(['state = %s' % state])
|
||||
|
||||
@given(u'I mark all draft email to state "{state}"')
|
||||
def impl(ctx, state):
|
||||
wiz = model('credit.control.marker').create({'name': state})
|
||||
lines = model('credit.control.line').search([('state', '=', 'draft')])
|
||||
assert lines
|
||||
ctx.lines = lines
|
||||
wiz.write({'line_ids': lines})
|
||||
wiz.mark_lines()
|
||||
|
||||
@then(u'the draft line should be in state "{state}"')
|
||||
def impl(ctx, state):
|
||||
assert ctx.lines
|
||||
lines = model('credit.control.line').search([('state', '!=', state),
|
||||
('id', 'in', ctx.lines)])
|
||||
assert not lines
|
||||
|
||||
@given(u'I ignore the "{partner}" credit line at level "{level:d}" for move line "{move_line_name}" with amount "{amount:f}"')
|
||||
def impl(ctx, partner, level, move_line_name, amount):
|
||||
print ctx, partner, level, move_line_name, amount
|
||||
to_ignore = model('credit.control.line').search([('partner_id.name', '=', partner),
|
||||
('level', '=', level),
|
||||
('amount_due', '=', amount),
|
||||
('move_line_id.name', '=', move_line_name)])
|
||||
assert to_ignore
|
||||
wiz = model('credit.control.marker').create({'name': 'ignored'})
|
||||
ctx.lines = to_ignore
|
||||
wiz.write({'line_ids': to_ignore})
|
||||
wiz.mark_lines()
|
||||
assert model('credit.control.line').get(to_ignore[0]).state == 'ignored'
|
||||
|
||||
@given(u'I have for "{partner}" "{number:d}" credit lines at level "{level:d}" for move line "{move_line_name}" with amount "{amount:f}" respectively in state "draft" and "ignored"')
|
||||
def impl(ctx, partner, number, level, move_line_name, amount):
|
||||
to_check = model('credit.control.line').search([('partner_id.name', '=', partner),
|
||||
('level', '=', level),
|
||||
('amount_due', '=', amount),
|
||||
('move_line_id.name', '=', move_line_name),
|
||||
('state', 'in', ('draft', 'ignored'))])
|
||||
assert_equal(len(to_check), int(number), msg="More than %s found" % number)
|
||||
lines = model('credit.control.line').browse(to_check)
|
||||
assert set(['ignored', 'draft']) == set(lines.state)
|
||||
@@ -0,0 +1,79 @@
|
||||
# -*- 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):
|
||||
invoice = model('account.invoice').get([('number', '=', invoice_name)])
|
||||
assert_true(invoice, msg='No invoices found')
|
||||
level = model('credit.control.policy.level').get([('name', '=', level_name)])
|
||||
assert_true(level, 'level not found')
|
||||
policy = model('credit.control.policy').get([('name', '=', policy_name)])
|
||||
assert_true(policy, 'Policy not found')
|
||||
assert_equal(policy.id, level.policy_id.id)
|
||||
context = {'active_ids': [invoice.id]}
|
||||
data = {'new_policy_id': policy.id,
|
||||
'new_policy_level_id': level.id}
|
||||
wizard = model('credit.control.policy.changer').create(data, context=context)
|
||||
ctx.wizard = wizard
|
||||
|
||||
@then(u'wizard selected move lines should be')
|
||||
def impl(ctx):
|
||||
assert_true(ctx.wizard)
|
||||
names = [x.name for x in ctx.wizard.move_line_ids]
|
||||
for line in ctx.table:
|
||||
assert_in(line['name'], names)
|
||||
|
||||
@when(u'I confirm the level change')
|
||||
def impl(ctx):
|
||||
assert_true(ctx.wizard)
|
||||
ctx.wizard.set_new_policy()
|
||||
|
||||
@when(u'I should have "{line_number:d}" credit control lines overridden')
|
||||
def impl(ctx, line_number):
|
||||
assert_true(ctx.wizard)
|
||||
move_ids = [x.id for x in ctx.wizard.move_line_ids]
|
||||
overridden = model('credit.control.line').search([('move_line_id', 'in', move_ids),
|
||||
('manually_overridden', '=', True)])
|
||||
# assert len(overridden) == line_number
|
||||
|
||||
@when(u'one new credit control line of level "{level_name}" related to invoice "{invoice_name}"')
|
||||
def impl(ctx, level_name, invoice_name):
|
||||
invoice = model('account.invoice').get([('number', '=', invoice_name)])
|
||||
assert_true(invoice, msg='No invoices found')
|
||||
level = model('credit.control.policy.level').get([('name', '=', level_name)])
|
||||
assert_true(level, 'level not found')
|
||||
assert_true(ctx.wizard)
|
||||
move_ids = [x.id for x in ctx.wizard.move_line_ids]
|
||||
created_id = model('credit.control.line').search([('move_line_id', 'in', move_ids),
|
||||
('manually_overridden', '=', False)])
|
||||
|
||||
assert len(created_id) == 1
|
||||
created = model('credit.control.line').get(created_id[0])
|
||||
ctx.created = created
|
||||
assert_equal(created.policy_level_id.id, level.id)
|
||||
assert_equal(created.invoice_id.id, invoice.id)
|
||||
assert_equal(created.invoice_id.credit_policy_id.id, level.policy_id.id)
|
||||
|
||||
@then(u'I force date of generated credit line to "{date}"')
|
||||
def impl(ctx, date):
|
||||
assert_true(ctx.created)
|
||||
ctx.created.write({'date': date})
|
||||
|
||||
@given(u'the invoice "{invoice_name}" with manual changes')
|
||||
def impl(ctx, invoice_name):
|
||||
invoice = model('account.invoice').get([('number', '=', invoice_name)])
|
||||
assert_true(invoice, msg='No invoices found')
|
||||
man_lines = (x for x in invoice.credit_control_line_ids if x.manually_overridden)
|
||||
assert_true(next(man_lines, None), 'No manual change on the invoice')
|
||||
ctx.invoice = invoice
|
||||
|
||||
@given(u'the invoice has "{line_number:d}" line of level "{level:d}" for policy "{policy_name}"')
|
||||
def impl(ctx, line_number, level, policy_name):
|
||||
assert_true(ctx.invoice)
|
||||
policy = model('credit.control.policy').get([('name', '=', policy_name)])
|
||||
assert_true(policy)
|
||||
lines = model('credit.control.line').search([('invoice_id', '=', ctx.invoice.id),
|
||||
('level', '=', level),
|
||||
('policy_id', '=', policy.id)])
|
||||
assert_equal(len(lines), line_number)
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# flake8: noqa
|
||||
from support import model, assert_equal, assert_almost_equal
|
||||
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')
|
||||
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()
|
||||
# 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('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)
|
||||
21
account_credit_control/security/ir.model.access.csv
Normal file
21
account_credit_control/security/ir.model.access.csv
Normal file
@@ -0,0 +1,21 @@
|
||||
"id","perm_create","perm_unlink","group_id/id","name","model_id/id","perm_read","perm_write"
|
||||
"account_credit_control.ir_model_access_270",1,1,"group_account_credit_control_manager","credit_control_manager_line","account_credit_control.model_credit_control_line",1,1
|
||||
"account_credit_control.ir_model_access_271",1,1,"group_account_credit_control_user","credit_control_user_line","account_credit_control.model_credit_control_line",1,1
|
||||
"account_credit_control.ir_model_access_272",0,0,"group_account_credit_control_info","credit_control_info_line","account_credit_control.model_credit_control_line",1,0
|
||||
"account_credit_control.ir_model_access_273",1,1,"group_account_credit_control_manager","credit_control_manager_mail_template","email_template.model_email_template",1,1
|
||||
"account_credit_control.ir_model_access_274",1,1,"group_account_credit_control_manager","credit_control_manager_mail_template_preview","email_template.model_email_template_preview",1,1
|
||||
"account_credit_control.ir_model_access_275",1,1,"group_account_credit_control_manager","credit_control_manager_mail_message","mail.model_mail_message",1,1
|
||||
"account_credit_control.ir_model_access_276",1,0,"group_account_credit_control_user","credit_control_user_mail_message","mail.model_mail_message",1,1
|
||||
"account_credit_control.ir_model_access_277",0,0,"group_account_credit_control_info","credit_control_info_mail_message","mail.model_mail_message",1,0
|
||||
"account_credit_control.ir_model_access_281",1,1,"group_account_credit_control_manager","credit_control_mananger_run","account_credit_control.model_credit_control_run",1,1
|
||||
"account_credit_control.ir_model_access_282",1,1,"group_account_credit_control_user","credit_control_user_run","account_credit_control.model_credit_control_run",1,1
|
||||
"account_credit_control.ir_model_access_283",0,0,"group_account_credit_control_info","credit_control_info_run","account_credit_control.model_credit_control_run",1,0
|
||||
"account_credit_control.ir_model_access_284",1,1,"group_account_credit_control_manager","credit_control_manager_policy","account_credit_control.model_credit_control_policy",1,1
|
||||
"account_credit_control.ir_model_access_285",0,0,"group_account_credit_control_user","credit_control_user_policy","account_credit_control.model_credit_control_policy",1,0
|
||||
"account_credit_control.ir_model_access_286",0,0,"group_account_credit_control_info","credit_control_info_policy","account_credit_control.model_credit_control_policy",1,0
|
||||
"account_credit_control.ir_model_access_287",1,1,"group_account_credit_control_manager","credit_control_manager_level","account_credit_control.model_credit_control_policy_level",1,1
|
||||
"account_credit_control.ir_model_access_288",0,0,"group_account_credit_control_user","credit_control_user_level","account_credit_control.model_credit_control_policy_level",1,0
|
||||
"account_credit_control.ir_model_access_289",0,0,"group_account_credit_control_info","credit_control_info_level","account_credit_control.model_credit_control_policy_level",1,0
|
||||
"account_credit_control.ir_model_access_290",0,0,"account.group_account_user","credit_control_fin_user_line","account_credit_control.model_credit_control_line",1,0
|
||||
"account_credit_control.ir_model_access_291",0,0,"account.group_account_invoice","credit_control_fin_invoice_line","account_credit_control.model_credit_control_line",1,0
|
||||
"account_credit_control.ir_model_access_292",1,1,"account.group_account_manager","credit_control_fin_manager_line","account_credit_control.model_credit_control_line",1,1
|
||||
|
25
account_credit_control/wizard/__init__.py
Normal file
25
account_credit_control/wizard/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from . import credit_control_emailer
|
||||
from . import credit_control_marker
|
||||
from . import credit_control_printer
|
||||
from . import credit_control_communication
|
||||
from . import credit_control_policy_changer
|
||||
239
account_credit_control/wizard/credit_control_communication.py
Normal file
239
account_credit_control/wizard/credit_control_communication.py
Normal file
@@ -0,0 +1,239 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
from openerp import models, fields, api
|
||||
|
||||
logger = logging.getLogger('credit.control.line.mailing')
|
||||
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
_name = "credit.control.communication"
|
||||
_description = "credit control communication"
|
||||
_rec_name = 'partner_id'
|
||||
|
||||
partner_id = fields.Many2one('res.partner', 'Partner', required=True)
|
||||
|
||||
current_policy_level = fields.Many2one('credit.control.policy.level',
|
||||
'Level',
|
||||
required=True)
|
||||
|
||||
currency_id = fields.Many2one('res.currency', 'Currency', 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)
|
||||
report_date = fields.Date(string='Report Date',
|
||||
default=fields.Date.context_today)
|
||||
|
||||
@api.model
|
||||
def _get_company(self):
|
||||
company_obj = self.env['res.company']
|
||||
return company_obj._company_default_get('credit.control.policy')
|
||||
|
||||
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')
|
||||
|
||||
total_invoiced = fields.Float(string='Total Invoiced',
|
||||
compute='_compute_total')
|
||||
|
||||
total_due = fields.Float(string='Total Invoiced',
|
||||
compute='_compute_total')
|
||||
|
||||
@api.depends('credit_control_line_ids',
|
||||
'credit_control_line_ids.amount_due',
|
||||
'credit_control_line_ids.balance_due')
|
||||
def _compute_total(self):
|
||||
amount_field = 'credit_control_line_ids.amount_due'
|
||||
balance_field = 'credit_control_line_ids.balance_due'
|
||||
self.total_invoiced = sum(self.mapped(amount_field))
|
||||
self.total_due = sum(self.mapped(balance_field))
|
||||
|
||||
@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.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 = partner_obj.browse(partner_id)
|
||||
add_ids = partner.address_get(adr_pref=['invoice']) or {}
|
||||
add_id = add_ids['invoice']
|
||||
return partner_obj.browse(add_id)
|
||||
|
||||
@api.model
|
||||
@api.returns('credit.control.line')
|
||||
def _get_credit_lines(self, line_ids, partner_id, level_id, currency_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),
|
||||
('currency_id', '=', currency_id)])
|
||||
return cr_lines
|
||||
|
||||
@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.
|
||||
"""
|
||||
comms = self.browse()
|
||||
if not lines:
|
||||
return comms
|
||||
sql = (
|
||||
"SELECT distinct partner_id, policy_level_id, "
|
||||
" credit_control_line.currency_id, "
|
||||
" credit_control_policy_level.level"
|
||||
" FROM credit_control_line JOIN credit_control_policy_level "
|
||||
" ON (credit_control_line.policy_level_id = "
|
||||
" credit_control_policy_level.id)"
|
||||
" WHERE credit_control_line.id in %s"
|
||||
" ORDER by credit_control_policy_level.level, "
|
||||
" credit_control_line.currency_id"
|
||||
)
|
||||
cr = self.env.cr
|
||||
cr.execute(sql, (tuple(lines.ids), ))
|
||||
res = cr.dictfetchall()
|
||||
company_currency = self.env.user.company_id.currency_id
|
||||
for group in res:
|
||||
data = {}
|
||||
level_lines = self._get_credit_lines(lines.ids,
|
||||
group['partner_id'],
|
||||
group['policy_level_id'],
|
||||
group['currency_id']
|
||||
)
|
||||
|
||||
data['credit_control_line_ids'] = [(6, 0, level_lines.ids)]
|
||||
data['partner_id'] = group['partner_id']
|
||||
data['current_policy_level'] = group['policy_level_id']
|
||||
data['currency_id'] = group['currency_id'] or company_currency.id
|
||||
comm = self.create(data)
|
||||
comms += comm
|
||||
return comms
|
||||
|
||||
@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']
|
||||
# Warning: still using the old-api on 'email.template' because
|
||||
# the method generate_email() does not follow the cr, uid, ids
|
||||
# convention and the new api wrapper can't translate the call
|
||||
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['type'] = 'email'
|
||||
|
||||
email = email_message_obj.create(email_values)
|
||||
|
||||
state = 'sent'
|
||||
# The mail will not be send, however it will be in the pool, in an
|
||||
# error state. So we create it, link it with
|
||||
# the credit control line
|
||||
# and put this latter in a `email_error` state we not that we have
|
||||
# a problem with the email
|
||||
if not all(email_values.get(field) for field in required_fields):
|
||||
state = 'email_error'
|
||||
|
||||
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]
|
||||
data_attach = {
|
||||
'name': attach_fname,
|
||||
'datas': attach_datas,
|
||||
'datas_fname': attach_fname,
|
||||
'res_model': 'mail.mail',
|
||||
'res_id': email.id,
|
||||
'type': 'binary',
|
||||
}
|
||||
attachments += att_obj.create(data_attach)
|
||||
email.write({'attachment_ids': [(6, 0, attachments.ids)]})
|
||||
emails += email
|
||||
return emails
|
||||
|
||||
@api.multi
|
||||
def _generate_report(self):
|
||||
""" Will generate a report by inserting mako template
|
||||
of related policy template
|
||||
|
||||
"""
|
||||
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')
|
||||
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
|
||||
69
account_credit_control/wizard/credit_control_emailer.py
Normal file
69
account_credit_control/wizard/credit_control_emailer.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class CreditControlEmailer(models.TransientModel):
|
||||
""" Send emails for each selected credit control lines. """
|
||||
|
||||
_name = "credit.control.emailer"
|
||||
_description = """Mass credit line emailer"""
|
||||
_rec_name = 'id'
|
||||
|
||||
@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')):
|
||||
return False
|
||||
line_obj = self.env['credit.control.line']
|
||||
lines = line_obj.browse(context['active_id'])
|
||||
return self._filter_lines(lines)
|
||||
|
||||
line_ids = fields.Many2many('credit.control.line',
|
||||
string='Credit Control Lines',
|
||||
default=_get_line_ids,
|
||||
domain=[('state', '=', 'to_be_sent'),
|
||||
('channel', '=', 'email')])
|
||||
|
||||
@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', lines.ids),
|
||||
('channel', '=', 'email')]
|
||||
return line_obj.search(domain)
|
||||
|
||||
@api.multi
|
||||
def email_lines(self):
|
||||
self.ensure_one()
|
||||
if not self.line_ids:
|
||||
raise api.Warning(_('No credit control lines selected.'))
|
||||
|
||||
comm_obj = self.env['credit.control.communication']
|
||||
|
||||
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'}
|
||||
@@ -0,0 +1,54 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="credit_line_emailer_form" model="ir.ui.view">
|
||||
<field name="name">credit.line.emailer.form</field>
|
||||
<field name="model">credit.control.emailer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailer" version="7.0">
|
||||
<separator string="Send emails for the selected lines" colspan="4"/>
|
||||
<newline/>
|
||||
<notebook>
|
||||
<page string="Lines">
|
||||
<field name="line_ids" colspan="4" nolabel="1" />
|
||||
</page>
|
||||
</notebook>
|
||||
<newline/>
|
||||
<footer>
|
||||
<button class="oe_highlight"
|
||||
name="email_lines"
|
||||
string="Send the emails"
|
||||
type="object"/>
|
||||
or
|
||||
<button
|
||||
class="oe_link"
|
||||
special="cancel"
|
||||
string="Cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- for menu -->
|
||||
<act_window name="Send By Email"
|
||||
res_model="credit.control.emailer"
|
||||
src_model="credit.control.line"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="open_credit_line_emailer_wizard_menu_action"/>
|
||||
|
||||
<!-- for button -->
|
||||
<record id="open_credit_line_emailer_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Send By Email</field>
|
||||
<field name="res_model">credit.control.emailer</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="credit_line_emailer_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="help">Send an email for the selected lines.</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
88
account_credit_control/wizard/credit_control_marker.py
Normal file
88
account_credit_control/wizard/credit_control_marker.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class CreditControlMarker(models.TransientModel):
|
||||
""" Change the state of lines in mass """
|
||||
|
||||
_name = 'credit.control.marker'
|
||||
_description = 'Mass marker'
|
||||
|
||||
@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')):
|
||||
return False
|
||||
line_obj = self.env['credit.control.line']
|
||||
lines = line_obj.browse(context['active_ids'])
|
||||
return self._filter_lines(lines)
|
||||
|
||||
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')]")
|
||||
|
||||
@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)
|
||||
|
||||
@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
|
||||
|
||||
@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()
|
||||
|
||||
if not self.line_ids:
|
||||
raise api.Warning(_('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.'))
|
||||
|
||||
self._mark_lines(filtered_lines, self.name)
|
||||
|
||||
return {'domain': unicode([('id', 'in', filtered_lines.ids)]),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'view_id': False,
|
||||
'res_model': 'credit.control.line',
|
||||
'type': 'ir.actions.act_window'}
|
||||
60
account_credit_control/wizard/credit_control_marker_view.xml
Normal file
60
account_credit_control/wizard/credit_control_marker_view.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="credit_line_marker_form" model="ir.ui.view">
|
||||
<field name="name">credit.line.marker.form</field>
|
||||
<field name="model">credit.control.marker</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Lines marker" version="7.0">
|
||||
<separator string="Change the state of the selected lines" colspan="4"/>
|
||||
<newline/>
|
||||
<label string="Warning: you will maybe not be able to revert this operation." colspan="4"></label>
|
||||
<newline/>
|
||||
<group>
|
||||
<group><field name="name"/></group>
|
||||
<group></group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Lines">
|
||||
<field name="line_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<newline/>
|
||||
<footer>
|
||||
<button
|
||||
class="oe_highlight"
|
||||
name="mark_lines"
|
||||
string="Change Lines' State"
|
||||
type="object"/>
|
||||
or
|
||||
<button
|
||||
class="oe_link"
|
||||
special="cancel"
|
||||
string="Cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- for menu -->
|
||||
<act_window name="Change Lines' State"
|
||||
res_model="credit.control.marker"
|
||||
src_model="credit.control.line"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="open_credit_line_marker_wizard_menu_action"/>
|
||||
|
||||
<record id="open_credit_line_marker_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Change Lines' State</field>
|
||||
<field name="res_model">credit.control.marker</field>
|
||||
<field name="src_model">credit.control.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="credit_line_marker_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="help">Change the state of the selected lines.</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
148
account_credit_control/wizard/credit_control_policy_changer.py
Normal file
148
account_credit_control/wizard/credit_control_policy_changer.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# Copyright 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
from openerp import models, fields, api, _
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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"
|
||||
|
||||
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 lines
|
||||
|
||||
"""
|
||||
context = self.env.context
|
||||
active_ids = context.get('active_ids')
|
||||
invoice_obj = self.env['account.invoice']
|
||||
move_line_obj = self.env['account.move.line']
|
||||
if not active_ids:
|
||||
return False
|
||||
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 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_lines = move_line_obj.search(domain)
|
||||
selected_lines |= move_lines
|
||||
return selected_lines
|
||||
|
||||
move_line_ids = fields.Many2many('account.move.line',
|
||||
rel='credit_changer_ml_rel',
|
||||
string='Move line to change',
|
||||
default=_get_default_lines)
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
:return: list of credit lines that where marked as overridden
|
||||
"""
|
||||
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
|
||||
|
||||
@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})
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
the wizard form
|
||||
|
||||
:return: ir.actions.act_windows dict
|
||||
|
||||
"""
|
||||
self.ensure_one()
|
||||
credit_line_obj = self.env['credit.control.line']
|
||||
|
||||
controlling_date = fields.date.today()
|
||||
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
|
||||
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
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="credit_control_policy_changer_form" model="ir.ui.view">
|
||||
<field name="name">credit control policy form</field>
|
||||
<field name="model">credit.control.policy.changer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form version="7.0" string="Set current credit level">
|
||||
<separator string="Change the overdue level of current invoice" colspan="4"/>
|
||||
<label string="This wizard will let you set the overdue policy and level for selected invoices"/>
|
||||
<newline/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="new_policy_id"/>
|
||||
<field name="do_nothing"
|
||||
invisible="1"/>
|
||||
<field name="new_policy_level_id"
|
||||
domain="[('policy_id', '=', new_policy_id)]"/>
|
||||
</group>
|
||||
<group></group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Move lines to affect">
|
||||
<field name="move_line_ids"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<footer>
|
||||
<button class="oe_highlight"
|
||||
name="set_new_policy"
|
||||
string="Set new policy"
|
||||
type="object"/>
|
||||
or
|
||||
<button class="oe_link"
|
||||
special="cancel"
|
||||
string="Cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- for button -->
|
||||
<record id="action_wizard_credit_policy_changer" model="ir.actions.act_window">
|
||||
<field name="name">Change current credit policy</field>
|
||||
<field name="res_model">credit.control.policy.changer</field>
|
||||
<field name="src_model">account.invoice</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="credit_control_policy_changer_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="help">Allows to manually change credit level</field>
|
||||
</record>
|
||||
|
||||
<!-- for menu -->
|
||||
<act_window name="Change current credit policy"
|
||||
res_model="credit.control.policy.changer"
|
||||
src_model="account.invoice"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="action_wizard_credit_policy_changer_menu_action"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
71
account_credit_control/wizard/credit_control_printer.py
Normal file
71
account_credit_control/wizard/credit_control_printer.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Nicolas Bessi, Guewen Baconnier
|
||||
# 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
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class CreditControlPrinter(models.TransientModel):
|
||||
""" Print lines """
|
||||
|
||||
_name = "credit.control.printer"
|
||||
_rec_name = 'id'
|
||||
_description = 'Mass printer'
|
||||
|
||||
@api.model
|
||||
def _get_line_ids(self):
|
||||
context = self.env.context
|
||||
if context.get('active_model') != 'credit.control.line':
|
||||
return False
|
||||
return context.get('active_ids', False)
|
||||
|
||||
mark_as_sent = fields.Boolean(string='Mark letter lines as sent',
|
||||
default=True,
|
||||
help="Only letter lines will be marked.")
|
||||
line_ids = fields.Many2many('credit.control.line',
|
||||
string='Credit Control Lines',
|
||||
default=_get_line_ids)
|
||||
|
||||
@api.model
|
||||
def _credit_line_predicate(self, line):
|
||||
return True
|
||||
|
||||
@api.model
|
||||
@api.returns('credit.control.line')
|
||||
def _get_lines(self, lines, predicate):
|
||||
return lines.filtered(predicate)
|
||||
|
||||
@api.multi
|
||||
def print_lines(self):
|
||||
self.ensure_one()
|
||||
comm_obj = self.env['credit.control.communication']
|
||||
if not self.line_ids:
|
||||
raise api.Warning(_('No credit control lines selected.'))
|
||||
|
||||
lines = self._get_lines(self.line_ids, self._credit_line_predicate)
|
||||
|
||||
comms = comm_obj._generate_comm_from_credit_lines(lines)
|
||||
|
||||
if self.mark_as_sent:
|
||||
comms._mark_credit_line_as_sent()
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,51 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="credit_line_printer_form" model="ir.ui.view">
|
||||
<field name="name">credit.line.printer.form</field>
|
||||
<field name="model">credit.control.printer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Lines report" version="7.0">
|
||||
<separator string="Print the selected lines" colspan="4"/>
|
||||
<newline/>
|
||||
<group>
|
||||
<field name="mark_as_sent"
|
||||
colspan="4"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<notebook>
|
||||
<page string="Lines">
|
||||
<field name="line_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<footer>
|
||||
<button class="oe_highlight" name="print_lines" string="Print" type="object"/>
|
||||
or
|
||||
<button class="oe_link" special="cancel" string="Cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- for menu -->
|
||||
<act_window name="Print Lines"
|
||||
res_model="credit.control.printer"
|
||||
src_model="credit.control.line"
|
||||
view_mode="form"
|
||||
target="new"
|
||||
key2="client_action_multi"
|
||||
id="open_credit_line_printer_wizard_menu_action"/>
|
||||
|
||||
<record id="open_credit_line_printer_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Print Lines</field>
|
||||
<field name="res_model">credit.control.printer</field>
|
||||
<field name="src_model">credit.control.line</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="credit_line_printer_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="help">Print selected lines</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user