diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-06-05 16:27:49 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-06-05 16:27:49 +0200 |
| commit | aba8c2b8fbfb024652f0a080c657eb2369088eda (patch) | |
| tree | bbb10ed85d2ae7f0bc1e77234fc1d24d9067de8d /addons/account | |
| parent | fc5e61b63d1357f4c89e68e108de1ade21db724f (diff) | |
| parent | 1b272a20504e16e0c170ea717f5bcf63d38e7cb5 (diff) | |
[MERGE] forward port branch 11.0 up to 1b272a20504
Diffstat (limited to 'addons/account')
| -rw-r--r-- | addons/account/data/mail_template_data.xml | 2 | ||||
| -rw-r--r-- | addons/account/models/account_invoice.py | 2 | ||||
| -rw-r--r-- | addons/account/models/company.py | 59 | ||||
| -rw-r--r-- | addons/account/tests/test_account_move_closed_period.py | 18 | ||||
| -rw-r--r-- | addons/account/tests/test_reconciliation.py | 49 | ||||
| -rw-r--r-- | addons/account/views/account_view.xml | 3 |
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 <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field> + <field name="email_from">${(object.user_id.email and '"%s" <%s>' % (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"> |
