summaryrefslogtreecommitdiff
path: root/addons/account
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-06-05 16:27:49 +0200
committerChristophe Simonis <chs@odoo.com>2018-06-05 16:27:49 +0200
commitaba8c2b8fbfb024652f0a080c657eb2369088eda (patch)
treebbb10ed85d2ae7f0bc1e77234fc1d24d9067de8d /addons/account
parentfc5e61b63d1357f4c89e68e108de1ade21db724f (diff)
parent1b272a20504e16e0c170ea717f5bcf63d38e7cb5 (diff)
[MERGE] forward port branch 11.0 up to 1b272a20504
Diffstat (limited to 'addons/account')
-rw-r--r--addons/account/data/mail_template_data.xml2
-rw-r--r--addons/account/models/account_invoice.py2
-rw-r--r--addons/account/models/company.py59
-rw-r--r--addons/account/tests/test_account_move_closed_period.py18
-rw-r--r--addons/account/tests/test_reconciliation.py49
-rw-r--r--addons/account/views/account_view.xml3
6 files changed, 121 insertions, 12 deletions
diff --git a/addons/account/data/mail_template_data.xml b/addons/account/data/mail_template_data.xml
index f461a271452..4bb340a4f5c 100644
--- a/addons/account/data/mail_template_data.xml
+++ b/addons/account/data/mail_template_data.xml
@@ -7,7 +7,7 @@
<!--Email template -->
<record id="email_template_edi_invoice" model="mail.template">
<field name="name">Invoicing: Invoice email</field>
- <field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
+ <field name="email_from">${(object.user_id.email and '&quot;%s&quot; &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="account.model_account_invoice"/>
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index bc809c577f7..94c1fa485c7 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1370,8 +1370,6 @@ class AccountInvoice(models.Model):
else:
payment_method = self.env.ref('account.account_payment_method_manual_out')
journal_payment_methods = pay_journal.outbound_payment_method_ids
- if payment_method not in journal_payment_methods:
- raise UserError(_('No appropriate payment method enabled on journal %s') % pay_journal.name)
communication = self.type in ('in_invoice', 'in_refund') and self.reference or self.number
if self.origin:
diff --git a/addons/account/models/company.py b/addons/account/models/company.py
index 8ae4230a7b2..0209ea5c849 100644
--- a/addons/account/models/company.py
+++ b/addons/account/models/company.py
@@ -2,10 +2,12 @@
from datetime import timedelta, datetime
import calendar
+import time
+from dateutil.relativedelta import relativedelta
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError, UserError
-from odoo.exceptions import UserError
+from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
from odoo.tools.float_utils import float_round, float_is_zero
@@ -63,6 +65,61 @@ Best Regards,'''))
account_setup_coa_done = fields.Boolean(string='Chart of Account Checked', help="Technical field holding the status of the chart of account setup step.")
account_setup_bar_closed = fields.Boolean(string='Setup Bar Closed', help="Technical field set to True when setup bar has been closed by the user.")
+ @api.multi
+ def _check_lock_dates(self, vals):
+ '''Check the lock dates for the current companies. This can't be done in a api.constrains because we need
+ to perform some comparison between new/old values. This method forces the lock dates to be irreversible.
+
+ * You cannot define stricter conditions on advisors than on users. Then, the lock date on advisor must be set
+ after the lock date for users.
+ * You cannot lock a period that is not finished yet. Then, the lock date for advisors must be set after the
+ last day of the previous month.
+ * The new lock date for advisors must be set after the previous lock date.
+
+ :param vals: The values passed to the write method.
+ '''
+ period_lock_date = vals.get('period_lock_date') and\
+ time.strptime(vals['period_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+ fiscalyear_lock_date = vals.get('fiscalyear_lock_date') and\
+ time.strptime(vals['fiscalyear_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+
+ previous_month = datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT) + relativedelta(months=-1)
+ days_previous_month = calendar.monthrange(previous_month.year, previous_month.month)
+ previous_month = previous_month.replace(day=days_previous_month[1]).timetuple()
+ for company in self:
+ old_fiscalyear_lock_date = company.fiscalyear_lock_date and\
+ time.strptime(company.fiscalyear_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+
+ # The user attempts to remove the lock date for advisors
+ if old_fiscalyear_lock_date and not fiscalyear_lock_date and 'fiscalyear_lock_date' in vals:
+ raise ValidationError(_('The lock date for advisors is irreversible and can\'t be removed.'))
+
+ # The user attempts to set a lock date for advisors prior to the previous one
+ if old_fiscalyear_lock_date and fiscalyear_lock_date and fiscalyear_lock_date < old_fiscalyear_lock_date:
+ raise ValidationError(_('The new lock date for advisors must be set after the previous lock date.'))
+
+ # In case of no new fiscal year in vals, fallback to the oldest
+ if not fiscalyear_lock_date:
+ if old_fiscalyear_lock_date:
+ fiscalyear_lock_date = old_fiscalyear_lock_date
+ else:
+ continue
+
+ # The user attempts to set a lock date for advisors prior to the last day of previous month
+ if fiscalyear_lock_date > previous_month:
+ raise ValidationError(_('You cannot lock a period that is not finished yet. Please make sure that the lock date for advisors is not set after the last day of the previous month.'))
+
+ # In case of no new period lock date in vals, fallback to the one defined in the company
+ if not period_lock_date:
+ if company.period_lock_date:
+ period_lock_date = time.strptime(company.period_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+ else:
+ continue
+
+ # The user attempts to set a lock date for advisors prior to the lock date for users
+ if period_lock_date < fiscalyear_lock_date:
+ raise ValidationError(_('You cannot define stricter conditions on advisors than on users. Please make sure that the lock date on advisor is set before the lock date for users.'))
+
@api.model
def _verify_fiscalyear_last_day(self, company_id, last_day, last_month):
company = self.browse(company_id)
diff --git a/addons/account/tests/test_account_move_closed_period.py b/addons/account/tests/test_account_move_closed_period.py
index 298738a0d6c..5de3e97ac4f 100644
--- a/addons/account/tests/test_account_move_closed_period.py
+++ b/addons/account/tests/test_account_move_closed_period.py
@@ -1,6 +1,8 @@
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
from odoo.osv.orm import except_orm
-from datetime import datetime, timedelta
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from calendar import monthrange
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from odoo.tests import tagged
@@ -14,14 +16,16 @@ class TestPeriodState(AccountingTestCase):
def setUp(self):
super(TestPeriodState, self).setUp()
self.user_id = self.env.user
- self.day_before_yesterday = datetime.now() - timedelta(2)
- self.yesterday = datetime.now() - timedelta(1)
- self.yesterday_str = self.yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
+ last_day_month = datetime.now() - relativedelta(months=1)
+ last_day_month = last_day_month.replace(day=monthrange(last_day_month.year, last_day_month.month)[1])
+ self.last_day_month_str = last_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
#make sure there is no unposted entry
- draft_entries = self.env['account.move'].search([('date', '<=', self.yesterday_str), ('state', '=', 'draft')])
+ draft_entries = self.env['account.move'].search([('date', '<=', self.last_day_month_str), ('state', '=', 'draft')])
if draft_entries:
draft_entries.post()
- self.user_id.company_id.write({'fiscalyear_lock_date': self.yesterday_str})
+ self.user_id.company_id.fiscalyear_lock_date = self.last_day_month_str
self.sale_journal_id = self.env['account.journal'].search([('type', '=', 'sale')])[0]
self.account_id = self.env['account.account'].search([('internal_type', '=', 'receivable')])[0]
@@ -30,7 +34,7 @@ class TestPeriodState(AccountingTestCase):
move = self.env['account.move'].create({
'name': '/',
'journal_id': self.sale_journal_id.id,
- 'date': self.day_before_yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT),
+ 'date': self.last_day_month_str,
'line_ids': [(0, 0, {
'name': 'foo',
'debit': 10,
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 4a50c812620..1fce897b602 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -42,6 +42,12 @@ class TestReconciliation(AccountingTestCase):
self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id
self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id
+ self.inbound_payment_method = self.env['account.payment.method'].create({
+ 'name': 'inbound',
+ 'code': 'IN',
+ 'payment_type': 'inbound',
+ })
+
def create_invoice(self, type='out_invoice', invoice_amount=50, currency_id=None):
#we create an invoice in given currency
invoice = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
@@ -709,6 +715,49 @@ class TestReconciliation(AccountingTestCase):
credit_aml.with_context(invoice_id=inv.id).remove_move_reconcile()
self.assertAlmostEquals(inv.residual, 111)
+ def test_revert_payment_and_reconcile(self):
+ payment = self.env['account.payment'].create({
+ 'payment_method_id': self.inbound_payment_method.id,
+ 'payment_type': 'inbound',
+ 'partner_type': 'customer',
+ 'partner_id': self.partner_agrolait_id,
+ 'journal_id': self.bank_journal_usd.id,
+ 'payment_date': '2018-06-04',
+ 'amount': 666,
+ })
+ payment.post()
+
+ self.assertEqual(len(payment.move_line_ids), 2)
+
+ bank_line = payment.move_line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+ customer_line = payment.move_line_ids - bank_line
+
+ self.assertEqual(len(bank_line), 1)
+ self.assertEqual(len(customer_line), 1)
+ self.assertNotEqual(bank_line.id, customer_line.id)
+
+ self.assertEqual(bank_line.move_id.id, customer_line.move_id.id)
+ move = bank_line.move_id
+
+ # Reversing the payment's move
+ reversed_move_list = move.reverse_moves('2018-06-04')
+ self.assertEqual(len(reversed_move_list), 1)
+ reversed_move = self.env['account.move'].browse(reversed_move_list[0])
+
+ self.assertEqual(len(reversed_move.line_ids), 2)
+
+ # Testing the reconciliation matching between the move lines and their reversed counterparts
+ reversed_bank_line = reversed_move.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+ reversed_customer_line = reversed_move.line_ids - reversed_bank_line
+
+ self.assertEqual(len(reversed_bank_line), 1)
+ self.assertEqual(len(reversed_customer_line), 1)
+ self.assertNotEqual(reversed_bank_line.id, reversed_customer_line.id)
+ self.assertEqual(reversed_bank_line.move_id.id, reversed_customer_line.move_id.id)
+
+ self.assertEqual(reversed_bank_line.full_reconcile_id.id, bank_line.full_reconcile_id.id)
+ self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)
+
def test_partial_reconcile_currencies_02(self):
####
# Day 1: Invoice Cust/001 to customer (expressed in USD)
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 712bfe7b1f9..8a925dbdf8c 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -1141,7 +1141,8 @@
<group>
<field name="name"/>
<field name="partner_id"
- domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/>
+ domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"
+ attrs="{'readonly': [('parent_state', '=', 'posted')]}"/>
</group>
<notebook colspan="4">
<page string="Information">