+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/account_credit_control/run.py b/account_credit_control/run.py
new file mode 100644
index 000000000..c6afb1d1e
--- /dev/null
+++ b/account_credit_control/run.py
@@ -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 .
+#
+##############################################################################
+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 \"%s\" has generated %d Credit "
+ "Control Lines. ") %
+ (policy.name, len(policy_lines_generated)))
+ else:
+ report += _(
+ "Policy \"%s\" has not generated any "
+ "Credit Control Lines. " % 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
diff --git a/account_credit_control/run_view.xml b/account_credit_control/run_view.xml
new file mode 100644
index 000000000..aa3cd0ea0
--- /dev/null
+++ b/account_credit_control/run_view.xml
@@ -0,0 +1,75 @@
+
+
+
+
+ credit.control.run.tree
+ credit.control.run
+
+
+
+
+
+
+
+
+
+ credit.control.run.form
+ credit.control.run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Credit Control Run
+ ir.actions.act_window
+ credit.control.run
+
+ form
+ tree,form
+
+
+
+
+
+
+
+
diff --git a/account_credit_control/scenarios/features/00_credit_control_param.feature b/account_credit_control/scenarios/features/00_credit_control_param.feature
new file mode 100644
index 000000000..c02bb6384
--- /dev/null
+++ b/account_credit_control/scenarios/features/00_credit_control_param.feature
@@ -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 |
diff --git a/account_credit_control/scenarios/features/01_credit_control_partners.feature b/account_credit_control/scenarios/features/01_credit_control_partners.feature
new file mode 100644
index 000000000..9edb192c7
--- /dev/null
+++ b/account_credit_control/scenarios/features/01_credit_control_partners.feature
@@ -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 |
diff --git a/account_credit_control/scenarios/features/02_credit_control_invoices.feature b/account_credit_control/scenarios/features/02_credit_control_invoices.feature
new file mode 100644
index 000000000..a153d39f7
--- /dev/null
+++ b/account_credit_control/scenarios/features/02_credit_control_invoices.feature
@@ -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
diff --git a/account_credit_control/scenarios/features/03_credit_control_run_jan.feature b/account_credit_control/scenarios/features/03_credit_control_run_jan.feature
new file mode 100644
index 000000000..9839f9bf1
--- /dev/null
+++ b/account_credit_control/scenarios/features/03_credit_control_run_jan.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/04_credit_control_run_feb.feature b/account_credit_control/scenarios/features/04_credit_control_run_feb.feature
new file mode 100644
index 000000000..f9b1865ca
--- /dev/null
+++ b/account_credit_control/scenarios/features/04_credit_control_run_feb.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/05_credit_control_run_mar.feature b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature
new file mode 100644
index 000000000..af5199b10
--- /dev/null
+++ b/account_credit_control/scenarios/features/05_credit_control_run_mar.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/06_credit_control_run_apr.feature b/account_credit_control/scenarios/features/06_credit_control_run_apr.feature
new file mode 100644
index 000000000..635bfeee7
--- /dev/null
+++ b/account_credit_control/scenarios/features/06_credit_control_run_apr.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/07_credit_control_run_may.feature b/account_credit_control/scenarios/features/07_credit_control_run_may.feature
new file mode 100644
index 000000000..2b73fa660
--- /dev/null
+++ b/account_credit_control/scenarios/features/07_credit_control_run_may.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/08_credit_control_run_jun.feature b/account_credit_control/scenarios/features/08_credit_control_run_jun.feature
new file mode 100644
index 000000000..75c7df7f5
--- /dev/null
+++ b/account_credit_control/scenarios/features/08_credit_control_run_jun.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/09_credit_control_run_jul.feature b/account_credit_control/scenarios/features/09_credit_control_run_jul.feature
new file mode 100644
index 000000000..df0aa4c55
--- /dev/null
+++ b/account_credit_control/scenarios/features/09_credit_control_run_jul.feature
@@ -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 | |
diff --git a/account_credit_control/scenarios/features/10_credit_control_run_aug.feature b/account_credit_control/scenarios/features/10_credit_control_run_aug.feature
new file mode 100644
index 000000000..5351b4b9c
--- /dev/null
+++ b/account_credit_control/scenarios/features/10_credit_control_run_aug.feature
@@ -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"
diff --git a/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature b/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature
new file mode 100644
index 000000000..6c30d67b1
--- /dev/null
+++ b/account_credit_control/scenarios/features/11_credit_control_manual_setting.feature
@@ -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"
diff --git a/account_credit_control/scenarios/features/steps/account_credit_control.py b/account_credit_control/scenarios/features/steps/account_credit_control.py
new file mode 100644
index 000000000..2a292d0a5
--- /dev/null
+++ b/account_credit_control/scenarios/features/steps/account_credit_control.py
@@ -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)
diff --git a/account_credit_control/scenarios/features/steps/account_credit_control_changer.py b/account_credit_control/scenarios/features/steps/account_credit_control_changer.py
new file mode 100644
index 000000000..6f46d11fc
--- /dev/null
+++ b/account_credit_control/scenarios/features/steps/account_credit_control_changer.py
@@ -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)
diff --git a/account_credit_control/scenarios/features/steps/account_voucher.py b/account_credit_control/scenarios/features/steps/account_voucher.py
new file mode 100644
index 000000000..6d314316f
--- /dev/null
+++ b/account_credit_control/scenarios/features/steps/account_voucher.py
@@ -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)
diff --git a/account_credit_control/security/ir.model.access.csv b/account_credit_control/security/ir.model.access.csv
new file mode 100644
index 000000000..c47087e6e
--- /dev/null
+++ b/account_credit_control/security/ir.model.access.csv
@@ -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
\ No newline at end of file
diff --git a/account_credit_control/wizard/__init__.py b/account_credit_control/wizard/__init__.py
new file mode 100644
index 000000000..82938e856
--- /dev/null
+++ b/account_credit_control/wizard/__init__.py
@@ -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 .
+#
+##############################################################################
+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
diff --git a/account_credit_control/wizard/credit_control_communication.py b/account_credit_control/wizard/credit_control_communication.py
new file mode 100644
index 000000000..7ef92585e
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_communication.py
@@ -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 .
+#
+##############################################################################
+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
diff --git a/account_credit_control/wizard/credit_control_emailer.py b/account_credit_control/wizard/credit_control_emailer.py
new file mode 100644
index 000000000..981892e64
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_emailer.py
@@ -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 .
+#
+##############################################################################
+
+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'}
diff --git a/account_credit_control/wizard/credit_control_emailer_view.xml b/account_credit_control/wizard/credit_control_emailer_view.xml
new file mode 100644
index 000000000..2c299a64b
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_emailer_view.xml
@@ -0,0 +1,54 @@
+
+
+
+
+ credit.line.emailer.form
+ credit.control.emailer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Send By Email
+ credit.control.emailer
+ form
+ form
+
+ new
+ Send an email for the selected lines.
+
+
+
+
+
diff --git a/account_credit_control/wizard/credit_control_marker.py b/account_credit_control/wizard/credit_control_marker.py
new file mode 100644
index 000000000..516c4febc
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_marker.py
@@ -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 .
+#
+##############################################################################
+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'}
diff --git a/account_credit_control/wizard/credit_control_marker_view.xml b/account_credit_control/wizard/credit_control_marker_view.xml
new file mode 100644
index 000000000..c32cc8e18
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_marker_view.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ credit.line.marker.form
+ credit.control.marker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change Lines' State
+ credit.control.marker
+ credit.control.line
+ form
+ form
+
+ new
+ Change the state of the selected lines.
+
+
+
+
diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py
new file mode 100644
index 000000000..6829d7aff
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_policy_changer.py
@@ -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 .
+#
+##############################################################################
+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
diff --git a/account_credit_control/wizard/credit_control_policy_changer_view.xml b/account_credit_control/wizard/credit_control_policy_changer_view.xml
new file mode 100644
index 000000000..cee46799d
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_policy_changer_view.xml
@@ -0,0 +1,64 @@
+
+
+
+
+ credit control policy form
+ credit.control.policy.changer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change current credit policy
+ credit.control.policy.changer
+ account.invoice
+ form
+ form
+
+ new
+ Allows to manually change credit level
+
+
+
+
+
+
+
diff --git a/account_credit_control/wizard/credit_control_printer.py b/account_credit_control/wizard/credit_control_printer.py
new file mode 100644
index 000000000..f6e0bf04d
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_printer.py
@@ -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 .
+#
+##############################################################################
+
+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)
diff --git a/account_credit_control/wizard/credit_control_printer_view.xml b/account_credit_control/wizard/credit_control_printer_view.xml
new file mode 100644
index 000000000..5a388ca9f
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_printer_view.xml
@@ -0,0 +1,51 @@
+
+
+
+
+ credit.line.printer.form
+ credit.control.printer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Print Lines
+ credit.control.printer
+ credit.control.line
+ form
+ form
+
+ new
+ Print selected lines
+
+
+
+