[13.0][IMP] account_move_template, add options to overwirte output lines
This commit is contained in:
parent
36b5238751
commit
9dedd88bf1
@ -17,7 +17,7 @@ class AccountMoveTemplate(models.Model):
|
|||||||
string="Company",
|
string="Company",
|
||||||
required=True,
|
required=True,
|
||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
default=lambda self: self.env["res.company"]._company_default_get(),
|
default=lambda self: self.env.company,
|
||||||
)
|
)
|
||||||
journal_id = fields.Many2one("account.journal", string="Journal", required=True)
|
journal_id = fields.Many2one("account.journal", string="Journal", required=True)
|
||||||
ref = fields.Char(string="Reference", copy=False)
|
ref = fields.Char(string="Reference", copy=False)
|
||||||
@ -159,6 +159,12 @@ class AccountMoveTemplateLine(models.Model):
|
|||||||
store=True,
|
store=True,
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
|
opt_account_id = fields.Many2one(
|
||||||
|
"account.account",
|
||||||
|
string="Account Opt.",
|
||||||
|
domain=[("deprecated", "=", False)],
|
||||||
|
help="When amount is negative, use this account in stead",
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends("is_refund", "account_id", "tax_line_id")
|
@api.depends("is_refund", "account_id", "tax_line_id")
|
||||||
def _compute_tax_repartition_line_id(self):
|
def _compute_tax_repartition_line_id(self):
|
||||||
|
@ -19,3 +19,7 @@ Module Contributors
|
|||||||
|
|
||||||
* Harald Panten <harald.panten@sygel.es>
|
* Harald Panten <harald.panten@sygel.es>
|
||||||
* Valentin Vinagre <valentin.vinagre@sygel.es>
|
* Valentin Vinagre <valentin.vinagre@sygel.es>
|
||||||
|
|
||||||
|
* `Ecosoft <http://ecosoft.co.th>`_:
|
||||||
|
|
||||||
|
* Kitti U. <kittiu@ecosoft.co.th> (Add context overwrite)
|
||||||
|
@ -7,3 +7,23 @@ the amount of every input lines.
|
|||||||
|
|
||||||
The journal entry form allows lo load, through a wizard,
|
The journal entry form allows lo load, through a wizard,
|
||||||
the template to use and the amounts to fill.
|
the template to use and the amounts to fill.
|
||||||
|
|
||||||
|
**Notable features:**
|
||||||
|
|
||||||
|
This module enhance the capability of module account_move_template with following features,
|
||||||
|
|
||||||
|
#. Optional account for negative amount.
|
||||||
|
|
||||||
|
When the Journal entry is created, and credit/debit is negative value, change debit/credit
|
||||||
|
side and use the opt_account_id
|
||||||
|
|
||||||
|
#. Allow overwrite move line values with overwrite dict.
|
||||||
|
|
||||||
|
Normally, the journal items created by the template will require user input on wizard.
|
||||||
|
This feature allow passing the overwrite values with a dictionary.
|
||||||
|
This is particularly useful when the wizard is called by code.
|
||||||
|
|
||||||
|
Sample of dictionary to overwrite move lines::
|
||||||
|
|
||||||
|
{'L1': {'partner_id': 1, 'amount': 100, 'name': 'some label'},
|
||||||
|
'L2': {'partner_id': 2, 'amount': 200, 'name': 'some label 2'}, }
|
||||||
|
@ -10,4 +10,5 @@ To use an existing template:
|
|||||||
|
|
||||||
#. Go to *Invoicing / Accounting / Miscellaneous / Create Entry from Template*
|
#. Go to *Invoicing / Accounting / Miscellaneous / Create Entry from Template*
|
||||||
#. Select one of the available templates.
|
#. Select one of the available templates.
|
||||||
|
#. As option, you can overwrite output lines with dict, i.e., {"L1": {"partner": 1}}
|
||||||
#. Complete the entries according to the template and click on the button *Generate Journal Entry*.
|
#. Complete the entries according to the template and click on the button *Generate Journal Entry*.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
# Needs to be re-written because wizard.multi.charts.accounts
|
# Needs to be re-written because wizard.multi.charts.accounts
|
||||||
# doesn't exist any more on v12
|
# doesn't exist any more on v12
|
||||||
# from . import test_account_move_template
|
# from . import test_account_move_template
|
||||||
|
from . import test_account_move_template_options
|
||||||
|
@ -0,0 +1,186 @@
|
|||||||
|
# Copyright 2020 Ecosoft (http://ecosoft.co.th)
|
||||||
|
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tests.common import Form, TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccountMoveTemplateEnhanced(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestAccountMoveTemplateEnhanced, self).setUp()
|
||||||
|
self.Move = self.env["account.move"]
|
||||||
|
self.Journal = self.env["account.journal"]
|
||||||
|
self.Account = self.env["account.account"]
|
||||||
|
self.Template = self.env["account.move.template"]
|
||||||
|
self.Partner = self.env["res.partner"]
|
||||||
|
|
||||||
|
self.journal = self.Journal.search([("type", "=", "general")], limit=1)
|
||||||
|
self.ar_account_id = self.Account.search(
|
||||||
|
[("user_type_id.type", "=", "receivable")], limit=1
|
||||||
|
)
|
||||||
|
self.ap_account_id = self.Account.search(
|
||||||
|
[("user_type_id.type", "=", "payable")], limit=1
|
||||||
|
)
|
||||||
|
self.income_account_id = self.Account.search(
|
||||||
|
[
|
||||||
|
("user_type_id.type", "=", "other"),
|
||||||
|
("user_type_id.internal_group", "=", "income"),
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
self.expense_account_id = self.Account.search(
|
||||||
|
[
|
||||||
|
("user_type_id.type", "=", "other"),
|
||||||
|
("user_type_id.internal_group", "=", "expense"),
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
self.partners = self.Partner.search([], limit=3)
|
||||||
|
|
||||||
|
# Create a simple move tempalte
|
||||||
|
ar_line = {
|
||||||
|
"sequence": 0,
|
||||||
|
"name": "AR Line 1",
|
||||||
|
"account_id": self.ar_account_id.id,
|
||||||
|
"opt_account_id": self.ap_account_id.id,
|
||||||
|
"move_line_type": "dr",
|
||||||
|
"type": "input",
|
||||||
|
}
|
||||||
|
income_line1 = {
|
||||||
|
"sequence": 1,
|
||||||
|
"name": "Income Line 2",
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"opt_account_id": self.expense_account_id.id,
|
||||||
|
"move_line_type": "cr",
|
||||||
|
"type": "computed",
|
||||||
|
"python_code": "L0*1/3",
|
||||||
|
}
|
||||||
|
income_line2 = {
|
||||||
|
"sequence": 2,
|
||||||
|
"name": "Income Line 2",
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"opt_account_id": self.expense_account_id.id,
|
||||||
|
"move_line_type": "cr",
|
||||||
|
"type": "computed",
|
||||||
|
"python_code": "L0*2/3",
|
||||||
|
}
|
||||||
|
|
||||||
|
self.move_template = self.Template.create(
|
||||||
|
{
|
||||||
|
"name": "Test Template",
|
||||||
|
"journal_id": self.journal.id,
|
||||||
|
"line_ids": [
|
||||||
|
(0, 0, ar_line),
|
||||||
|
(0, 0, income_line1),
|
||||||
|
(0, 0, income_line2),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_move_template_normal(self):
|
||||||
|
""" Test normal case, input amount 300 """
|
||||||
|
with Form(self.env["account.move.template.run"]) as f:
|
||||||
|
f.template_id = self.move_template
|
||||||
|
template_run = f.save()
|
||||||
|
template_run.load_lines()
|
||||||
|
template_run.line_ids[0].amount = 300
|
||||||
|
res = template_run.generate_move()
|
||||||
|
move = self.Move.browse(res["res_id"])
|
||||||
|
self.assertRecordValues(
|
||||||
|
move.line_ids.sorted("credit"),
|
||||||
|
[
|
||||||
|
{"account_id": self.ar_account_id.id, "credit": 0.0, "debit": 300.0},
|
||||||
|
{
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"credit": 100.0,
|
||||||
|
"debit": 0.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"credit": 200.0,
|
||||||
|
"debit": 0.0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_move_template_optional(self):
|
||||||
|
""" Test optional case, input amount -300, expect optional account """
|
||||||
|
with Form(self.env["account.move.template.run"]) as f:
|
||||||
|
f.template_id = self.move_template
|
||||||
|
template_run = f.save()
|
||||||
|
template_run.load_lines()
|
||||||
|
template_run.line_ids[0].amount = -300 # Negative amount
|
||||||
|
res = template_run.generate_move()
|
||||||
|
move = self.Move.browse(res["res_id"])
|
||||||
|
self.assertRecordValues(
|
||||||
|
move.line_ids.sorted("debit"),
|
||||||
|
[
|
||||||
|
{"account_id": self.ap_account_id.id, "credit": 300.0, "debit": 0.0},
|
||||||
|
{
|
||||||
|
"account_id": self.expense_account_id.id,
|
||||||
|
"credit": 0.0,
|
||||||
|
"debit": 100.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account_id": self.expense_account_id.id,
|
||||||
|
"credit": 0.0,
|
||||||
|
"debit": 200.0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_move_template_overwrite(self):
|
||||||
|
""" Test case overwrite, amount = 3000, no need to manual input """
|
||||||
|
# Test for error when debit is not a valid field
|
||||||
|
with Form(self.env["account.move.template.run"]) as f:
|
||||||
|
f.template_id = self.move_template
|
||||||
|
f.overwrite = str(
|
||||||
|
{
|
||||||
|
"L0": {
|
||||||
|
"partner_id": self.partners[0].id,
|
||||||
|
"amount": 3000,
|
||||||
|
"debit": 3000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
template_run = f.save()
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
template_run.load_lines()
|
||||||
|
# Assign only on valid fields, and load_lines again
|
||||||
|
with Form(self.env["account.move.template.run"]) as f:
|
||||||
|
f.template_id = self.move_template
|
||||||
|
f.overwrite = str(
|
||||||
|
{
|
||||||
|
"L0": {"partner_id": self.partners[0].id, "amount": 3000},
|
||||||
|
"L1": {"partner_id": self.partners[1].id},
|
||||||
|
"L2": {"partner_id": self.partners[2].id},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
template_run = f.save()
|
||||||
|
res = template_run.load_lines()
|
||||||
|
self.assertEqual(template_run.line_ids[0].partner_id, self.partners[0])
|
||||||
|
self.assertEqual(template_run.line_ids[0].amount, 3000)
|
||||||
|
res = template_run.with_context(res["context"]).generate_move()
|
||||||
|
move = self.Move.browse(res["res_id"])
|
||||||
|
self.assertRecordValues(
|
||||||
|
move.line_ids.sorted("credit"),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"partner_id": self.partners[0].id,
|
||||||
|
"account_id": self.ar_account_id.id,
|
||||||
|
"credit": 0.0,
|
||||||
|
"debit": 3000.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"partner_id": self.partners[1].id,
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"credit": 1000.0,
|
||||||
|
"debit": 0.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"partner_id": self.partners[2].id,
|
||||||
|
"account_id": self.income_account_id.id,
|
||||||
|
"credit": 2000.0,
|
||||||
|
"debit": 0.0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
@ -7,6 +7,10 @@
|
|||||||
<tree>
|
<tree>
|
||||||
<field name="sequence" />
|
<field name="sequence" />
|
||||||
<field name="account_id" domain="[('company_id', '=', company_id)]" />
|
<field name="account_id" domain="[('company_id', '=', company_id)]" />
|
||||||
|
<field
|
||||||
|
name="opt_account_id"
|
||||||
|
domain="[('company_id', '=', company_id)]"
|
||||||
|
/>
|
||||||
<field name="partner_id" />
|
<field name="partner_id" />
|
||||||
<field name="name" />
|
<field name="name" />
|
||||||
<field
|
<field
|
||||||
@ -47,6 +51,10 @@
|
|||||||
name="account_id"
|
name="account_id"
|
||||||
domain="[('company_id', '=', company_id)]"
|
domain="[('company_id', '=', company_id)]"
|
||||||
/>
|
/>
|
||||||
|
<field
|
||||||
|
name="opt_account_id"
|
||||||
|
domain="[('company_id', '=', company_id)]"
|
||||||
|
/>
|
||||||
<field name="partner_id" />
|
<field name="partner_id" />
|
||||||
<field name="payment_term_id" />
|
<field name="payment_term_id" />
|
||||||
<field name="company_id" invisible="1" />
|
<field name="company_id" invisible="1" />
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
# Copyright 2015-2019 See manifest
|
# Copyright 2015-2019 See manifest
|
||||||
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
||||||
|
from ast import literal_eval
|
||||||
|
|
||||||
from odoo import _, fields, models
|
from odoo import _, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.tools import float_is_zero
|
from odoo.tools import float_is_zero
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
"res.company",
|
"res.company",
|
||||||
required=True,
|
required=True,
|
||||||
readonly=True,
|
readonly=True,
|
||||||
default=lambda self: self.env["res.company"]._company_default_get(),
|
default=lambda self: self.env.company,
|
||||||
)
|
)
|
||||||
partner_id = fields.Many2one(
|
partner_id = fields.Many2one(
|
||||||
"res.partner",
|
"res.partner",
|
||||||
@ -33,6 +34,13 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
readonly=True,
|
readonly=True,
|
||||||
default="select_template",
|
default="select_template",
|
||||||
)
|
)
|
||||||
|
overwrite = fields.Text(
|
||||||
|
help="""
|
||||||
|
Valid dictionary to overwrite template lines:
|
||||||
|
{'L1': {'partner_id': 1, 'amount': 100, 'name': 'some label'},
|
||||||
|
'L2': {'partner_id': 2, 'amount': 200, 'name': 'some label 2'}, }
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
def _prepare_wizard_line(self, tmpl_line):
|
def _prepare_wizard_line(self, tmpl_line):
|
||||||
vals = {
|
vals = {
|
||||||
@ -57,6 +65,8 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
# STEP 1
|
# STEP 1
|
||||||
def load_lines(self):
|
def load_lines(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
# Verify and get overwrite dict
|
||||||
|
overwrite_vals = self._get_overwrite_vals()
|
||||||
amtlro = self.env["account.move.template.line.run"]
|
amtlro = self.env["account.move.template.line.run"]
|
||||||
if self.company_id != self.template_id.company_id:
|
if self.company_id != self.template_id.company_id:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
@ -86,8 +96,75 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
action = self.env.ref("account_move_template.account_move_template_run_action")
|
action = self.env.ref("account_move_template.account_move_template_run_action")
|
||||||
result = action.read()[0]
|
result = action.read()[0]
|
||||||
result.update({"res_id": self.id, "context": self.env.context})
|
result.update({"res_id": self.id, "context": self.env.context})
|
||||||
|
|
||||||
|
# Overwrite self.line_ids to show overwrite values
|
||||||
|
self._overwrite_line(overwrite_vals)
|
||||||
|
# Pass context furtner to generate_move function, only readonly field
|
||||||
|
for key in overwrite_vals.keys():
|
||||||
|
overwrite_vals[key].pop("amount", None)
|
||||||
|
context = result.get("context", {}).copy()
|
||||||
|
context.update({"overwrite": overwrite_vals})
|
||||||
|
result["context"] = context
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _get_valid_keys(self):
|
||||||
|
return ["partner_id", "amount", "name", "date_maturity"]
|
||||||
|
|
||||||
|
def _get_overwrite_vals(self):
|
||||||
|
""" valid_dict = {
|
||||||
|
'L1': {'partner_id': 1, 'amount': 10},
|
||||||
|
'L2': {'partner_id': 2, 'amount': 20},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
valid_keys = self._get_valid_keys()
|
||||||
|
overwrite_vals = self.overwrite or "{}"
|
||||||
|
try:
|
||||||
|
overwrite_vals = literal_eval(overwrite_vals)
|
||||||
|
assert isinstance(overwrite_vals, dict)
|
||||||
|
except (SyntaxError, ValueError, AssertionError):
|
||||||
|
raise ValidationError(_("Overwrite value must be a valid python dict"))
|
||||||
|
# First level keys must be L1, L2, ...
|
||||||
|
keys = overwrite_vals.keys()
|
||||||
|
if list(filter(lambda x: x[:1] != "L" or not x[1:].isdigit(), keys)):
|
||||||
|
raise ValidationError(_("Keys must be line sequence, i..e, L1, L2, ..."))
|
||||||
|
# Second level keys must be a valid keys
|
||||||
|
try:
|
||||||
|
if dict(
|
||||||
|
filter(lambda x: set(overwrite_vals[x].keys()) - set(valid_keys), keys)
|
||||||
|
):
|
||||||
|
raise ValidationError(
|
||||||
|
_("Valid fields to overwrite are %s") % valid_keys
|
||||||
|
)
|
||||||
|
except ValidationError as e:
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
msg = """
|
||||||
|
valid_dict = {
|
||||||
|
'L1': {'partner_id': 1, 'amount': 10},
|
||||||
|
'L2': {'partner_id': 2, 'amount': 20},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
raise ValidationError(_("Invalid dictionary: {}\n{}".format(e, msg)))
|
||||||
|
return overwrite_vals
|
||||||
|
|
||||||
|
def _safe_vals(self, model, vals):
|
||||||
|
obj = self.env[model]
|
||||||
|
copy_vals = vals.copy()
|
||||||
|
invalid_keys = list(
|
||||||
|
set(list(vals.keys())) - set(list(dict(obj._fields).keys()))
|
||||||
|
)
|
||||||
|
for key in invalid_keys:
|
||||||
|
copy_vals.pop(key)
|
||||||
|
return copy_vals
|
||||||
|
|
||||||
|
def _overwrite_line(self, overwrite_vals):
|
||||||
|
self.ensure_one()
|
||||||
|
for line in self.line_ids:
|
||||||
|
vals = overwrite_vals.get("L{}".format(line.sequence), {})
|
||||||
|
safe_vals = self._safe_vals(line._name, vals)
|
||||||
|
line.write(safe_vals)
|
||||||
|
|
||||||
# STEP 2
|
# STEP 2
|
||||||
def generate_move(self):
|
def generate_move(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@ -138,7 +215,7 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
def _prepare_move_line(self, line, amount):
|
def _prepare_move_line(self, line, amount):
|
||||||
date_maturity = False
|
date_maturity = False
|
||||||
if line.payment_term_id:
|
if line.payment_term_id:
|
||||||
pterm_list = line.payment_term_id.compute(value=1, date_ref=self.date)[0]
|
pterm_list = line.payment_term_id.compute(value=1, date_ref=self.date)
|
||||||
date_maturity = max(l[0] for l in pterm_list)
|
date_maturity = max(l[0] for l in pterm_list)
|
||||||
debit = line.move_line_type == "dr"
|
debit = line.move_line_type == "dr"
|
||||||
values = {
|
values = {
|
||||||
@ -165,8 +242,24 @@ class AccountMoveTemplateRun(models.TransientModel):
|
|||||||
values["tag_ids"] = [(6, 0, atrl_ids.mapped("tag_ids").ids)]
|
values["tag_ids"] = [(6, 0, atrl_ids.mapped("tag_ids").ids)]
|
||||||
if line.tax_repartition_line_id:
|
if line.tax_repartition_line_id:
|
||||||
values["tag_ids"] = [(6, 0, line.tax_repartition_line_id.tag_ids.ids)]
|
values["tag_ids"] = [(6, 0, line.tax_repartition_line_id.tag_ids.ids)]
|
||||||
|
# With overwrite options
|
||||||
|
overwrite = self._context.get("overwrite", {})
|
||||||
|
move_line_vals = overwrite.get("L{}".format(line.sequence), {})
|
||||||
|
values.update(move_line_vals)
|
||||||
|
# Use optional account, when amount is negative
|
||||||
|
self._update_account_on_negative(line, values)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
def _update_account_on_negative(self, line, vals):
|
||||||
|
if not line.opt_account_id:
|
||||||
|
return
|
||||||
|
for key in ["debit", "credit"]:
|
||||||
|
if vals[key] < 0:
|
||||||
|
ikey = (key == "debit") and "credit" or "debit"
|
||||||
|
vals["account_id"] = line.opt_account_id.id
|
||||||
|
vals[ikey] = abs(vals[key])
|
||||||
|
vals[key] = 0
|
||||||
|
|
||||||
|
|
||||||
class AccountMoveTemplateLineRun(models.TransientModel):
|
class AccountMoveTemplateLineRun(models.TransientModel):
|
||||||
_name = "account.move.template.line.run"
|
_name = "account.move.template.line.run"
|
||||||
|
@ -12,6 +12,13 @@
|
|||||||
domain="[('company_id', '=', company_id)]"
|
domain="[('company_id', '=', company_id)]"
|
||||||
attrs="{'readonly': [('state', '=', 'set_lines')]}"
|
attrs="{'readonly': [('state', '=', 'set_lines')]}"
|
||||||
/>
|
/>
|
||||||
|
<field
|
||||||
|
name="overwrite"
|
||||||
|
widget="ace"
|
||||||
|
options="{'mode': 'python'}"
|
||||||
|
attrs="{'invisible': [('state', '=', 'set_lines')]}"
|
||||||
|
placeholder="Add an internal note here..."
|
||||||
|
/>
|
||||||
<field name="company_id" groups="base.group_multi_company" />
|
<field name="company_id" groups="base.group_multi_company" />
|
||||||
<field name="date" states="set_lines" />
|
<field name="date" states="set_lines" />
|
||||||
<field name="journal_id" states="set_lines" />
|
<field name="journal_id" states="set_lines" />
|
||||||
|
Loading…
Reference in New Issue
Block a user