Compare commits
223 Commits
12.0-imp-b
...
14.0-accou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a59c2e774a | ||
|
|
f166fe93be | ||
|
|
b8a6cbcfea | ||
|
|
f6b10a7caa | ||
|
|
371229e9e5 | ||
|
|
1dbfd23524 | ||
|
|
9a9459f013 | ||
|
|
5a58ae0d9a | ||
|
|
b162227645 | ||
|
|
3c24e94122 | ||
|
|
400f316c7f | ||
|
|
86af01667a | ||
|
|
8967bf289a | ||
|
|
1fdaf52787 | ||
|
|
6907302f8e | ||
|
|
a3e23ab5e7 | ||
|
|
a850586716 | ||
|
|
fdef51ea57 | ||
|
|
806389e04b | ||
|
|
c12b496004 | ||
|
|
447b0107be | ||
|
|
f9a7983d71 | ||
|
|
870746b965 | ||
|
|
f22c6522d5 | ||
|
|
1ef97629b7 | ||
|
|
77a372b3ca | ||
|
|
221b090cc8 | ||
|
|
e4a2ff5bd4 | ||
|
|
c1d334b109 | ||
|
|
c7c9b2d341 | ||
|
|
eddab6020a | ||
|
|
d1181ca91d | ||
|
|
df673718e5 | ||
|
|
bb83765ee2 | ||
|
|
88615a0774 | ||
|
|
05e649fa86 | ||
|
|
08118ec4f5 | ||
|
|
6d496ba302 | ||
|
|
40b79890fe | ||
|
|
58b8d300b8 | ||
|
|
cdcf4eb406 | ||
|
|
a22f79ef44 | ||
|
|
99dd4de4f7 | ||
|
|
f3d6b67043 | ||
|
|
1963af114b | ||
|
|
1da4c40927 | ||
|
|
edc9db5839 | ||
|
|
882d068f1a | ||
|
|
878db1d0a1 | ||
|
|
600acd2f26 | ||
|
|
059c3b4a09 | ||
|
|
895e1d9dd0 | ||
|
|
0b3ffc804f | ||
|
|
c0a03dbb0e | ||
|
|
6e5f263283 | ||
|
|
d4ebbb28d9 | ||
|
|
7dd204e57e | ||
|
|
e1a84973da | ||
|
|
13e68ac0f5 | ||
|
|
3b17c2e5fb | ||
|
|
0be112dc84 | ||
|
|
ce2255623d | ||
|
|
2854d4fdda | ||
|
|
6c51a92acc | ||
|
|
f3910ab528 | ||
|
|
05374c4b4a | ||
|
|
279dc7c6c0 | ||
|
|
f6ddbb48ac | ||
|
|
d3bddf5fda | ||
|
|
6f3a468a7c | ||
|
|
0bfa960153 | ||
|
|
a520ccff51 | ||
|
|
fa7611eb08 | ||
|
|
b2eda2a23b | ||
|
|
74ff1b5cb5 | ||
|
|
a7b4ed65eb | ||
|
|
7dfb32c2d6 | ||
|
|
d081bb0fd2 | ||
|
|
f3fdbec140 | ||
|
|
ac54c5cc75 | ||
|
|
78c11411c3 | ||
|
|
28ce11b216 | ||
|
|
00e034dacf | ||
|
|
8510b9518a | ||
|
|
15ef5df155 | ||
|
|
45500f5bd8 | ||
|
|
a28a853f45 | ||
|
|
82b9a1e502 | ||
|
|
b05abba064 | ||
|
|
75e3463a76 | ||
|
|
3066c0545d | ||
|
|
62a84469c8 | ||
|
|
00339e44b6 | ||
|
|
d1ae620079 | ||
|
|
3db9f0f096 | ||
|
|
732ee2c55b | ||
|
|
e08d658b25 | ||
|
|
2a7ec92a37 | ||
|
|
dc30ce4e69 | ||
|
|
32d45d228b | ||
|
|
939de0c9bd | ||
|
|
48a19b8f97 | ||
|
|
8a2e662d43 | ||
|
|
de2e5f2121 | ||
|
|
42e014bcb1 | ||
|
|
e70e3b23cf | ||
|
|
50b4944c8b | ||
|
|
96bfda6e1b | ||
|
|
cfb58ed80f | ||
|
|
91e9c1fe33 | ||
|
|
dff4e47cf5 | ||
|
|
452cc399c5 | ||
|
|
f4c22501a7 | ||
|
|
9e8874eb4b | ||
|
|
fc6c0384ed | ||
|
|
96bd915c4f | ||
|
|
f61296cafc | ||
|
|
b585d06489 | ||
|
|
cef81ad749 | ||
|
|
7c1a2fabd3 | ||
|
|
c3f72a9b68 | ||
|
|
a0d73834ad | ||
|
|
66ebc5c6ad | ||
|
|
5c06d79b69 | ||
|
|
034c89b399 | ||
|
|
6356171619 | ||
|
|
771001ca2e | ||
|
|
45d734badf | ||
|
|
0d4ff37786 | ||
|
|
9c30d4ef53 | ||
|
|
b2ce8f0aca | ||
|
|
183bba3752 | ||
|
|
92742dfc9d | ||
|
|
f30bf4791a | ||
|
|
ca6de3adf6 | ||
|
|
5e2d25f7c4 | ||
|
|
d64262099b | ||
|
|
6ad589d4bd | ||
|
|
5496aa38f8 | ||
|
|
b7c0b4720c | ||
|
|
bd25fe4866 | ||
|
|
c3da933e62 | ||
|
|
1d8a72828c | ||
|
|
20af679569 | ||
|
|
48dc98d08b | ||
|
|
835a7e3c35 | ||
|
|
a3cac9e82d | ||
|
|
a90965109d | ||
|
|
6682c9e048 | ||
|
|
5a9476422c | ||
|
|
bf27beadae | ||
|
|
3b67f46b4c | ||
|
|
dd00d34d43 | ||
|
|
94ca97d3f0 | ||
|
|
3d4354cd67 | ||
|
|
cb625d4e1a | ||
|
|
ee0dfb29d6 | ||
|
|
78ef2a0ebe | ||
|
|
977dae3eae | ||
|
|
210ac987a0 | ||
|
|
ad362a11ce | ||
|
|
c268e28b3d | ||
|
|
9aa4613dd7 | ||
|
|
4d177801c5 | ||
|
|
937a4e612f | ||
|
|
7e461670dd | ||
|
|
87341b2cf3 | ||
|
|
dc45655441 | ||
|
|
25df0f8837 | ||
|
|
0acc4a3439 | ||
|
|
dbc0f07caa | ||
|
|
aeaa699638 | ||
|
|
40dc5485a9 | ||
|
|
eb91ae6a48 | ||
|
|
82f5f56438 | ||
|
|
e8e2ccd1c2 | ||
|
|
6711001019 | ||
|
|
acb0c88ce1 | ||
|
|
444786c971 | ||
|
|
1783ceb26a | ||
|
|
4feb258814 | ||
|
|
75b8bef3c4 | ||
|
|
17549ca457 | ||
|
|
d72872eb90 | ||
|
|
86ec56b610 | ||
|
|
7110f5afcc | ||
|
|
92cf447add | ||
|
|
e060de7efc | ||
|
|
f818bb5895 | ||
|
|
44a4f795d0 | ||
|
|
e505e7e07f | ||
|
|
f3782cfa2b | ||
|
|
9592097487 | ||
|
|
03a57798b6 | ||
|
|
22e9234d23 | ||
|
|
ac89271d5b | ||
|
|
261442d903 | ||
|
|
d63c4b2433 | ||
|
|
4dadc8047e | ||
|
|
34ec0dfa27 | ||
|
|
7a6600431c | ||
|
|
341717b75d | ||
|
|
1061111f9b | ||
|
|
d28a40e035 | ||
|
|
3f06231c22 | ||
|
|
67d31f9658 | ||
|
|
1b931d066b | ||
|
|
f605b56a5e | ||
|
|
58f01d9673 | ||
|
|
8878ab5bd1 | ||
|
|
80f5341da0 | ||
|
|
a4ca584e90 | ||
|
|
4d81dee7b4 | ||
|
|
140217da6e | ||
|
|
a9a0a2a999 | ||
|
|
d7f3a70d48 | ||
|
|
1074fcba21 | ||
|
|
0ae76be885 | ||
|
|
59cdcbd173 | ||
|
|
7cf224cf23 | ||
|
|
d0d65ebbca | ||
|
|
ca10381be8 | ||
|
|
44b19dfe60 |
1
account_fiscal_position_payable_receivable/__init__.py
Normal file
1
account_fiscal_position_payable_receivable/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
23
account_fiscal_position_payable_receivable/__manifest__.py
Normal file
23
account_fiscal_position_payable_receivable/__manifest__.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# © 2016-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Account Fiscal Position Payable Receivable",
|
||||||
|
"version": "12.0.1.0.0",
|
||||||
|
"category": "Accounting & Finance",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"summary": "Configure payable/receivable accounts on fiscal positions",
|
||||||
|
"description": """
|
||||||
|
Account Fiscal Position Payable Receivable
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
This module allows to configure a special *Partner Receivable Account* and a special *Partner Payable Account* on fiscal positions. This is used in the onchange of the fiscal position of partners.
|
||||||
|
|
||||||
|
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
||||||
|
""",
|
||||||
|
"author": "Akretion",
|
||||||
|
"website": "http://www.akretion.com",
|
||||||
|
"depends": ["account"],
|
||||||
|
"data": ["views/account_fiscal_position_view.xml"],
|
||||||
|
"installable": False,
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from . import account_fiscal_position
|
||||||
|
from . import res_partner
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# © 2016-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class AccountFiscalPosition(models.Model):
|
||||||
|
_inherit = "account.fiscal.position"
|
||||||
|
|
||||||
|
receivable_account_id = fields.Many2one(
|
||||||
|
"account.account",
|
||||||
|
string="Partner Receivable Account",
|
||||||
|
company_dependent=True,
|
||||||
|
domain=[("internal_type", "=", "receivable")],
|
||||||
|
)
|
||||||
|
payable_account_id = fields.Many2one(
|
||||||
|
"account.account",
|
||||||
|
string="Partner Payable Account",
|
||||||
|
company_dependent=True,
|
||||||
|
domain=[("internal_type", "=", "payable")],
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# © 2016-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models, api
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = "res.partner"
|
||||||
|
|
||||||
|
@api.onchange("property_account_position_id")
|
||||||
|
def fiscal_position_receivable_payable_change(self):
|
||||||
|
fp = self.property_account_position_id
|
||||||
|
ipo = self.env["ir.property"]
|
||||||
|
if fp.receivable_account_id:
|
||||||
|
self.property_account_receivable_id = fp.receivable_account_id
|
||||||
|
else:
|
||||||
|
self.property_account_receivable_id = ipo.get(
|
||||||
|
"property_account_receivable_id", "res.partner"
|
||||||
|
)
|
||||||
|
if fp.payable_account_id:
|
||||||
|
self.property_account_payable_id = fp.payable_account_id
|
||||||
|
else:
|
||||||
|
self.property_account_payable_id = ipo.get(
|
||||||
|
"property_account_payable_id", "res.partner"
|
||||||
|
)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_position_form" model="ir.ui.view">
|
||||||
|
<field name="name">receivable_payable.fiscal_position_form</field>
|
||||||
|
<field name="model">account.fiscal.position</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_position_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="company_id" position="after">
|
||||||
|
<field name="receivable_account_id"/>
|
||||||
|
<field name="payable_account_id"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -20,5 +20,5 @@ This module has been written by Alexis de Lattre from Akretion
|
|||||||
'data': [
|
'data': [
|
||||||
'account_invoice_view.xml',
|
'account_invoice_view.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,5 +17,5 @@
|
|||||||
'wizard/account_invoice_update_view.xml',
|
'wizard/account_invoice_update_view.xml',
|
||||||
'views/account_invoice.xml',
|
'views/account_invoice.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ This modules adds the following functions:
|
|||||||
* don't attach PDF upon invoice report generation on supplier invoices/refunds
|
* don't attach PDF upon invoice report generation on supplier invoices/refunds
|
||||||
* Add filter on debit and credit amount for Move Lines
|
* Add filter on debit and credit amount for Move Lines
|
||||||
* Add supplier invoice number in invoice tree view
|
* Add supplier invoice number in invoice tree view
|
||||||
|
* Add date in outstanding payment widget on invoice form view (requires `odoo PR 84180 <https://github.com/odoo/odoo/pull/84180>`_)
|
||||||
|
|
||||||
Together with this module, I recommend the use of the following modules:
|
Together with this module, I recommend the use of the following modules:
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
from . import account
|
from . import models
|
||||||
#from . import account_invoice_report
|
|
||||||
from . import partner
|
|
||||||
from . import wizard
|
from . import wizard
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Copyright 2015-2019 Akretion (http://www.akretion.com)
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Account Usability',
|
'name': 'Account Usability',
|
||||||
'version': '12.0.1.0.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Accounting & Finance',
|
'category': 'Accounting & Finance',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': 'Small usability enhancements in account module',
|
'summary': 'Small usability enhancements in account module',
|
||||||
@@ -17,12 +17,28 @@
|
|||||||
# in v12, I may create a module only for group_nobody
|
# in v12, I may create a module only for group_nobody
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'account_view.xml',
|
'views/account_account_type.xml',
|
||||||
'account_report.xml',
|
'views/account_account.xml',
|
||||||
'account_invoice_report_view.xml',
|
'views/account_group.xml',
|
||||||
'partner_view.xml',
|
'views/account_analytic_account.xml',
|
||||||
|
'views/account_analytic_group.xml',
|
||||||
|
'views/account_bank_statement.xml',
|
||||||
|
'views/account_invoice_report.xml',
|
||||||
|
'views/account_journal.xml',
|
||||||
|
'views/account_move.xml',
|
||||||
|
'views/account_menu.xml',
|
||||||
|
'views/account_tax.xml',
|
||||||
|
'views/product.xml',
|
||||||
|
'views/res_config_settings.xml',
|
||||||
|
'views/res_partner.xml',
|
||||||
|
'views/res_company.xml',
|
||||||
|
'views/account_report.xml',
|
||||||
'wizard/account_invoice_mark_sent_view.xml',
|
'wizard/account_invoice_mark_sent_view.xml',
|
||||||
|
'wizard/account_group_generate_view.xml',
|
||||||
|
'wizard/account_payment_register_views.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
'report/invoice_report.xml',
|
'report/invoice_report.xml',
|
||||||
],
|
],
|
||||||
|
'qweb': ['static/src/xml/account_payment.xml'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,706 +0,0 @@
|
|||||||
# Copyright 2015-2019 Akretion (http://www.akretion.com)
|
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import models, fields, api, _
|
|
||||||
from odoo.tools import float_compare, float_is_zero
|
|
||||||
from odoo.tools.misc import formatLang
|
|
||||||
from odoo.exceptions import UserError, ValidationError
|
|
||||||
from odoo.osv import expression
|
|
||||||
from odoo import SUPERUSER_ID
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class AccountInvoice(models.Model):
|
|
||||||
_inherit = 'account.invoice'
|
|
||||||
|
|
||||||
origin = fields.Char(track_visibility='onchange')
|
|
||||||
reference = fields.Char(track_visibility='onchange')
|
|
||||||
sent = fields.Boolean(track_visibility='onchange')
|
|
||||||
date_invoice = fields.Date(track_visibility='onchange')
|
|
||||||
date_due = fields.Date(track_visibility='onchange')
|
|
||||||
payment_term_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
account_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
journal_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
partner_bank_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
fiscal_position_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
amount_total = fields.Monetary(track_visibility='onchange')
|
|
||||||
# I want to see the number of cancelled invoice in chatter
|
|
||||||
move_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
# for invoice report
|
|
||||||
has_discount = fields.Boolean(
|
|
||||||
compute='_compute_has_discount', readonly=True)
|
|
||||||
# has_attachment is useful for those who use attachment to archive
|
|
||||||
# supplier invoices. It allows them to find supplier invoices
|
|
||||||
# that don't have any attachment
|
|
||||||
has_attachment = fields.Boolean(
|
|
||||||
compute='_compute_has_attachment',
|
|
||||||
search='_search_has_attachment', readonly=True)
|
|
||||||
sale_dates = fields.Char(
|
|
||||||
compute="_compute_sales_dates", readonly=True,
|
|
||||||
help="This information appears on invoice qweb report "
|
|
||||||
"(you may use it for your own report)")
|
|
||||||
|
|
||||||
def _compute_has_discount(self):
|
|
||||||
prec = self.env['decimal.precision'].precision_get('Discount')
|
|
||||||
for inv in self:
|
|
||||||
has_discount = False
|
|
||||||
for line in inv.invoice_line_ids:
|
|
||||||
if not float_is_zero(line.discount, precision_digits=prec):
|
|
||||||
has_discount = True
|
|
||||||
break
|
|
||||||
inv.has_discount = has_discount
|
|
||||||
|
|
||||||
def _compute_has_attachment(self):
|
|
||||||
iao = self.env['ir.attachment']
|
|
||||||
for inv in self:
|
|
||||||
if iao.search([
|
|
||||||
('res_model', '=', 'account.invoice'),
|
|
||||||
('res_id', '=', inv.id),
|
|
||||||
('type', '=', 'binary'),
|
|
||||||
('company_id', '=', inv.company_id.id)], limit=1):
|
|
||||||
inv.has_attachment = True
|
|
||||||
else:
|
|
||||||
inv.has_attachment = False
|
|
||||||
|
|
||||||
def _search_has_attachment(self, operator, value):
|
|
||||||
att_inv_ids = {}
|
|
||||||
if operator == '=':
|
|
||||||
search_res = self.env['ir.attachment'].search_read([
|
|
||||||
('res_model', '=', 'account.invoice'),
|
|
||||||
('type', '=', 'binary'),
|
|
||||||
('res_id', '!=', False)], ['res_id'])
|
|
||||||
for att in search_res:
|
|
||||||
att_inv_ids[att['res_id']] = True
|
|
||||||
res = [('id', value and 'in' or 'not in', list(att_inv_ids))]
|
|
||||||
return res
|
|
||||||
|
|
||||||
# when you have an invoice created from a lot of sale orders, the 'name'
|
|
||||||
# field is very large, which makes the name_get() of that invoice very big
|
|
||||||
# which screws-up the form view of that invoice because of the link at the
|
|
||||||
# top of the screen
|
|
||||||
# That's why we have to cut the name_get() when it's too long
|
|
||||||
def name_get(self):
|
|
||||||
old_res = super().name_get()
|
|
||||||
res = []
|
|
||||||
for old_re in old_res:
|
|
||||||
name = old_re[1]
|
|
||||||
if name and len(name) > 100:
|
|
||||||
# nice cut
|
|
||||||
name = u'%s ...' % ', '.join(name.split(', ')[:3])
|
|
||||||
# if not enough, hard cut
|
|
||||||
if len(name) > 120:
|
|
||||||
name = u'%s ...' % old_re[1][:120]
|
|
||||||
res.append((old_re[0], name))
|
|
||||||
return res
|
|
||||||
|
|
||||||
# I really hate to see a "/" in the 'name' field of the account.move.line
|
|
||||||
# generated from customer invoices linked to the partners' account because:
|
|
||||||
# 1) the label of an account move line is an important field, we can't
|
|
||||||
# write a rubbish '/' in it !
|
|
||||||
# 2) the 'name' field of the account.move.line is used in the overdue
|
|
||||||
# letter, and '/' is not meaningful for our customer !
|
|
||||||
# TODO mig to v12
|
|
||||||
# def action_move_create(self):
|
|
||||||
# res = super().action_move_create()
|
|
||||||
# for inv in self:
|
|
||||||
# self._cr.execute(
|
|
||||||
# "UPDATE account_move_line SET name= "
|
|
||||||
# "CASE WHEN name='/' THEN %s "
|
|
||||||
# "ELSE %s||' - '||name END "
|
|
||||||
# "WHERE move_id=%s", (inv.number, inv.number, inv.move_id.id))
|
|
||||||
# self.invalidate_cache()
|
|
||||||
# return res
|
|
||||||
|
|
||||||
def delete_lines_qty_zero(self):
|
|
||||||
lines = self.env['account.invoice.line'].search([
|
|
||||||
('invoice_id', 'in', self.ids), ('quantity', '=', 0)])
|
|
||||||
lines.unlink()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def fix_invoice_attachment_filename(self):
|
|
||||||
# This script is designed to fix attachment of invoices
|
|
||||||
# badly generated by Odoo v8. I found this problem in Nov 2018 at
|
|
||||||
# Encres Dubuit when investigating a bug where Odoo would create a
|
|
||||||
# new attachment when printing an old invoice that already had the
|
|
||||||
# PDF of the invoice as attachment
|
|
||||||
logger.info('START fix customer invoice attachment filename')
|
|
||||||
# Run this script as admin to fix problem in all companies
|
|
||||||
self = self.sudo()
|
|
||||||
attachs = self.env['ir.attachment'].search([
|
|
||||||
('res_model', '=', 'account.invoice'),
|
|
||||||
('res_id', '!=', False),
|
|
||||||
('type', '=', 'binary'),
|
|
||||||
('name', '=like', 'INV%.pdf'),
|
|
||||||
('datas_fname', '=like', 'INV%.pdf.pdf')])
|
|
||||||
for attach in attachs:
|
|
||||||
inv = self.browse(attach.res_id)
|
|
||||||
if inv.type in ('out_invoice', 'out_refund'):
|
|
||||||
attach.datas_fname = attach.name
|
|
||||||
logger.info(
|
|
||||||
'Fixed field datas_fname of attachment ID %s name %s',
|
|
||||||
attach.id, attach.name)
|
|
||||||
logger.info('END fix customer invoice attachment filename')
|
|
||||||
|
|
||||||
# for report
|
|
||||||
def py3o_lines_layout(self):
|
|
||||||
self.ensure_one()
|
|
||||||
res = []
|
|
||||||
has_sections = False
|
|
||||||
subtotal = 0.0
|
|
||||||
sign = self.type == 'out_refund' and -1 or 1
|
|
||||||
for line in self.invoice_line_ids:
|
|
||||||
if line.display_type == 'line_section':
|
|
||||||
# insert line
|
|
||||||
if has_sections:
|
|
||||||
res.append({'subtotal': subtotal})
|
|
||||||
subtotal = 0.0 # reset counter
|
|
||||||
has_sections = True
|
|
||||||
else:
|
|
||||||
if not line.display_type:
|
|
||||||
subtotal += line.price_subtotal * sign
|
|
||||||
res.append({'line': line})
|
|
||||||
if has_sections: # insert last subtotal line
|
|
||||||
res.append({'subtotal': subtotal})
|
|
||||||
# res:
|
|
||||||
# [
|
|
||||||
# {'line': account_invoice_line(1) with display_type=='line_section'},
|
|
||||||
# {'line': account_invoice_line(2) without display_type},
|
|
||||||
# {'line': account_invoice_line(3) without display_type},
|
|
||||||
# {'line': account_invoice_line(4) with display_type=='line_note'},
|
|
||||||
# {'subtotal': 8932.23},
|
|
||||||
# ]
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _compute_sales_dates(self):
|
|
||||||
""" French law requires to set sale order dates into invoice
|
|
||||||
returned string: "sale1 (date1), sale2 (date2) ..."
|
|
||||||
"""
|
|
||||||
for inv in self:
|
|
||||||
sales = inv.invoice_line_ids.mapped(
|
|
||||||
'sale_line_ids').mapped('order_id')
|
|
||||||
lang = inv.partner_id.commercial_partner_id.lang
|
|
||||||
date_format = self.env["res.lang"]._lang_get(
|
|
||||||
lang or "").date_format
|
|
||||||
dates = ["%s%s" % (
|
|
||||||
x.name,
|
|
||||||
x.confirmation_date and " (%s)" %
|
|
||||||
# only when confirmation_date display it
|
|
||||||
x.confirmation_date.strftime(date_format) or "")
|
|
||||||
for x in sales]
|
|
||||||
inv.sale_dates = ", ".join(dates)
|
|
||||||
|
|
||||||
|
|
||||||
class AccountInvoiceLine(models.Model):
|
|
||||||
_inherit = 'account.invoice.line'
|
|
||||||
|
|
||||||
# In the 'account' module, we have related stored field for:
|
|
||||||
# company_id, partner_id, currency_id
|
|
||||||
invoice_type = fields.Selection(store=True)
|
|
||||||
date_invoice = fields.Date(
|
|
||||||
related='invoice_id.date_invoice', store=True, readonly=True)
|
|
||||||
commercial_partner_id = fields.Many2one(
|
|
||||||
related='invoice_id.partner_id.commercial_partner_id',
|
|
||||||
store=True, readonly=True, compute_sudo=True)
|
|
||||||
state = fields.Selection(
|
|
||||||
related='invoice_id.state', store=True, readonly=True,
|
|
||||||
string='Invoice State')
|
|
||||||
invoice_number = fields.Char(
|
|
||||||
related='invoice_id.move_id.name', store=True, readonly=True,
|
|
||||||
string='Invoice Number')
|
|
||||||
|
|
||||||
|
|
||||||
class AccountJournal(models.Model):
|
|
||||||
_inherit = 'account.journal'
|
|
||||||
|
|
||||||
hide_bank_statement_balance = fields.Boolean(
|
|
||||||
string='Hide Bank Statement Balance',
|
|
||||||
help="You may want to enable this option when your bank "
|
|
||||||
"journal is generated from a bank statement file that "
|
|
||||||
"doesn't handle start/end balance (QIF for instance) and "
|
|
||||||
"you don't want to enter the start/end balance manually: it "
|
|
||||||
"will prevent the display of wrong information in the accounting "
|
|
||||||
"dashboard and on bank statements.")
|
|
||||||
|
|
||||||
@api.depends(
|
|
||||||
'name', 'currency_id', 'company_id', 'company_id.currency_id', 'code')
|
|
||||||
def name_get(self):
|
|
||||||
res = []
|
|
||||||
if self._context.get('journal_show_code_only'):
|
|
||||||
for journal in self:
|
|
||||||
res.append((journal.id, journal.code))
|
|
||||||
return res
|
|
||||||
else:
|
|
||||||
for journal in self:
|
|
||||||
currency = journal.currency_id or\
|
|
||||||
journal.company_id.currency_id
|
|
||||||
name = "[%s] %s (%s)" % (
|
|
||||||
journal.code, journal.name, currency.name)
|
|
||||||
res.append((journal.id, name))
|
|
||||||
return res
|
|
||||||
|
|
||||||
@api.constrains('default_credit_account_id', 'default_debit_account_id')
|
|
||||||
def _check_account_type_on_bank_journal(self):
|
|
||||||
bank_acc_type = self.env.ref('account.data_account_type_liquidity')
|
|
||||||
for jrl in self:
|
|
||||||
if jrl.type in ('bank', 'cash'):
|
|
||||||
if (
|
|
||||||
jrl.default_debit_account_id and
|
|
||||||
jrl.default_debit_account_id.user_type_id !=
|
|
||||||
bank_acc_type):
|
|
||||||
raise ValidationError(_(
|
|
||||||
"On journal '%s', the default debit account '%s' "
|
|
||||||
"should be configured with Type = 'Bank and Cash'.")
|
|
||||||
% (jrl.display_name,
|
|
||||||
jrl.default_debit_account_id.display_name))
|
|
||||||
if (
|
|
||||||
jrl.default_credit_account_id and
|
|
||||||
jrl.default_credit_account_id.user_type_id !=
|
|
||||||
bank_acc_type):
|
|
||||||
raise ValidationError(_(
|
|
||||||
"On journal '%s', the default credit account '%s' "
|
|
||||||
"should be configured with Type = 'Bank and Cash'.")
|
|
||||||
% (jrl.display_name,
|
|
||||||
jrl.default_credit_account_id.display_name))
|
|
||||||
|
|
||||||
|
|
||||||
class AccountAccount(models.Model):
|
|
||||||
_inherit = 'account.account'
|
|
||||||
|
|
||||||
@api.depends('name', 'code')
|
|
||||||
def name_get(self):
|
|
||||||
if self._context.get('account_account_show_code_only'):
|
|
||||||
res = []
|
|
||||||
for record in self:
|
|
||||||
res.append((record.id, record.code))
|
|
||||||
return res
|
|
||||||
else:
|
|
||||||
return super().name_get()
|
|
||||||
|
|
||||||
# https://github.com/odoo/odoo/issues/23040
|
|
||||||
# TODO mig to v12
|
|
||||||
def fix_bank_account_types(self):
|
|
||||||
aao = self.env['account.account']
|
|
||||||
companies = self.env['res.company'].search([])
|
|
||||||
if len(companies) > 1 and self.env.user.id != SUPERUSER_ID:
|
|
||||||
raise UserError(
|
|
||||||
"In multi-company setups, you should run this "
|
|
||||||
"script as admin user")
|
|
||||||
logger.info("START the script 'fix bank and cash account types'")
|
|
||||||
bank_type = self.env.ref('account.data_account_type_liquidity')
|
|
||||||
asset_type = self.env.ref('account.data_account_type_current_assets')
|
|
||||||
journals = self.env['account.journal'].search(
|
|
||||||
[('type', 'in', ('bank', 'cash'))], order='company_id')
|
|
||||||
journal_accounts_bank_type = aao
|
|
||||||
for journal in journals:
|
|
||||||
for account in [
|
|
||||||
journal.default_credit_account_id,
|
|
||||||
journal.default_debit_account_id]:
|
|
||||||
if account:
|
|
||||||
if account.user_type_id != bank_type:
|
|
||||||
account.user_type_id = bank_type.id
|
|
||||||
logger.info(
|
|
||||||
'Company %s: Account %s updated to Bank '
|
|
||||||
'and Cash type',
|
|
||||||
account.company_id.display_name, account.code)
|
|
||||||
if account not in journal_accounts_bank_type:
|
|
||||||
journal_accounts_bank_type += account
|
|
||||||
accounts = aao.search([
|
|
||||||
('user_type_id', '=', bank_type.id)], order='company_id, code')
|
|
||||||
for account in accounts:
|
|
||||||
if account not in journal_accounts_bank_type:
|
|
||||||
account.user_type_id = asset_type.id
|
|
||||||
logger.info(
|
|
||||||
'Company %s: Account %s updated to Current Asset type',
|
|
||||||
account.company_id.display_name, account.code)
|
|
||||||
logger.info("END of the script 'fix bank and cash account types'")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# TODO mig to v12
|
|
||||||
@api.model
|
|
||||||
def create_account_groups(self, level=2, name_prefix=u'Comptes '):
|
|
||||||
'''Should be launched by a script. Make sure the account_group module is installed
|
|
||||||
(the account_usability module doesn't depend on it currently'''
|
|
||||||
assert level >= 1
|
|
||||||
assert isinstance(level, int)
|
|
||||||
companies = self.env['res.company'].search([])
|
|
||||||
if len(companies) > 1:
|
|
||||||
logger.info(
|
|
||||||
'Multi-company detected: running script create_account_groups '
|
|
||||||
'as admin')
|
|
||||||
self = self.sudo()
|
|
||||||
ago = self.env['account.group']
|
|
||||||
groups = ago.search([])
|
|
||||||
if groups:
|
|
||||||
raise UserError(_("Some account groups already exists"))
|
|
||||||
accounts = self.search([])
|
|
||||||
struct = {'childs': {}}
|
|
||||||
for account in accounts:
|
|
||||||
assert len(account.code) > level
|
|
||||||
n = 1
|
|
||||||
parent = struct
|
|
||||||
gparent = False
|
|
||||||
while n <= level:
|
|
||||||
group_code = account.code[:n]
|
|
||||||
if group_code not in parent['childs']:
|
|
||||||
new_group = ago.create({
|
|
||||||
'name': u'%s%s' % (name_prefix or '', group_code),
|
|
||||||
'code_prefix': group_code,
|
|
||||||
'parent_id': gparent and gparent.id or False,
|
|
||||||
})
|
|
||||||
parent['childs'][group_code] = {'obj': new_group, 'childs': {}}
|
|
||||||
parent = parent['childs'][group_code]
|
|
||||||
gparent = parent['obj']
|
|
||||||
n += 1
|
|
||||||
account.group_id = gparent.id
|
|
||||||
|
|
||||||
|
|
||||||
class AccountAnalyticAccount(models.Model):
|
|
||||||
_inherit = 'account.analytic.account'
|
|
||||||
|
|
||||||
def name_get(self):
|
|
||||||
if self._context.get('analytic_account_show_code_only'):
|
|
||||||
res = []
|
|
||||||
for record in self:
|
|
||||||
res.append((record.id, record.code or record.name))
|
|
||||||
return res
|
|
||||||
else:
|
|
||||||
return super().name_get()
|
|
||||||
|
|
||||||
_sql_constraints = [(
|
|
||||||
'code_company_unique',
|
|
||||||
'unique(code, company_id)',
|
|
||||||
'An analytic account with the same code already '
|
|
||||||
'exists in the same company!')]
|
|
||||||
|
|
||||||
|
|
||||||
class AccountMove(models.Model):
|
|
||||||
_inherit = 'account.move'
|
|
||||||
|
|
||||||
default_move_line_name = fields.Char(
|
|
||||||
string='Default Label', states={'posted': [('readonly', True)]})
|
|
||||||
# By default, we can still modify "ref" when account move is posted
|
|
||||||
# which seems a bit lazy for me...
|
|
||||||
ref = fields.Char(states={'posted': [('readonly', True)]})
|
|
||||||
date = fields.Date(copy=False)
|
|
||||||
default_account_id = fields.Many2one(
|
|
||||||
related='journal_id.default_debit_account_id', readonly=True)
|
|
||||||
default_credit = fields.Float(
|
|
||||||
compute='_compute_default_credit_debit', readonly=True)
|
|
||||||
default_debit = fields.Float(
|
|
||||||
compute='_compute_default_credit_debit', readonly=True)
|
|
||||||
|
|
||||||
@api.depends('line_ids.credit', 'line_ids.debit')
|
|
||||||
def _compute_default_credit_debit(self):
|
|
||||||
for move in self:
|
|
||||||
total_debit = total_credit = default_debit = default_credit = 0.0
|
|
||||||
for l in move.line_ids:
|
|
||||||
total_debit += l.debit
|
|
||||||
total_credit += l.credit
|
|
||||||
# I could use float_compare, but I don't think it's really needed
|
|
||||||
# in this context
|
|
||||||
if total_debit > total_credit:
|
|
||||||
default_credit = total_debit - total_credit
|
|
||||||
else:
|
|
||||||
default_debit = total_credit - total_debit
|
|
||||||
move.default_credit = default_credit
|
|
||||||
move.default_debit = default_debit
|
|
||||||
|
|
||||||
|
|
||||||
class AccountMoveLine(models.Model):
|
|
||||||
_inherit = 'account.move.line'
|
|
||||||
# Native order:
|
|
||||||
# _order = "date desc, id desc"
|
|
||||||
# Problem: when you manually create a journal entry, the
|
|
||||||
# order of the lines is inverted when you save ! It is quite annoying for
|
|
||||||
# the user...
|
|
||||||
_order = "date desc, id asc"
|
|
||||||
|
|
||||||
# Update field only to add a string (there is no string in account module)
|
|
||||||
invoice_id = fields.Many2one(string='Invoice')
|
|
||||||
account_reconcile = fields.Boolean(
|
|
||||||
related='account_id.reconcile', readonly=True)
|
|
||||||
full_reconcile_id = fields.Many2one(string='Full Reconcile')
|
|
||||||
matched_debit_ids = fields.One2many(string='Partial Reconcile Debit')
|
|
||||||
matched_credit_ids = fields.One2many(string='Partial Reconcile Credit')
|
|
||||||
reconcile_string = fields.Char(
|
|
||||||
compute='_compute_reconcile_string', string='Reconcile', store=True)
|
|
||||||
|
|
||||||
@api.onchange('credit')
|
|
||||||
def _credit_onchange(self):
|
|
||||||
prec = self.env['decimal.precision'].precision_get('Account')
|
|
||||||
if (
|
|
||||||
not float_is_zero(self.credit, precision_digits=prec) and
|
|
||||||
not float_is_zero(self.debit, precision_digits=prec)):
|
|
||||||
self.debit = 0
|
|
||||||
|
|
||||||
@api.onchange('debit')
|
|
||||||
def _debit_onchange(self):
|
|
||||||
prec = self.env['decimal.precision'].precision_get('Account')
|
|
||||||
if (
|
|
||||||
not float_is_zero(self.debit, precision_digits=prec) and
|
|
||||||
not float_is_zero(self.credit, precision_digits=prec)):
|
|
||||||
self.credit = 0
|
|
||||||
|
|
||||||
@api.onchange('currency_id', 'amount_currency')
|
|
||||||
def _amount_currency_change(self):
|
|
||||||
prec = self.env['decimal.precision'].precision_get('Account')
|
|
||||||
if (
|
|
||||||
self.currency_id and
|
|
||||||
self.amount_currency and
|
|
||||||
float_is_zero(self.credit, precision_digits=prec) and
|
|
||||||
float_is_zero(self.debit, precision_digits=prec)):
|
|
||||||
date = self.date or None
|
|
||||||
amount_company_currency = self.currency_id.with_context(
|
|
||||||
date=date).compute(
|
|
||||||
self.amount_currency, self.env.user.company_id.currency_id)
|
|
||||||
precision = self.env['decimal.precision'].precision_get('Account')
|
|
||||||
if float_compare(
|
|
||||||
amount_company_currency, 0,
|
|
||||||
precision_digits=precision) == -1:
|
|
||||||
self.debit = amount_company_currency * -1
|
|
||||||
else:
|
|
||||||
self.credit = amount_company_currency
|
|
||||||
|
|
||||||
def show_account_move_form(self):
|
|
||||||
self.ensure_one()
|
|
||||||
action = self.env['ir.actions.act_window'].for_xml_id(
|
|
||||||
'account', 'action_move_line_form')
|
|
||||||
action.update({
|
|
||||||
'res_id': self.move_id.id,
|
|
||||||
'view_id': False,
|
|
||||||
'views': False,
|
|
||||||
'view_mode': 'form,tree',
|
|
||||||
})
|
|
||||||
return action
|
|
||||||
|
|
||||||
@api.depends(
|
|
||||||
'full_reconcile_id', 'matched_debit_ids', 'matched_credit_ids')
|
|
||||||
def _compute_reconcile_string(self):
|
|
||||||
for line in self:
|
|
||||||
rec_str = False
|
|
||||||
if line.full_reconcile_id:
|
|
||||||
rec_str = line.full_reconcile_id.name
|
|
||||||
else:
|
|
||||||
rec_str = ', '.join([
|
|
||||||
'a%d' % pr.id for pr in line.matched_debit_ids + line.matched_credit_ids])
|
|
||||||
line.reconcile_string = rec_str
|
|
||||||
|
|
||||||
|
|
||||||
class AccountPartialReconcile(models.Model):
|
|
||||||
_inherit = "account.partial.reconcile"
|
|
||||||
_rec_name = "id"
|
|
||||||
|
|
||||||
def name_get(self):
|
|
||||||
res = []
|
|
||||||
for rec in self:
|
|
||||||
# There is no seq for partial rec, so I simulate one with the ID
|
|
||||||
# Prefix for full rec: 'A' (upper case)
|
|
||||||
# Prefix for partial rec: 'a' (lower case)
|
|
||||||
amount_fmt = formatLang(self.env, rec.amount, currency_obj=rec.company_currency_id)
|
|
||||||
name = 'a%d (%s)' % (rec.id, amount_fmt)
|
|
||||||
res.append((rec.id, name))
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
class AccountBankStatement(models.Model):
|
|
||||||
_inherit = 'account.bank.statement'
|
|
||||||
|
|
||||||
start_date = fields.Date(
|
|
||||||
compute='_compute_dates', string='Start Date', readonly=True,
|
|
||||||
store=True)
|
|
||||||
end_date = fields.Date(
|
|
||||||
compute='_compute_dates', string='End Date', readonly=True,
|
|
||||||
store=True)
|
|
||||||
hide_bank_statement_balance = fields.Boolean(
|
|
||||||
related='journal_id.hide_bank_statement_balance', readonly=True)
|
|
||||||
|
|
||||||
@api.depends('line_ids.date')
|
|
||||||
def _compute_dates(self):
|
|
||||||
for st in self:
|
|
||||||
dates = [line.date for line in st.line_ids]
|
|
||||||
st.start_date = dates and min(dates) or False
|
|
||||||
st.end_date = dates and max(dates) or False
|
|
||||||
|
|
||||||
def _balance_check(self):
|
|
||||||
for stmt in self:
|
|
||||||
if stmt.hide_bank_statement_balance:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
super(AccountBankStatement, stmt)._balance_check()
|
|
||||||
return True
|
|
||||||
|
|
||||||
@api.depends('name', 'start_date', 'end_date')
|
|
||||||
def name_get(self):
|
|
||||||
res = []
|
|
||||||
for statement in self:
|
|
||||||
name = "%s (%s => %s)" % (
|
|
||||||
statement.name, statement.start_date, statement.end_date)
|
|
||||||
res.append((statement.id, name))
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
class AccountBankStatementLine(models.Model):
|
|
||||||
_inherit = 'account.bank.statement.line'
|
|
||||||
# Native order is:
|
|
||||||
# _order = 'statement_id desc, sequence, id desc'
|
|
||||||
_order = 'statement_id desc, date desc, sequence, id desc'
|
|
||||||
|
|
||||||
# Disable guessing for reconciliation
|
|
||||||
# because my experience with several customers shows that it is a problem
|
|
||||||
# in the following scenario : move line 'x' has been "guessed" by OpenERP
|
|
||||||
# to be reconciled with a statement line 'Y' at the end of the bank
|
|
||||||
# statement, but it is a mistake because it should be reconciled with
|
|
||||||
# statement line 'B' at the beginning of the bank statement
|
|
||||||
# When the user is on statement line 'B', he tries to select
|
|
||||||
# move line 'x', but it can't find it... because it is already "reserved"
|
|
||||||
# by the guess of OpenERP for statement line 'Y' ! To solve this problem,
|
|
||||||
# the user must go to statement line 'Y' and unselect move line 'x'
|
|
||||||
# and then come back on statement line 'B' and select move line 'A'...
|
|
||||||
# but non super-expert users can't do that because it is impossible to
|
|
||||||
# figure out that the fact that the user can't find move line 'x'
|
|
||||||
# is caused by this.
|
|
||||||
# Set search_reconciliation_proposition to False by default
|
|
||||||
# TODO: re-write in v10
|
|
||||||
# def get_data_for_reconciliations(
|
|
||||||
# self, cr, uid, ids, excluded_ids=None,
|
|
||||||
# search_reconciliation_proposition=False, context=None):
|
|
||||||
# # Make variable name shorted for PEP8 !
|
|
||||||
# search_rec_prop = search_reconciliation_proposition
|
|
||||||
# return super().\
|
|
||||||
# get_data_for_reconciliations(
|
|
||||||
# cr, uid, ids, excluded_ids=excluded_ids,
|
|
||||||
# search_reconciliation_proposition=search_rec_prop,
|
|
||||||
# context=context)
|
|
||||||
|
|
||||||
def _prepare_reconciliation_move(self, move_ref):
|
|
||||||
vals = super()._prepare_reconciliation_move(move_ref)
|
|
||||||
# By default, ref contains the name of the statement + name of the
|
|
||||||
# statement line. It causes 2 problems:
|
|
||||||
# 1) The 'ref' field is too big
|
|
||||||
# 2) The name of the statement line is already written in the name of
|
|
||||||
# the move line -> not useful to have the info 2 times
|
|
||||||
# In the end, I think it's better to just put nothing (we could write
|
|
||||||
# the name of the statement which has the account number, but it
|
|
||||||
# doesn't bring any useful info to the accountant)
|
|
||||||
# The only "good" thing to do would be to have a sequence per
|
|
||||||
# statement line and write it in this 'ref' field
|
|
||||||
# But that would required an additionnal field on statement lines
|
|
||||||
vals['ref'] = False
|
|
||||||
return vals
|
|
||||||
|
|
||||||
def show_account_move(self):
|
|
||||||
self.ensure_one()
|
|
||||||
action = self.env['ir.actions.act_window'].for_xml_id(
|
|
||||||
'account', 'action_move_line_form')
|
|
||||||
if self.journal_entry_ids:
|
|
||||||
action.update({
|
|
||||||
'views': False,
|
|
||||||
'view_id': False,
|
|
||||||
'view_mode': 'form,tree',
|
|
||||||
'res_id': self.journal_entry_ids[0].move_id.id,
|
|
||||||
})
|
|
||||||
return action
|
|
||||||
else:
|
|
||||||
raise UserError(_(
|
|
||||||
'No journal entry linked to this bank statement line.'))
|
|
||||||
|
|
||||||
|
|
||||||
class AccountFiscalPosition(models.Model):
|
|
||||||
_inherit = 'account.fiscal.position'
|
|
||||||
|
|
||||||
# TODO mig to v12 ?
|
|
||||||
@api.model
|
|
||||||
def get_fiscal_position_no_partner(
|
|
||||||
self, company_id=None, vat_subjected=False, country_id=None):
|
|
||||||
'''This method is inspired by the method get_fiscal_position()
|
|
||||||
in odoo/addons/account/partner.py : it uses the same algo
|
|
||||||
but without a real partner.
|
|
||||||
Returns a recordset of fiscal position, or False'''
|
|
||||||
domains = [[
|
|
||||||
('auto_apply', '=', True),
|
|
||||||
('vat_required', '=', vat_subjected),
|
|
||||||
('company_id', '=', company_id)]]
|
|
||||||
if vat_subjected:
|
|
||||||
domains += [[
|
|
||||||
('auto_apply', '=', True),
|
|
||||||
('vat_required', '=', False),
|
|
||||||
('company_id', '=', company_id)]]
|
|
||||||
|
|
||||||
for domain in domains:
|
|
||||||
if country_id:
|
|
||||||
fps = self.search(
|
|
||||||
domain + [('country_id', '=', country_id)], limit=1)
|
|
||||||
if fps:
|
|
||||||
return fps[0]
|
|
||||||
|
|
||||||
fps = self.search(
|
|
||||||
domain +
|
|
||||||
[('country_group_id.country_ids', '=', country_id)],
|
|
||||||
limit=1)
|
|
||||||
if fps:
|
|
||||||
return fps[0]
|
|
||||||
|
|
||||||
fps = self.search(
|
|
||||||
domain +
|
|
||||||
[('country_id', '=', None), ('country_group_id', '=', None)],
|
|
||||||
limit=1)
|
|
||||||
if fps:
|
|
||||||
return fps[0]
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class AccountReconcileModel(models.Model):
|
|
||||||
_inherit = 'account.reconcile.model'
|
|
||||||
|
|
||||||
@api.onchange('name')
|
|
||||||
def onchange_name(self):
|
|
||||||
# Do NOT copy by default name on label
|
|
||||||
# Because it's much better to have the bank statement line label as
|
|
||||||
# label of the counter-part move line, then the label of the button
|
|
||||||
assert True # Stupid line of code just to have something...
|
|
||||||
|
|
||||||
|
|
||||||
class AccountIncoterms(models.Model):
|
|
||||||
_inherit = 'account.incoterms'
|
|
||||||
|
|
||||||
@api.depends('code', 'name')
|
|
||||||
def name_get(self):
|
|
||||||
res = []
|
|
||||||
for rec in self:
|
|
||||||
res.append((rec.id, '[%s] %s' % (rec.code, rec.name)))
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
class AccountReconciliation(models.AbstractModel):
|
|
||||||
_inherit = 'account.reconciliation.widget'
|
|
||||||
|
|
||||||
# Add ability to filter by account code in the work interface of the
|
|
||||||
# bank statement
|
|
||||||
@api.model
|
|
||||||
def _domain_move_lines(self, search_str):
|
|
||||||
str_domain = super()._domain_move_lines(search_str)
|
|
||||||
account_code_domain = [('account_id.code', '=ilike', search_str + '%')]
|
|
||||||
str_domain = expression.OR([str_domain, account_code_domain])
|
|
||||||
return str_domain
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _domain_move_lines_for_reconciliation(
|
|
||||||
self, st_line, aml_accounts, partner_id,
|
|
||||||
excluded_ids=None, search_str=False):
|
|
||||||
domain = super()._domain_move_lines_for_reconciliation(
|
|
||||||
st_line, aml_accounts, partner_id,
|
|
||||||
excluded_ids=excluded_ids, search_str=search_str)
|
|
||||||
# We want to replace a domain item by another one
|
|
||||||
position = domain.index(('payment_id', '<>', False))
|
|
||||||
domain[position] = ['journal_id', '=', st_line.journal_id.id]
|
|
||||||
return domain
|
|
||||||
|
|
||||||
|
|
||||||
class ResConfigSettings(models.TransientModel):
|
|
||||||
_inherit = 'res.config.settings'
|
|
||||||
|
|
||||||
transfer_account_id = fields.Many2one(
|
|
||||||
related='company_id.transfer_account_id', readonly=False)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Copyright 2018-2019 Akretion (http://www.akretion.com)
|
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import models, fields
|
|
||||||
|
|
||||||
|
|
||||||
class AccountInvoiceReport(models.Model):
|
|
||||||
_inherit = 'account.invoice.report'
|
|
||||||
|
|
||||||
number = fields.Char(string="Number", readonly=True)
|
|
||||||
|
|
||||||
def _sub_select(self):
|
|
||||||
select_str = super(AccountInvoiceReport, self)._sub_select()
|
|
||||||
select_str += ", ai.number"
|
|
||||||
return select_str
|
|
||||||
|
|
||||||
def _select(self):
|
|
||||||
select_str = super(AccountInvoiceReport, self)._select()
|
|
||||||
select_str += ", sub.number"
|
|
||||||
return select_str
|
|
||||||
|
|
||||||
def _group_by(self):
|
|
||||||
group_by_str = super(AccountInvoiceReport, self)._group_by()
|
|
||||||
group_by_str += ", ai.number"
|
|
||||||
return group_by_str
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2018 Akretion (http://www.akretion.com/)
|
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
-->
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
|
|
||||||
<record id="account_invoice_report_tree" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.invoice.report.tree</field>
|
|
||||||
<field name="model">account.invoice.report</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Invoices Analysis">
|
|
||||||
<field name="number"/>
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="date_due"/>
|
|
||||||
<field name="type"/>
|
|
||||||
<field name="commercial_partner_id"/>
|
|
||||||
<field name="user_id"/>
|
|
||||||
<field name="product_id"/>
|
|
||||||
<field name="product_qty" sum="1"/>
|
|
||||||
<field name="uom_name" groups="uom.group_uom"/>
|
|
||||||
<field name="price_total" sum="1"/>
|
|
||||||
<field name="state"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
|
||||||
<field name="context">{'search_default_current': 1, 'search_default_supplier': 1, 'search_default_year': 1}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
|
|
||||||
<field name="context">{'search_default_current': 1, 'search_default_customer': 1, 'search_default_year': 1}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.invoice.report</field>
|
|
||||||
<field name="model">account.invoice.report</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_invoice_report_pivot"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<pivot position="attributes">
|
|
||||||
<attribute name="disable_linking"></attribute>
|
|
||||||
</pivot>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2018-2019 Akretion (http://www.akretion.com/)
|
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
-->
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
<record id="account.account_invoices" model="ir.actions.report">
|
|
||||||
<!-- Don't attach on supplier invoices/refunds ! -->
|
|
||||||
<field name="attachment">(object.type in ('out_invoice', 'out_refund')) and (object.state in ('open','in_payment','paid')) and ('INV'+(object.number or '').replace('/','')+'.pdf')</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
|
|
||||||
index 53f28e0c620..1721d01edf9 100644
|
|
||||||
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
|
|
||||||
+++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
|
|
||||||
@@ -514,7 +514,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
|
|
||||||
}
|
|
||||||
return this.model.makeRecord('account.bank.statement.line', [field], {
|
|
||||||
partner_id: {
|
|
||||||
- domain: ["|", ["is_company", "=", true], ["parent_id", "=", false], "|", ["customer", "=", true], ["supplier", "=", true]],
|
|
||||||
+ domain: ["|", ["is_company", "=", true], ["parent_id", "=", false]],
|
|
||||||
options: {
|
|
||||||
no_open: true
|
|
||||||
}
|
|
||||||
@@ -1,619 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2015-2019 Akretion France (http://www.akretion.com/)
|
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
-->
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
<!-- INVOICE -->
|
|
||||||
<record id="invoice_supplier_form" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.supplier.invoice.form</field>
|
|
||||||
<field name="model">account.invoice</field>
|
|
||||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="fiscal_position_id" position="attributes">
|
|
||||||
<attribute name="widget">selection</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="incoterm_id" position="attributes">
|
|
||||||
<attribute name="widget">selection</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="invoice_line_ids" position="before">
|
|
||||||
<button name="delete_lines_qty_zero" states="draft" string="⇒ Delete lines qty=0" type="object" class="oe_link oe_right" groups="account.group_account_invoice"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//field[@name='tax_line_ids']/tree/field[@name='amount']" position="before">
|
|
||||||
<field name="base" readonly="1"/>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="invoice_form" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.invoice.form</field>
|
|
||||||
<field name="model">account.invoice</field>
|
|
||||||
<field name="inherit_id" ref="account.invoice_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="fiscal_position_id" position="attributes">
|
|
||||||
<attribute name="widget">selection</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="incoterm_id" position="attributes">
|
|
||||||
<attribute name="widget">selection</attribute>
|
|
||||||
</field>
|
|
||||||
<!-- move sent field and make it visible -->
|
|
||||||
<field name="sent" position="replace"/>
|
|
||||||
<field name="move_id" position="before">
|
|
||||||
<field name="sent"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//field[@name='tax_line_ids']/tree/field[@name='amount']" position="before">
|
|
||||||
<field name="base" readonly="1"/>
|
|
||||||
</xpath>
|
|
||||||
<!-- Warning: there are 2 invoice_print buttons in the native view... probably a bug -->
|
|
||||||
<!--
|
|
||||||
<xpath expr="//button[@name='invoice_print']" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('state', 'not in', ('open', 'paid'))]}</attribute>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//button[@name='invoice_print'][2]" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': True}</attribute>
|
|
||||||
</xpath> -->
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="invoice_tree" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.invoice_tree</field>
|
|
||||||
<field name="model">account.invoice</field>
|
|
||||||
<field name="inherit_id" ref="account.invoice_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="reference" position="attributes">
|
|
||||||
<attribute name="invisible">not context.get('type') in ('in_invoice', 'in_refund')</attribute>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_invoice_filter" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.invoice.search</field>
|
|
||||||
<field name="model">account.invoice</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<filter name="late" position="after">
|
|
||||||
<separator/>
|
|
||||||
<filter name="to_send" string="To Send" domain="[('sent', '=', False), ('state', 'in', ('open', 'paid'))]"/>
|
|
||||||
<filter name="sent" string="Sent" domain="[('sent', '=', True)]"/>
|
|
||||||
<separator/>
|
|
||||||
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
|
|
||||||
</filter>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Having a menu entry on invoice lines is often very usefull for odoo user:
|
|
||||||
they can search in their lines, etc...
|
|
||||||
So I enhance the generic views and add actions, but I don't add menu entries here ;
|
|
||||||
the creation of the corresponding menu entry should be done in the customer-specifc
|
|
||||||
module -->
|
|
||||||
<record id="view_invoice_line_tree" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.invoice_line_tree</field>
|
|
||||||
<field name="model">account.invoice.line</field>
|
|
||||||
<field name="inherit_id" ref="account.view_invoice_line_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="name" position="before">
|
|
||||||
<field name="partner_id" invisible="not context.get('show_invoice_fields')"/>
|
|
||||||
<field name="date_invoice" invisible="not context.get('show_invoice_fields')"/>
|
|
||||||
<field name="invoice_number" invisible="not context.get('show_invoice_fields')"/>
|
|
||||||
</field>
|
|
||||||
<field name="currency_id" position="after">
|
|
||||||
<field name="state" invisible="not context.get('show_invoice_fields')"/>
|
|
||||||
<field name="invoice_type" invisible="1"/>
|
|
||||||
</field>
|
|
||||||
<field name="quantity" position="attributes">
|
|
||||||
<attribute name="sum">1</attribute>
|
|
||||||
</field>
|
|
||||||
<xpath expr="/tree" position="attributes">
|
|
||||||
<attribute name="decoration-info">state == 'draft'</attribute>
|
|
||||||
<attribute name="decoration-muted">state == 'cancel'</attribute>
|
|
||||||
<attribute name="edit">0</attribute>
|
|
||||||
<attribute name="create">0</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_invoice_line_search" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.invoice_line_search</field>
|
|
||||||
<field name="model">account.invoice.line</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<search string="Search Invoice Lines">
|
|
||||||
<field name="partner_id"/>
|
|
||||||
<field name="product_id"/>
|
|
||||||
<field name="account_id"/>
|
|
||||||
<field name="invoice_number"/>
|
|
||||||
<field name="name"/>
|
|
||||||
<filter name="out_invoice" string="Customer Invoices"
|
|
||||||
domain="[('invoice_type', '=', 'out_invoice')]"/>
|
|
||||||
<filter name="out_refund" string="Customer Refunds"
|
|
||||||
domain="[('invoice_type', '=', 'out_refund')]"/>
|
|
||||||
<filter name="in_invoice" string="Supplier Invoices"
|
|
||||||
domain="[('invoice_type', '=', 'in_invoice')]"/>
|
|
||||||
<filter name="in_refund" string="Supplier Refunds"
|
|
||||||
domain="[('invoice_type', '=', 'in_refund')]"/>
|
|
||||||
<separator/>
|
|
||||||
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]"/>
|
|
||||||
<filter name="unpaid" string="Not Paid" domain="[('state', '=', 'open')]"/>
|
|
||||||
<filter name="paid" string="Paid" domain="[('state', '=', 'paid')]"/>
|
|
||||||
|
|
||||||
<group string="Group By" name="groupby">
|
|
||||||
<filter name="partner_groupby" string="Partner"
|
|
||||||
context="{'group_by': 'partner_id'}"/>
|
|
||||||
<filter name="date_groupby" string="Invoice Date"
|
|
||||||
context="{'group_by': 'date_invoice'}"/>
|
|
||||||
<filter name="product_groupby" string="Product"
|
|
||||||
context="{'group_by': 'product_id'}"/>
|
|
||||||
<filter name="account_groupby" string="Account"
|
|
||||||
context="{'group_by': 'account_id'}"/>
|
|
||||||
</group>
|
|
||||||
</search>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="out_invoice_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Customer Invoice Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', '=', 'out_invoice')]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="out_refund_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Customer Refund Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', '=', 'out_refund')]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="out_invoice_refund_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Customer Invoice Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', 'in', ('out_invoice', 'out_refund'))]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="in_invoice_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Supplier Invoice Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', '=', 'in_invoice')]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="in_refund_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Supplier Refund Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', '=', 'in_refund')]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="in_invoice_refund_line_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Supplier Invoice Lines</field>
|
|
||||||
<field name="res_model">account.invoice.line</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
<field name="domain">[('invoice_type', 'in', ('in_invoice', 'in_refund'))]</field>
|
|
||||||
<field name="context">{'show_invoice_fields': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_invoice_report_tree" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.invoice.report.tree</field>
|
|
||||||
<field name="model">account.invoice.report</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Invoices Analysis">
|
|
||||||
<field name="date"/>
|
|
||||||
<field name="commercial_partner_id"/>
|
|
||||||
<field name="type"/>
|
|
||||||
<field name="product_id"/>
|
|
||||||
<field name="product_qty" sum="1"/>
|
|
||||||
<field name="price_total" sum="1"/>
|
|
||||||
<field name="state"/>
|
|
||||||
<field name="currency_id" invisible="1"/>
|
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
|
||||||
<field name="view_mode">pivot,graph,tree</field>
|
|
||||||
<field name="context">{'search_default_current':1, 'search_default_supplier':1, 'search_default_year': 1}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
|
|
||||||
<field name="view_mode">pivot,graph,tree</field>
|
|
||||||
<field name="context">{'search_default_current':1, 'search_default_customer':1, 'search_default_year': 1}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.invoice.report.pivot</field>
|
|
||||||
<field name="model">account.invoice.report</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_invoice_report_pivot"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="/pivot" position="attributes">
|
|
||||||
<attribute name="disable_linking"></attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_invoice_tax_form" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.invoice.tax.form</field>
|
|
||||||
<field name="model">account.invoice.tax</field>
|
|
||||||
<field name="inherit_id" ref="account.view_invoice_tax_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="name" position="after">
|
|
||||||
<field name="tax_id"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_payment_form" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.payment.form</field>
|
|
||||||
<field name="model">account.payment</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_payment_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="communication" position="after">
|
|
||||||
<field name="payment_reference"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- model account.move.line / Journal Items -->
|
|
||||||
<record id="account.action_account_moves_all_a" model="ir.actions.act_window">
|
|
||||||
<field name="limit">200</field>
|
|
||||||
<!-- Win space, because there are already many columns -->
|
|
||||||
<field name="context">{'journal_show_code_only': True}</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- replace group_account_manager on Journal Items-->
|
|
||||||
<record id="account.menu_action_account_moves_all" model="ir.ui.menu">
|
|
||||||
<field name="groups_id" eval="[(6, 0, [ref('account.group_account_user')])]"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- model account.move / Journal Entries -->
|
|
||||||
<record id="account.action_move_journal_line" model="ir.actions.act_window">
|
|
||||||
<field name="limit">200</field>
|
|
||||||
<field name="context">{'view_no_maturity': True}</field> <!-- Don't filter by default on misc journal -->
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_move_form" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_move_form</field>
|
|
||||||
<field name="model">account.move</field>
|
|
||||||
<field name="inherit_id" ref="account.view_move_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="journal_id" position="after">
|
|
||||||
<field name="default_move_line_name"/>
|
|
||||||
<field name="default_account_id" invisible="1"/>
|
|
||||||
<field name="default_credit" invisible="1"/>
|
|
||||||
<field name="default_debit" invisible="1"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//field[@name='line_ids']" position="attributes">
|
|
||||||
<attribute name="context" operation="python_dict" key="default_name">default_move_line_name</attribute>
|
|
||||||
<attribute name="context" operation="python_dict" key="default_account_id">default_account_id</attribute>
|
|
||||||
<attribute name="context" operation="python_dict" key="default_credit">default_credit</attribute>
|
|
||||||
<attribute name="context" operation="python_dict" key="default_debit">default_debit</attribute>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//field[@name='line_ids']/tree/field[@name='credit']" position="after">
|
|
||||||
<field name="reconcile_string"/>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_move_line_filter" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_move_line_search</field>
|
|
||||||
<field name="model">account.move.line</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="partner_id" position="after">
|
|
||||||
<field name="reconcile_string" />
|
|
||||||
<field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/>
|
|
||||||
</field>
|
|
||||||
<filter name="unreconciled" position="before">
|
|
||||||
<filter name="reconciled" string="Fully Reconciled" domain="[('full_reconcile_id', '!=', False)]"/>
|
|
||||||
<!-- <filter name="partial_reconciled" string="Partially Reconciled" domain="[('reconcile_partial_id', '!=', False)]"/> -->
|
|
||||||
</filter>
|
|
||||||
<filter name="unreconciled" position="attributes">
|
|
||||||
<attribute name="string">Unreconciled or Partially Reconciled</attribute>
|
|
||||||
</filter>
|
|
||||||
<field name="name" position="attributes">
|
|
||||||
<attribute name="string">Name or Reference</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="partner_id" position="attributes">
|
|
||||||
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_move_line_form" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_move_line_form</field>
|
|
||||||
<field name="model">account.move.line</field>
|
|
||||||
<field name="inherit_id" ref="account.view_move_line_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="quantity" position="after">
|
|
||||||
<field name="product_id" />
|
|
||||||
</field>
|
|
||||||
<field name="move_id" position="after">
|
|
||||||
<field name="invoice_id"/>
|
|
||||||
<field name="account_reconcile" invisible="1"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//field[@name='full_reconcile_id']/.." position="replace">
|
|
||||||
<field name="full_reconcile_id" nolabel="1"/> <!-- label is already in view -->
|
|
||||||
<field name="matched_debit_ids" readonly="1" widget="many2many_tags" attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), ('matched_debit_ids', '=', [])]}"/>
|
|
||||||
<field name="matched_credit_ids" readonly="1" widget="many2many_tags" attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), ('matched_credit_ids', '=', [])]}"/>
|
|
||||||
<field name="reconciled" invisible="1"/>
|
|
||||||
<button name="open_reconcile_view" class="oe_link" type="object"
|
|
||||||
string="-> View partially reconciled entries" colspan="2"
|
|
||||||
attrs="{'invisible': ['|', ('full_reconcile_id', '!=', False), '&', ('matched_debit_ids', '=', []), ('matched_credit_ids', '=', [])]}"/>
|
|
||||||
<span colspan="2" attrs="{'invisible': ['|', '|', ('full_reconcile_id', '!=', False), ('matched_debit_ids', '!=', []), ('matched_credit_ids', '!=', [])]}" class="o_form_field">No Partial Reconcile</span>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//label[@for='full_reconcile_id']/.." position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('account_reconcile', '=', False)]}</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_move_line_tree" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_move_line_tree</field>
|
|
||||||
<field name="model">account.move.line</field>
|
|
||||||
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="full_reconcile_id" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="credit" position="after">
|
|
||||||
<field name="balance" sum="Total Balance"/>
|
|
||||||
<field name="reconcile_string"/>
|
|
||||||
</field>
|
|
||||||
<field name="date_maturity" position="after">
|
|
||||||
<button name="show_account_move_form" type="object" icon="fa-arrows-h" string="Show Journal Entry"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_move_filter" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_move_search</field>
|
|
||||||
<field name="model">account.move</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_move_filter"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="partner_id" position="attributes">
|
|
||||||
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_search" model="ir.ui.view">
|
|
||||||
<field name="name">account.account.search</field>
|
|
||||||
<field name="model">account.account</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_search"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<!-- The native "name" filter uses a domain ['|', ('name','ilike',self), ('code','=like',str(self)+'%')]
|
|
||||||
This is good because it uses '=like' on 'code', but sometimes there are digits in account names,
|
|
||||||
so you get additionnal unexpected accounts in the result of the search -->
|
|
||||||
<field name="name" position="after">
|
|
||||||
<field name="code" filter_domain="[('code', '=like', str(self)+'%')]" string="Code"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_type_tree" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability.account_type_tree</field>
|
|
||||||
<field name="model">account.account.type</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_type_tree" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="type" position="after">
|
|
||||||
<field name="include_initial_balance" />
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_journal_form" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.journal.form</field>
|
|
||||||
<field name="model">account.journal</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_journal_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="bank_statements_source" position="after">
|
|
||||||
<field name="hide_bank_statement_balance" groups="account.group_account_user"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_journal_dashboard_kanban_view" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.journal.dashboard</field>
|
|
||||||
<field name="model">account.journal</field>
|
|
||||||
<field name="inherit_id" ref="account.account_journal_dashboard_kanban_view"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="kanban_dashboard" position="after">
|
|
||||||
<field name="hide_bank_statement_balance"/>
|
|
||||||
</field>
|
|
||||||
<xpath expr="//div[@name='latest_statement']/.." position="attributes">
|
|
||||||
<attribute name="t-if">dashboard.last_balance != dashboard.account_balance && !record.hide_bank_statement_balance.raw_value</attribute>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_journal_tree" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.journal.tree</field>
|
|
||||||
<field name="model">account.journal</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_journal_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="name" position="after">
|
|
||||||
<field name="code"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_account_journal_search" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.journal.search</field>
|
|
||||||
<field name="model">account.journal</field>
|
|
||||||
<field name="inherit_id" ref="account.view_account_journal_search"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<filter name="dashboard" position="after">
|
|
||||||
<group name="groupby" string="Group By">
|
|
||||||
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
|
|
||||||
</group>
|
|
||||||
</filter>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_bank_statement_form" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.bank.statement.form</field>
|
|
||||||
<field name="model">account.bank.statement</field>
|
|
||||||
<field name="inherit_id" ref="account.view_bank_statement_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//field[@name='line_ids']/tree/field[@name='bank_account_id']" position="after">
|
|
||||||
<!-- The cancel button is provided by the account_cancel module, but we don't want to depend on it -->
|
|
||||||
<button name="show_account_move" type="object"
|
|
||||||
string="View Account Move" icon="fa-arrow-right"
|
|
||||||
attrs="{'invisible': [('journal_entry_ids', '=', [])]}"/>
|
|
||||||
</xpath>
|
|
||||||
<field name="date" position="after">
|
|
||||||
<field name="start_date"/>
|
|
||||||
<field name="end_date"/>
|
|
||||||
<field name="hide_bank_statement_balance" invisible="1"/>
|
|
||||||
</field>
|
|
||||||
<field name="date" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</field>
|
|
||||||
<label for="balance_start" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
|
||||||
</label>
|
|
||||||
<label for="balance_end_real" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
|
||||||
</label>
|
|
||||||
<xpath expr="//field[@name='balance_start']/.." position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
|
||||||
</xpath>
|
|
||||||
<xpath expr="//field[@name='balance_end_real']/.." position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
|
||||||
</xpath>
|
|
||||||
<group name="sale_total" position="attributes">
|
|
||||||
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
|
||||||
</group>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_bank_statement_tree" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.bank.statement.tree</field>
|
|
||||||
<field name="model">account.bank.statement</field>
|
|
||||||
<field name="inherit_id" ref="account.view_bank_statement_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="date" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="journal_id" position="after">
|
|
||||||
<field name="start_date"/>
|
|
||||||
<field name="end_date"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_bank_statement_search" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.bank.statement.search</field>
|
|
||||||
<field name="model">account.bank.statement</field>
|
|
||||||
<field name="inherit_id" ref="account.view_bank_statement_search"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="date" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</field>
|
|
||||||
<field name="date" position="after">
|
|
||||||
<field name="start_date"/>
|
|
||||||
<field name="end_date"/>
|
|
||||||
</field>
|
|
||||||
<filter name="date" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</filter>
|
|
||||||
<filter name="date" position="after">
|
|
||||||
<filter name="start_date_groupby" string="Start Date"
|
|
||||||
context="{'group_by': 'start_date'}"/>
|
|
||||||
<filter name="end_date_groupby" string="End Date"
|
|
||||||
context="{'group_by': 'end_date'}"/>
|
|
||||||
</filter>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- ACCOUNT TAX -->
|
|
||||||
<record id="view_tax_tree" model="ir.ui.view">
|
|
||||||
<field name="model">account.tax</field>
|
|
||||||
<field name="inherit_id" ref="account.view_tax_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="company_id" position="before">
|
|
||||||
<field name="price_include" string="Include"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- ACCOUNT TAX GROUP -->
|
|
||||||
<!-- in the account module, there is nothing for account.tax.group : no form/tree view, no menu... -->
|
|
||||||
<record id="account_tax_group_form" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.tax.group.form</field>
|
|
||||||
<field name="model">account.tax.group</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<form string="Tax Group">
|
|
||||||
<group name="main">
|
|
||||||
<field name="name"/>
|
|
||||||
<field name="sequence" invisible="1"/>
|
|
||||||
</group>
|
|
||||||
</form>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_tax_group_tree" model="ir.ui.view">
|
|
||||||
<field name="name">usability.account.tax.group.tree</field>
|
|
||||||
<field name="model">account.tax.group</field>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<tree string="Tax Groups">
|
|
||||||
<field name="sequence" widget="handle"/>
|
|
||||||
<field name="name"/>
|
|
||||||
</tree>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="account_tax_group_action" model="ir.actions.act_window">
|
|
||||||
<field name="name">Tax Groups</field>
|
|
||||||
<field name="res_model">account.tax.group</field>
|
|
||||||
<field name="view_mode">tree,form</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<menuitem id="account_tax_group_menu" action="account_tax_group_action" parent="account.account_account_menu" sequence="2"/>
|
|
||||||
|
|
||||||
<!-- Account config page -->
|
|
||||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
|
||||||
<field name="name">account_usability account config page</field>
|
|
||||||
<field name="model">res.config.settings</field>
|
|
||||||
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//field[@name='account_bank_reconciliation_start']/../.." position="after">
|
|
||||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
|
||||||
<div class="o_setting_left_pane"/>
|
|
||||||
<div class="o_setting_right_pane">
|
|
||||||
<label for="transfer_account_id"/>
|
|
||||||
<div class="text-muted">
|
|
||||||
Transit account when you transfer money from a bank account of your company to another bank account of your company.
|
|
||||||
</div>
|
|
||||||
<field name="transfer_account_id"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Remove menu entry "Accounting > Configuration > Accounting > Bank Accounts"
|
|
||||||
(account.journal filtered on type = 'bank' with special tree and form view)
|
|
||||||
because it is useless and confusing -->
|
|
||||||
<record id="account.menu_action_account_bank_journal_form" model="ir.ui.menu">
|
|
||||||
<field name="groups_id" eval="[(6, 0, [ref('base_usability.group_nobody')])]"/>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Duplicate the menu "Sales > Configuration > Contacts > Bank Accounts"
|
|
||||||
under "Accounting > Configuration", because most users will try to find it there -->
|
|
||||||
<menuitem id="bank_account_account_config_menu" name="Bank Accounts" parent="account.menu_finance_configuration" sequence="9"/>
|
|
||||||
|
|
||||||
<menuitem id="res_bank_account_config_menu" action="base.action_res_bank_form" parent="bank_account_account_config_menu" sequence="10"/>
|
|
||||||
|
|
||||||
<menuitem id="res_partner_bank_account_config_menu" action="base.action_res_partner_bank_account_form" parent="bank_account_account_config_menu" sequence="20"/>
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,33 +1,55 @@
|
|||||||
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
|
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
|
||||||
index f38f459c533..a475d6a82c7 100644
|
index 2dd1f9cef83..62275fca65e 100644
|
||||||
--- a/addons/account/models/account_payment.py
|
--- a/addons/account/models/account_payment.py
|
||||||
+++ b/addons/account/models/account_payment.py
|
+++ b/addons/account/models/account_payment.py
|
||||||
@@ -62,6 +62,7 @@ class account_abstract_payment(models.AbstractModel):
|
@@ -262,6 +262,7 @@ class AccountPayment(models.Model):
|
||||||
payment_difference = fields.Monetary(compute='_compute_payment_difference', readonly=True)
|
'credit': write_off_balance > 0.0 and write_off_balance or 0.0,
|
||||||
payment_difference_handling = fields.Selection([('open', 'Keep open'), ('reconcile', 'Mark invoice as fully paid')], default='open', string="Payment Difference Handling", copy=False)
|
'partner_id': self.partner_id.id,
|
||||||
writeoff_account_id = fields.Many2one('account.account', string="Difference Account", domain=[('deprecated', '=', False)], copy=False)
|
'account_id': write_off_line_vals.get('account_id'),
|
||||||
+ writeoff_analytic_account_id = fields.Many2one('account.analytic.account', string="Difference Analytic Account", copy=False)
|
+ 'analytic_account_id': write_off_line_vals.get('analytic_account_id'),
|
||||||
writeoff_label = fields.Char(
|
})
|
||||||
string='Journal Item Label',
|
return line_vals_list
|
||||||
help='Change label of the counterpart that will hold the payment difference',
|
|
||||||
@@ -717,6 +718,7 @@ class account_payment(models.Model):
|
@@ -699,6 +700,7 @@ class AccountPayment(models.Model):
|
||||||
debit_wo, credit_wo, amount_currency_wo, currency_id = aml_obj.with_context(date=self.payment_date)._compute_amount_fields(self.payment_difference, self.currency_id, self.company_id.currency_id)
|
'name': writeoff_lines[0].name,
|
||||||
writeoff_line['name'] = self.writeoff_label
|
'amount': writeoff_amount * sign,
|
||||||
writeoff_line['account_id'] = self.writeoff_account_id.id
|
'account_id': writeoff_lines[0].account_id.id,
|
||||||
+ writeoff_line['analytic_account_id'] = self.writeoff_analytic_account_id.id or False
|
+ 'analytic_account_id': writeoff_lines[0].analytic_account_id.id,
|
||||||
writeoff_line['debit'] = debit_wo
|
}
|
||||||
writeoff_line['credit'] = credit_wo
|
else:
|
||||||
writeoff_line['amount_currency'] = amount_currency_wo
|
write_off_line_vals = {}
|
||||||
diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml
|
diff --git a/addons/account/wizard/account_payment_register.py b/addons/account/wizard/account_payment_register.py
|
||||||
index 07230902ee8..1359009bf23 100644
|
index 3fc91f716ad..35636774c7e 100644
|
||||||
--- a/addons/account/views/account_payment_view.xml
|
--- a/addons/account/wizard/account_payment_register.py
|
||||||
+++ b/addons/account/views/account_payment_view.xml
|
+++ b/addons/account/wizard/account_payment_register.py
|
||||||
@@ -277,6 +277,8 @@
|
@@ -93,6 +93,7 @@ class AccountPaymentRegister(models.TransientModel):
|
||||||
<div attrs="{'invisible': [('payment_difference_handling','=','open')]}">
|
], default='open', string="Payment Difference Handling")
|
||||||
<label for="writeoff_account_id" class="oe_edit_only" string="Post Difference In"/>
|
writeoff_account_id = fields.Many2one('account.account', string="Difference Account", copy=False,
|
||||||
<field name="writeoff_account_id" string="Post Difference In" attrs="{'required': [('payment_difference_handling', '=', 'reconcile')]}"/>
|
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]")
|
||||||
+ <label for="writeoff_analytic_account_id" class="oe_edit_only" string="Analytic Account" groups="analytic.group_analytic_accounting"/>
|
+ writeoff_analytic_account_id = fields.Many2one('account.analytic.account', string="Difference Analytic Account", copy=False, domain="[('company_id', '=', company_id)]")
|
||||||
+ <field name="writeoff_analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
writeoff_label = fields.Char(string='Journal Item Label', default='Write-Off',
|
||||||
<label for="journal_id" string="Journal" attrs="{'invisible': [('amount', '!=', 0)]}"/>
|
help='Change label of the counterpart that will hold the payment difference')
|
||||||
<field name="journal_id" string="Journal" widget="selection" attrs="{'invisible': [('amount', '!=', 0)]}"/>
|
|
||||||
<label for="writeoff_label" class="oe_edit_only" string="Label"/>
|
@@ -422,6 +423,7 @@ class AccountPaymentRegister(models.TransientModel):
|
||||||
|
'name': self.writeoff_label,
|
||||||
|
'amount': self.payment_difference,
|
||||||
|
'account_id': self.writeoff_account_id.id,
|
||||||
|
+ 'analytic_account_id': self.writeoff_analytic_account_id.id or False,
|
||||||
|
}
|
||||||
|
return payment_vals
|
||||||
|
|
||||||
|
diff --git a/addons/account/wizard/account_payment_register_views.xml b/addons/account/wizard/account_payment_register_views.xml
|
||||||
|
index 16eec30e265..b9386567baa 100644
|
||||||
|
--- a/addons/account/wizard/account_payment_register_views.xml
|
||||||
|
+++ b/addons/account/wizard/account_payment_register_views.xml
|
||||||
|
@@ -65,6 +65,10 @@
|
||||||
|
string="Post Difference In"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
attrs="{'required': [('payment_difference_handling', '=', 'reconcile')]}"/>
|
||||||
|
+ <label for="writeoff_analytic_account_id" class="oe_edit_only" string="Analytic Account" groups="analytic.group_analytic_accounting"/>
|
||||||
|
+ <field name="writeoff_analytic_account_id"
|
||||||
|
+ groups="analytic.group_analytic_accounting"
|
||||||
|
+ options="{'no_create': True}" />
|
||||||
|
<label for="writeoff_label" class="oe_edit_only" string="Label"/>
|
||||||
|
<field name="writeoff_label" attrs="{'required': [('payment_difference_handling', '=', 'reconcile')]}"/>
|
||||||
|
</div>
|
||||||
|
|||||||
11
account_usability/models/__init__.py
Normal file
11
account_usability/models/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from . import account_account
|
||||||
|
from . import account_analytic_account
|
||||||
|
from . import account_bank_statement
|
||||||
|
from . import account_incoterms
|
||||||
|
from . import account_journal
|
||||||
|
from . import account_move
|
||||||
|
from . import account_partial_reconcile
|
||||||
|
from . import res_partner
|
||||||
|
from . import res_company
|
||||||
|
from . import product
|
||||||
|
from . import account_invoice_report
|
||||||
59
account_usability/models/account_account.py
Normal file
59
account_usability/models/account_account.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, models
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AccountAccount(models.Model):
|
||||||
|
_inherit = 'account.account'
|
||||||
|
|
||||||
|
@api.depends('name', 'code')
|
||||||
|
def name_get(self):
|
||||||
|
if self._context.get('account_account_show_code_only'):
|
||||||
|
res = []
|
||||||
|
for record in self:
|
||||||
|
res.append((record.id, record.code))
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
return super().name_get()
|
||||||
|
|
||||||
|
# https://github.com/odoo/odoo/issues/23040
|
||||||
|
# TODO mig to v14 ?
|
||||||
|
def fix_bank_account_types(self):
|
||||||
|
aao = self.env['account.account']
|
||||||
|
companies = self.env['res.company'].search([])
|
||||||
|
if len(companies) > 1:
|
||||||
|
self = self.sudo()
|
||||||
|
logger.info("START the script 'fix bank and cash account types'")
|
||||||
|
bank_type = self.env.ref('account.data_account_type_liquidity')
|
||||||
|
asset_type = self.env.ref('account.data_account_type_current_assets')
|
||||||
|
journals = self.env['account.journal'].search(
|
||||||
|
[('type', 'in', ('bank', 'cash'))], order='company_id')
|
||||||
|
journal_accounts_bank_type = aao
|
||||||
|
for journal in journals:
|
||||||
|
for account in [
|
||||||
|
journal.default_credit_account_id,
|
||||||
|
journal.default_debit_account_id]:
|
||||||
|
if account:
|
||||||
|
if account.user_type_id != bank_type:
|
||||||
|
account.user_type_id = bank_type.id
|
||||||
|
logger.info(
|
||||||
|
'Company %s: Account %s updated to Bank '
|
||||||
|
'and Cash type',
|
||||||
|
account.company_id.display_name, account.code)
|
||||||
|
if account not in journal_accounts_bank_type:
|
||||||
|
journal_accounts_bank_type += account
|
||||||
|
accounts = aao.search([
|
||||||
|
('user_type_id', '=', bank_type.id)], order='company_id, code')
|
||||||
|
for account in accounts:
|
||||||
|
if account not in journal_accounts_bank_type:
|
||||||
|
account.user_type_id = asset_type.id
|
||||||
|
logger.info(
|
||||||
|
'Company %s: Account %s updated to Current Asset type',
|
||||||
|
account.company_id.display_name, account.code)
|
||||||
|
logger.info("END of the script 'fix bank and cash account types'")
|
||||||
|
return True
|
||||||
24
account_usability/models/account_analytic_account.py
Normal file
24
account_usability/models/account_analytic_account.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountAnalyticAccount(models.Model):
|
||||||
|
_inherit = 'account.analytic.account'
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
if self._context.get('analytic_account_show_code_only'):
|
||||||
|
res = []
|
||||||
|
for record in self:
|
||||||
|
res.append((record.id, record.code or record.name))
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
return super().name_get()
|
||||||
|
|
||||||
|
_sql_constraints = [(
|
||||||
|
'code_company_unique',
|
||||||
|
'unique(code, company_id)',
|
||||||
|
'An analytic account with the same code already '
|
||||||
|
'exists in the same company!')]
|
||||||
90
account_usability/models/account_bank_statement.py
Normal file
90
account_usability/models/account_bank_statement.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from odoo.tools.misc import format_date
|
||||||
|
|
||||||
|
|
||||||
|
class AccountBankStatement(models.Model):
|
||||||
|
_inherit = 'account.bank.statement'
|
||||||
|
|
||||||
|
start_date = fields.Date(
|
||||||
|
compute='_compute_dates', string='Start Date', readonly=True,
|
||||||
|
store=True)
|
||||||
|
end_date = fields.Date(
|
||||||
|
compute='_compute_dates', string='End Date', readonly=True,
|
||||||
|
store=True)
|
||||||
|
hide_bank_statement_balance = fields.Boolean(
|
||||||
|
related='journal_id.hide_bank_statement_balance', readonly=True)
|
||||||
|
|
||||||
|
@api.depends('line_ids.date')
|
||||||
|
def _compute_dates(self):
|
||||||
|
for st in self:
|
||||||
|
dates = [line.date for line in st.line_ids]
|
||||||
|
st.start_date = dates and min(dates) or False
|
||||||
|
st.end_date = dates and max(dates) or False
|
||||||
|
|
||||||
|
def _check_balance_end_real_same_as_computed(self):
|
||||||
|
for stmt in self:
|
||||||
|
if not stmt.hide_bank_statement_balance:
|
||||||
|
super(AccountBankStatement, stmt)._check_balance_end_real_same_as_computed()
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.depends('name', 'start_date', 'end_date')
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for statement in self:
|
||||||
|
name = "%s (%s => %s)" % (
|
||||||
|
statement.name,
|
||||||
|
statement.start_date and format_date(self.env, statement.start_date) or '',
|
||||||
|
statement.end_date and format_date(self.env, statement.end_date) or '')
|
||||||
|
res.append((statement.id, name))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class AccountBankStatementLine(models.Model):
|
||||||
|
_inherit = 'account.bank.statement.line'
|
||||||
|
# Native order is:
|
||||||
|
# _order = 'statement_id desc, sequence, id desc'
|
||||||
|
_order = 'statement_id desc, date desc, sequence, id desc'
|
||||||
|
|
||||||
|
# Disable guessing for reconciliation
|
||||||
|
# because my experience with several customers shows that it is a problem
|
||||||
|
# in the following scenario : move line 'x' has been "guessed" by OpenERP
|
||||||
|
# to be reconciled with a statement line 'Y' at the end of the bank
|
||||||
|
# statement, but it is a mistake because it should be reconciled with
|
||||||
|
# statement line 'B' at the beginning of the bank statement
|
||||||
|
# When the user is on statement line 'B', he tries to select
|
||||||
|
# move line 'x', but it can't find it... because it is already "reserved"
|
||||||
|
# by the guess of OpenERP for statement line 'Y' ! To solve this problem,
|
||||||
|
# the user must go to statement line 'Y' and unselect move line 'x'
|
||||||
|
# and then come back on statement line 'B' and select move line 'A'...
|
||||||
|
# but non super-expert users can't do that because it is impossible to
|
||||||
|
# figure out that the fact that the user can't find move line 'x'
|
||||||
|
# is caused by this.
|
||||||
|
# Set search_reconciliation_proposition to False by default
|
||||||
|
# TODO: re-write in v10
|
||||||
|
# def get_data_for_reconciliations(
|
||||||
|
# self, cr, uid, ids, excluded_ids=None,
|
||||||
|
# search_reconciliation_proposition=False, context=None):
|
||||||
|
# # Make variable name shorted for PEP8 !
|
||||||
|
# search_rec_prop = search_reconciliation_proposition
|
||||||
|
# return super().\
|
||||||
|
# get_data_for_reconciliations(
|
||||||
|
# cr, uid, ids, excluded_ids=excluded_ids,
|
||||||
|
# search_reconciliation_proposition=search_rec_prop,
|
||||||
|
# context=context)
|
||||||
|
|
||||||
|
def show_account_move(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||||
|
'account.action_move_line_form')
|
||||||
|
# Note: this action is on account.move, not account.move.line !
|
||||||
|
action.update({
|
||||||
|
'views': False,
|
||||||
|
'view_id': False,
|
||||||
|
'view_mode': 'form,tree',
|
||||||
|
'res_id': self.move_id.id,
|
||||||
|
})
|
||||||
|
return action
|
||||||
26
account_usability/models/account_incoterms.py
Normal file
26
account_usability/models/account_incoterms.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountIncoterms(models.Model):
|
||||||
|
_inherit = 'account.incoterms'
|
||||||
|
|
||||||
|
@api.depends('code', 'name')
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for rec in self:
|
||||||
|
res.append((rec.id, '[%s] %s' % (rec.code, rec.name)))
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if name and operator == 'ilike':
|
||||||
|
recs = self.search([('code', '=ilike', name + '%')] + args, limit=limit)
|
||||||
|
if recs:
|
||||||
|
return recs.name_get()
|
||||||
|
return super().name_search(name=name, args=args, operator=operator, limit=limit)
|
||||||
17
account_usability/models/account_invoice_report.py
Normal file
17
account_usability/models/account_invoice_report.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Copyright 2022 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountInvoiceReport(models.Model):
|
||||||
|
_inherit = 'account.invoice.report'
|
||||||
|
|
||||||
|
industry_id = fields.Many2one('res.partner.industry', string='Partner Industry', readonly=True)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _select(self):
|
||||||
|
res = super()._select()
|
||||||
|
res += ", COALESCE(partner.industry_id, commercial_partner.industry_id) AS industry_id"
|
||||||
|
return res
|
||||||
67
account_usability/models/account_journal.py
Normal file
67
account_usability/models/account_journal.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountJournal(models.Model):
|
||||||
|
_inherit = 'account.journal'
|
||||||
|
|
||||||
|
hide_bank_statement_balance = fields.Boolean(
|
||||||
|
string='Hide Bank Statement Balance',
|
||||||
|
help="You may want to enable this option when your bank "
|
||||||
|
"journal is generated from a bank statement file that "
|
||||||
|
"doesn't handle start/end balance (QIF for instance) and "
|
||||||
|
"you don't want to enter the start/end balance manually: it "
|
||||||
|
"will prevent the display of wrong information in the accounting "
|
||||||
|
"dashboard and on bank statements.")
|
||||||
|
# Used to set default user_type_id on account fields
|
||||||
|
account_type_current_liabilities_id = fields.Many2one(
|
||||||
|
'account.account.type',
|
||||||
|
default=lambda self: self.env.ref('account.data_account_type_current_liabilities').id)
|
||||||
|
account_type_current_assets_id = fields.Many2one(
|
||||||
|
'account.account.type',
|
||||||
|
default=lambda self: self.env.ref('account.data_account_type_current_assets').id)
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
'name', 'currency_id', 'company_id', 'company_id.currency_id', 'code')
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
if self._context.get('journal_show_code_only'):
|
||||||
|
for journal in self:
|
||||||
|
res.append((journal.id, journal.code))
|
||||||
|
return res
|
||||||
|
else:
|
||||||
|
for journal in self:
|
||||||
|
name = "[%s] %s" % (journal.code, journal.name)
|
||||||
|
if (
|
||||||
|
journal.currency_id and
|
||||||
|
journal.currency_id != journal.company_id.currency_id):
|
||||||
|
name = "%s (%s)" % (name, journal.currency_id.name)
|
||||||
|
res.append((journal.id, name))
|
||||||
|
return res
|
||||||
|
|
||||||
|
# @api.constrains('default_credit_account_id', 'default_debit_account_id')
|
||||||
|
# def _check_account_type_on_bank_journal(self):
|
||||||
|
# bank_acc_type = self.env.ref('account.data_account_type_liquidity')
|
||||||
|
# for jrl in self:
|
||||||
|
# if jrl.type in ('bank', 'cash'):
|
||||||
|
# if (
|
||||||
|
# jrl.default_debit_account_id and
|
||||||
|
# jrl.default_debit_account_id.user_type_id !=
|
||||||
|
# bank_acc_type):
|
||||||
|
# raise ValidationError(_(
|
||||||
|
# "On journal '%s', the default debit account '%s' "
|
||||||
|
# "should be configured with Type = 'Bank and Cash'.")
|
||||||
|
# % (jrl.display_name,
|
||||||
|
# jrl.default_debit_account_id.display_name))
|
||||||
|
# if (
|
||||||
|
# jrl.default_credit_account_id and
|
||||||
|
# jrl.default_credit_account_id.user_type_id !=
|
||||||
|
# bank_acc_type):
|
||||||
|
# raise ValidationError(_(
|
||||||
|
# "On journal '%s', the default credit account '%s' "
|
||||||
|
# "should be configured with Type = 'Bank and Cash'.")
|
||||||
|
# % (jrl.display_name,
|
||||||
|
# jrl.default_credit_account_id.display_name))
|
||||||
301
account_usability/models/account_move.py
Normal file
301
account_usability/models/account_move.py
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.tools import float_is_zero
|
||||||
|
from odoo.tools.misc import format_date
|
||||||
|
from odoo.osv import expression
|
||||||
|
from datetime import timedelta
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMove(models.Model):
|
||||||
|
_inherit = 'account.move'
|
||||||
|
|
||||||
|
# By default, we can still modify "ref" when account move is posted
|
||||||
|
# which seems a bit lazy for me...
|
||||||
|
ref = fields.Char(states={'posted': [('readonly', True)]})
|
||||||
|
date = fields.Date(tracking=True)
|
||||||
|
invoice_date_due = fields.Date(tracking=True)
|
||||||
|
invoice_payment_term_id = fields.Many2one(tracking=True)
|
||||||
|
journal_id = fields.Many2one(tracking=True)
|
||||||
|
partner_bank_id = fields.Many2one(tracking=True)
|
||||||
|
fiscal_position_id = fields.Many2one(tracking=True)
|
||||||
|
amount_total = fields.Monetary(tracking=True)
|
||||||
|
# for invoice report
|
||||||
|
has_discount = fields.Boolean(
|
||||||
|
compute='_compute_has_discount', readonly=True)
|
||||||
|
# has_attachment is useful for those who use attachment to archive
|
||||||
|
# supplier invoices. It allows them to find supplier invoices
|
||||||
|
# that don't have any attachment
|
||||||
|
has_attachment = fields.Boolean(
|
||||||
|
compute='_compute_has_attachment',
|
||||||
|
search='_search_has_attachment', readonly=True)
|
||||||
|
sale_dates = fields.Char(
|
||||||
|
compute="_compute_sales_dates", readonly=True,
|
||||||
|
help="This information appears on invoice qweb report "
|
||||||
|
"(you may use it for your own report)")
|
||||||
|
|
||||||
|
def _compute_has_discount(self):
|
||||||
|
prec = self.env['decimal.precision'].precision_get('Discount')
|
||||||
|
for inv in self:
|
||||||
|
has_discount = False
|
||||||
|
for line in inv.invoice_line_ids:
|
||||||
|
if not line.display_type and not float_is_zero(line.discount, precision_digits=prec):
|
||||||
|
has_discount = True
|
||||||
|
break
|
||||||
|
inv.has_discount = has_discount
|
||||||
|
|
||||||
|
def _compute_has_attachment(self):
|
||||||
|
iao = self.env['ir.attachment']
|
||||||
|
for move in self:
|
||||||
|
if iao.search_count([
|
||||||
|
('res_model', '=', 'account.move'),
|
||||||
|
('res_id', '=', move.id),
|
||||||
|
('type', '=', 'binary'),
|
||||||
|
('company_id', '=', move.company_id.id)]):
|
||||||
|
move.has_attachment = True
|
||||||
|
else:
|
||||||
|
move.has_attachment = False
|
||||||
|
|
||||||
|
def _search_has_attachment(self, operator, value):
|
||||||
|
att_inv_ids = {}
|
||||||
|
if operator == '=':
|
||||||
|
search_res = self.env['ir.attachment'].search_read([
|
||||||
|
('res_model', '=', 'account.move'),
|
||||||
|
('type', '=', 'binary'),
|
||||||
|
('res_id', '!=', False)], ['res_id'])
|
||||||
|
for att in search_res:
|
||||||
|
att_inv_ids[att['res_id']] = True
|
||||||
|
res = [('id', value and 'in' or 'not in', list(att_inv_ids))]
|
||||||
|
return res
|
||||||
|
|
||||||
|
# when you have an invoice created from a lot of sale orders, the 'name'
|
||||||
|
# field is very large, which makes the name_get() of that invoice very big
|
||||||
|
# which screws-up the form view of that invoice because of the link at the
|
||||||
|
# top of the screen
|
||||||
|
# That's why we have to cut the name_get() when it's too long
|
||||||
|
def name_get(self):
|
||||||
|
old_res = super().name_get()
|
||||||
|
res = []
|
||||||
|
for old_re in old_res:
|
||||||
|
name = old_re[1]
|
||||||
|
if name and len(name) > 100:
|
||||||
|
# nice cut
|
||||||
|
name = '%s ...' % ', '.join(name.split(', ')[:3])
|
||||||
|
# if not enough, hard cut
|
||||||
|
if len(name) > 120:
|
||||||
|
name = '%s ...' % old_re[1][:120]
|
||||||
|
res.append((old_re[0], name))
|
||||||
|
return res
|
||||||
|
|
||||||
|
# I really hate to see a "/" in the 'name' field of the account.move.line
|
||||||
|
# generated from customer invoices linked to the partners' account because:
|
||||||
|
# 1) the label of an account move line is an important field, we can't
|
||||||
|
# write a rubbish '/' in it !
|
||||||
|
# 2) the 'name' field of the account.move.line is used in the overdue
|
||||||
|
# letter, and '/' is not meaningful for our customer !
|
||||||
|
# TODO mig to v12
|
||||||
|
# def action_move_create(self):
|
||||||
|
# res = super().action_move_create()
|
||||||
|
# for inv in self:
|
||||||
|
# self._cr.execute(
|
||||||
|
# "UPDATE account_move_line SET name= "
|
||||||
|
# "CASE WHEN name='/' THEN %s "
|
||||||
|
# "ELSE %s||' - '||name END "
|
||||||
|
# "WHERE move_id=%s", (inv.number, inv.number, inv.move_id.id))
|
||||||
|
# self.invalidate_cache()
|
||||||
|
# return res
|
||||||
|
|
||||||
|
def delete_lines_qty_zero(self):
|
||||||
|
lines = self.env['account.move.line'].search([
|
||||||
|
('display_type', '=', False),
|
||||||
|
('move_id', 'in', self.ids),
|
||||||
|
('quantity', '=', 0)])
|
||||||
|
lines.unlink()
|
||||||
|
return True
|
||||||
|
|
||||||
|
# for report
|
||||||
|
def py3o_lines_layout(self):
|
||||||
|
self.ensure_one()
|
||||||
|
res = []
|
||||||
|
has_sections = False
|
||||||
|
subtotal = 0.0
|
||||||
|
sign = self.move_type == 'out_refund' and -1 or 1
|
||||||
|
# Warning: the order of invoice line is forced in the view
|
||||||
|
# <tree editable="bottom" default_order="sequence, date desc, move_name desc, id"
|
||||||
|
# it's not the same as the _order in the class AccountMoveLine
|
||||||
|
lines = self.env['account.move.line'].search([('exclude_from_invoice_tab', '=', False), ('move_id', '=', self.id)], order="sequence, date desc, move_name desc, id")
|
||||||
|
for line in lines:
|
||||||
|
if line.display_type == 'line_section':
|
||||||
|
# insert line
|
||||||
|
if has_sections:
|
||||||
|
res.append({'subtotal': subtotal})
|
||||||
|
subtotal = 0.0 # reset counter
|
||||||
|
has_sections = True
|
||||||
|
else:
|
||||||
|
if not line.display_type:
|
||||||
|
subtotal += line.price_subtotal * sign
|
||||||
|
res.append({'line': line})
|
||||||
|
if has_sections: # insert last subtotal line
|
||||||
|
res.append({'subtotal': subtotal})
|
||||||
|
# res:
|
||||||
|
# [
|
||||||
|
# {'line': account_invoice_line(1) with display_type=='line_section'},
|
||||||
|
# {'line': account_invoice_line(2) without display_type},
|
||||||
|
# {'line': account_invoice_line(3) without display_type},
|
||||||
|
# {'line': account_invoice_line(4) with display_type=='line_note'},
|
||||||
|
# {'subtotal': 8932.23},
|
||||||
|
# ]
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _compute_sales_dates(self):
|
||||||
|
""" French law requires to set sale order dates into invoice
|
||||||
|
returned string: "sale1 (date1), sale2 (date2) ..."
|
||||||
|
"""
|
||||||
|
for move in self:
|
||||||
|
sales = move.invoice_line_ids.mapped(
|
||||||
|
'sale_line_ids').mapped('order_id')
|
||||||
|
dates = ["%s (%s)" % (
|
||||||
|
x.name, format_date(move.env, x.date_order))
|
||||||
|
for x in sales]
|
||||||
|
move.sale_dates = ", ".join(dates)
|
||||||
|
|
||||||
|
# allow to manually create moves not only in general journals,
|
||||||
|
# but also in cash journal and check journals (= bank journals not linked to a bank account)
|
||||||
|
@api.depends('company_id', 'invoice_filter_type_domain')
|
||||||
|
def _compute_suitable_journal_ids(self):
|
||||||
|
for move in self:
|
||||||
|
if move.invoice_filter_type_domain:
|
||||||
|
super(AccountMove, move)._compute_suitable_journal_ids()
|
||||||
|
else:
|
||||||
|
company_id = move.company_id.id or self.env.company.id
|
||||||
|
domain = expression.AND([
|
||||||
|
[('company_id', '=', company_id)],
|
||||||
|
expression.OR([
|
||||||
|
[('type', 'in', ('general', 'cash'))],
|
||||||
|
[('type', '=', 'bank'), ('bank_account_id', '=', False)]
|
||||||
|
])
|
||||||
|
])
|
||||||
|
move.suitable_journal_ids = self.env['account.journal'].search(domain)
|
||||||
|
|
||||||
|
def button_draft(self):
|
||||||
|
super().button_draft()
|
||||||
|
# Delete attached pdf invoice
|
||||||
|
try:
|
||||||
|
report_invoice = self.env['ir.actions.report']._get_report_from_name('account.report_invoice')
|
||||||
|
except IndexError:
|
||||||
|
report_invoice = False
|
||||||
|
if report_invoice and report_invoice.attachment:
|
||||||
|
for move in self.filtered(lambda x: x.move_type in ('out_invoice', 'out_refund')):
|
||||||
|
# The pb is that the filename is dynamic and related to move.state
|
||||||
|
# in v12, the feature was native and they used that kind of code:
|
||||||
|
# with invoice.env.do_in_draft():
|
||||||
|
# invoice.number, invoice.state = invoice.move_name, 'open'
|
||||||
|
# attachment = self.env.ref('account.account_invoices').retrieve_attachment(invoice)
|
||||||
|
# But do_in_draft() doesn't exists in v14
|
||||||
|
# If you know how we could do that, please update the code below
|
||||||
|
attachment = self.env['ir.attachment'].search([
|
||||||
|
('name', '=', self._get_invoice_attachment_name()),
|
||||||
|
('res_id', '=', move.id),
|
||||||
|
('res_model', '=', self._name),
|
||||||
|
('type', '=', 'binary'),
|
||||||
|
], limit=1)
|
||||||
|
if attachment:
|
||||||
|
attachment.unlink()
|
||||||
|
|
||||||
|
def _get_invoice_attachment_name(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return '%s.pdf' % (self.name and self.name.replace('/', '_') or 'INV')
|
||||||
|
|
||||||
|
def _get_accounting_date(self, invoice_date, has_tax):
|
||||||
|
# On vendor bills/refunds, we want date = invoice_date unless
|
||||||
|
# we have a company tax_lock_date and the invoice has taxes
|
||||||
|
# and invoice_date <= tax_lock_date
|
||||||
|
date = super()._get_accounting_date(invoice_date, has_tax)
|
||||||
|
if self.is_purchase_document(include_receipts=True):
|
||||||
|
tax_lock_date = self.company_id.tax_lock_date
|
||||||
|
if invoice_date and tax_lock_date and has_tax and invoice_date <= tax_lock_date:
|
||||||
|
invoice_date = tax_lock_date + timedelta(days=1)
|
||||||
|
date = invoice_date
|
||||||
|
return date
|
||||||
|
|
||||||
|
|
||||||
|
class AccountMoveLine(models.Model):
|
||||||
|
_inherit = 'account.move.line'
|
||||||
|
# Native order:
|
||||||
|
# _order = "date desc, move_name desc, id"
|
||||||
|
# Problem: when you manually create a journal entry, the
|
||||||
|
# order of the lines is inverted when you save ! It is quite annoying for
|
||||||
|
# the user...
|
||||||
|
_order = "date desc, id asc"
|
||||||
|
|
||||||
|
# In the 'account' module, we have related stored field for:
|
||||||
|
# name (move_name), date, ref, state (parent_state),
|
||||||
|
# journal_id, company_id, payment_id, statement_line_id,
|
||||||
|
account_reconcile = fields.Boolean(related='account_id.reconcile')
|
||||||
|
full_reconcile_id = fields.Many2one(string='Full Reconcile')
|
||||||
|
matched_debit_ids = fields.One2many(string='Partial Reconcile Debit')
|
||||||
|
matched_credit_ids = fields.One2many(string='Partial Reconcile Credit')
|
||||||
|
reconcile_string = fields.Char(
|
||||||
|
compute='_compute_reconcile_string', string='Reconcile', store=True)
|
||||||
|
# for optional display in tree view
|
||||||
|
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
|
||||||
|
balance = fields.Monetary(
|
||||||
|
string='Balance',
|
||||||
|
default=0.0,
|
||||||
|
currency_field='company_currency_id',
|
||||||
|
compute="_compute_balance",
|
||||||
|
store=True)
|
||||||
|
|
||||||
|
@api.depends("credit", "debit")
|
||||||
|
def _compute_balance(self):
|
||||||
|
for line in self:
|
||||||
|
line.balance = line.debit - line.credit
|
||||||
|
|
||||||
|
def show_account_move_form(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||||
|
'account.action_move_line_form')
|
||||||
|
action.update({
|
||||||
|
'res_id': self.move_id.id,
|
||||||
|
'view_id': False,
|
||||||
|
'views': False,
|
||||||
|
'view_mode': 'form,tree',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
'full_reconcile_id', 'matched_debit_ids', 'matched_credit_ids')
|
||||||
|
def _compute_reconcile_string(self):
|
||||||
|
for line in self:
|
||||||
|
rec_str = False
|
||||||
|
if line.full_reconcile_id:
|
||||||
|
rec_str = line.full_reconcile_id.name
|
||||||
|
else:
|
||||||
|
rec_str = ', '.join([
|
||||||
|
'a%d' % pr.id for pr in line.matched_debit_ids + line.matched_credit_ids])
|
||||||
|
line.reconcile_string = rec_str
|
||||||
|
|
||||||
|
def _get_computed_name(self):
|
||||||
|
# This is useful when you want to have the product code in a dedicated
|
||||||
|
# column in your customer invoice report
|
||||||
|
# The same ir.config_parameter is used in sale_usability,
|
||||||
|
# purchase_usability and account_usability
|
||||||
|
no_product_code_param = self.env['ir.config_parameter'].sudo().get_param(
|
||||||
|
'usability.line_name_no_product_code')
|
||||||
|
if no_product_code_param and no_product_code_param == 'True':
|
||||||
|
self = self.with_context(display_default_code=False)
|
||||||
|
return super()._get_computed_name()
|
||||||
|
|
||||||
|
def reconcile(self):
|
||||||
|
"""Explicit error message if unposted lines"""
|
||||||
|
unposted_ids = self.filtered(lambda l: l.move_id.state != "posted")
|
||||||
|
if unposted_ids:
|
||||||
|
m = _("Please post the following entries before reconciliation :")
|
||||||
|
sep = "\n - "
|
||||||
|
unpost = sep.join([am.display_name for am in unposted_ids.move_id])
|
||||||
|
raise UserError(m + sep + unpost)
|
||||||
|
|
||||||
|
return super().reconcile()
|
||||||
23
account_usability/models/account_partial_reconcile.py
Normal file
23
account_usability/models/account_partial_reconcile.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
from odoo.tools.misc import formatLang
|
||||||
|
|
||||||
|
|
||||||
|
class AccountPartialReconcile(models.Model):
|
||||||
|
_inherit = "account.partial.reconcile"
|
||||||
|
_rec_name = "id"
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for rec in self:
|
||||||
|
# There is no seq for partial rec, so I simulate one with the ID
|
||||||
|
# Prefix for full rec: 'A' (upper case)
|
||||||
|
# Prefix for partial rec: 'a' (lower case)
|
||||||
|
amount_fmt = formatLang(
|
||||||
|
self.env, rec.amount, currency_obj=rec.company_currency_id)
|
||||||
|
name = 'a%d (%s)' % (rec.id, amount_fmt)
|
||||||
|
res.append((rec.id, name))
|
||||||
|
return res
|
||||||
45
account_usability/models/product.py
Normal file
45
account_usability/models/product.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Copyright 2015-2021 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
# DON'T put store=True on those fields, because they are company dependent
|
||||||
|
sale_price_type = fields.Selection(
|
||||||
|
'_sale_purchase_price_type_sel', compute='_compute_sale_price_type',
|
||||||
|
string='Sale Price Type', compute_sudo=False, readonly=True)
|
||||||
|
purchase_price_type = fields.Selection(
|
||||||
|
'_sale_purchase_price_type_sel', compute='_compute_purchase_price_type',
|
||||||
|
string='Purchase Price Type', compute_sudo=False, readonly=True)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _sale_purchase_price_type_sel(self):
|
||||||
|
return [('incl', _('Tax incl.')), ('excl', _('Tax excl.'))]
|
||||||
|
|
||||||
|
@api.depends('taxes_id')
|
||||||
|
def _compute_sale_price_type(self):
|
||||||
|
for pt in self:
|
||||||
|
sale_price_type = 'incl'
|
||||||
|
if pt.taxes_id and all([not t.price_include for t in pt.taxes_id if t.amount_type == 'percent']):
|
||||||
|
sale_price_type = 'excl'
|
||||||
|
pt.sale_price_type = sale_price_type
|
||||||
|
|
||||||
|
@api.depends('supplier_taxes_id')
|
||||||
|
def _compute_purchase_price_type(self):
|
||||||
|
for pt in self:
|
||||||
|
purchase_price_type = 'incl'
|
||||||
|
if pt.supplier_taxes_id and all([not t.price_include for t in pt.supplier_taxes_id if t.amount_type == 'percent']):
|
||||||
|
purchase_price_type = 'excl'
|
||||||
|
pt.purchase_price_type = purchase_price_type
|
||||||
|
|
||||||
|
|
||||||
|
class ProductSupplierinfo(models.Model):
|
||||||
|
_inherit = 'product.supplierinfo'
|
||||||
|
|
||||||
|
# DON'T put store=True on those fields, because they are company dependent
|
||||||
|
purchase_price_type = fields.Selection(
|
||||||
|
related='product_tmpl_id.purchase_price_type', related_sudo=False)
|
||||||
21
account_usability/models/res_company.py
Normal file
21
account_usability/models/res_company.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Copyright 2021 Akretion France (https://akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResCompany(models.Model):
|
||||||
|
_inherit = 'res.company'
|
||||||
|
|
||||||
|
# There is a native field invoice_terms which is displayed on res.config.settings
|
||||||
|
# when the ir.config_parameter account.use_invoice_terms is True
|
||||||
|
# But there are several problems with this native field:
|
||||||
|
# - it is copied on the 'narration' field of account.move => we don't want that
|
||||||
|
# - the text block is very small on the form view of res.config.settings
|
||||||
|
# So I decided to have our own field "fixed_invoice_terms"
|
||||||
|
# The native field can still be used when you need to customise some
|
||||||
|
# terms and conditions on each invoice (not very common, but...)
|
||||||
|
# To underline this different with the native field, I prefix it with 'static_'
|
||||||
|
static_invoice_terms = fields.Text(
|
||||||
|
translate=True, string="Legal Terms on Invoice")
|
||||||
12
account_usability/models/res_partner.py
Normal file
12
account_usability/models/res_partner.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright 2017-2020 Akretion France (https://akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
invoice_warn = fields.Selection(tracking=True)
|
||||||
|
property_account_position_id = fields.Many2one(tracking=True)
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# Copyright 2017-2019 Akretion France (https://akretion.com/)
|
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import models, fields
|
|
||||||
|
|
||||||
|
|
||||||
class ResPartner(models.Model):
|
|
||||||
_inherit = 'res.partner'
|
|
||||||
|
|
||||||
invoice_warn = fields.Selection(track_visibility='onchange')
|
|
||||||
property_account_position_id = fields.Many2one(
|
|
||||||
track_visibility='onchange')
|
|
||||||
3
account_usability/security/ir.model.access.csv
Normal file
3
account_usability/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_account_group_generate_full,Full access on account.group.generate,model_account_group_generate,account.group_account_manager,1,1,1,1
|
||||||
|
access_account_invoice_mark_sent_full,Full access on account.invoice.mark.sent,model_account_invoice_mark_sent,account.group_account_invoice,1,1,1,1
|
||||||
|
19
account_usability/static/src/xml/account_payment.xml
Normal file
19
account_usability/static/src/xml/account_payment.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2022 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
|
||||||
|
<!-- Requires https://github.com/odoo/odoo/pull/84180 -->
|
||||||
|
<t t-extend="ShowPaymentInfo" >
|
||||||
|
<t t-jquery="td:first" t-operation="after">
|
||||||
|
<td style="max-width: 25em;" id="outstanding-date">
|
||||||
|
<div class="oe_form_field" style="margin-right: 5px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;"><t t-esc="line.date"></t></div>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
41
account_usability/views/account_account.xml
Normal file
41
account_usability/views/account_account.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.account.form</field>
|
||||||
|
<field name="model">account.account</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="deprecated" position="before">
|
||||||
|
<field name="reconcile" attrs="{'invisible': ['|', ('internal_type','=','liquidity'), ('internal_group', '=', 'off_balance')]}"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_search" model="ir.ui.view">
|
||||||
|
<field name="name">account.account.search</field>
|
||||||
|
<field name="model">account.account</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- The native "name" filter uses a domain ['|', ('name','ilike',self), ('code','=like',str(self)+'%')]
|
||||||
|
This is good because it uses '=like' on 'code', but sometimes there are digits in account names,
|
||||||
|
so you get additionnal unexpected accounts in the result of the search -->
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="code" filter_domain="[('code', '=like', str(self)+'%')]" string="Code"/>
|
||||||
|
</field>
|
||||||
|
<filter name="accounttype" position="after">
|
||||||
|
<filter name="group_groupby" string="Group" context="{'group_by': 'group_id'}"/>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
21
account_usability/views/account_account_type.xml
Normal file
21
account_usability/views/account_account_type.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_account_type_tree" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.account_type_tree</field>
|
||||||
|
<field name="model">account.account.type</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_type_tree" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="type" position="after">
|
||||||
|
<field name="include_initial_balance" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
33
account_usability/views/account_analytic_account.xml
Normal file
33
account_usability/views/account_analytic_account.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_analytic_account_list" model="ir.ui.view">
|
||||||
|
<field name="model">account.analytic.account</field>
|
||||||
|
<field name="inherit_id" ref="analytic.view_account_analytic_account_list"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="code" position="after">
|
||||||
|
<field name="group_id" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_analytic_account_search" model="ir.ui.view">
|
||||||
|
<field name="model">account.analytic.account</field>
|
||||||
|
<field name="inherit_id" ref="analytic.view_account_analytic_account_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<filter name="associatedpartner" position="before">
|
||||||
|
<filter name="group_groupby" string="Group" context="{'group_by': 'group_id'}"/>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
22
account_usability/views/account_analytic_group.xml
Normal file
22
account_usability/views/account_analytic_group.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="account_analytic_group_tree_view" model="ir.ui.view">
|
||||||
|
<field name="model">account.analytic.group</field>
|
||||||
|
<field name="inherit_id" ref="analytic.account_analytic_group_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="parent_id" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
90
account_usability/views/account_bank_statement.xml
Normal file
90
account_usability/views/account_bank_statement.xml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_bank_statement_form" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.bank.statement.form</field>
|
||||||
|
<field name="model">account.bank.statement</field>
|
||||||
|
<field name="inherit_id" ref="account.view_bank_statement_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='line_ids']/tree/button[@name='button_undo_reconciliation']" position="after">
|
||||||
|
<field name="move_id" invisible="1"/>
|
||||||
|
<button name="show_account_move" type="object"
|
||||||
|
title="View Journal Entry" icon="fa-arrow-right"/>
|
||||||
|
</xpath>
|
||||||
|
<field name="date" position="after">
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
<field name="hide_bank_statement_balance" invisible="1"/>
|
||||||
|
</field>
|
||||||
|
<field name="date" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<label for="balance_start" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
||||||
|
</label>
|
||||||
|
<label for="balance_end_real" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
||||||
|
</label>
|
||||||
|
<xpath expr="//field[@name='balance_start']/.." position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='balance_end_real']/.." position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<group name="sale_total" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': [('hide_bank_statement_balance', '=', True)]}</attribute>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_bank_statement_tree" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.bank.statement.tree</field>
|
||||||
|
<field name="model">account.bank.statement</field>
|
||||||
|
<field name="inherit_id" ref="account.view_bank_statement_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="date" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="journal_id" position="after">
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_bank_statement_search" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.bank.statement.search</field>
|
||||||
|
<field name="model">account.bank.statement</field>
|
||||||
|
<field name="inherit_id" ref="account.view_bank_statement_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="date" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<filter name="filter_date" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</filter>
|
||||||
|
<filter name="date" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</filter>
|
||||||
|
<field name="date" position="after">
|
||||||
|
<field name="start_date"/>
|
||||||
|
<field name="end_date"/>
|
||||||
|
</field>
|
||||||
|
<filter name="date" position="after">
|
||||||
|
<filter name="start_date_groupby" string="Start Date"
|
||||||
|
context="{'group_by': 'start_date'}"/>
|
||||||
|
<filter name="end_date_groupby" string="End Date"
|
||||||
|
context="{'group_by': 'end_date'}"/>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
32
account_usability/views/account_group.xml
Normal file
32
account_usability/views/account_group.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_account_group_form" model="ir.ui.view">
|
||||||
|
<field name="model">account.group</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_group_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="parent_id"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_group_tree" model="ir.ui.view">
|
||||||
|
<field name="model">account.group</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_group_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="parent_id" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
63
account_usability/views/account_invoice_report.xml
Normal file
63
account_usability/views/account_invoice_report.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2018-2020 Akretion (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="account_invoice_report_tree" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.invoice.report.tree</field>
|
||||||
|
<field name="model">account.invoice.report</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Invoices Analysis">
|
||||||
|
<field name="move_id"/>
|
||||||
|
<field name="journal_id" optional="hide"/>
|
||||||
|
<field name="company_id" optional="hide" groups="base.group_multi_company"/>
|
||||||
|
<field name="invoice_date"/>
|
||||||
|
<field name="invoice_date_due"/>
|
||||||
|
<field name="move_type"/>
|
||||||
|
<field name="commercial_partner_id"/>
|
||||||
|
<field name="partner_id" optional="hide"/>
|
||||||
|
<field name="country_id" optional="hide"/>
|
||||||
|
<field name="industry_id" optional="hide"/>
|
||||||
|
<field name="invoice_user_id"/>
|
||||||
|
<field name="fiscal_position_id" optional="hide"/>
|
||||||
|
<field name="product_id"/>
|
||||||
|
<field name="product_categ_id" optional="hide"/>
|
||||||
|
<field name="account_id" optional="hide"/>
|
||||||
|
<field name="analytic_account_id" optional="hide" groups="analytic.group_analytic_accounting"/>
|
||||||
|
<field name="quantity" sum="1"/>
|
||||||
|
<field name="product_uom_id" groups="uom.group_uom"/>
|
||||||
|
<field name="price_subtotal" sum="1"/>
|
||||||
|
<field name="state"/>
|
||||||
|
<field name="payment_state" optional="hide"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
||||||
|
<field name="context">{'search_default_current': 1, 'search_default_supplier': 1, 'group_by': ['invoice_date']}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
||||||
|
<field name="view_mode">pivot,graph</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
|
||||||
|
<field name="context">{'search_default_current': 1, 'search_default_customer': 1, 'group_by': ['invoice_date']}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
||||||
|
<field name="view_mode">pivot,graph</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.invoice.report</field>
|
||||||
|
<field name="model">account.invoice.report</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_invoice_report_pivot"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<pivot position="attributes">
|
||||||
|
<attribute name="disable_linking"></attribute>
|
||||||
|
</pivot>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
70
account_usability/views/account_journal.xml
Normal file
70
account_usability/views/account_journal.xml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_account_journal_form" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.journal.form</field>
|
||||||
|
<field name="model">account.journal</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_journal_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="bank_statements_source" position="after">
|
||||||
|
<field name="hide_bank_statement_balance" groups="account.group_account_readonly"/>
|
||||||
|
<field name="account_type_current_liabilities_id" invisible="1"/>
|
||||||
|
<field name="account_type_current_assets_id" invisible="1"/>
|
||||||
|
</field>
|
||||||
|
<field name="suspense_account_id" position="attributes">
|
||||||
|
<attribute name="context">{'default_user_type_id': account_type_current_liabilities_id, 'default_reconcile': True}</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="payment_debit_account_id" position="attributes">
|
||||||
|
<attribute name="context">{'default_user_type_id': account_type_current_assets_id, 'default_reconcile': True}</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="payment_credit_account_id" position="attributes">
|
||||||
|
<attribute name="context">{'default_user_type_id': account_type_current_assets_id, 'default_reconcile': True}</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account_journal_dashboard_kanban_view" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.journal.dashboard</field>
|
||||||
|
<field name="model">account.journal</field>
|
||||||
|
<field name="inherit_id" ref="account.account_journal_dashboard_kanban_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="kanban_dashboard" position="after">
|
||||||
|
<field name="hide_bank_statement_balance"/>
|
||||||
|
</field>
|
||||||
|
<xpath expr="//div[@name='latest_statement']/.." position="attributes">
|
||||||
|
<attribute name="t-if">dashboard.has_at_least_one_statement and dashboard.account_balance != dashboard.last_balance and !record.hide_bank_statement_balance.raw_value</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_journal_tree" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.journal.tree</field>
|
||||||
|
<field name="model">account.journal</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_journal_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="code" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_journal_search" model="ir.ui.view">
|
||||||
|
<field name="name">usability.account.journal.search</field>
|
||||||
|
<field name="model">account.journal</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_journal_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<filter name="inactive" position="after">
|
||||||
|
<group name="groupby" string="Group By">
|
||||||
|
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
|
||||||
|
</group>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
16
account_usability/views/account_menu.xml
Normal file
16
account_usability/views/account_menu.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- Duplicate the menu "Sales > Configuration > Contacts > Bank Accounts"
|
||||||
|
under "Accounting > Configuration", because most users will try to find it there -->
|
||||||
|
<menuitem id="res_bank_account_config_menu" action="base.action_res_bank_form" parent="account.account_banks_menu" sequence="10"/>
|
||||||
|
|
||||||
|
<menuitem id="res_partner_bank_account_config_menu" action="base.action_res_partner_bank_account_form" parent="account.account_banks_menu" sequence="20"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
115
account_usability/views/account_move.xml
Normal file
115
account_usability/views/account_move.xml
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_move_form" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.account.move.form</field>
|
||||||
|
<field name="model">account.move</field>
|
||||||
|
<field name="inherit_id" ref="account.view_move_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="fiscal_position_id" position="attributes">
|
||||||
|
<attribute name="widget">selection</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="invoice_incoterm_id" position="attributes">
|
||||||
|
<attribute name="widget">selection</attribute>
|
||||||
|
</field>
|
||||||
|
<button name="action_register_payment" position="attributes">
|
||||||
|
<attribute name="class">btn-default</attribute>
|
||||||
|
</button>
|
||||||
|
<button name="action_register_payment" position="before">
|
||||||
|
<button name="%(account.account_invoices)d" type="action" string="Print" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}"/>
|
||||||
|
</button>
|
||||||
|
<button name="preview_invoice" position="attributes">
|
||||||
|
<attribute name="attrs">{'invisible': 1}</attribute>
|
||||||
|
</button>
|
||||||
|
<!-- move sent field and make it visible -->
|
||||||
|
<field name="is_move_sent" position="replace"/>
|
||||||
|
<field name="invoice_origin" position="attributes">
|
||||||
|
<attribute name="invisible">0</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="invoice_origin" position="after">
|
||||||
|
<field name="is_move_sent" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}"/>
|
||||||
|
</field>
|
||||||
|
<xpath expr="//field[@name='line_ids']/tree/field[@name='analytic_account_id']" position="attributes">
|
||||||
|
<attribute name="optional">show</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='line_ids']/tree/field[@name='tax_tag_ids']" position="after">
|
||||||
|
<field name="matching_number" optional="hide"/>
|
||||||
|
<field name="reconcile_string" optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
|
||||||
|
<field name="product_barcode" optional="hide"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_invoice_filter" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.account.move.search</field>
|
||||||
|
<field name="model">account.move</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<filter name="due_date" position="after">
|
||||||
|
<separator/>
|
||||||
|
<filter name="to_send" string="To Send" domain="[('is_move_sent', '=', False), ('state', '=', 'posted'), ('move_type', 'in', ('out_invoice', 'out_refund'))]"/>
|
||||||
|
<filter name="sent" string="Sent" domain="[('is_move_sent', '=', True), ('move_type', 'in', ('out_invoice', 'out_refund'))]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_move_line_tree" model="ir.ui.view">
|
||||||
|
<field name="model">account.move.line</field>
|
||||||
|
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="matching_number" position="after">
|
||||||
|
<button title="View Journal Entry Form" type="object" name="show_account_move_form" icon="fa-arrow-right"/>
|
||||||
|
</field>
|
||||||
|
<field name="credit" position="after">
|
||||||
|
<field name="balance" sum="Balance" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_account_move_line_filter" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.account_move_line_search</field>
|
||||||
|
<field name="model">account.move.line</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<filter name="unposted" position="before">
|
||||||
|
<filter name="current_year" string="Current Year" domain="[('date', '>=', (context_today().strftime('%Y-01-01'))), ('date', '<=', (context_today().strftime('%Y-12-31')))]"/>
|
||||||
|
<filter name="previous_year" string="Previous Year" domain="[('date', '>=', (context_today() + relativedelta(day=1, month=1, years=-1)).strftime('%Y-%m-%d')), ('date', '<=', (context_today() + relativedelta(day=31, month=12, years=-1)).strftime('%Y-%m-%d'))]"/>
|
||||||
|
<separator/>
|
||||||
|
</filter>
|
||||||
|
<field name="partner_id" position="after">
|
||||||
|
<field name="reconcile_string" />
|
||||||
|
<field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/>
|
||||||
|
</field>
|
||||||
|
<filter name="unreconciled" position="before">
|
||||||
|
<filter name="reconciled" string="Fully Reconciled" domain="[('reconciled', '=', True)]"/>
|
||||||
|
</filter>
|
||||||
|
<filter name="unreconciled" position="attributes">
|
||||||
|
<attribute name="string">Unreconciled or Partially Reconciled</attribute>
|
||||||
|
<attribute name="domain">[('reconciled', '=', False), ('balance', '!=', 0), ('account_id.reconcile', '=', True)]</attribute>
|
||||||
|
</filter>
|
||||||
|
<!--
|
||||||
|
<field name="name" position="attributes">
|
||||||
|
<attribute name="string">Name or Reference</attribute>
|
||||||
|
</field> -->
|
||||||
|
<field name="partner_id" position="attributes">
|
||||||
|
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account.action_move_journal_line" model="ir.actions.act_window">
|
||||||
|
<field name="context">{'default_move_type': 'entry', 'view_no_maturity': True}</field>
|
||||||
|
<!-- Remove 'search_default_misc_filter': 1 -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
21
account_usability/views/account_report.xml
Normal file
21
account_usability/views/account_report.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2018-2020 Akretion (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="account.account_invoices" model="ir.actions.report">
|
||||||
|
<!-- Attach only on customer invoices/refunds -->
|
||||||
|
<field name="attachment">(object.move_type in ('out_invoice', 'out_refund')) and (object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.pdf')</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account.account_invoices_without_payment" model="ir.actions.report">
|
||||||
|
<!-- Attach only on customer invoices/refunds -->
|
||||||
|
<field name="attachment">(object.move_type in ('out_invoice', 'out_refund')) and (object.state == 'posted') and ((object.name or 'INV').replace('/','_')+'.pdf')</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
20
account_usability/views/account_tax.xml
Normal file
20
account_usability/views/account_tax.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_tax_tree" model="ir.ui.view">
|
||||||
|
<field name="model">account.tax</field>
|
||||||
|
<field name="inherit_id" ref="account.view_tax_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="description" position="after">
|
||||||
|
<field name="price_include" optional="show"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
72
account_usability/views/product.xml
Normal file
72
account_usability/views/product.xml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2017-2020 Akretion (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- In the official account module, on product category and product template,
|
||||||
|
some fields/groups are on account.group_account_invoice, some on
|
||||||
|
account.group_account_user and some on account.group_account_manager
|
||||||
|
Here, we set all those fields on account.group_account_invoice
|
||||||
|
-->
|
||||||
|
|
||||||
|
<record id="product_template_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.product.template.form</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="priority">100</field> <!-- when you replace a field, it's always better to inherit at the end -->
|
||||||
|
<field name="inherit_id" ref="account.product_template_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="property_account_income_id" position="attributes">
|
||||||
|
<attribute name="groups">account.group_account_invoice</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="property_account_expense_id" position="attributes">
|
||||||
|
<attribute name="groups">account.group_account_invoice</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="list_price" position="replace">
|
||||||
|
<div name="list_price">
|
||||||
|
<field name="list_price" widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}" class="oe_inline"/>
|
||||||
|
<label for="sale_price_type" string=" "/>
|
||||||
|
<field name="sale_price_type"/>
|
||||||
|
</div>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_category_property_form" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.product.category.form</field>
|
||||||
|
<field name="model">product.category</field>
|
||||||
|
<field name="inherit_id" ref="account.view_category_property_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<group name="account_property" position="attributes">
|
||||||
|
<attribute name="groups">account.group_account_invoice</attribute>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.product.supplierinfo.form</field>
|
||||||
|
<field name="model">product.supplierinfo</field>
|
||||||
|
<field name="inherit_id" ref="product.product_supplierinfo_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="currency_id" position="after">
|
||||||
|
<field name="purchase_price_type"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.product.supplierinfo.tree</field>
|
||||||
|
<field name="model">product.supplierinfo</field>
|
||||||
|
<field name="inherit_id" ref="product.product_supplierinfo_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="price" position="after">
|
||||||
|
<field name="purchase_price_type" string="Tax"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
27
account_usability/views/res_company.xml
Normal file
27
account_usability/views/res_company.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Akretion (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_company_form" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability.res.company.form</field>
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<notebook position="inside">
|
||||||
|
<page string="Legal Terms" name="legal_terms">
|
||||||
|
<group string="Invoice Legal Terms" name="static_invoice_terms">
|
||||||
|
<field name="static_invoice_terms" nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
31
account_usability/views/res_config_settings.xml
Normal file
31
account_usability/views/res_config_settings.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||||
|
<field name="name">account_usability account config page</field>
|
||||||
|
<field name="model">res.config.settings</field>
|
||||||
|
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@id='bank_cash']" position="inside">
|
||||||
|
<div class="col-xs-12 col-md-6 o_setting_box" id="transfer_account">
|
||||||
|
<div class="o_setting_left_pane"/>
|
||||||
|
<div class="o_setting_right_pane">
|
||||||
|
<label for="transfer_account_id"/>
|
||||||
|
<div class="text-muted">
|
||||||
|
Transit account when you transfer money from a bank account of your company to another bank account of your company.
|
||||||
|
</div>
|
||||||
|
<field name="transfer_account_id"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2017-2019 Akretion (http://www.akretion.com/)
|
Copyright 2017-2020 Akretion (http://www.akretion.com/)
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
@@ -16,6 +16,9 @@
|
|||||||
<field name="property_account_position_id" position="attributes">
|
<field name="property_account_position_id" position="attributes">
|
||||||
<attribute name="widget">selection</attribute>
|
<attribute name="widget">selection</attribute>
|
||||||
</field>
|
</field>
|
||||||
|
<xpath expr="//field[@name='bank_ids']/tree/field[@name='acc_number']" position="after">
|
||||||
|
<field name="acc_type"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
from . import account_invoice_mark_sent
|
from . import account_invoice_mark_sent
|
||||||
from . import account_move_reversal
|
from . import account_move_reversal
|
||||||
|
from . import res_config_settings
|
||||||
|
from . import account_group_generate
|
||||||
|
|||||||
59
account_usability/wizard/account_group_generate.py
Normal file
59
account_usability/wizard/account_group_generate.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
|
class AccountGroupGenerate(models.TransientModel):
|
||||||
|
_name = 'account.group.generate'
|
||||||
|
_description = 'Generate Account Groups'
|
||||||
|
|
||||||
|
name_prefix = fields.Char(string='Prefix', required=True, default='Comptes')
|
||||||
|
level = fields.Integer(default=2, required=True)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.level < 1:
|
||||||
|
raise UserError(_("The level must be >= 1."))
|
||||||
|
ago = self.env['account.group']
|
||||||
|
aao = self.env['account.account']
|
||||||
|
company = self.env.company
|
||||||
|
groups = ago.search([('company_id', '=', company.id)])
|
||||||
|
if groups:
|
||||||
|
raise UserError(_(
|
||||||
|
"%d account groups already exists in company '%s'. This wizard is "
|
||||||
|
"designed to generate account groups from scratch.")
|
||||||
|
% (len(groups), company.display_name))
|
||||||
|
accounts = aao.search([('company_id', '=', company.id)])
|
||||||
|
struct = {'childs': {}}
|
||||||
|
for account in accounts:
|
||||||
|
if len(account.code) <= self.level:
|
||||||
|
raise UserError(_(
|
||||||
|
"The code of account '%s' is %d caracters. "
|
||||||
|
"It cannot be inferior to level (%d).")
|
||||||
|
% (account.display_name, len(account.code), self.level))
|
||||||
|
n = 1
|
||||||
|
parent = struct
|
||||||
|
gparent = False
|
||||||
|
while n <= self.level:
|
||||||
|
group_code = account.code[:n]
|
||||||
|
if group_code not in parent['childs']:
|
||||||
|
new_group = ago.create({
|
||||||
|
'name': '%s %s' % (self.name_prefix or '', group_code),
|
||||||
|
'code_prefix_start': group_code,
|
||||||
|
'parent_id': gparent and gparent.id or False,
|
||||||
|
'company_id': company.id,
|
||||||
|
})
|
||||||
|
parent['childs'][group_code] = {'obj': new_group, 'childs': {}}
|
||||||
|
parent = parent['childs'][group_code]
|
||||||
|
gparent = parent['obj']
|
||||||
|
n += 1
|
||||||
|
account.write({'group_id': gparent.id})
|
||||||
|
action = {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': _('Account Groups'),
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
'res_model': 'account.group',
|
||||||
|
}
|
||||||
|
return action
|
||||||
42
account_usability/wizard/account_group_generate_view.xml
Normal file
42
account_usability/wizard/account_group_generate_view.xml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2020 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="account_group_generate_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.group.generate.form</field>
|
||||||
|
<field name="model">account.group.generate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Generate account groups">
|
||||||
|
<p>
|
||||||
|
This wizard is designed to auto-generate account groups from the chart of account.
|
||||||
|
</p>
|
||||||
|
<group name="main">
|
||||||
|
<field name="name_prefix"/>
|
||||||
|
<field name="level"/>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button type="object" name="run" string="Generate" class="btn-primary"/>
|
||||||
|
<button special="cancel" string="Cancel"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="account_group_generate_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Generate Account Groups</field>
|
||||||
|
<field name="res_model">account.group.generate</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="account_group_generate_menu"
|
||||||
|
action="account_group_generate_action"
|
||||||
|
parent="account.account_account_menu"
|
||||||
|
sequence="51"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2017-2019 Akretion France (https://akretion.com/en)
|
# Copyright 2017-2020 Akretion France (https://akretion.com/en)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -12,12 +12,13 @@ class AccountInvoiceMarkSent(models.TransientModel):
|
|||||||
_description = 'Mark invoices as sent'
|
_description = 'Mark invoices as sent'
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
assert self.env.context.get('active_model') == 'account.invoice',\
|
assert self.env.context.get('active_model') == 'account.move',\
|
||||||
'Source model must be invoices'
|
'Source model must be invoices'
|
||||||
assert self.env.context.get('active_ids'), 'No invoices selected'
|
assert self.env.context.get('active_ids'), 'No invoices selected'
|
||||||
invoices = self.env['account.invoice'].search([
|
invoices = self.env['account.move'].search([
|
||||||
('id', 'in', self.env.context.get('active_ids')),
|
('id', 'in', self.env.context.get('active_ids')),
|
||||||
('state', 'in', ('open', 'paid'))])
|
('move_type', 'in', ('out_invoice', 'out_refund')),
|
||||||
invoices.write({'sent': True})
|
('state', '=', 'posted')])
|
||||||
|
invoices.write({'is_move_sent': True})
|
||||||
logger.info('Marking invoices with ID %s as sent', invoices.ids)
|
logger.info('Marking invoices with ID %s as sent', invoices.ids)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2017-2019 Akretion France
|
Copyright 2017-2020 Akretion France
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
@@ -23,14 +23,13 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<act_window id="account_invoice_mark_sent_action"
|
<record id="account_invoice_mark_sent_action" model="ir.actions.act_window">
|
||||||
multi="True"
|
<field name="name">Mark as Sent</field>
|
||||||
key2="client_action_multi"
|
<field name="res_model">account.invoice.mark.sent</field>
|
||||||
name="Mark as Sent"
|
<field name="view_mode">form</field>
|
||||||
res_model="account.invoice.mark.sent"
|
<field name="target">new</field>
|
||||||
src_model="account.invoice"
|
<field name="binding_model_id" ref="account.model_account_move" />
|
||||||
view_mode="form"
|
<field name="binding_view_types">list</field>
|
||||||
target="new"
|
</record>
|
||||||
groups="account.group_account_invoice" />
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2018-2019 Akretion France (https://akretion.com/)
|
# Copyright 2018-2020 Akretion France (https://akretion.com/)
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -11,14 +11,12 @@ class AccountMoveReversal(models.TransientModel):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_date(self):
|
def _default_date(self):
|
||||||
date = None
|
date_dt = None
|
||||||
if (
|
if (
|
||||||
self._context.get('active_model') == 'account.move' and
|
self._context.get('active_model') == 'account.move' and
|
||||||
self._context.get('active_id')):
|
self._context.get('active_id')):
|
||||||
move = self.env['account.move'].browse(self._context['active_id'])
|
move = self.env['account.move'].browse(self._context['active_id'])
|
||||||
date_dt = fields.Date.from_string(move.date) +\
|
date_dt = move.date + relativedelta(days=1)
|
||||||
relativedelta(days=1)
|
return date_dt
|
||||||
date = fields.Date.to_string(date_dt)
|
|
||||||
return date
|
|
||||||
|
|
||||||
date = fields.Date(default=_default_date)
|
date = fields.Date(default=_default_date)
|
||||||
|
|||||||
23
account_usability/wizard/account_payment_register_views.xml
Normal file
23
account_usability/wizard/account_payment_register_views.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- When you change the date, it resets the amount via the onchange
|
||||||
|
So, in the view, the date should be BEFORE the amount -->
|
||||||
|
<record id="view_account_payment_register_form" model="ir.ui.view">
|
||||||
|
<field name="model">account.payment.register</field>
|
||||||
|
<field name="inherit_id" ref="account.view_account_payment_register_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<label for="amount" position="before">
|
||||||
|
<field name="payment_date" position="move"/>
|
||||||
|
</label>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
12
account_usability/wizard/res_config_settings.py
Normal file
12
account_usability/wizard/res_config_settings.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResConfigSettings(models.TransientModel):
|
||||||
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
|
transfer_account_id = fields.Many2one(
|
||||||
|
related='company_id.transfer_account_id', readonly=False)
|
||||||
@@ -19,16 +19,12 @@ Base Company Extension
|
|||||||
|
|
||||||
|badge1| |badge2| |badge3|
|
|badge1| |badge2| |badge3|
|
||||||
|
|
||||||
This module adds the following fields to the ResCompany model:
|
This module adds the following fields on the company:
|
||||||
|
|
||||||
* Capital Amount
|
* Capital Amount
|
||||||
* Legal Type
|
* Legal Type
|
||||||
|
|
||||||
This is useful to display the legal name of the company in reports
|
This is useful to display the legal name of the company in reports.
|
||||||
|
|
||||||
**Table of contents**
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:local:
|
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
@@ -36,7 +32,7 @@ Bug Tracker
|
|||||||
Bugs are tracked on `GitHub Issues <https://github.com/akretion/odoo-usability/issues>`_.
|
Bugs are tracked on `GitHub Issues <https://github.com/akretion/odoo-usability/issues>`_.
|
||||||
In case of trouble, please check there if your issue has already been reported.
|
In case of trouble, please check there if your issue has already been reported.
|
||||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||||
`feedback <https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
`feedback <https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
Do not contact contributors directly about support or help with technical issues.
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Copyright 2014-2020 Akretion (http://www.akretion.com)
|
||||||
# Copyright 2014-2019 Akretion (http://www.akretion.com)
|
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Base Company Extension',
|
'name': 'Base Company Extension',
|
||||||
'version': '12.0.1.0.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Partner',
|
'category': 'Partner',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': 'Adds capital and title on company',
|
'summary': 'Adds capital and title on company',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2014-2019 Akretion (http://www.akretion.com)
|
# Copyright 2014-2020 Akretion (http://www.akretion.com)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ class ResCompany(models.Model):
|
|||||||
def _report_company_legal_name(self):
|
def _report_company_legal_name(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
if self.legal_type:
|
if self.legal_type:
|
||||||
name = u'%s %s' % (self.name, self.legal_type)
|
name = '%s %s' % (self.name, self.legal_type)
|
||||||
else:
|
else:
|
||||||
name = self.name
|
name = self.name
|
||||||
return name
|
return name
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2014-2019 Akretion (http://www.akretion.com/)
|
Copyright 2014-2020 Akretion (http://www.akretion.com/)
|
||||||
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
|
|||||||
1
base_dynamic_list/__init__.py
Normal file
1
base_dynamic_list/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
62
base_dynamic_list/__manifest__.py
Normal file
62
base_dynamic_list/__manifest__.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Copyright 2020-2021 Akretion France (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Base Dynamic List',
|
||||||
|
'version': '14.0.1.0.0',
|
||||||
|
'category': 'Tools',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'summary': 'Dynamic lists',
|
||||||
|
'description': """
|
||||||
|
Base Dynamic List
|
||||||
|
=================
|
||||||
|
|
||||||
|
Very often during an Odoo implementation, we need to add selection fields on a native objet, and we don't want to have a hard-coded selection list (fields.Selection), but a selection list that can be changed by users (Many2one field). For that, the developper needs to add a new object (with just a 'name' and 'sequence' field) with a form/tree view. The goal of this module is to speed-up this process by defining a dynamic list object that already has all the required views.
|
||||||
|
|
||||||
|
This module provides several ready-to-go objects:
|
||||||
|
|
||||||
|
* simple list : fields *name*, *sequence* and *active*
|
||||||
|
* translatable list : fields *name* with translate=True, *sequence* and *active*
|
||||||
|
* code list : fields *code* (unique), *name*, *sequence* and *active*
|
||||||
|
* translatable code list : fields *code* (unique), *name* with translate=True, *sequence* and *active*
|
||||||
|
|
||||||
|
These objects are readable by the employee group. The system group has full rights on it.
|
||||||
|
|
||||||
|
To use it, you need to do 2 or 3 things :
|
||||||
|
|
||||||
|
1) Add an entry in the domain field and the object you selected:
|
||||||
|
|
||||||
|
domain = fields.Selection(selection_add=[('risk.type', "Risk Type")], ondelete={"risk.type": "cascade"})
|
||||||
|
|
||||||
|
2) Add the many2one field on your object:
|
||||||
|
|
||||||
|
risk_type_id = fields.Many2one(
|
||||||
|
'dynamic.list', string="Risk Type",
|
||||||
|
ondelete='restrict', domain=[('domain', '=', 'risk.type')])
|
||||||
|
|
||||||
|
|
||||||
|
3) Optionally, you can add a dedicated action and a menu entry (otherwize, you can use the generic menu entry under *Settings > Technical > Dynamic Lists*:
|
||||||
|
|
||||||
|
<record id="dynamic_list_risk_type_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Risk Type</field>
|
||||||
|
<field name="res_model">dynamic.list</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="domain">[('domain', '=', 'risk.type')]</field>
|
||||||
|
<field name="context">{'default_domain': 'risk.type'}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_risk_type_menu" action="dynamic_list_risk_type_action"
|
||||||
|
parent="parent_menu_xmlid"/>
|
||||||
|
|
||||||
|
Limitation: when you want to have different access rights on these lists depending on the source object, you should prefer to use dedicated objects.
|
||||||
|
""",
|
||||||
|
'author': 'Akretion',
|
||||||
|
'website': 'http://www.akretion.com',
|
||||||
|
'depends': ['base'],
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/dynamic_list.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
}
|
||||||
1
base_dynamic_list/models/__init__.py
Normal file
1
base_dynamic_list/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import dynamic_list
|
||||||
115
base_dynamic_list/models/dynamic_list.py
Normal file
115
base_dynamic_list/models/dynamic_list.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Copyright 2020-2021 Akretion France (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicList(models.Model):
|
||||||
|
_name = 'dynamic.list'
|
||||||
|
_description = 'Dynamic List (non translatable)'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
|
name = fields.Char(required=True)
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
domain = fields.Selection([], string='Domain', required=True, index=True)
|
||||||
|
|
||||||
|
_sql_constraint = [(
|
||||||
|
'domain_name_uniq',
|
||||||
|
'unique(domain, name)',
|
||||||
|
'This entry already exists!'
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicListTranslate(models.Model):
|
||||||
|
_name = 'dynamic.list.translate'
|
||||||
|
_description = 'Translatable Dynamic List'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
|
name = fields.Char(translate=True, required=True)
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
domain = fields.Selection([], string='Domain', required=True, index=True)
|
||||||
|
|
||||||
|
_sql_constraint = [(
|
||||||
|
'domain_name_uniq',
|
||||||
|
'unique(domain, name)',
|
||||||
|
'This entry already exists!'
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicListCode(models.Model):
|
||||||
|
_name = 'dynamic.list.code'
|
||||||
|
_description = 'Dynamic list with code'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
|
code = fields.Char(required=True)
|
||||||
|
name = fields.Char(translate=True, required=True)
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
domain = fields.Selection([], string='Domain', required=True, index=True)
|
||||||
|
|
||||||
|
_sql_constraint = [(
|
||||||
|
'domain_code_uniq',
|
||||||
|
'unique(domain, code)',
|
||||||
|
'This code already exists!'
|
||||||
|
)]
|
||||||
|
|
||||||
|
@api.depends('code', 'name')
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for rec in self:
|
||||||
|
res.append((rec.id, '[%s] %s' % (rec.code, rec.name)))
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(
|
||||||
|
self, name='', args=None, operator='ilike', limit=80):
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if name and operator == 'ilike':
|
||||||
|
recs = self.search(
|
||||||
|
[('code', '=', name)] + args, limit=limit)
|
||||||
|
if recs:
|
||||||
|
return recs.name_get()
|
||||||
|
return super().name_search(
|
||||||
|
name=name, args=args, operator=operator, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicListCodeTranslate(models.Model):
|
||||||
|
_name = 'dynamic.list.code.translate'
|
||||||
|
_description = 'Translatable dynamic list with code'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
|
code = fields.Char(required=True)
|
||||||
|
name = fields.Char(translate=True, required=True)
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
active = fields.Boolean(default=True)
|
||||||
|
domain = fields.Selection([], string='Domain', required=True, index=True)
|
||||||
|
|
||||||
|
_sql_constraint = [(
|
||||||
|
'domain_code_uniq',
|
||||||
|
'unique(domain, code)',
|
||||||
|
'This code already exists!'
|
||||||
|
)]
|
||||||
|
|
||||||
|
@api.depends('code', 'name')
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for rec in self:
|
||||||
|
res.append((rec.id, '[%s] %s' % (rec.code, rec.name)))
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(
|
||||||
|
self, name='', args=None, operator='ilike', limit=80):
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if name and operator == 'ilike':
|
||||||
|
recs = self.search(
|
||||||
|
[('code', '=', name)] + args, limit=limit)
|
||||||
|
if recs:
|
||||||
|
return recs.name_get()
|
||||||
|
return super().name_search(
|
||||||
|
name=name, args=args, operator=operator, limit=limit)
|
||||||
9
base_dynamic_list/security/ir.model.access.csv
Normal file
9
base_dynamic_list/security/ir.model.access.csv
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_dynamic_list_read,Read access on dynamic.list to employees,model_dynamic_list,base.group_user,1,0,0,0
|
||||||
|
access_dynamic_list_full,Full access to dynamic.list to System group,model_dynamic_list,base.group_system,1,1,1,1
|
||||||
|
access_dynamic_list_translate_read,Read access on dynamic.list.translate to employees,model_dynamic_list_translate,base.group_user,1,0,0,0
|
||||||
|
access_dynamic_list_translate_full,Full access to dynamic.list.translate to System group,model_dynamic_list_translate,base.group_system,1,1,1,1
|
||||||
|
access_dynamic_list_code_read,Read access on dynamic.list.code to employees,model_dynamic_list_code,base.group_user,1,0,0,0
|
||||||
|
access_dynamic_list_code_full,Full access to dynamic.list.code to System group,model_dynamic_list_code,base.group_system,1,1,1,1
|
||||||
|
access_dynamic_list_code_translate_read,Read access on dynamic.list.code.translate to employees,model_dynamic_list_code_translate,base.group_user,1,0,0,0
|
||||||
|
access_dynamic_list_code_translate_full,Full access to dynamic.list.code.translate to System group,model_dynamic_list_code_translate,base.group_system,1,1,1,1
|
||||||
|
220
base_dynamic_list/views/dynamic_list.xml
Normal file
220
base_dynamic_list/views/dynamic_list.xml
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2020-2021 Akretion France (http://www.akretion.com/)
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_root_menu" name="Dynamic Lists" parent="base.menu_custom" sequence="100"/>
|
||||||
|
|
||||||
|
<record id="dynamic_list_form" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||||
|
<group name="main">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_main_view')"/>
|
||||||
|
<field name="active" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_tree" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_main_view')"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_search" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||||
|
<group string="Group By" name="groupby">
|
||||||
|
<filter name="domain_groupby" string="Domain" context="{'group_by': 'domain'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Simple List</field>
|
||||||
|
<field name="res_model">dynamic.list</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="context">{'dynamic_list_main_view': True, 'search_default_domain_groupby': True}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_menu" action="dynamic_list_action" parent="dynamic_list_root_menu" sequence="10"/>
|
||||||
|
|
||||||
|
<record id="dynamic_list_translate_form" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||||
|
<group name="main">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_translate_main_view')"/>
|
||||||
|
<field name="active" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_translate_tree" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_translate_main_view')"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_translate_search" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||||
|
<group string="Group By" name="groupby">
|
||||||
|
<filter name="domain_groupby" string="Domain" context="{'group_by': 'domain'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_translate_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Translatable Simple List</field>
|
||||||
|
<field name="res_model">dynamic.list.translate</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="context">{'dynamic_list_translate_main_view': True, 'search_default_domain_groupby': True}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_translate_menu" action="dynamic_list_translate_action" parent="dynamic_list_root_menu" sequence="20"/>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_form" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||||
|
<group name="main">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_code_main_view')"/>
|
||||||
|
<field name="active" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_tree" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_code_main_view')"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_search" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="Name or Code" filter_domain="['|', ('name', 'ilike', self), ('code', 'ilike', self)]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||||
|
<field name="code"/>
|
||||||
|
<group string="Group By" name="groupby">
|
||||||
|
<filter name="domain_groupby" string="Domain" context="{'group_by': 'domain'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Code List</field>
|
||||||
|
<field name="res_model">dynamic.list.code</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="context">{'dynamic_list_code_main_view': True, 'search_default_domain_groupby': True}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_code_menu" action="dynamic_list_code_action" parent="dynamic_list_root_menu" sequence="30"/>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_translate_form" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||||
|
<group name="main">
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_code_translate_main_view')"/>
|
||||||
|
<field name="active" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_translate_tree" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="domain" invisible="not context.get('dynamic_list_code_translate_main_view')"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_translate_search" model="ir.ui.view">
|
||||||
|
<field name="model">dynamic.list.code.translate</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="name" string="Name or Code" filter_domain="['|', ('name', 'ilike', self), ('code', 'ilike', self)]"/>
|
||||||
|
<field name="code"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
|
||||||
|
<group string="Group By" name="groupby">
|
||||||
|
<filter name="domain_groupby" string="Domain" context="{'group_by': 'domain'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="dynamic_list_code_translate_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Translatable Code List</field>
|
||||||
|
<field name="res_model">dynamic.list.code.translate</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="context">{'dynamic_list_code_translate_main_view': True, 'search_default_domain_groupby': True}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="dynamic_list_code_translate_menu" action="dynamic_list_code_translate_action" parent="dynamic_list_root_menu" sequence="40"/>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
1
base_mail_sender_bcc/__init__.py
Normal file
1
base_mail_sender_bcc/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
21
base_mail_sender_bcc/__manifest__.py
Normal file
21
base_mail_sender_bcc/__manifest__.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Copyright 2017-2022 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Mail Sender Bcc',
|
||||||
|
'version': '14.0.1.0.0',
|
||||||
|
'category': 'Mail',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'summary': "Always send a copy of the mail to the sender",
|
||||||
|
'description': """
|
||||||
|
Mail Sender Bcc
|
||||||
|
===============
|
||||||
|
|
||||||
|
With this module, when Odoo sends an outgoing email, it adds the sender as Bcc (blind copy) of the email.
|
||||||
|
""",
|
||||||
|
'author': 'Akretion',
|
||||||
|
'website': 'https://github.com/akretion/odoo-usability',
|
||||||
|
'depends': ['base'],
|
||||||
|
'installable': True,
|
||||||
|
}
|
||||||
1
base_mail_sender_bcc/models/__init__.py
Normal file
1
base_mail_sender_bcc/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import ir_mail_server
|
||||||
27
base_mail_sender_bcc/models/ir_mail_server.py
Normal file
27
base_mail_sender_bcc/models/ir_mail_server.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Copyright 2017-2022 Akretion France
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class IrMailServer(models.Model):
|
||||||
|
_inherit = 'ir.mail_server'
|
||||||
|
|
||||||
|
def build_email(
|
||||||
|
self, email_from, email_to, subject, body, email_cc=None,
|
||||||
|
email_bcc=None, reply_to=False, attachments=None,
|
||||||
|
message_id=None, references=None, object_id=False,
|
||||||
|
subtype='plain', headers=None,
|
||||||
|
body_alternative=None, subtype_alternative='plain'):
|
||||||
|
if email_from:
|
||||||
|
if email_bcc is None:
|
||||||
|
email_bcc = [email_from]
|
||||||
|
elif isinstance(email_bcc, list) and email_from not in email_bcc:
|
||||||
|
email_bcc.append(email_from)
|
||||||
|
return super().build_email(
|
||||||
|
email_from, email_to, subject, body, email_cc=email_cc,
|
||||||
|
email_bcc=email_bcc, reply_to=reply_to, attachments=attachments,
|
||||||
|
message_id=message_id, references=references, object_id=object_id,
|
||||||
|
subtype=subtype, headers=headers,
|
||||||
|
body_alternative=body_alternative, subtype_alternative=subtype_alternative)
|
||||||
2
base_partner_one2many_phone/__init__.py
Normal file
2
base_partner_one2many_phone/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import partner_phone
|
||||||
|
from .post_install import migrate_to_partner_phone
|
||||||
31
base_partner_one2many_phone/__manifest__.py
Normal file
31
base_partner_one2many_phone/__manifest__.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Copyright 2014-2020 Abbaye du Barroux (http://www.barroux.org)
|
||||||
|
# Copyright 2014-2020 Akretion (http://www.akretion.com>)
|
||||||
|
# @author: Frère Bernard <informatique@barroux.org>
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Base Partner One2many Phone',
|
||||||
|
'version': '14.0.1.0.0',
|
||||||
|
'category': 'Phone',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'summary': 'One2many link between partners and phone numbers/emails',
|
||||||
|
'description': """
|
||||||
|
Base Partner One2many Phone
|
||||||
|
===========================
|
||||||
|
|
||||||
|
With this module, one partner can have several phone numbers and several emails. It adds a new table dedicated to phone numbers and emails and a one2many link between partners and phone numbers. This module keeps compatibility with the native behavior of Odoo on phone numbers and emails.
|
||||||
|
|
||||||
|
It has been developped by brother Bernard from Barroux Abbey and Alexis de Lattre from Akretion.
|
||||||
|
""",
|
||||||
|
'author': 'Akretion',
|
||||||
|
'website': 'https://akretion.com/',
|
||||||
|
'depends': ['contacts', 'base_usability', 'phone_validation'],
|
||||||
|
'excludes': ['sms'], # because sms introduces big changes in partner form view
|
||||||
|
'data': [
|
||||||
|
'partner_phone_view.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'post_init_hook': 'migrate_to_partner_phone',
|
||||||
|
}
|
||||||
202
base_partner_one2many_phone/i18n/base_partner_one2many_phone.pot
Normal file
202
base_partner_one2many_phone/i18n/base_partner_one2many_phone.pot
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_partner_one2many_phone
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-10-29 21:12+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-10-29 21:12+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__email
|
||||||
|
msgid "E-Mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"E-mail field must be empty when type is Primary/Secondary Phone, "
|
||||||
|
"Primary/Secondary Mobile or Primary/Secondary Fax."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"E-mail field must have a value when type is Primary E-mail or Secondary "
|
||||||
|
"E-mail."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__email
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__email
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__id
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__mobile
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__mobile
|
||||||
|
msgid "Mobile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner_phone
|
||||||
|
msgid "Multiple emails and phones for partners"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__note
|
||||||
|
msgid "Note"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__phone
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_form
|
||||||
|
msgid "Phone and E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Phone field must be empty when type is Primary E-mail or Secondary E-mail."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Phone field must have a value when type is Primary/Secondary Phone, "
|
||||||
|
"Primary/Secondary Mobile or Primary/Secondary Fax."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_tree
|
||||||
|
msgid "Phones and E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.actions.act_window,name:base_partner_one2many_phone.res_partner_phone_action
|
||||||
|
#: model:ir.ui.menu,name:base_partner_one2many_phone.res_partner_phone_menu
|
||||||
|
msgid "Phones/E-mails"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__phone_ids
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__phone_ids
|
||||||
|
msgid "Phones/Emails"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__1_email_primary
|
||||||
|
msgid "Primary E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__7_fax_primary
|
||||||
|
msgid "Primary Fax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__5_mobile_primary
|
||||||
|
msgid "Primary Mobile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__3_phone_primary
|
||||||
|
msgid "Primary Phone"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__partner_id
|
||||||
|
msgid "Related Partner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Search Phones/E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__2_email_secondary
|
||||||
|
msgid "Secondary E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__8_fax_secondary
|
||||||
|
msgid "Secondary Fax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__6_mobile_secondary
|
||||||
|
msgid "Secondary Mobile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__4_phone_secondary
|
||||||
|
msgid "Secondary Phone"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__type
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Type"
|
||||||
|
msgstr ""
|
||||||
202
base_partner_one2many_phone/i18n/fr.po
Normal file
202
base_partner_one2many_phone/i18n/fr.po
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_partner_one2many_phone
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-10-29 21:12+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-10-29 21:12+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr "Contact"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr "Créé par"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr "Créé le"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nom affiché"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__email
|
||||||
|
msgid "E-Mail"
|
||||||
|
msgstr "E-Mail"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"E-mail field must be empty when type is Primary/Secondary Phone, "
|
||||||
|
"Primary/Secondary Mobile or Primary/Secondary Fax."
|
||||||
|
msgstr "Le champ E-mail doit être vide quand le type est Tél. principal/secondaire, Portable principal/secondaire ou Fax principal/secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"E-mail field must have a value when type is Primary E-mail or Secondary "
|
||||||
|
"E-mail."
|
||||||
|
msgstr "Le champ E-mail doit avoir une valeur quand le type est E-mail principal ou secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__email
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__email
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "E-mail"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__id
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "Dernière modification le"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr "Dernière modification par"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr "Dernière modification le"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__mobile
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__mobile
|
||||||
|
msgid "Mobile"
|
||||||
|
msgstr "Portable"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner_phone
|
||||||
|
msgid "Multiple emails and phones for partners"
|
||||||
|
msgstr "Multiples e-mails et téléphones pour les partenaires"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__note
|
||||||
|
msgid "Note"
|
||||||
|
msgstr "Note"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__phone
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr "Tél."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_form
|
||||||
|
msgid "Phone and E-mail"
|
||||||
|
msgstr "Tél. et E-mail"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Phone field must be empty when type is Primary E-mail or Secondary E-mail."
|
||||||
|
msgstr "Le champ Tél. doit être vide quand le type est E-mail principal ou E-mail secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Phone field must have a value when type is Primary/Secondary Phone, "
|
||||||
|
"Primary/Secondary Mobile or Primary/Secondary Fax."
|
||||||
|
msgstr "Le champ Tél. doit avoir une valeur quand le type est Tél. principal/secondaire, Portable principal/secondaire ou Fax principal/secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_tree
|
||||||
|
msgid "Phones and E-mail"
|
||||||
|
msgstr "Téls et E-mail"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.actions.act_window,name:base_partner_one2many_phone.res_partner_phone_action
|
||||||
|
#: model:ir.ui.menu,name:base_partner_one2many_phone.res_partner_phone_menu
|
||||||
|
msgid "Phones/E-mails"
|
||||||
|
msgstr "Téls/E-mails"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner__phone_ids
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_users__phone_ids
|
||||||
|
msgid "Phones/Emails"
|
||||||
|
msgstr "Téls/E-mails"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__1_email_primary
|
||||||
|
msgid "Primary E-mail"
|
||||||
|
msgstr "E-mail principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__7_fax_primary
|
||||||
|
msgid "Primary Fax"
|
||||||
|
msgstr "Fax principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__5_mobile_primary
|
||||||
|
msgid "Primary Mobile"
|
||||||
|
msgstr "Portable principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__3_phone_primary
|
||||||
|
msgid "Primary Phone"
|
||||||
|
msgstr "Tél. principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__partner_id
|
||||||
|
msgid "Related Partner"
|
||||||
|
msgstr "Partenaire associé"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Search Phones/E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__2_email_secondary
|
||||||
|
msgid "Secondary E-mail"
|
||||||
|
msgstr "E-mail secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__8_fax_secondary
|
||||||
|
msgid "Secondary Fax"
|
||||||
|
msgstr "Fax secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__6_mobile_secondary
|
||||||
|
msgid "Secondary Mobile"
|
||||||
|
msgstr "Portable secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields.selection,name:base_partner_one2many_phone.selection__res_partner_phone__type__4_phone_secondary
|
||||||
|
msgid "Secondary Phone"
|
||||||
|
msgstr "Tél. secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone__type
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Type"
|
||||||
193
base_partner_one2many_phone/partner_phone.py
Normal file
193
base_partner_one2many_phone/partner_phone.py
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# Copyright 2014-2020 Abbaye du Barroux (http://www.barroux.org)
|
||||||
|
# Copyright 2016-2020 Akretion (http://www.akretion.com>)
|
||||||
|
# @author: Frère Bernard <informatique@barroux.org>
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
EMAIL_TYPES = ('1_email_primary', '2_email_secondary')
|
||||||
|
PHONE_TYPES = ('3_phone_primary', '4_phone_secondary', '5_mobile_primary', '6_mobile_secondary', '7_fax_primary', '8_fax_secondary')
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartnerPhone(models.Model):
|
||||||
|
_name = 'res.partner.phone'
|
||||||
|
_order = 'partner_id, type'
|
||||||
|
_phone_name_sequence = 8
|
||||||
|
_inherit = ['phone.validation.mixin']
|
||||||
|
_description = 'Multiple emails and phones for partners'
|
||||||
|
|
||||||
|
partner_id = fields.Many2one(
|
||||||
|
'res.partner', string='Related Partner', index=True, ondelete='cascade')
|
||||||
|
type = fields.Selection([
|
||||||
|
('1_email_primary', 'Primary E-mail'),
|
||||||
|
('2_email_secondary', 'Secondary E-mail'),
|
||||||
|
('3_phone_primary', 'Primary Phone'),
|
||||||
|
('4_phone_secondary', 'Secondary Phone'),
|
||||||
|
('5_mobile_primary', 'Primary Mobile'),
|
||||||
|
('6_mobile_secondary', 'Secondary Mobile'),
|
||||||
|
('7_fax_primary', 'Primary Fax'),
|
||||||
|
('8_fax_secondary', 'Secondary Fax'),
|
||||||
|
],
|
||||||
|
string='Type', required=True, index=True)
|
||||||
|
phone = fields.Char(string='Phone')
|
||||||
|
email = fields.Char(string='E-Mail')
|
||||||
|
note = fields.Char('Note')
|
||||||
|
|
||||||
|
@api.onchange('type')
|
||||||
|
def type_change(self):
|
||||||
|
if self.type:
|
||||||
|
if self.type in EMAIL_TYPES:
|
||||||
|
self.phone = False
|
||||||
|
elif self.type in PHONE_TYPES:
|
||||||
|
self.email = False
|
||||||
|
|
||||||
|
@api.onchange('phone', 'partner_id')
|
||||||
|
def _onchange_phone_validation(self):
|
||||||
|
if self.phone:
|
||||||
|
self.phone = self.phone_format(self.phone, country=self.partner_id.country_id)
|
||||||
|
|
||||||
|
@api.constrains('type', 'phone', 'email')
|
||||||
|
def _check_partner_phone(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.type in EMAIL_TYPES:
|
||||||
|
if not rec.email:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"E-mail field must have a value when type is Primary E-mail or Secondary E-mail."))
|
||||||
|
if rec.phone:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"Phone field must be empty when type is Primary E-mail or Secondary E-mail."))
|
||||||
|
elif rec.type in PHONE_TYPES:
|
||||||
|
if not rec.phone:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"Phone field must have a value when type is Primary/Secondary Phone, Primary/Secondary Mobile or Primary/Secondary Fax."))
|
||||||
|
if rec.email:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"E-mail field must be empty when type is Primary/Secondary Phone, Primary/Secondary Mobile or Primary/Secondary Fax."))
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
res = []
|
||||||
|
for pphone in self:
|
||||||
|
if pphone.partner_id:
|
||||||
|
if self._context.get('callerid'):
|
||||||
|
name = pphone.partner_id.display_name
|
||||||
|
else:
|
||||||
|
name = u'%s (%s)' % (pphone.phone, pphone.partner_id.name)
|
||||||
|
else:
|
||||||
|
name = pphone.phone
|
||||||
|
res.append((pphone.id, name))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self._cr.execute('''
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS single_email_primary
|
||||||
|
ON res_partner_phone (partner_id, type)
|
||||||
|
WHERE (type='1_email_primary')
|
||||||
|
''')
|
||||||
|
self._cr.execute('''
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS single_phone_primary
|
||||||
|
ON res_partner_phone (partner_id, type)
|
||||||
|
WHERE (type='3_phone_primary')
|
||||||
|
''')
|
||||||
|
self._cr.execute('''
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS single_mobile_primary
|
||||||
|
ON res_partner_phone (partner_id, type)
|
||||||
|
WHERE (type='5_mobile_primary')
|
||||||
|
''')
|
||||||
|
self._cr.execute('''
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS single_fax_primary
|
||||||
|
ON res_partner_phone (partner_id, type)
|
||||||
|
WHERE (type='7_fax_primary')
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
# in v10, we are supposed to have in DB E.164 format
|
||||||
|
# with the current implementation, we have:
|
||||||
|
# in res.partner : PhoneNumberFormat.INTERNATIONAL
|
||||||
|
# in res.partner.phone : E.164
|
||||||
|
# It is not good, but it is not a big bug and it's complex to fix
|
||||||
|
# so let's let it like that. In v12, we store in
|
||||||
|
# PhoneNumberFormat.INTERNATIONAL, so this bug is kind of an anticipation
|
||||||
|
# for the future :)
|
||||||
|
|
||||||
|
phone_ids = fields.One2many(
|
||||||
|
'res.partner.phone', 'partner_id', string='Phones/Emails')
|
||||||
|
phone = fields.Char(
|
||||||
|
compute='_compute_partner_phone',
|
||||||
|
store=True, readonly=True, compute_sudo=True)
|
||||||
|
mobile = fields.Char(
|
||||||
|
compute='_compute_partner_phone',
|
||||||
|
store=True, readonly=True, compute_sudo=True)
|
||||||
|
email = fields.Char(
|
||||||
|
compute='_compute_partner_phone',
|
||||||
|
store=True, readonly=True, compute_sudo=True)
|
||||||
|
|
||||||
|
@api.depends('phone_ids.phone', 'phone_ids.type', 'phone_ids.email')
|
||||||
|
def _compute_partner_phone(self):
|
||||||
|
for partner in self:
|
||||||
|
phone = mobile = email = False
|
||||||
|
for pphone in partner.phone_ids:
|
||||||
|
if pphone.type == '1_email_primary' and pphone.email:
|
||||||
|
email = pphone.email
|
||||||
|
elif pphone.phone:
|
||||||
|
if pphone.type == '5_mobile_primary':
|
||||||
|
mobile = pphone.phone
|
||||||
|
elif pphone.type == '3_phone_primary':
|
||||||
|
phone = pphone.phone
|
||||||
|
partner.phone = phone
|
||||||
|
partner.mobile = mobile
|
||||||
|
partner.email = email
|
||||||
|
|
||||||
|
def _update_create_vals(
|
||||||
|
self, vals, type, partner_field, partner_phone_field):
|
||||||
|
if vals.get(partner_field):
|
||||||
|
vals['phone_ids'].append(
|
||||||
|
(0, 0, {'type': type, partner_phone_field: vals[partner_field]}))
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
if 'phone_ids' not in vals:
|
||||||
|
vals['phone_ids'] = []
|
||||||
|
self._update_create_vals(vals, '1_email_primary', 'email', 'email')
|
||||||
|
self._update_create_vals(vals, '3_phone_primary', 'phone', 'phone')
|
||||||
|
self._update_create_vals(vals, '5_mobile_primary', 'mobile', 'phone')
|
||||||
|
# self._update_create_vals(vals, '7_fax_primary', 'fax', 'phone')
|
||||||
|
return super().create(vals)
|
||||||
|
|
||||||
|
def _update_write_vals(
|
||||||
|
self, vals, type, partner_field, partner_phone_field):
|
||||||
|
self.ensure_one()
|
||||||
|
rppo = self.env['res.partner.phone']
|
||||||
|
if partner_field in vals:
|
||||||
|
pphone = rppo.search([
|
||||||
|
('partner_id', '=', self.id),
|
||||||
|
('type', '=', type)], limit=1)
|
||||||
|
if vals[partner_field]:
|
||||||
|
if pphone:
|
||||||
|
vals['phone_ids'].append((1, pphone.id, {
|
||||||
|
partner_phone_field: vals[partner_field]}))
|
||||||
|
else:
|
||||||
|
vals['phone_ids'].append((0, 0, {
|
||||||
|
'type': type,
|
||||||
|
partner_phone_field: vals[partner_field],
|
||||||
|
}))
|
||||||
|
else:
|
||||||
|
if pphone:
|
||||||
|
vals['phone_ids'].append((2, pphone.id))
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
if 'phone_ids' not in vals:
|
||||||
|
for rec in self:
|
||||||
|
vals['phone_ids'] = []
|
||||||
|
rec._update_write_vals(vals, '1_email_primary', 'email', 'email')
|
||||||
|
rec._update_write_vals(vals, '3_phone_primary', 'phone', 'phone')
|
||||||
|
rec._update_write_vals(vals, '5_mobile_primary', 'mobile', 'phone')
|
||||||
|
rec._update_write_vals(vals, '7_fax_primary', 'fax', 'phone')
|
||||||
|
super(ResPartner, rec).write(vals)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return super().write(vals)
|
||||||
167
base_partner_one2many_phone/partner_phone_view.xml
Normal file
167
base_partner_one2many_phone/partner_phone_view.xml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2014-2020 Abbaye du Barroux (http://www.barroux.org)
|
||||||
|
Copyright 2016-2020 Akretion (http://www.akretion.com>)
|
||||||
|
@author: Frère Bernard <informatique@barroux.org>
|
||||||
|
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- Partner phones -->
|
||||||
|
<record id="res_partner_phone_tree" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.phone.tree</field>
|
||||||
|
<field name="model">res.partner.phone</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'readonly': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||||
|
<field name="email" widget="email" attrs="{'readonly': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||||
|
<field name="note"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_phone_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.phone.form</field>
|
||||||
|
<field name="model">res.partner.phone</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group name="main">
|
||||||
|
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'invisible': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||||
|
<field name="email" widget="email" attrs="{'invisible': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||||
|
<field name="note"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_phone_search" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.phone.search</field>
|
||||||
|
<field name="model">res.partner.phone</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="phone" />
|
||||||
|
<field name="email" />
|
||||||
|
<group name="groupby">
|
||||||
|
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_phone_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Phones/E-mails</field>
|
||||||
|
<field name="res_model">res.partner.phone</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="context">{'partner_phone_main_view': True}</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="res_partner_phone_menu" action="res_partner_phone_action"
|
||||||
|
parent="contacts.menu_contacts" sequence="10"/>
|
||||||
|
|
||||||
|
<record id="contacts.res_partner_menu_config" model="ir.ui.menu">
|
||||||
|
<field name="sequence">20</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- PARTNER views -->
|
||||||
|
<record id="view_partner_form" model="ir.ui.view">
|
||||||
|
<field name="name">add.phone_ids.on.partner.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="mail.res_partner_view_form_inherit_mail"/>
|
||||||
|
<!-- This module depends on contacts which depends on mail
|
||||||
|
and the mail module replaces the email field -->
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="phone" position="after">
|
||||||
|
<field name="phone_ids" nolabel="1" colspan="2"/>
|
||||||
|
</field>
|
||||||
|
<field name="phone" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="mobile" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<label for="email" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</label>
|
||||||
|
<xpath expr="//field[@name='email']/.." position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- I can't display phone_ids in the Contacts
|
||||||
|
because there is a very strange thing in the web client: if
|
||||||
|
you have a res.partner.phone on one of the fields,
|
||||||
|
it will send to write {'child_ids': [1, ID_child, {'phone_ids': [[5], [4, id_phone_child]]}]}
|
||||||
|
=> it will delete res.partner.phone and then try to re-create it,
|
||||||
|
which triggers the message 'Record does not exist or has been deleted.'
|
||||||
|
<xpath expr="//field[@name='child_ids']/form//field[@name='phone']" position="after">
|
||||||
|
<field name="phone_ids" nolabel="1" colspan="2" widget="many2many_tags"/>
|
||||||
|
</xpath>
|
||||||
|
-->
|
||||||
|
<xpath expr="//field[@name='child_ids']/form//field[@name='phone']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']/form//field[@name='mobile']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']/form//field[@name='email']" position="attributes">
|
||||||
|
<attribute name="readonly">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_partner_simple_form" model="ir.ui.view">
|
||||||
|
<field name="name">add.phone_ids.on.res.partner.simplified.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_simple_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="phone" position="after">
|
||||||
|
<field name="phone_ids" nolabel="1" colspan="2"/>
|
||||||
|
</field>
|
||||||
|
<field name="phone" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="mobile" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="email" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_view_form_private" model="ir.ui.view">
|
||||||
|
<field name="name">add.phone_ids.on.res.partner.private.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.res_partner_view_form_private"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="phone" position="after">
|
||||||
|
<field name="phone_ids" nolabel="1" colspan="2"/>
|
||||||
|
</field>
|
||||||
|
<field name="phone" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="mobile" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="email" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_res_partner_filter" model="ir.ui.view">
|
||||||
|
<field name="name">phone.one2many.res.partner.search</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base_usability.view_res_partner_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="attributes">
|
||||||
|
<attribute name="filter_domain">['|', '|', ('display_name', 'ilike', self), ('ref', '=ilike', self + '%'), ('phone_ids.email', 'ilike', self)]</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
49
base_partner_one2many_phone/post_install.py
Normal file
49
base_partner_one2many_phone/post_install.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Copyright 2017-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_partner_phone(cr, phone_field, phone_type):
|
||||||
|
cr.execute(
|
||||||
|
'SELECT id, ' + phone_field + ' FROM res_partner WHERE ' +
|
||||||
|
phone_field + ' IS NOT null AND ' + phone_field + "!= ''")
|
||||||
|
to_create = []
|
||||||
|
for partner in cr.fetchall():
|
||||||
|
to_create.append({
|
||||||
|
'partner_id': partner[0],
|
||||||
|
'type': phone_type,
|
||||||
|
'phone': partner[1],
|
||||||
|
})
|
||||||
|
return to_create
|
||||||
|
|
||||||
|
|
||||||
|
def create_partner_email(cr):
|
||||||
|
cr.execute(
|
||||||
|
"SELECT id, email FROM res_partner WHERE email IS NOT null AND email != ''")
|
||||||
|
to_create = []
|
||||||
|
for partner in cr.fetchall():
|
||||||
|
to_create.append({
|
||||||
|
'partner_id': partner[0],
|
||||||
|
'type': '1_email_primary',
|
||||||
|
'email': partner[1],
|
||||||
|
})
|
||||||
|
return to_create
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_to_partner_phone(cr, registry):
|
||||||
|
logger.info('start data migration for one2many_phone')
|
||||||
|
with api.Environment.manage():
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
rppo = env['res.partner.phone']
|
||||||
|
to_create = []
|
||||||
|
to_create += create_partner_phone(cr, 'phone', '3_phone_primary')
|
||||||
|
to_create += create_partner_phone(cr, 'mobile', '5_mobile_primary')
|
||||||
|
to_create += create_partner_email(cr)
|
||||||
|
# I need to create all at the end for invalidation purposes
|
||||||
|
rppo.create(to_create)
|
||||||
|
logger.info('end data migration for one2many_phone')
|
||||||
|
return
|
||||||
3
base_partner_one2many_phone/security/ir.model.access.csv
Normal file
3
base_partner_one2many_phone/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_partner_phone_full,Full access on res.partner.phone to Contact Manager grp,model_res_partner_phone,base.group_partner_manager,1,1,1,1
|
||||||
|
access_partner_phone_read,Read access on res.partner.phone to Employees grp,model_res_partner_phone,base.group_user,1,0,0,0
|
||||||
|
1
base_partner_one2many_phone/tests/__init__.py
Normal file
1
base_partner_one2many_phone/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import test_partner_phone
|
||||||
134
base_partner_one2many_phone/tests/test_partner_phone.py
Normal file
134
base_partner_one2many_phone/tests/test_partner_phone.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2019 Barroux Abbey
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestPartnerPhone(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPartnerPhone, self).setUp()
|
||||||
|
|
||||||
|
def _check_result(self, partner, result):
|
||||||
|
rppo = self.env['res.partner.phone']
|
||||||
|
pphone_email = rppo.search(
|
||||||
|
[('type', '=', '1_email_primary'), ('partner_id', '=', partner.id)])
|
||||||
|
if result['email']:
|
||||||
|
self.assertEqual(partner.email, result['email'])
|
||||||
|
self.assertEqual(len(pphone_email), 1)
|
||||||
|
self.assertEqual(pphone_email.email, result['email'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(partner.email)
|
||||||
|
self.assertFalse(pphone_email)
|
||||||
|
if result['phone']:
|
||||||
|
self.assertEqual(partner.phone, result['phone'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(partner.phone)
|
||||||
|
if result['mobile']:
|
||||||
|
self.assertEqual(partner.mobile, result['mobile'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(partner.mobile)
|
||||||
|
field2type = {
|
||||||
|
'phone': '3_phone_primary',
|
||||||
|
'mobile': '5_mobile_primary',
|
||||||
|
'fax': '7_fax_primary',
|
||||||
|
}
|
||||||
|
for field, value in result.items():
|
||||||
|
if field in field2type:
|
||||||
|
type = field2type[field]
|
||||||
|
pphone = rppo.search(
|
||||||
|
[('type', '=', type), ('partner_id', '=', partner.id)])
|
||||||
|
if value:
|
||||||
|
self.assertEqual(len(pphone), 1)
|
||||||
|
self.assertEqual(pphone.phone, value)
|
||||||
|
else:
|
||||||
|
self.assertFalse(pphone)
|
||||||
|
|
||||||
|
def test_create_partner(self):
|
||||||
|
rpo = self.env['res.partner']
|
||||||
|
p = rpo.create({
|
||||||
|
'name': 'Test Me',
|
||||||
|
'email': 'testme@example.com',
|
||||||
|
'phone': '+33198089246',
|
||||||
|
'mobile': '+33198089247',
|
||||||
|
})
|
||||||
|
result = {
|
||||||
|
'email': 'testme@example.com',
|
||||||
|
'phone': '+33198089246',
|
||||||
|
'mobile': '+33198089247',
|
||||||
|
}
|
||||||
|
self._check_result(p, result)
|
||||||
|
p2 = rpo.create({
|
||||||
|
'name': 'Test me now',
|
||||||
|
'email': 'testmenow@example.com',
|
||||||
|
'phone': '+33972727272',
|
||||||
|
})
|
||||||
|
result = {
|
||||||
|
'email': 'testmenow@example.com',
|
||||||
|
'phone': '+33972727272',
|
||||||
|
'mobile': False,
|
||||||
|
}
|
||||||
|
self._check_result(p2, result)
|
||||||
|
p3 = rpo.create({
|
||||||
|
'name': 'Test me now',
|
||||||
|
'phone_ids': [
|
||||||
|
(0, 0, {'type': '3_phone_primary', 'phone': '+33972727272'}),
|
||||||
|
(0, 0, {'type': '1_email_primary', 'email': 'tutu@example.fr'})],
|
||||||
|
})
|
||||||
|
result = {
|
||||||
|
'email': 'tutu@example.fr',
|
||||||
|
'phone': '+33972727272',
|
||||||
|
'mobile': False,
|
||||||
|
}
|
||||||
|
self._check_result(p3, result)
|
||||||
|
|
||||||
|
def test_write_partner(self):
|
||||||
|
p1 = self.env['res.partner'].create({
|
||||||
|
'name': 'test me now',
|
||||||
|
'country_id': self.env.ref('base.fr').id,
|
||||||
|
})
|
||||||
|
result_none = {
|
||||||
|
'email': False,
|
||||||
|
'phone': False,
|
||||||
|
'mobile': False,
|
||||||
|
}
|
||||||
|
self._check_result(p1, result_none)
|
||||||
|
p1.write({
|
||||||
|
'mobile': '+33198089247',
|
||||||
|
'email': 'testmenow@example.com',
|
||||||
|
})
|
||||||
|
result = {
|
||||||
|
'email': 'testmenow@example.com',
|
||||||
|
'phone': False,
|
||||||
|
'mobile': '+33198089247',
|
||||||
|
}
|
||||||
|
self._check_result(p1, result)
|
||||||
|
p1.write({
|
||||||
|
'email': 'testmenow2@example.com',
|
||||||
|
'phone': False,
|
||||||
|
'mobile': '+33472727272',
|
||||||
|
})
|
||||||
|
result = {
|
||||||
|
'email': 'testmenow2@example.com',
|
||||||
|
'phone': False,
|
||||||
|
'mobile': '+33472727272',
|
||||||
|
}
|
||||||
|
self._check_result(p1, result)
|
||||||
|
p1.write({
|
||||||
|
'phone': False,
|
||||||
|
'mobile': False,
|
||||||
|
'email': False,
|
||||||
|
})
|
||||||
|
self._check_result(p1, result_none)
|
||||||
|
p2 = self.env['res.partner'].create({'name': 'Toto', 'email': 'toto@example.com'})
|
||||||
|
p_multi = p1 + p2
|
||||||
|
p_multi.write({'email': 'all@example.com', 'phone': '+33560606070'})
|
||||||
|
result = {
|
||||||
|
'email': 'all@example.com',
|
||||||
|
'phone': '+33560606070',
|
||||||
|
'mobile': False,
|
||||||
|
}
|
||||||
|
self._check_result(p1, result)
|
||||||
|
self._check_result(p2, result)
|
||||||
@@ -1 +1 @@
|
|||||||
from . import partner
|
from . import models
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Copyright 2017-2019 Akretion (http://www.akretion.com)
|
# Copyright 2017-2021 Akretion (http://www.akretion.com)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Base Partner Reference',
|
'name': 'Base Partner Reference',
|
||||||
'version': '12.0.1.0.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Partner',
|
'category': 'Partner',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': "Improve usage of partner's Internal Reference",
|
'summary': "Improve usage of partner's Internal Reference",
|
||||||
@@ -21,6 +21,6 @@ Base Partner Reference
|
|||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'depends': ['base'],
|
'depends': ['base'],
|
||||||
'data': ['partner_view.xml'],
|
'data': ['views/res_partner.xml'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
1
base_partner_ref/models/__init__.py
Normal file
1
base_partner_ref/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import res_partner
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright 2017-2019 Akretion
|
# Copyright 2017-2021 Akretion
|
||||||
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -18,9 +18,9 @@ class ResPartner(models.Model):
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
# add 'ref' in depends
|
# add 'ref' in depends
|
||||||
@api.depends('is_company', 'name', 'parent_id.name', 'type', 'company_name', 'ref', 'invalidate_display_name')
|
@api.depends('ref', 'invalidate_display_name')
|
||||||
def _compute_display_name(self):
|
def _compute_display_name(self):
|
||||||
super(ResPartner, self)._compute_display_name()
|
super()._compute_display_name()
|
||||||
|
|
||||||
def _get_name(self):
|
def _get_name(self):
|
||||||
partner = self
|
partner = self
|
||||||
@@ -28,16 +28,17 @@ class ResPartner(models.Model):
|
|||||||
|
|
||||||
# START modif of native method
|
# START modif of native method
|
||||||
if partner.ref:
|
if partner.ref:
|
||||||
name = u"[%s] %s" % (partner.ref, name)
|
name = "[%s] %s" % (partner.ref, name)
|
||||||
# END modif of native method
|
# END modif of native method
|
||||||
if partner.company_name or partner.parent_id:
|
if partner.company_name or partner.parent_id:
|
||||||
if not name and partner.type in ['invoice', 'delivery', 'other']:
|
if not name and partner.type in ['invoice', 'delivery', 'other']:
|
||||||
name = dict(self.fields_get(['type'])['type']['selection'])[partner.type]
|
name = dict(self.fields_get(
|
||||||
|
['type'])['type']['selection'])[partner.type]
|
||||||
if not partner.is_company:
|
if not partner.is_company:
|
||||||
# START modif of native name_get() method
|
# START modif of native name_get() method
|
||||||
company_name = partner.commercial_company_name or partner.parent_id.name
|
company_name = partner.commercial_company_name or partner.parent_id.name
|
||||||
if partner.parent_id.ref:
|
if partner.parent_id.ref:
|
||||||
company_name = u"[%s] %s" % (partner.parent_id.ref, company_name)
|
company_name = "[%s] %s" % (partner.parent_id.ref, company_name)
|
||||||
name = "%s, %s" % (company_name, name)
|
name = "%s, %s" % (company_name, name)
|
||||||
# END modif of native name_get() method
|
# END modif of native name_get() method
|
||||||
if self._context.get('show_address_only'):
|
if self._context.get('show_address_only'):
|
||||||
@@ -47,7 +48,8 @@ class ResPartner(models.Model):
|
|||||||
name = name.replace('\n\n', '\n')
|
name = name.replace('\n\n', '\n')
|
||||||
name = name.replace('\n\n', '\n')
|
name = name.replace('\n\n', '\n')
|
||||||
if self._context.get('address_inline'):
|
if self._context.get('address_inline'):
|
||||||
name = name.replace('\n', ', ')
|
splitted_names = name.split("\n")
|
||||||
|
name = ", ".join([n for n in splitted_names if n.strip()])
|
||||||
if self._context.get('show_email') and partner.email:
|
if self._context.get('show_email') and partner.email:
|
||||||
name = "%s <%s>" % (name, partner.email)
|
name = "%s <%s>" % (name, partner.email)
|
||||||
if self._context.get('html_format'):
|
if self._context.get('html_format'):
|
||||||
@@ -55,3 +57,14 @@ class ResPartner(models.Model):
|
|||||||
if self._context.get('show_vat') and partner.vat:
|
if self._context.get('show_vat') and partner.vat:
|
||||||
name = "%s ‒ %s" % (name, partner.vat)
|
name = "%s ‒ %s" % (name, partner.vat)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||||
|
if args is None:
|
||||||
|
args = []
|
||||||
|
if name and operator == 'ilike':
|
||||||
|
recs = self.search([('ref', '=', name)] + args, limit=limit)
|
||||||
|
if recs:
|
||||||
|
rec_childs = self.search([('id', 'child_of', recs.ids)])
|
||||||
|
return rec_childs.name_get()
|
||||||
|
return super().name_search(name=name, args=args, operator=operator, limit=limit)
|
||||||
@@ -11,29 +11,34 @@
|
|||||||
<field name="name">Move ref in partner form to make it more visible</field>
|
<field name="name">Move ref in partner form to make it more visible</field>
|
||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="priority">1000</field> <!-- inherit after l10n_fr -->
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="type" position="after">
|
<field name="type" position="after">
|
||||||
<field name="ref"/>
|
<field name="ref"/>
|
||||||
</field>
|
</field>
|
||||||
<xpath expr="//page[@name='sales_purchases']//field[@name='ref']" position="replace"/>
|
<xpath expr="//page[@name='sales_purchases']//field[@name='ref']" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<!-- show name and ref in separate columns -->
|
||||||
|
<!-- ref is added in tree view by base_usability with optional="hide"
|
||||||
<record id="view_partner_tree" model="ir.ui.view">
|
<record id="view_partner_tree" model="ir.ui.view">
|
||||||
<field name="name">Add ref in partner tree view</field>
|
<field name="name">Add ref in partner tree view</field>
|
||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<!-- show name and ref in separate columns -->
|
|
||||||
<field name="display_name" position="after">
|
<field name="display_name" position="after">
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="ref"/>
|
<field name="ref" optional="hide"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="display_name" position="attributes">
|
<field name="display_name" position="attributes">
|
||||||
<attribute name="invisible">1</attribute>
|
<attribute name="invisible">1</attribute>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
-->
|
||||||
|
|
||||||
<record id="res_partner_kanban_view" model="ir.ui.view">
|
<record id="res_partner_kanban_view" model="ir.ui.view">
|
||||||
<field name="name">Add ref in partner kanban view</field>
|
<field name="name">Add ref in partner kanban view</field>
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# © 2014-2016 Akretion (http://www.akretion.com)
|
# Copyright 2014-2020 Akretion France (http://www.akretion.com)
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Base Usability',
|
'name': 'Base Usability',
|
||||||
'version': '12.0.0.1.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Partner',
|
'category': 'Partner',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': 'Better usability in base module',
|
'summary': 'Better usability in base module',
|
||||||
@@ -13,12 +13,12 @@
|
|||||||
'depends': ['base'],
|
'depends': ['base'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/group.xml',
|
'security/group.xml',
|
||||||
'views/partner_view.xml',
|
'security/ir.model.access.csv',
|
||||||
'views/partner_bank_view.xml',
|
'views/res_partner.xml',
|
||||||
'views/users_view.xml',
|
'views/res_partner_bank.xml',
|
||||||
'views/country_view.xml',
|
'views/res_country.xml',
|
||||||
'views/module_view.xml',
|
'views/ir_module.xml',
|
||||||
'views/base_view.xml',
|
'views/ir_sequence.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py
|
diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py
|
||||||
index 083607f9..99ae8857 100644
|
index a3baf47c615..e546d450107 100644
|
||||||
--- a/odoo/addons/base/models/res_users.py
|
--- a/odoo/addons/base/models/res_users.py
|
||||||
+++ b/odoo/addons/base/models/res_users.py
|
+++ b/odoo/addons/base/models/res_users.py
|
||||||
@@ -426,7 +426,13 @@ class Users(models.Model):
|
@@ -544,7 +544,13 @@ class Users(models.Model):
|
||||||
for user in users:
|
for user in users:
|
||||||
user.partner_id.active = user.active
|
# if partner is global we keep it that way
|
||||||
if user.partner_id.company_id:
|
if user.partner_id.company_id:
|
||||||
- user.partner_id.write({'company_id': user.company_id.id})
|
- user.partner_id.company_id = user.company_id
|
||||||
+ # AKRETION HACK: if you have a multi-company setup where
|
+ # AKRETION HACK: if you have a multi-company setup where
|
||||||
+ # partners are NOT shared between companies, having
|
+ # partners are NOT shared between companies, having
|
||||||
+ # company_id=False on partners related to users
|
+ # company_id=False on partners related to users
|
||||||
+ # avoids a lot of trouble (you should also disable 'read'
|
+ # avoids a lot of trouble (you should also disable 'read'
|
||||||
+ # on the ir.rule 'user rule' (XMLID base.res_users_rule)
|
+ # on the ir.rule 'user rule' (XMLID base.res_users_rule)
|
||||||
+ # user.partner_id.write({'company_id': user.company_id.id})
|
+ # user.partner_id.company_id = user.company_id
|
||||||
+ user.partner_id.write({'company_id': False})
|
+ user.partner_id.write({'company_id': False})
|
||||||
|
user.partner_id.active = user.active
|
||||||
return users
|
return users
|
||||||
|
|
||||||
@api.multi
|
|
||||||
|
|||||||
194
base_usability/i18n/base_usability.pot
Normal file
194
base_usability/i18n/base_usability.pot
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_usability
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-07-01 10:02+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-07-01 10:02+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner_bank
|
||||||
|
msgid "Bank Accounts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__bank_name
|
||||||
|
msgid "Bank Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_company
|
||||||
|
msgid "Companies"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Customer Number:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "E-mail:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Group By"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.view_module_filter
|
||||||
|
msgid "Installable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_ir_mail_server
|
||||||
|
msgid "Mail Server"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Mobile:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_ir_model
|
||||||
|
msgid "Models"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Name or Code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.view_res_partner_filter
|
||||||
|
msgid "Name or Email or Reference"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__name_title
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__name_title
|
||||||
|
msgid "Name with Title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:res.groups,name:base_usability.group_nobody
|
||||||
|
msgid "Nobody (used to hide native menus)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner_category
|
||||||
|
msgid "Partner Tags"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__ref
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__ref
|
||||||
|
msgid "Reference"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Search Countries"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Supplier Number:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__name
|
||||||
|
msgid "Tag Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Tel:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_users
|
||||||
|
msgid "Users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "VAT Number:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "VAT:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Website:"
|
||||||
|
msgstr ""
|
||||||
192
base_usability/i18n/fr.po
Normal file
192
base_usability/i18n/fr.po
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_usability
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2021-07-01 10:02+0000\n"
|
||||||
|
"PO-Revision-Date: 2021-07-01 12:15+0200\n"
|
||||||
|
"Last-Translator: Alexis de Lattre <alexis@via.ecp.fr>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner_bank
|
||||||
|
msgid "Bank Accounts"
|
||||||
|
msgstr "Comptes bancaires"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__bank_name
|
||||||
|
msgid "Bank Name"
|
||||||
|
msgstr "Nom de la banque"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_company
|
||||||
|
msgid "Companies"
|
||||||
|
msgstr "Sociétés"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr "Contact"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "Devise"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Customer Number:"
|
||||||
|
msgstr "N° client :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__display_name
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nom affiché"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "E-mail:"
|
||||||
|
msgstr "E-mail :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Group By"
|
||||||
|
msgstr "Grouper par"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__id
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr "ID"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.view_module_filter
|
||||||
|
msgid "Installable"
|
||||||
|
msgstr "Installable"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_ir_model____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_company____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_bank____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category____last_update
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "Dernière modification le"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_ir_mail_server
|
||||||
|
msgid "Mail Server"
|
||||||
|
msgstr "Serveur mail"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Mobile:"
|
||||||
|
msgstr "Portable :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_ir_model
|
||||||
|
msgid "Models"
|
||||||
|
msgstr "Modèles"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Name or Code"
|
||||||
|
msgstr "Nom ou code"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.view_res_partner_filter
|
||||||
|
msgid "Name or Email or Reference"
|
||||||
|
msgstr "Nom ou e-mail ou référence"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__name_title
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__name_title
|
||||||
|
msgid "Name with Title"
|
||||||
|
msgstr "Nom avec titre"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:res.groups,name:base_usability.group_nobody
|
||||||
|
msgid "Nobody (used to hide native menus)"
|
||||||
|
msgstr "Personne (utilisé pour cacher des entrées de menu natifs)"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_partner_category
|
||||||
|
msgid "Partner Tags"
|
||||||
|
msgstr "Étiquettes du partenaire"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner__ref
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_users__ref
|
||||||
|
msgid "Reference"
|
||||||
|
msgstr "Référence"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||||
|
msgid "Search Countries"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Supplier Number:"
|
||||||
|
msgstr "N° fournisseur :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model.fields,field_description:base_usability.field_res_partner_category__name
|
||||||
|
msgid "Tag Name"
|
||||||
|
msgstr "Nom de l'étiquette"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Tel:"
|
||||||
|
msgstr "Tél :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: model:ir.model,name:base_usability.model_res_users
|
||||||
|
msgid "Users"
|
||||||
|
msgstr "Utilisateurs"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "VAT Number:"
|
||||||
|
msgstr "N° TVA :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "VAT:"
|
||||||
|
msgstr "TVA :"
|
||||||
|
|
||||||
|
#. module: base_usability
|
||||||
|
#: code:addons/base_usability/models/res_company.py:0
|
||||||
|
#: code:addons/base_usability/models/res_partner.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Website:"
|
||||||
|
msgstr "Site web :"
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
from . import users
|
from . import res_users
|
||||||
from . import partner
|
from . import res_partner
|
||||||
from . import company
|
from . import res_partner_bank
|
||||||
from . import mail
|
from . import res_partner_category
|
||||||
from . import misc
|
from . import res_company
|
||||||
|
from . import ir_mail_server
|
||||||
|
from . import ir_actions_report
|
||||||
from . import ir_model
|
from . import ir_model
|
||||||
|
|||||||
15
base_usability/models/ir_actions_report.py
Normal file
15
base_usability/models/ir_actions_report.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2021 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class IrActionsReport(models.Model):
|
||||||
|
_inherit = "ir.actions.report"
|
||||||
|
|
||||||
|
# since v13, print_report_name is a translatable field
|
||||||
|
# It means that you can't set the value via an inherit of
|
||||||
|
# ir.actions.report as XML
|
||||||
|
# I think it was easier when this field was not translatable
|
||||||
|
print_report_name = fields.Char(translate=False)
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
# Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import models, api
|
from odoo import api, models
|
||||||
from odoo.addons.base.models.ir_mail_server import extract_rfc2822_addresses
|
from odoo.addons.base.models.ir_mail_server import extract_rfc2822_addresses
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ class IrMailServer(models.Model):
|
|||||||
"with subject '%s'",
|
"with subject '%s'",
|
||||||
smtp_from, message.get('To'), message.get('Cc'),
|
smtp_from, message.get('To'), message.get('Cc'),
|
||||||
message.get('Bcc'), message.get('Subject'))
|
message.get('Bcc'), message.get('Subject'))
|
||||||
return super(IrMailServer, self).send_email(
|
return super().send_email(
|
||||||
message, mail_server_id=mail_server_id,
|
message, mail_server_id=mail_server_id,
|
||||||
smtp_server=smtp_server, smtp_port=smtp_port,
|
smtp_server=smtp_server, smtp_port=smtp_port,
|
||||||
smtp_user=smtp_user, smtp_password=smtp_password,
|
smtp_user=smtp_user, smtp_password=smtp_password,
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import models, fields, api
|
|
||||||
from odoo.tools import misc
|
|
||||||
from odoo.tools import float_compare
|
|
||||||
|
|
||||||
|
|
||||||
class BaseLanguageExport(models.TransientModel):
|
|
||||||
_inherit = 'base.language.export'
|
|
||||||
|
|
||||||
# Default format for language files = format used by OpenERP modules
|
|
||||||
format = fields.Selection(default='po')
|
|
||||||
|
|
||||||
|
|
||||||
class BaseLanguageInstall(models.TransientModel):
|
|
||||||
_inherit = 'base.language.install'
|
|
||||||
|
|
||||||
overwrite = fields.Boolean(default=True)
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
# Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import models, api, _
|
from odoo import api, models, _
|
||||||
|
|
||||||
|
|
||||||
class ResCompany(models.Model):
|
class ResCompany(models.Model):
|
||||||
@@ -31,7 +32,6 @@ class ResCompany(models.Model):
|
|||||||
line = separator.join(content)
|
line = separator.join(content)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _prepare_header_options(self):
|
def _prepare_header_options(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
options = {
|
options = {
|
||||||
@@ -51,7 +51,7 @@ class ResCompany(models.Model):
|
|||||||
'label': _('Website:')},
|
'label': _('Website:')},
|
||||||
'vat': {
|
'vat': {
|
||||||
'value': self.vat,
|
'value': self.vat,
|
||||||
'label': _('TVA :')}, # TODO translate
|
'label': _('VAT:')},
|
||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
|
|
||||||
@@ -61,7 +61,6 @@ class ResCompany(models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
# for reports
|
# for reports
|
||||||
@api.multi
|
|
||||||
def _display_report_header(
|
def _display_report_header(
|
||||||
self, line_details=[['phone', 'website'], ['vat']],
|
self, line_details=[['phone', 'website'], ['vat']],
|
||||||
icon=True, line_separator=' - '):
|
icon=True, line_separator=' - '):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
# Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
@@ -7,33 +8,14 @@ from odoo import models, fields, api, _
|
|||||||
class ResPartner(models.Model):
|
class ResPartner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
# track_visibility is handled in the 'mail' module, and base_usability
|
# tracking=True is handled in the 'mail' module, and base_usability
|
||||||
# doesn't depend on 'mail', but that doesn't hurt, it will just be
|
# doesn't depend on 'mail', so adding tracking on res.partner fields
|
||||||
# ignored if mail is not installed
|
# has been moved to mail_usability
|
||||||
name = fields.Char(track_visibility='onchange')
|
ref = fields.Char(copy=False)
|
||||||
parent_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
ref = fields.Char(track_visibility='onchange', copy=False)
|
|
||||||
lang = fields.Selection(track_visibility='onchange')
|
|
||||||
user_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
vat = fields.Char(track_visibility='onchange')
|
|
||||||
customer = fields.Boolean(track_visibility='onchange')
|
|
||||||
supplier = fields.Boolean(track_visibility='onchange')
|
|
||||||
type = fields.Selection(track_visibility='onchange')
|
|
||||||
street = fields.Char(track_visibility='onchange')
|
|
||||||
street2 = fields.Char(track_visibility='onchange')
|
|
||||||
zip = fields.Char(track_visibility='onchange')
|
|
||||||
city = fields.Char(track_visibility='onchange')
|
|
||||||
state_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
country_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
email = fields.Char(track_visibility='onchange')
|
|
||||||
is_company = fields.Boolean(track_visibility='onchange')
|
|
||||||
active = fields.Boolean(track_visibility='onchange')
|
|
||||||
company_id = fields.Many2one(track_visibility='onchange')
|
|
||||||
# For reports
|
# For reports
|
||||||
name_title = fields.Char(
|
name_title = fields.Char(
|
||||||
compute='_compute_name_title', string='Name with Title')
|
compute='_compute_name_title', string='Name with Title')
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.depends('name', 'title')
|
@api.depends('name', 'title')
|
||||||
def _compute_name_title(self):
|
def _compute_name_title(self):
|
||||||
for partner in self:
|
for partner in self:
|
||||||
@@ -49,21 +31,17 @@ class ResPartner(models.Model):
|
|||||||
name_title = ' '.join([title, name_title])
|
name_title = ' '.join([title, name_title])
|
||||||
partner.name_title = name_title
|
partner.name_title = name_title
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _display_address(self, without_company=False):
|
def _display_address(self, without_company=False):
|
||||||
'''Remove empty lines'''
|
'''Remove empty lines'''
|
||||||
res = super(ResPartner, self)._display_address(
|
res = super()._display_address(without_company=without_company)
|
||||||
without_company=without_company)
|
|
||||||
while "\n\n" in res:
|
while "\n\n" in res:
|
||||||
res = res.replace('\n\n', '\n')
|
res = res.replace('\n\n', '\n')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# for reports
|
# for reports
|
||||||
@api.multi
|
|
||||||
def _display_full_address(
|
def _display_full_address(
|
||||||
self, details=[
|
self,
|
||||||
'company', 'name', 'address', 'phone',
|
details=['company', 'name', 'address', 'phone', 'mobile', 'email'],
|
||||||
'mobile', 'email'],
|
|
||||||
icon=True):
|
icon=True):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
# To make the icons work with py3o with PDF export, on the py3o server:
|
# To make the icons work with py3o with PDF export, on the py3o server:
|
||||||
@@ -72,10 +50,16 @@ class ResPartner(models.Model):
|
|||||||
if self.is_company:
|
if self.is_company:
|
||||||
company = self.name
|
company = self.name
|
||||||
name = False
|
name = False
|
||||||
|
name_no_title = False
|
||||||
|
title = False
|
||||||
|
title_short = False
|
||||||
else:
|
else:
|
||||||
name = self.name_title
|
|
||||||
company = self.parent_id and self.parent_id.is_company and\
|
company = self.parent_id and self.parent_id.is_company and\
|
||||||
self.parent_id.name or False
|
self.parent_id.name or False
|
||||||
|
name = self.name_title
|
||||||
|
name_no_title = self.name
|
||||||
|
title = self.title.name
|
||||||
|
title_short = self.title.shortcut
|
||||||
options = {
|
options = {
|
||||||
'name': {
|
'name': {
|
||||||
'value': name,
|
'value': name,
|
||||||
@@ -83,6 +67,15 @@ class ResPartner(models.Model):
|
|||||||
'company': {
|
'company': {
|
||||||
'value': company,
|
'value': company,
|
||||||
},
|
},
|
||||||
|
'title': {
|
||||||
|
'value': title,
|
||||||
|
},
|
||||||
|
'title_short': {
|
||||||
|
'value': title_short,
|
||||||
|
},
|
||||||
|
'name_no_title': {
|
||||||
|
'value': name_no_title,
|
||||||
|
},
|
||||||
'phone': {
|
'phone': {
|
||||||
'value': self.phone,
|
'value': self.phone,
|
||||||
# http://www.fileformat.info/info/unicode/char/1f4de/index.htm
|
# http://www.fileformat.info/info/unicode/char/1f4de/index.htm
|
||||||
@@ -109,7 +102,28 @@ class ResPartner(models.Model):
|
|||||||
},
|
},
|
||||||
'address': {
|
'address': {
|
||||||
'value': self._display_address(without_company=True),
|
'value': self._display_address(without_company=True),
|
||||||
}
|
},
|
||||||
|
'vat': {
|
||||||
|
'value': self.commercial_partner_id.vat,
|
||||||
|
'label': _('VAT Number:'),
|
||||||
|
},
|
||||||
|
'commercial_ref': {
|
||||||
|
'value': self.commercial_partner_id.ref,
|
||||||
|
'label': _('Customer Number:'),
|
||||||
|
},
|
||||||
|
'ref': {
|
||||||
|
'value': self.ref,
|
||||||
|
'label': _('Customer Number:'),
|
||||||
|
},
|
||||||
|
# Same with 'supplier_' prefix, to change the label
|
||||||
|
'supplier_commercial_ref': {
|
||||||
|
'value': self.commercial_partner_id.ref,
|
||||||
|
'label': _('Supplier Number:'),
|
||||||
|
},
|
||||||
|
'supplier_ref': {
|
||||||
|
'value': self.ref,
|
||||||
|
'label': _('Supplier Number:'),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
res = []
|
res = []
|
||||||
for detail in details:
|
for detail in details:
|
||||||
@@ -122,18 +136,3 @@ class ResPartner(models.Model):
|
|||||||
res.append('%s' % entry['value'])
|
res.append('%s' % entry['value'])
|
||||||
res = '\n'.join(res)
|
res = '\n'.join(res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class ResPartnerCategory(models.Model):
|
|
||||||
_inherit = 'res.partner.category'
|
|
||||||
|
|
||||||
name = fields.Char(translate=False)
|
|
||||||
|
|
||||||
|
|
||||||
class ResPartnerBank(models.Model):
|
|
||||||
_inherit = 'res.partner.bank'
|
|
||||||
|
|
||||||
# In the 'base' module, they didn't put any string, so the bank name is
|
|
||||||
# displayed as 'Name', which the string of the related field it
|
|
||||||
# points to
|
|
||||||
bank_name = fields.Char(string='Bank Name')
|
|
||||||
14
base_usability/models/res_partner_bank.py
Normal file
14
base_usability/models/res_partner_bank.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartnerBank(models.Model):
|
||||||
|
_inherit = 'res.partner.bank'
|
||||||
|
|
||||||
|
# In the 'base' module, they didn't put any string, so the bank name is
|
||||||
|
# displayed as 'Name', which the string of the related field it
|
||||||
|
# points to
|
||||||
|
bank_name = fields.Char(string='Bank Name')
|
||||||
11
base_usability/models/res_partner_category.py
Normal file
11
base_usability/models/res_partner_category.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Copyright 2015-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartnerCategory(models.Model):
|
||||||
|
_inherit = 'res.partner.category'
|
||||||
|
|
||||||
|
name = fields.Char(translate=False)
|
||||||
27
base_usability/models/res_users.py
Normal file
27
base_usability/models/res_users.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Copyright 2018-2020 Akretion France (http://www.akretion.com/)
|
||||||
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, models, SUPERUSER_ID
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResUsers(models.Model):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _script_partners_linked_to_users_no_company(self):
|
||||||
|
if self.env.user.id != SUPERUSER_ID:
|
||||||
|
self = self.sudo()
|
||||||
|
logger.info(
|
||||||
|
'START to set company_id=False on partners related to users')
|
||||||
|
users = self.with_context(active_test=False).search([])
|
||||||
|
for user in users:
|
||||||
|
if user.partner_id.company_id:
|
||||||
|
user.partner_id.write({'company_id': False})
|
||||||
|
logger.info(
|
||||||
|
'Wrote company_id=False on user %s ID %d',
|
||||||
|
user.login, user.id)
|
||||||
|
logger.info(
|
||||||
|
'END setting company_id=False on partners related to users')
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Copyright 2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import models, api, SUPERUSER_ID, _
|
|
||||||
from odoo.exceptions import UserError
|
|
||||||
import logging
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class ResUsers(models.Model):
|
|
||||||
_inherit = 'res.users'
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def default_get(self, fields_list):
|
|
||||||
res = super(ResUsers, self).default_get(fields_list)
|
|
||||||
# For a new partner auto-created when you create a new user, we prefer
|
|
||||||
# customer=False and supplier=True by default
|
|
||||||
res.update({
|
|
||||||
'customer': False,
|
|
||||||
'supplier': True,
|
|
||||||
})
|
|
||||||
return res
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _script_partners_linked_to_users_no_company(self):
|
|
||||||
if self.env.user.id != SUPERUSER_ID:
|
|
||||||
raise UserError(_('You must run this script as admin user'))
|
|
||||||
logger.info(
|
|
||||||
'START to set company_id=False on partners related to users')
|
|
||||||
users = self.search(
|
|
||||||
['|', ('active', '=', True), ('active', '=', False)])
|
|
||||||
for user in users:
|
|
||||||
if user.partner_id.company_id:
|
|
||||||
user.partner_id.company_id = False
|
|
||||||
logger.info(
|
|
||||||
'Wrote company_id=False on user %s ID %d',
|
|
||||||
user.login, user.id)
|
|
||||||
logger.info(
|
|
||||||
'END setting company_id=False on partners related to users')
|
|
||||||
return True
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user