diff --git a/CHANGELOG b/CHANGELOG
index d7d29c3eb8..2f38076404 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add optional credit type on account
* Always require a date to compute taxes
* Add support for Python 3.10
* Remove support for Python 3.6
diff --git a/account.py b/account.py
index 5449fadfd8..398af065b6 100644
--- a/account.py
+++ b/account.py
@@ -14,7 +14,7 @@ from sql.conditionals import Case, Coalesce
from trytond.i18n import gettext
from trytond.model import (
- ModelSQL, ModelView, Unique, fields, sequence_ordered, tree)
+ Check, ModelSQL, ModelView, Unique, fields, sequence_ordered, tree)
from trytond.model.exceptions import AccessError
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool
@@ -319,18 +319,24 @@ class Type(
accounts = Account.search([
('type', 'in', [t.id for t in childs]),
])
- debit_accounts = Account.search([
+ debit_credit_accounts = Account.search([
('type', '!=', None),
- ('debit_type', 'in', [t.id for t in childs]),
+ ['OR',
+ ('debit_type', 'in', [t.id for t in childs]),
+ ('credit_type', 'in', [t.id for t in childs]),
+ ],
])
for account in accounts:
balance = account.credit - account.debit
- if not account.debit_type or balance > 0:
+ if ((not account.debit_type or balance > 0)
+ and (not account.credit_type or balance < 0)):
type_sum[account.type.id] += balance
- for account in debit_accounts:
+ for account in debit_credit_accounts:
balance = account.credit - account.debit
- if balance < 0:
+ if account.debit_type and balance < 0:
type_sum[account.debit_type.id] += balance
+ elif account.credit_type and balance > 0:
+ type_sum[account.credit_type.id] += balance
for type_ in types:
childs = cls.search([
@@ -627,6 +633,8 @@ class AccountTemplate(
'account.account.type.template', "Type", ondelete="RESTRICT")
debit_type = fields.Many2One(
'account.account.type.template', "Debit Type", ondelete="RESTRICT")
+ credit_type = fields.Many2One(
+ 'account.account.type.template', "Credit Type", ondelete="RESTRICT")
parent = fields.Many2One('account.account.template', 'Parent', select=True,
ondelete="RESTRICT")
childs = fields.One2Many('account.account.template', 'parent', 'Children')
@@ -644,6 +652,11 @@ class AccountTemplate(
super(AccountTemplate, cls).__setup__()
cls._order.insert(0, ('code', 'ASC'))
cls._order.insert(1, ('name', 'ASC'))
+ table = cls.__table__()
+ cls._sql_constraints.append(
+ ('only_one_debit_credit_types', Check(
+ table, (table.debit_type + table.credit_type) == Null),
+ 'account.msg_only_one_debit_credit_types'))
@classmethod
def __register__(cls, module_name):
@@ -721,6 +734,11 @@ class AccountTemplate(
template.debit_type.id)
else:
vals['debit_type'] = None
+ if template.credit_type:
+ vals['credit_type'] = template2type.get(
+ template.credit_type.id)
+ else:
+ vals['credit_type'] = None
values.append(vals)
created.append(template)
@@ -825,13 +843,28 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView):
'account.account.type', "Debit Type", ondelete='RESTRICT',
states={
'readonly': _states['readonly'],
- 'invisible': ~Eval('type'),
+ 'invisible': (
+ ~Eval('type') | Eval('credit_type')
+ | (_states['readonly']) & ~Eval('debit_type')),
},
domain=[
('company', '=', Eval('company')),
],
- depends=['company', 'type'],
+ depends=['company', 'type', 'credit_type'],
help="The type used if not empty and debit > credit.")
+ credit_type = fields.Many2One(
+ 'account.account.type', "Credit Type", ondelete='RESTRICT',
+ states={
+ 'readonly': _states['readonly'],
+ 'invisible': (
+ ~Eval('type') | Eval('debit_type')
+ | (_states['readonly']) & ~Eval('credit_type')),
+ },
+ domain=[
+ ('company', '=', Eval('company')),
+ ],
+ depends=['company', 'type', 'debit_type'],
+ help="The type used if not empty and debit < credit.")
parent = fields.Many2One(
'account.account', 'Parent', select=True,
left="left", right="right", ondelete="RESTRICT", states=_states)
@@ -899,6 +932,11 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView):
}
cls._order.insert(0, ('code', 'ASC'))
cls._order.insert(1, ('name', 'ASC'))
+ table = cls.__table__()
+ cls._sql_constraints.append(
+ ('only_one_debit_credit_types', Check(
+ table, (table.debit_type + table.credit_type) == Null),
+ 'account.msg_only_one_debit_credit_types'))
@classmethod
def __register__(cls, module_name):
@@ -1094,7 +1132,7 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView):
return values
__on_change_parent_fields = ['name', 'code', 'company', 'type',
- 'debit_type', 'reconcile', 'deferral', 'party_required',
+ 'debit_type', 'credit_type', 'reconcile', 'deferral', 'party_required',
'general_ledger_balance', 'taxes']
@fields.depends('parent', *(__on_change_parent_fields
@@ -1231,6 +1269,16 @@ class Account(AccountMixin(), ActivePeriodMixin, tree(), ModelSQL, ModelView):
template_debit_type = None
if current_debit_type != template_debit_type:
vals['debit_type'] = template_debit_type
+ current_credit_type = (
+ child.credit_type.id if child.credit_type
+ else None)
+ if child.template.credit_type:
+ template_credit_type = template2type.get(
+ child.template.credit_type.id)
+ else:
+ template_credit_type = None
+ if current_credit_type != template_credit_type:
+ vals['credit_type'] = template_credit_type
if vals:
values.append([child])
values.append(vals)
@@ -1329,6 +1377,7 @@ class AccountParty(ActivePeriodMixin, ModelSQL):
company = fields.Many2One('company.company', "Company")
type = fields.Many2One('account.account.type', "Type")
debit_type = fields.Many2One('account.account.type', "Debit Type")
+ credit_type = fields.Many2One('account.account.type', "Credit Type")
closed = fields.Boolean("Closed")
balance = fields.Function(Monetary(
@@ -1854,6 +1903,7 @@ class GeneralLedgerAccount(_GeneralLedgerAccount):
type = fields.Many2One('account.account.type', "Type")
debit_type = fields.Many2One('account.account.type', "Debit Type")
+ credit_type = fields.Many2One('account.account.type', "Credit Type")
lines = fields.One2Many(
'account.general_ledger.line', 'account', "Lines", readonly=True)
general_ledger_balance = fields.Boolean("General Ledger Balance")
@@ -2595,6 +2645,7 @@ class AgedBalance(ModelSQL, ModelView):
account = Account.__table__()
type_ = Type.__table__()
debit_type = Type.__table__()
+ credit_type = Type.__table__()
company_id = context.get('company')
date = context.get('date')
@@ -2602,6 +2653,7 @@ class AgedBalance(ModelSQL, ModelView):
line_query, _ = MoveLine.query_get(line)
kind = cls.get_kind(type_)
debit_kind = cls.get_kind(debit_type)
+ credit_kind = cls.get_kind(credit_type)
columns = [
line.party.as_('id'),
Literal(0).as_('create_uid'),
@@ -2638,11 +2690,13 @@ class AgedBalance(ModelSQL, ModelView):
).join(type_, condition=account.type == type_.id
).join(debit_type, 'LEFT',
condition=account.debit_type == debit_type.id
+ ).join(credit_type, 'LEFT',
+ condition=account.credit_type == credit_type.id
).join(reconciliation, 'LEFT',
condition=reconciliation.id == line.reconciliation
).select(*columns,
where=(line.party != Null)
- & (kind | debit_kind)
+ & (kind | debit_kind | credit_kind)
& ((line.reconciliation == Null)
| (reconciliation.date > date))
& (move.date <= date)
diff --git a/message.xml b/message.xml
index 1d2507ec3a..9963a45e89 100644
--- a/message.xml
+++ b/message.xml
@@ -83,6 +83,9 @@ this repository contains the full copyright notices and license terms. -->
You cannot erase party "%(party)s" while they have pending receivable/payable with company "%(company)s".
+
+ Account can have only one of the debit or credit type set.
+
You cannot delete account "%(account)s" because it has move lines.
diff --git a/tests/test_account.py b/tests/test_account.py
index c42e22f706..39d8d89cb3 100644
--- a/tests/test_account.py
+++ b/tests/test_account.py
@@ -540,6 +540,7 @@ class AccountTestCase(
with Transaction().set_context(fiscalyear=fiscalyear.id):
debit_receivable_type, = Type.copy([receivable_type])
receivable.debit_type = debit_receivable_type
+ receivable.credit_type = None
receivable.save()
self.assertEqual(receivable_type.amount, Decimal(0))
self.assertEqual(debit_receivable_type.amount, Decimal(100))
@@ -548,10 +549,29 @@ class AccountTestCase(
with Transaction().set_context(fiscalyear=fiscalyear.id):
debit_revenue_type, = Type.copy([revenue_type])
revenue.debit_type = debit_revenue_type
+ revenue.credit_type = None
revenue.save()
self.assertEqual(revenue_type.amount, Decimal(100))
self.assertEqual(debit_revenue_type.amount, Decimal(0))
+ # Set a credit type on revenue
+ with Transaction().set_context(fiscalyear=fiscalyear.id):
+ credit_revenue_type, = Type.copy([revenue_type])
+ revenue.credit_type = credit_revenue_type
+ revenue.debit_type = None
+ revenue.save()
+ self.assertEqual(revenue_type.amount, Decimal(0))
+ self.assertEqual(credit_revenue_type.amount, Decimal(100))
+
+ # Set a credit type on receivable
+ with Transaction().set_context(fiscalyear=fiscalyear.id):
+ credit_receivable_type, = Type.copy([receivable_type])
+ receivable.credit_type = credit_receivable_type
+ receivable.debit_type = None
+ receivable.save()
+ self.assertEqual(receivable_type.amount, Decimal(100))
+ self.assertEqual(credit_receivable_type.amount, Decimal(0))
+
@with_transaction()
def test_move_post(self):
"Test posting move"
diff --git a/view/account_form.xml b/view/account_form.xml
index 0485964ffd..29d19f7643 100644
--- a/view/account_form.xml
+++ b/view/account_form.xml
@@ -21,15 +21,19 @@ this repository contains the full copyright notices and license terms. -->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/view/account_list.xml b/view/account_list.xml
index 7336b62e29..04c31820ca 100644
--- a/view/account_list.xml
+++ b/view/account_list.xml
@@ -6,4 +6,5 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/view/account_template_form.xml b/view/account_template_form.xml
index 89e1af1d6a..e5e0d52080 100644
--- a/view/account_template_form.xml
+++ b/view/account_template_form.xml
@@ -10,15 +10,19 @@ this repository contains the full copyright notices and license terms. -->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/view/account_template_list.xml b/view/account_template_list.xml
index a1248d6efe..4862e26746 100644
--- a/view/account_template_list.xml
+++ b/view/account_template_list.xml
@@ -5,4 +5,5 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/view/account_template_tree.xml b/view/account_template_tree.xml
index a1248d6efe..4862e26746 100644
--- a/view/account_template_tree.xml
+++ b/view/account_template_tree.xml
@@ -5,4 +5,5 @@ this repository contains the full copyright notices and license terms. -->
+
diff --git a/view/account_tree.xml b/view/account_tree.xml
index 7336b62e29..04c31820ca 100644
--- a/view/account_tree.xml
+++ b/view/account_tree.xml
@@ -6,4 +6,5 @@ this repository contains the full copyright notices and license terms. -->
+