Compare commits
211 Commits
12_sale_pi
...
14.0-imp-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
279dc7c6c0 | ||
|
|
f6ddbb48ac | ||
|
|
d3bddf5fda | ||
|
|
6f3a468a7c | ||
|
|
0bfa960153 | ||
|
|
a520ccff51 | ||
|
|
fa7611eb08 | ||
|
|
b2eda2a23b | ||
|
|
74ff1b5cb5 | ||
|
|
a7b4ed65eb | ||
|
|
7dfb32c2d6 | ||
|
|
d081bb0fd2 | ||
|
|
f3fdbec140 | ||
|
|
ac54c5cc75 | ||
|
|
78c11411c3 | ||
|
|
28ce11b216 | ||
|
|
00e034dacf | ||
|
|
8510b9518a | ||
|
|
15ef5df155 | ||
|
|
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 | ||
|
|
c082baa340 | ||
|
|
15ee1b2f59 | ||
|
|
b080dd11ab | ||
|
|
568e6b41ac | ||
|
|
0dee23aeba | ||
|
|
c7d1e13d0d | ||
|
|
e7fae291ee | ||
|
|
bdfe0ce20a | ||
|
|
32eafedf04 | ||
|
|
ba78791ab1 | ||
|
|
946d169665 | ||
|
|
cd51a6784f | ||
|
|
c01942b1a8 | ||
|
|
e11b29deab | ||
|
|
28be3e4e6a | ||
|
|
5c7985a15c | ||
|
|
9b6c34a860 | ||
|
|
f735f8a34d | ||
|
|
74665bb2e8 | ||
|
|
d1deeeac31 | ||
|
|
e96c3d72eb | ||
|
|
a545c8dbb6 | ||
|
|
07824a0570 | ||
|
|
12bd157ad4 | ||
|
|
53f6ab5d0f | ||
|
|
cc88dd994b | ||
|
|
b99659dcc9 | ||
|
|
581bfe78d8 | ||
|
|
9a174c257d | ||
|
|
941c610c96 | ||
|
|
34a4c573b1 | ||
|
|
f64d190ac3 | ||
|
|
c633660e73 | ||
|
|
0dfe8df8e4 | ||
|
|
0e4a562263 | ||
|
|
253a5ebebe | ||
|
|
73c622956f | ||
|
|
38db0da20a | ||
|
|
361f5d3aae | ||
|
|
499d363daf | ||
|
|
035a3dfd1e | ||
|
|
83fec9264f | ||
|
|
9762b5392b | ||
|
|
fc4ea217e0 | ||
|
|
1812c7f5af | ||
|
|
92a4b0d6ac | ||
|
|
33f67279fb | ||
|
|
66c845f3cf | ||
|
|
8a9c75790a | ||
|
|
0de8425cfa | ||
|
|
f8563c1667 | ||
|
|
60cda426a9 | ||
|
|
fe7f2c00c5 | ||
|
|
76b113ed28 | ||
|
|
5140094633 |
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,
|
||||||
}
|
}
|
||||||
|
|||||||
96
account_usability/README.rst
Normal file
96
account_usability/README.rst
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
=================
|
||||||
|
Account Usability
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||||
|
:target: https://odoo-community.org/page/development-status
|
||||||
|
:alt: Beta
|
||||||
|
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
.. |badge3| image:: https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github
|
||||||
|
:target: https://github.com/akretion/odoo-usability/tree/12.0/account_usability
|
||||||
|
:alt: akretion/odoo-usability
|
||||||
|
|
||||||
|
|badge1| |badge2| |badge3|
|
||||||
|
|
||||||
|
This modules adds the following functions:
|
||||||
|
|
||||||
|
* Add an *Overdue* filter on invoice search view (this feature was previously
|
||||||
|
located in te module *account_invoice_overdue_filter*)
|
||||||
|
* Increase the default limit of 80 lines in account move and account move line view.
|
||||||
|
* disable reconciliation "guessing"
|
||||||
|
* fast search on *Reconcile Ref* for account move line.
|
||||||
|
* add sale dates to invoice report to be compliant with
|
||||||
|
https://www.service-public.fr/professionnels-entreprises/vosdroits/F31808
|
||||||
|
* Sale date on qweb invoices
|
||||||
|
* A wizard to mark several invoices as sent at once (forward from v8)
|
||||||
|
* Default date for Account Move Reversal is now D+1 instead of today
|
||||||
|
* Track more fields on invoice (see details in account.py)
|
||||||
|
* Add boolean fields `has_discount` and `has_attachment` on invoice
|
||||||
|
* Add button "Delete line qty = 0" on supplier invoice
|
||||||
|
* Cut name_get() of invoice if too long
|
||||||
|
* A script for if Odoo screws up invoice attachment filename
|
||||||
|
* help functions for py3o reports
|
||||||
|
* Show code on name_get of journal
|
||||||
|
* add direct search of journal using code
|
||||||
|
* add copy=False on some fields
|
||||||
|
* Add unicity constraint on analytic codes per company
|
||||||
|
* Better default values on account move
|
||||||
|
* Add link from account move line to invoice
|
||||||
|
* Add start_date and end_date on bank statements
|
||||||
|
* Add transfer_account_id to invoicing config page
|
||||||
|
* Improve domain reconciliation widget
|
||||||
|
* account.reconcile.model don't copy name to label via onchange
|
||||||
|
* Add method to get fiscal position without partner_id
|
||||||
|
* Restore drill-through on sale and invoice reports
|
||||||
|
* don't attach PDF upon invoice report generation on supplier invoices/refunds
|
||||||
|
* Add filter on debit and credit amount for Move Lines
|
||||||
|
* Add supplier invoice number in invoice tree view
|
||||||
|
|
||||||
|
Together with this module, I recommend the use of the following modules:
|
||||||
|
|
||||||
|
* account_invoice_supplier_ref_unique (OCA project account-invoicing)
|
||||||
|
* account_move_line_no_default_search (OCA project account-financial-tools)
|
||||||
|
* invoice_fiscal_position_update (OCA project account-invoicing)
|
||||||
|
|
||||||
|
**Table of contents**
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
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.
|
||||||
|
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:%20account_usability%0Aversion:%2012.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.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Akretion
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
* David Beal <david.beal@akretion.com>
|
||||||
|
|
||||||
|
Maintainers
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
This module is part of the `akretion/odoo-usability <https://github.com/akretion/odoo-usability/tree/12.0/account_usability>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute.
|
||||||
@@ -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,34 +1,13 @@
|
|||||||
# 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': '10.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',
|
||||||
'description': """
|
|
||||||
Account Usability
|
|
||||||
=================
|
|
||||||
|
|
||||||
The usability enhancements include:
|
|
||||||
* show the supplier invoice number in the tree view of supplier invoices
|
|
||||||
* add an *Overdue* filter on invoice search view (this feature was previously
|
|
||||||
located in te module *account_invoice_overdue_filter*)
|
|
||||||
* increase the default limit of 80 lines in account move and account move line view.
|
|
||||||
* fast search on *Reconcile Ref* for account move line.
|
|
||||||
* disable reconciliation "guessing"
|
|
||||||
* add sale dates to invoice report to be compliant with
|
|
||||||
https://www.service-public.fr/professionnels-entreprises/vosdroits/F31808
|
|
||||||
|
|
||||||
Together with this module, I recommend the use of the following modules:
|
|
||||||
* account_invoice_supplier_ref_unique (OCA project account-invoicing)
|
|
||||||
* account_move_line_no_default_search (OCA project account-financial-tools)
|
|
||||||
* invoice_fiscal_position_update (OCA project account-invoicing)
|
|
||||||
|
|
||||||
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
|
||||||
""",
|
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'depends': [
|
'depends': [
|
||||||
@@ -38,12 +17,21 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
|||||||
# 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_bank_statement.xml',
|
||||||
'partner_view.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/account_report.xml',
|
||||||
'wizard/account_invoice_mark_sent_view.xml',
|
'wizard/account_invoice_mark_sent_view.xml',
|
||||||
'report/invoice_report.xml',
|
'wizard/account_group_generate_view.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,661 +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 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(AccountInvoice, self).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
|
|
||||||
# @api.multi
|
|
||||||
# def action_move_create(self):
|
|
||||||
# res = super(AccountInvoice, self).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'
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@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.multi
|
|
||||||
@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(AccountAccount, self).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'
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
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(AccountAnalyticAccount, self).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)
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@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
|
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@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(AccountBankStatementLine, self).\
|
|
||||||
# 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(AccountBankStatementLine, self).\
|
|
||||||
_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_journal_line')
|
|
||||||
if self.journal_entry_ids:
|
|
||||||
action.update({
|
|
||||||
'views': False,
|
|
||||||
'view_id': False,
|
|
||||||
'view_mode': 'form,tree',
|
|
||||||
'res_id': self.journal_entry_ids[0].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
|
|
||||||
@@ -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,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,53 +0,0 @@
|
|||||||
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
|
|
||||||
index 8ed1e48..615da43 100644
|
|
||||||
--- a/addons/account/models/account_bank_statement.py
|
|
||||||
+++ b/addons/account/models/account_bank_statement.py
|
|
||||||
@@ -563,7 +563,13 @@ class AccountBankStatementLine(models.Model):
|
|
||||||
"""
|
|
||||||
# Blue lines = payment on bank account not assigned to a statement yet
|
|
||||||
reconciliation_aml_accounts = [self.journal_id.default_credit_account_id.id, self.journal_id.default_debit_account_id.id]
|
|
||||||
- domain_reconciliation = ['&', '&', ('statement_id', '=', False), ('account_id', 'in', reconciliation_aml_accounts), ('payment_id','<>', False)]
|
|
||||||
+ # AKRETION HACK 11/7/2017
|
|
||||||
+ # Remove ('payment_id','<>', False) in order to allow to select move lines
|
|
||||||
+ # generated from payment orders or check deposit
|
|
||||||
+ # but I add ('journal_id', '=', self.journal_id.id) to exclude the
|
|
||||||
+ # opening entry of the first fiscal year
|
|
||||||
+ #domain_reconciliation = ['&', '&', ('statement_id', '=', False), ('account_id', 'in', reconciliation_aml_accounts), ('payment_id','<>', False)]
|
|
||||||
+ domain_reconciliation = ['&', '&', ('statement_id', '=', False), ('account_id', 'in', reconciliation_aml_accounts), ('journal_id', '=', self.journal_id.id)]
|
|
||||||
|
|
||||||
# Black lines = unreconciled & (not linked to a payment or open balance created by statement
|
|
||||||
domain_matching = [('reconciled', '=', False)]
|
|
||||||
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
|
|
||||||
index b60ffbe..6c27c57 100644
|
|
||||||
--- a/addons/account/models/account_move.py
|
|
||||||
+++ b/addons/account/models/account_move.py
|
|
||||||
@@ -599,6 +599,7 @@ class AccountMoveLine(models.Model):
|
|
||||||
domain = expression.AND([domain, [('id', 'not in', excluded_ids)]])
|
|
||||||
if str:
|
|
||||||
str_domain = [
|
|
||||||
+ '|', ('account_id.code', '=ilike', str + '%'),
|
|
||||||
'|', ('move_id.name', 'ilike', str),
|
|
||||||
'|', ('move_id.ref', 'ilike', str),
|
|
||||||
'|', ('date_maturity', 'like', str),
|
|
||||||
diff --git a/addons/account/static/src/js/account_reconciliation_widgets.js b/addons/account/static/src/js/account_reconciliation_widgets.js
|
|
||||||
index 453bd41..48c396e 100644
|
|
||||||
--- a/addons/account/static/src/js/account_reconciliation_widgets.js
|
|
||||||
+++ b/addons/account/static/src/js/account_reconciliation_widgets.js
|
|
||||||
@@ -76,7 +76,7 @@ var abstractReconciliation = Widget.extend(ControlPanelMixin, {
|
|
||||||
this.model_res_users = new Model("res.users");
|
|
||||||
this.model_tax = new Model("account.tax");
|
|
||||||
this.model_presets = new Model("account.reconcile.model");
|
|
||||||
- this.max_move_lines_displayed = 5;
|
|
||||||
+ this.max_move_lines_displayed = 15;
|
|
||||||
// Number of reconciliations loaded initially and by clicking 'show more'
|
|
||||||
this.num_reconciliations_fetched_in_batch = 10;
|
|
||||||
this.animation_speed = 100; // "Blocking" animations
|
|
||||||
@@ -1755,7 +1755,7 @@ var bankStatementReconciliationLine = abstractReconciliationLine.extend({
|
|
||||||
relation: "res.partner",
|
|
||||||
string: _t("Partner"),
|
|
||||||
type: "many2one",
|
|
||||||
- domain: [['parent_id','=',false], '|', ['customer','=',true], ['supplier','=',true]],
|
|
||||||
+ domain: [['parent_id','=',false]], // AKRETION HACK 26/6/2017 allow all parent partners
|
|
||||||
help: "",
|
|
||||||
readonly: false,
|
|
||||||
required: true,
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
|
|
||||||
index 4374528..aea1361 100644
|
|
||||||
--- a/addons/account/models/account_bank_statement.py
|
|
||||||
+++ b/addons/account/models/account_bank_statement.py
|
|
||||||
@@ -1008,7 +1008,7 @@ class AccountBankStatementLine(models.Model):
|
|
||||||
#record the move name on the statement line to be able to retrieve it in case of unreconciliation
|
|
||||||
self.write({'move_name': move.name})
|
|
||||||
payment.write({'payment_reference': move.name})
|
|
||||||
- elif self.move_name:
|
|
||||||
- raise UserError(_('Operation not allowed. Since your statement line already received a number, you cannot reconcile it entirely with existing journal entries otherwise it would make a gap in the numbering. You should book an entry and make a regular revert of it in case you want to cancel it.'))
|
|
||||||
+ #elif self.move_name:
|
|
||||||
+ # raise UserError(_('Operation not allowed. Since your statement line already received a number, you cannot reconcile it entirely with existing journal entries otherwise it would make a gap in the numbering. You should book an entry and make a regular revert of it in case you want to cancel it.'))
|
|
||||||
counterpart_moves.assert_balanced()
|
|
||||||
return counterpart_moves
|
|
||||||
@@ -1,555 +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">
|
|
||||||
<!-- Move reconcile_id to a better position -->
|
|
||||||
<field name="full_reconcile_id" position="replace"/>
|
|
||||||
<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_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 fa-arrow-right"
|
|
||||||
attrs="{'invisible': [('journal_entry_ids', '=', [])]}"/>
|
|
||||||
</xpath>
|
|
||||||
<field name="date" position="after">
|
|
||||||
<field name="start_date"/>
|
|
||||||
<field name="end_date"/>
|
|
||||||
</field>
|
|
||||||
<field name="date" position="attributes">
|
|
||||||
<attribute name="invisible">1</attribute>
|
|
||||||
</field>
|
|
||||||
</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"/>
|
|
||||||
|
|
||||||
<!-- 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 b1d8012329d..b8a8e2a673d 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
|
||||||
@@ -210,6 +210,7 @@ class account_payment(models.Model):
|
@@ -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", 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'),
|
||||||
|
})
|
||||||
|
return line_vals_list
|
||||||
|
|
||||||
# FIXME: ondelete='restrict' not working (eg. cancel a bank statement reconciliation with a payment)
|
@@ -699,6 +700,7 @@ class AccountPayment(models.Model):
|
||||||
move_line_ids = fields.One2many('account.move.line', 'payment_id', readonly=True, copy=False, ondelete='restrict')
|
'name': writeoff_lines[0].name,
|
||||||
@@ -431,6 +432,7 @@ class account_payment(models.Model):
|
'amount': writeoff_amount * sign,
|
||||||
amount_currency_wo = -abs(amount_currency_wo)
|
'account_id': writeoff_lines[0].account_id.id,
|
||||||
writeoff_line['name'] = _('Counterpart')
|
+ 'analytic_account_id': writeoff_lines[0].analytic_account_id.id,
|
||||||
writeoff_line['account_id'] = self.writeoff_account_id.id
|
}
|
||||||
+ writeoff_line['analytic_account_id'] = self.writeoff_analytic_account_id.id or False
|
else:
|
||||||
writeoff_line['debit'] = debit_wo
|
write_off_line_vals = {}
|
||||||
writeoff_line['credit'] = credit_wo
|
diff --git a/addons/account/wizard/account_payment_register.py b/addons/account/wizard/account_payment_register.py
|
||||||
writeoff_line['amount_currency'] = amount_currency_wo
|
index 3fc91f716ad..35636774c7e 100644
|
||||||
diff --git a/addons/account/views/account_payment_view.xml b/addons/account/views/account_payment_view.xml
|
--- a/addons/account/wizard/account_payment_register.py
|
||||||
index 2460458fbaa..4065d8f9952 100644
|
+++ b/addons/account/wizard/account_payment_register.py
|
||||||
--- a/addons/account/views/account_payment_view.xml
|
@@ -93,6 +93,7 @@ class AccountPaymentRegister(models.TransientModel):
|
||||||
+++ b/addons/account/views/account_payment_view.xml
|
], default='open', string="Payment Difference Handling")
|
||||||
@@ -206,6 +206,8 @@
|
writeoff_account_id = fields.Many2one('account.account', string="Difference Account", copy=False,
|
||||||
|
domain="[('deprecated', '=', False), ('company_id', '=', company_id)]")
|
||||||
|
+ writeoff_analytic_account_id = fields.Many2one('account.analytic.account', string="Difference Analytic Account", copy=False, domain="[('company_id', '=', company_id)]")
|
||||||
|
writeoff_label = fields.Char(string='Journal Item Label', default='Write-Off',
|
||||||
|
help='Change label of the counterpart that will hold the payment difference')
|
||||||
|
|
||||||
|
@@ -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>
|
</div>
|
||||||
<field name="writeoff_account_id" string="Post Difference In"
|
|
||||||
attrs="{'invisible': [('payment_difference_handling','=','open')], 'required': [('payment_difference_handling', '=', 'reconcile')]}"/>
|
|
||||||
+ <field name="writeoff_analytic_account_id" string="Post Difference In Analytic Account"
|
|
||||||
+ attrs="{'invisible': [('payment_difference_handling','=','open')]}"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</sheet>
|
|
||||||
|
|||||||
9
account_usability/models/__init__.py
Normal file
9
account_usability/models/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
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 product
|
||||||
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!')]
|
||||||
91
account_usability/models/account_bank_statement.py
Normal file
91
account_usability/models/account_bank_statement.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# 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 stmt.hide_bank_statement_balance:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
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.ref('account.action_move_line_form').read()[0]
|
||||||
|
# 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
|
||||||
16
account_usability/models/account_incoterms.py
Normal file
16
account_usability/models/account_incoterms.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 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
|
||||||
60
account_usability/models/account_journal.py
Normal file
60
account_usability/models/account_journal.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# 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.")
|
||||||
|
|
||||||
|
@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))
|
||||||
226
account_usability/models/account_move.py
Normal file
226
account_usability/models/account_move.py
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
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(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 inv in self:
|
||||||
|
sales = inv.invoice_line_ids.mapped(
|
||||||
|
'sale_line_ids').mapped('order_id')
|
||||||
|
dates = ["%s (%s)" % (
|
||||||
|
x.name, format_date(inv.env, self.date_order))
|
||||||
|
for x in sales]
|
||||||
|
inv.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)
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
def show_account_move_form(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env.ref('account.action_move_line_form').read()[0]
|
||||||
|
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
|
||||||
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)
|
||||||
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')
|
|
||||||
2
account_usability/readme/CONTRIBUTORS.rst
Normal file
2
account_usability/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
* David Beal <david.beal@akretion.com>
|
||||||
39
account_usability/readme/DESCRIPTION.rst
Normal file
39
account_usability/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
This modules adds the following functions:
|
||||||
|
|
||||||
|
* Add an *Overdue* filter on invoice search view (this feature was previously
|
||||||
|
located in te module *account_invoice_overdue_filter*)
|
||||||
|
* Increase the default limit of 80 lines in account move and account move line view.
|
||||||
|
* disable reconciliation "guessing"
|
||||||
|
* fast search on *Reconcile Ref* for account move line.
|
||||||
|
* add sale dates to invoice report to be compliant with
|
||||||
|
https://www.service-public.fr/professionnels-entreprises/vosdroits/F31808
|
||||||
|
* Sale date on qweb invoices
|
||||||
|
* A wizard to mark several invoices as sent at once (forward from v8)
|
||||||
|
* Default date for Account Move Reversal is now D+1 instead of today
|
||||||
|
* Track more fields on invoice (see details in account.py)
|
||||||
|
* Add boolean fields `has_discount` and `has_attachment` on invoice
|
||||||
|
* Add button "Delete line qty = 0" on supplier invoice
|
||||||
|
* Cut name_get() of invoice if too long
|
||||||
|
* A script for if Odoo screws up invoice attachment filename
|
||||||
|
* help functions for py3o reports
|
||||||
|
* Show code on name_get of journal
|
||||||
|
* add direct search of journal using code
|
||||||
|
* add copy=False on some fields
|
||||||
|
* Add unicity constraint on analytic codes per company
|
||||||
|
* Better default values on account move
|
||||||
|
* Add link from account move line to invoice
|
||||||
|
* Add start_date and end_date on bank statements
|
||||||
|
* Add transfer_account_id to invoicing config page
|
||||||
|
* Improve domain reconciliation widget
|
||||||
|
* account.reconcile.model don't copy name to label via onchange
|
||||||
|
* Add method to get fiscal position without partner_id
|
||||||
|
* Restore drill-through on sale and invoice reports
|
||||||
|
* don't attach PDF upon invoice report generation on supplier invoices/refunds
|
||||||
|
* Add filter on debit and credit amount for Move Lines
|
||||||
|
* Add supplier invoice number in invoice tree view
|
||||||
|
|
||||||
|
Together with this module, I recommend the use of the following modules:
|
||||||
|
|
||||||
|
* account_invoice_supplier_ref_unique (OCA project account-invoicing)
|
||||||
|
* account_move_line_no_default_search (OCA project account-financial-tools)
|
||||||
|
* invoice_fiscal_position_update (OCA project account-invoicing)
|
||||||
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
|
||||||
|
454
account_usability/static/description/index.html
Normal file
454
account_usability/static/description/index.html
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||||
|
<title>Account Usability</title>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
/*
|
||||||
|
:Author: David Goodger (goodger@python.org)
|
||||||
|
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||||
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
|
Default cascading style sheet for the HTML output of Docutils.
|
||||||
|
|
||||||
|
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||||
|
customize this style sheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* used to remove borders from tables and images */
|
||||||
|
.borderless, table.borderless td, table.borderless th {
|
||||||
|
border: 0 }
|
||||||
|
|
||||||
|
table.borderless td, table.borderless th {
|
||||||
|
/* Override padding for "table.docutils td" with "! important".
|
||||||
|
The right padding separates the table cells. */
|
||||||
|
padding: 0 0.5em 0 0 ! important }
|
||||||
|
|
||||||
|
.first {
|
||||||
|
/* Override more specific margin styles with "! important". */
|
||||||
|
margin-top: 0 ! important }
|
||||||
|
|
||||||
|
.last, .with-subtitle {
|
||||||
|
margin-bottom: 0 ! important }
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none }
|
||||||
|
|
||||||
|
.subscript {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
.superscript {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
a.toc-backref {
|
||||||
|
text-decoration: none ;
|
||||||
|
color: black }
|
||||||
|
|
||||||
|
blockquote.epigraph {
|
||||||
|
margin: 2em 5em ; }
|
||||||
|
|
||||||
|
dl.docutils dd {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||||
|
dl.docutils dt {
|
||||||
|
font-weight: bold }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.abstract {
|
||||||
|
margin: 2em 5em }
|
||||||
|
|
||||||
|
div.abstract p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||||
|
div.hint, div.important, div.note, div.tip, div.warning {
|
||||||
|
margin: 2em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||||
|
div.important p.admonition-title, div.note p.admonition-title,
|
||||||
|
div.tip p.admonition-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||||
|
div.danger p.admonition-title, div.error p.admonition-title,
|
||||||
|
div.warning p.admonition-title, .code .error {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||||
|
compound paragraphs.
|
||||||
|
div.compound .compound-first, div.compound .compound-middle {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
div.compound .compound-last, div.compound .compound-middle {
|
||||||
|
margin-top: 0.5em }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.dedication {
|
||||||
|
margin: 2em 5em ;
|
||||||
|
text-align: center ;
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
div.dedication p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-style: normal }
|
||||||
|
|
||||||
|
div.figure {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
div.footer, div.header {
|
||||||
|
clear: both;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
div.line-block {
|
||||||
|
display: block ;
|
||||||
|
margin-top: 1em ;
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
div.line-block div.line-block {
|
||||||
|
margin-top: 0 ;
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-left: 1.5em }
|
||||||
|
|
||||||
|
div.sidebar {
|
||||||
|
margin: 0 0 0.5em 1em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em ;
|
||||||
|
background-color: #ffffee ;
|
||||||
|
width: 40% ;
|
||||||
|
float: right ;
|
||||||
|
clear: right }
|
||||||
|
|
||||||
|
div.sidebar p.rubric {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-size: medium }
|
||||||
|
|
||||||
|
div.system-messages {
|
||||||
|
margin: 5em }
|
||||||
|
|
||||||
|
div.system-messages h1 {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
div.system-message {
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.system-message p.system-message-title {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
margin: 2em }
|
||||||
|
|
||||||
|
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||||
|
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||||
|
margin-top: 0.4em }
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
h2.subtitle {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
hr.docutils {
|
||||||
|
width: 75% }
|
||||||
|
|
||||||
|
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||||
|
clear: left ;
|
||||||
|
float: left ;
|
||||||
|
margin-right: 1em }
|
||||||
|
|
||||||
|
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||||
|
clear: right ;
|
||||||
|
float: right ;
|
||||||
|
margin-left: 1em }
|
||||||
|
|
||||||
|
img.align-center, .figure.align-center, object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.align-center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left }
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
clear: both ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right }
|
||||||
|
|
||||||
|
/* reset inner alignment in figures */
|
||||||
|
div.align-right {
|
||||||
|
text-align: inherit }
|
||||||
|
|
||||||
|
/* div.align-center * { */
|
||||||
|
/* text-align: left } */
|
||||||
|
|
||||||
|
.align-top {
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
.align-middle {
|
||||||
|
vertical-align: middle }
|
||||||
|
|
||||||
|
.align-bottom {
|
||||||
|
vertical-align: bottom }
|
||||||
|
|
||||||
|
ol.simple, ul.simple {
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
ol.arabic {
|
||||||
|
list-style: decimal }
|
||||||
|
|
||||||
|
ol.loweralpha {
|
||||||
|
list-style: lower-alpha }
|
||||||
|
|
||||||
|
ol.upperalpha {
|
||||||
|
list-style: upper-alpha }
|
||||||
|
|
||||||
|
ol.lowerroman {
|
||||||
|
list-style: lower-roman }
|
||||||
|
|
||||||
|
ol.upperroman {
|
||||||
|
list-style: upper-roman }
|
||||||
|
|
||||||
|
p.attribution {
|
||||||
|
text-align: right ;
|
||||||
|
margin-left: 50% }
|
||||||
|
|
||||||
|
p.caption {
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
p.credits {
|
||||||
|
font-style: italic ;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
p.label {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger ;
|
||||||
|
color: maroon ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
p.sidebar-title {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger }
|
||||||
|
|
||||||
|
p.sidebar-subtitle {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
p.topic-title {
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
pre.address {
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-top: 0 ;
|
||||||
|
font: inherit }
|
||||||
|
|
||||||
|
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
pre.code .ln { color: grey; } /* line numbers */
|
||||||
|
pre.code, code { background-color: #eeeeee }
|
||||||
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
|
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||||
|
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||||
|
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||||
|
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||||
|
|
||||||
|
span.classifier {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-style: oblique }
|
||||||
|
|
||||||
|
span.classifier-delimiter {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
span.interpreted {
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
span.option {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
span.pre {
|
||||||
|
white-space: pre }
|
||||||
|
|
||||||
|
span.problematic {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
span.section-subtitle {
|
||||||
|
/* font-size relative to parent (h1..h6 element) */
|
||||||
|
font-size: 80% }
|
||||||
|
|
||||||
|
table.citation {
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docinfo {
|
||||||
|
margin: 2em 4em }
|
||||||
|
|
||||||
|
table.docutils {
|
||||||
|
margin-top: 0.5em ;
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
table.footnote {
|
||||||
|
border-left: solid 1px black;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docutils td, table.docutils th,
|
||||||
|
table.docinfo td, table.docinfo th {
|
||||||
|
padding-left: 0.5em ;
|
||||||
|
padding-right: 0.5em ;
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: left ;
|
||||||
|
white-space: nowrap ;
|
||||||
|
padding-left: 0 }
|
||||||
|
|
||||||
|
/* "booktabs" style (no vertical lines) */
|
||||||
|
table.docutils.booktabs {
|
||||||
|
border: 0px;
|
||||||
|
border-top: 2px solid;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs * {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs th {
|
||||||
|
border-bottom: thin solid;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||||
|
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||||
|
font-size: 100% }
|
||||||
|
|
||||||
|
ul.auto-toc {
|
||||||
|
list-style-type: none }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="document" id="account-usability">
|
||||||
|
<h1 class="title">Account Usability</h1>
|
||||||
|
|
||||||
|
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/account_usability"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p>
|
||||||
|
<p>This modules adds the following functions:</p>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Add an <em>Overdue</em> filter on invoice search view (this feature was previously
|
||||||
|
located in te module <em>account_invoice_overdue_filter</em>)</li>
|
||||||
|
<li>Increase the default limit of 80 lines in account move and account move line view.</li>
|
||||||
|
<li>disable reconciliation “guessing”</li>
|
||||||
|
<li>fast search on <em>Reconcile Ref</em> for account move line.</li>
|
||||||
|
<li>add sale dates to invoice report to be compliant with
|
||||||
|
<a class="reference external" href="https://www.service-public.fr/professionnels-entreprises/vosdroits/F31808">https://www.service-public.fr/professionnels-entreprises/vosdroits/F31808</a></li>
|
||||||
|
<li>Sale date on qweb invoices</li>
|
||||||
|
<li>A wizard to mark several invoices as sent at once (forward from v8)</li>
|
||||||
|
<li>Default date for Account Move Reversal is now D+1 instead of today</li>
|
||||||
|
<li>Track more fields on invoice (see details in account.py)</li>
|
||||||
|
<li>Add boolean fields <cite>has_discount</cite> and <cite>has_attachment</cite> on invoice</li>
|
||||||
|
<li>Add button “Delete line qty = 0” on supplier invoice</li>
|
||||||
|
<li>Cut name_get() of invoice if too long</li>
|
||||||
|
<li>A script for if Odoo screws up invoice attachment filename</li>
|
||||||
|
<li>help functions for py3o reports</li>
|
||||||
|
<li>Show code on name_get of journal</li>
|
||||||
|
<li>add direct search of journal using code</li>
|
||||||
|
<li>add copy=False on some fields</li>
|
||||||
|
<li>Add unicity constraint on analytic codes per company</li>
|
||||||
|
<li>Better default values on account move</li>
|
||||||
|
<li>Add link from account move line to invoice</li>
|
||||||
|
<li>Add start_date and end_date on bank statements</li>
|
||||||
|
<li>Add transfer_account_id to invoicing config page</li>
|
||||||
|
<li>Improve domain reconciliation widget</li>
|
||||||
|
<li>account.reconcile.model don’t copy name to label via onchange</li>
|
||||||
|
<li>Add method to get fiscal position without partner_id</li>
|
||||||
|
<li>Restore drill-through on sale and invoice reports</li>
|
||||||
|
<li>don’t attach PDF upon invoice report generation on supplier invoices/refunds</li>
|
||||||
|
<li>Add filter on debit and credit amount for Move Lines</li>
|
||||||
|
<li>Add supplier invoice number in invoice tree view</li>
|
||||||
|
</ul>
|
||||||
|
<p>Together with this module, I recommend the use of the following modules:</p>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>account_invoice_supplier_ref_unique (OCA project account-invoicing)</li>
|
||||||
|
<li>account_move_line_no_default_search (OCA project account-financial-tools)</li>
|
||||||
|
<li>invoice_fiscal_position_update (OCA project account-invoicing)</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Table of contents</strong></p>
|
||||||
|
<div class="contents local topic" id="contents">
|
||||||
|
<ul class="simple">
|
||||||
|
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li>
|
||||||
|
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul>
|
||||||
|
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li>
|
||||||
|
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li>
|
||||||
|
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="bug-tracker">
|
||||||
|
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1>
|
||||||
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/akretion/odoo-usability/issues">GitHub Issues</a>.
|
||||||
|
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
|
||||||
|
<a class="reference external" href="https://github.com/akretion/odoo-usability/issues/new?body=module:%20account_usability%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||||
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="credits">
|
||||||
|
<h1><a class="toc-backref" href="#id2">Credits</a></h1>
|
||||||
|
<div class="section" id="authors">
|
||||||
|
<h2><a class="toc-backref" href="#id3">Authors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Akretion</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="contributors">
|
||||||
|
<h2><a class="toc-backref" href="#id4">Contributors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Alexis de Lattre <<a class="reference external" href="mailto:alexis.delattre@akretion.com">alexis.delattre@akretion.com</a>></li>
|
||||||
|
<li>David Beal <<a class="reference external" href="mailto:david.beal@akretion.com">david.beal@akretion.com</a>></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="maintainers">
|
||||||
|
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2>
|
||||||
|
<p>This module is part of the <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/account_usability">akretion/odoo-usability</a> project on GitHub.</p>
|
||||||
|
<p>You are welcome to contribute.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
38
account_usability/views/account_account.xml
Normal file
38
account_usability/views/account_account.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?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>
|
||||||
|
</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>
|
||||||
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>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2018 Akretion (http://www.akretion.com/)
|
Copyright 2018-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).
|
||||||
-->
|
-->
|
||||||
@@ -13,27 +13,27 @@
|
|||||||
<field name="model">account.invoice.report</field>
|
<field name="model">account.invoice.report</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="Invoices Analysis">
|
<tree string="Invoices Analysis">
|
||||||
<field name="number"/>
|
<field name="move_id"/>
|
||||||
<field name="date"/>
|
<field name="invoice_date"/>
|
||||||
<field name="date_due"/>
|
<field name="invoice_date_due"/>
|
||||||
<field name="type"/>
|
<field name="move_type"/>
|
||||||
<field name="commercial_partner_id"/>
|
<field name="commercial_partner_id"/>
|
||||||
<field name="user_id"/>
|
<field name="invoice_user_id"/>
|
||||||
<field name="product_id"/>
|
<field name="product_id"/>
|
||||||
<field name="product_qty" sum="1"/>
|
<field name="quantity" sum="1"/>
|
||||||
<field name="uom_name" groups="uom.group_uom"/>
|
<field name="product_uom_id" groups="uom.group_uom"/>
|
||||||
<field name="price_total" sum="1"/>
|
<field name="price_subtotal" sum="1"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
<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 -->
|
<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 -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
|
<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 -->
|
<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 -->
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
||||||
59
account_usability/views/account_journal.xml
Normal file
59
account_usability/views/account_journal.xml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?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>
|
||||||
|
</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>
|
||||||
111
account_usability/views/account_move.xml
Normal file
111
account_usability/views/account_move.xml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?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>
|
||||||
|
</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="[('full_reconcile_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="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'}" 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>
|
||||||
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)
|
||||||
|
|||||||
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)
|
||||||
57
base_company_extension/README.rst
Normal file
57
base_company_extension/README.rst
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
======================
|
||||||
|
Base Company Extension
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||||
|
:target: https://odoo-community.org/page/development-status
|
||||||
|
:alt: Beta
|
||||||
|
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
.. |badge3| image:: https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github
|
||||||
|
:target: https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension
|
||||||
|
:alt: akretion/odoo-usability
|
||||||
|
|
||||||
|
|badge1| |badge2| |badge3|
|
||||||
|
|
||||||
|
This module adds the following fields on the company:
|
||||||
|
|
||||||
|
* Capital Amount
|
||||||
|
* Legal Type
|
||||||
|
|
||||||
|
This is useful to display the legal name of the company in reports.
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
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.
|
||||||
|
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:%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.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Akretion
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
|
||||||
|
Maintainers
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
This module is part of the `akretion/odoo-usability <https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute.
|
||||||
@@ -1,24 +1,13 @@
|
|||||||
# -*- 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',
|
||||||
'description': """
|
|
||||||
Base Company Extension
|
|
||||||
======================
|
|
||||||
|
|
||||||
This module adds 2 fields on the Company :
|
|
||||||
|
|
||||||
* *Capital Amount*
|
|
||||||
|
|
||||||
* *Legal Form*
|
|
||||||
""",
|
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
# I depend on base_usability only for _report_company_legal_name()
|
# I depend on base_usability only for _report_company_legal_name()
|
||||||
|
|||||||
@@ -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_company_extension/readme/CONTRIBUTORS.rst
Normal file
1
base_company_extension/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
5
base_company_extension/readme/DESCRIPTION.rst
Normal file
5
base_company_extension/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
This module adds the following fields to the ResCompany model:
|
||||||
|
* Capital Amount
|
||||||
|
* Legal Type
|
||||||
|
|
||||||
|
This is useful to display the legal name of the company in reports
|
||||||
417
base_company_extension/static/description/index.html
Normal file
417
base_company_extension/static/description/index.html
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||||
|
<title>Base Company Extension</title>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
/*
|
||||||
|
:Author: David Goodger (goodger@python.org)
|
||||||
|
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||||
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
|
Default cascading style sheet for the HTML output of Docutils.
|
||||||
|
|
||||||
|
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||||
|
customize this style sheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* used to remove borders from tables and images */
|
||||||
|
.borderless, table.borderless td, table.borderless th {
|
||||||
|
border: 0 }
|
||||||
|
|
||||||
|
table.borderless td, table.borderless th {
|
||||||
|
/* Override padding for "table.docutils td" with "! important".
|
||||||
|
The right padding separates the table cells. */
|
||||||
|
padding: 0 0.5em 0 0 ! important }
|
||||||
|
|
||||||
|
.first {
|
||||||
|
/* Override more specific margin styles with "! important". */
|
||||||
|
margin-top: 0 ! important }
|
||||||
|
|
||||||
|
.last, .with-subtitle {
|
||||||
|
margin-bottom: 0 ! important }
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none }
|
||||||
|
|
||||||
|
.subscript {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
.superscript {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
a.toc-backref {
|
||||||
|
text-decoration: none ;
|
||||||
|
color: black }
|
||||||
|
|
||||||
|
blockquote.epigraph {
|
||||||
|
margin: 2em 5em ; }
|
||||||
|
|
||||||
|
dl.docutils dd {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||||
|
dl.docutils dt {
|
||||||
|
font-weight: bold }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.abstract {
|
||||||
|
margin: 2em 5em }
|
||||||
|
|
||||||
|
div.abstract p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||||
|
div.hint, div.important, div.note, div.tip, div.warning {
|
||||||
|
margin: 2em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||||
|
div.important p.admonition-title, div.note p.admonition-title,
|
||||||
|
div.tip p.admonition-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||||
|
div.danger p.admonition-title, div.error p.admonition-title,
|
||||||
|
div.warning p.admonition-title, .code .error {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||||
|
compound paragraphs.
|
||||||
|
div.compound .compound-first, div.compound .compound-middle {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
div.compound .compound-last, div.compound .compound-middle {
|
||||||
|
margin-top: 0.5em }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.dedication {
|
||||||
|
margin: 2em 5em ;
|
||||||
|
text-align: center ;
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
div.dedication p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-style: normal }
|
||||||
|
|
||||||
|
div.figure {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
div.footer, div.header {
|
||||||
|
clear: both;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
div.line-block {
|
||||||
|
display: block ;
|
||||||
|
margin-top: 1em ;
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
div.line-block div.line-block {
|
||||||
|
margin-top: 0 ;
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-left: 1.5em }
|
||||||
|
|
||||||
|
div.sidebar {
|
||||||
|
margin: 0 0 0.5em 1em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em ;
|
||||||
|
background-color: #ffffee ;
|
||||||
|
width: 40% ;
|
||||||
|
float: right ;
|
||||||
|
clear: right }
|
||||||
|
|
||||||
|
div.sidebar p.rubric {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-size: medium }
|
||||||
|
|
||||||
|
div.system-messages {
|
||||||
|
margin: 5em }
|
||||||
|
|
||||||
|
div.system-messages h1 {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
div.system-message {
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.system-message p.system-message-title {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
margin: 2em }
|
||||||
|
|
||||||
|
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||||
|
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||||
|
margin-top: 0.4em }
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
h2.subtitle {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
hr.docutils {
|
||||||
|
width: 75% }
|
||||||
|
|
||||||
|
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||||
|
clear: left ;
|
||||||
|
float: left ;
|
||||||
|
margin-right: 1em }
|
||||||
|
|
||||||
|
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||||
|
clear: right ;
|
||||||
|
float: right ;
|
||||||
|
margin-left: 1em }
|
||||||
|
|
||||||
|
img.align-center, .figure.align-center, object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.align-center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left }
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
clear: both ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right }
|
||||||
|
|
||||||
|
/* reset inner alignment in figures */
|
||||||
|
div.align-right {
|
||||||
|
text-align: inherit }
|
||||||
|
|
||||||
|
/* div.align-center * { */
|
||||||
|
/* text-align: left } */
|
||||||
|
|
||||||
|
.align-top {
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
.align-middle {
|
||||||
|
vertical-align: middle }
|
||||||
|
|
||||||
|
.align-bottom {
|
||||||
|
vertical-align: bottom }
|
||||||
|
|
||||||
|
ol.simple, ul.simple {
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
ol.arabic {
|
||||||
|
list-style: decimal }
|
||||||
|
|
||||||
|
ol.loweralpha {
|
||||||
|
list-style: lower-alpha }
|
||||||
|
|
||||||
|
ol.upperalpha {
|
||||||
|
list-style: upper-alpha }
|
||||||
|
|
||||||
|
ol.lowerroman {
|
||||||
|
list-style: lower-roman }
|
||||||
|
|
||||||
|
ol.upperroman {
|
||||||
|
list-style: upper-roman }
|
||||||
|
|
||||||
|
p.attribution {
|
||||||
|
text-align: right ;
|
||||||
|
margin-left: 50% }
|
||||||
|
|
||||||
|
p.caption {
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
p.credits {
|
||||||
|
font-style: italic ;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
p.label {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger ;
|
||||||
|
color: maroon ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
p.sidebar-title {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger }
|
||||||
|
|
||||||
|
p.sidebar-subtitle {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
p.topic-title {
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
pre.address {
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-top: 0 ;
|
||||||
|
font: inherit }
|
||||||
|
|
||||||
|
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
pre.code .ln { color: grey; } /* line numbers */
|
||||||
|
pre.code, code { background-color: #eeeeee }
|
||||||
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
|
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||||
|
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||||
|
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||||
|
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||||
|
|
||||||
|
span.classifier {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-style: oblique }
|
||||||
|
|
||||||
|
span.classifier-delimiter {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
span.interpreted {
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
span.option {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
span.pre {
|
||||||
|
white-space: pre }
|
||||||
|
|
||||||
|
span.problematic {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
span.section-subtitle {
|
||||||
|
/* font-size relative to parent (h1..h6 element) */
|
||||||
|
font-size: 80% }
|
||||||
|
|
||||||
|
table.citation {
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docinfo {
|
||||||
|
margin: 2em 4em }
|
||||||
|
|
||||||
|
table.docutils {
|
||||||
|
margin-top: 0.5em ;
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
table.footnote {
|
||||||
|
border-left: solid 1px black;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docutils td, table.docutils th,
|
||||||
|
table.docinfo td, table.docinfo th {
|
||||||
|
padding-left: 0.5em ;
|
||||||
|
padding-right: 0.5em ;
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: left ;
|
||||||
|
white-space: nowrap ;
|
||||||
|
padding-left: 0 }
|
||||||
|
|
||||||
|
/* "booktabs" style (no vertical lines) */
|
||||||
|
table.docutils.booktabs {
|
||||||
|
border: 0px;
|
||||||
|
border-top: 2px solid;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs * {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs th {
|
||||||
|
border-bottom: thin solid;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||||
|
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||||
|
font-size: 100% }
|
||||||
|
|
||||||
|
ul.auto-toc {
|
||||||
|
list-style-type: none }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="document" id="base-company-extension">
|
||||||
|
<h1 class="title">Base Company Extension</h1>
|
||||||
|
|
||||||
|
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p>
|
||||||
|
<p>This module adds the following fields to the ResCompany model:
|
||||||
|
* Capital Amount
|
||||||
|
* Legal Type</p>
|
||||||
|
<p>This is useful to display the legal name of the company in reports</p>
|
||||||
|
<p><strong>Table of contents</strong></p>
|
||||||
|
<div class="contents local topic" id="contents">
|
||||||
|
<ul class="simple">
|
||||||
|
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li>
|
||||||
|
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul>
|
||||||
|
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li>
|
||||||
|
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li>
|
||||||
|
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="bug-tracker">
|
||||||
|
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1>
|
||||||
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/akretion/odoo-usability/issues">GitHub Issues</a>.
|
||||||
|
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
|
||||||
|
<a class="reference external" href="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</a>.</p>
|
||||||
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="credits">
|
||||||
|
<h1><a class="toc-backref" href="#id2">Credits</a></h1>
|
||||||
|
<div class="section" id="authors">
|
||||||
|
<h2><a class="toc-backref" href="#id3">Authors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Akretion</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="contributors">
|
||||||
|
<h2><a class="toc-backref" href="#id4">Contributors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Alexis de Lattre <<a class="reference external" href="mailto:alexis.delattre@akretion.com">alexis.delattre@akretion.com</a>></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="maintainers">
|
||||||
|
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2>
|
||||||
|
<p>This module is part of the <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension">akretion/odoo-usability</a> project on GitHub.</p>
|
||||||
|
<p>You are welcome to contribute.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
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>
|
||||||
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',
|
||||||
|
}
|
||||||
174
base_partner_one2many_phone/i18n/base_partner_one2many_phone.pot
Normal file
174
base_partner_one2many_phone/i18n/base_partner_one2many_phone.pot
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_partner_one2many_phone
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 10.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-01-27 18:03+0000\n"
|
||||||
|
"PO-Revision-Date: 2020-01-27 18:03+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.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_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:61
|
||||||
|
#, 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:51
|
||||||
|
#, 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_phone_id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: 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_phone_note
|
||||||
|
msgid "Note"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner
|
||||||
|
msgid "Partner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone_phone
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:54
|
||||||
|
#, 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:58
|
||||||
|
#, 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: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"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model: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
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary Fax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary Mobile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
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:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Search Phones/E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary Fax"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary Mobile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
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:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner_phone
|
||||||
|
msgid "res.partner.phone"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
174
base_partner_one2many_phone/i18n/fr.po
Normal file
174
base_partner_one2many_phone/i18n/fr.po
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * base_partner_one2many_phone
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 10.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-01-27 17:56+0000\n"
|
||||||
|
"PO-Revision-Date: 2020-01-27 17:56+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.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_phone_display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nom à afficher"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone_email
|
||||||
|
msgid "E-Mail"
|
||||||
|
msgstr "Courriel"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:61
|
||||||
|
#, 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 courriel doit être vide quand le type est tél. primaire/secondaire, portable primaire/secondaire ou fax primaire/secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:51
|
||||||
|
#, python-format
|
||||||
|
msgid "E-mail field must have a value when type is Primary E-mail or Secondary E-mail."
|
||||||
|
msgstr "Le champ courriel doit être renseigné quand le type est courriel primaire ou courriel secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone_id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr "ID"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: 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 mise à jour 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 mise à jour le"
|
||||||
|
|
||||||
|
#. 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,name:base_partner_one2many_phone.model_res_partner
|
||||||
|
msgid "Partner"
|
||||||
|
msgstr "Partenaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model.fields,field_description:base_partner_one2many_phone.field_res_partner_phone_phone
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr "Téléphone"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:54
|
||||||
|
#, python-format
|
||||||
|
msgid "Phone field must be empty when type is Primary E-mail or Secondary E-mail."
|
||||||
|
msgstr "Le champ téléphone doit être vide quand le type est courriel primaire ou courriel secondaire."
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: code:addons/base_partner_one2many_phone/partner_phone.py:58
|
||||||
|
#, 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éphone doit être renseigné quand le type est tél. primaire/secondaire, portable primaire/secondaire ou fax primaire/secondaire.."
|
||||||
|
|
||||||
|
#. 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"
|
||||||
|
msgstr "Téléphones"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_tree
|
||||||
|
msgid "Phones and E-mail"
|
||||||
|
msgstr "Téls et courriels"
|
||||||
|
|
||||||
|
#. 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/Courriels"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary E-mail"
|
||||||
|
msgstr "Courriel principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary Fax"
|
||||||
|
msgstr "Fax principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Primary Mobile"
|
||||||
|
msgstr "Portable principal"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
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:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Search Phones/E-mail"
|
||||||
|
msgstr "Search Phones/E-mail"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary E-mail"
|
||||||
|
msgstr "Courriel secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary Fax"
|
||||||
|
msgstr "Fax secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
msgid "Secondary Mobile"
|
||||||
|
msgstr "Portable secondaire"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: selection:res.partner.phone,type:0
|
||||||
|
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:ir.ui.view,arch_db:base_partner_one2many_phone.res_partner_phone_search
|
||||||
|
msgid "Type"
|
||||||
|
msgstr "Type"
|
||||||
|
|
||||||
|
#. module: base_partner_one2many_phone
|
||||||
|
#: model:ir.model,name:base_partner_one2many_phone.model_res_partner_phone
|
||||||
|
msgid "res.partner.phone"
|
||||||
|
msgstr "res.partner.phone"
|
||||||
|
|
||||||
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 string="Phones and E-mail" 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 string="Phone and E-mail">
|
||||||
|
<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 string="Search Phones/E-mail">
|
||||||
|
<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)
|
||||||
@@ -22,5 +22,5 @@ Base Partner Reference
|
|||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'depends': ['base'],
|
'depends': ['base'],
|
||||||
'data': ['partner_view.xml'],
|
'data': ['partner_view.xml'],
|
||||||
'installable': True,
|
'installable': False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
# @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).
|
||||||
|
|
||||||
from odoo import models, fields
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class ResPartner(models.Model):
|
class ResPartner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
ref = fields.Char(copy=False) # To avoid blocking duplicate
|
ref = fields.Char(copy=False) # To avoid blocking duplicate
|
||||||
|
invalidate_display_name = fields.Boolean()
|
||||||
|
|
||||||
_sql_constraints = [(
|
_sql_constraints = [(
|
||||||
'ref_unique',
|
'ref_unique',
|
||||||
@@ -16,13 +17,18 @@ class ResPartner(models.Model):
|
|||||||
'A partner already exists with this internal reference!'
|
'A partner already exists with this internal reference!'
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
# add 'ref' in depends
|
||||||
|
@api.depends('is_company', 'name', 'parent_id.name', 'type', 'company_name', 'ref', 'invalidate_display_name')
|
||||||
|
def _compute_display_name(self):
|
||||||
|
super(ResPartner, self)._compute_display_name()
|
||||||
|
|
||||||
def _get_name(self):
|
def _get_name(self):
|
||||||
partner = self
|
partner = self
|
||||||
name = partner.name or ''
|
name = partner.name or ''
|
||||||
|
|
||||||
# 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']:
|
||||||
@@ -49,3 +55,13 @@ 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:
|
||||||
|
return recs.name_get()
|
||||||
|
return super().name_search(name=name, args=args, operator=operator, limit=limit)
|
||||||
|
|||||||
82
base_usability/README.rst
Normal file
82
base_usability/README.rst
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
==============
|
||||||
|
Base Usability
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||||
|
:target: https://odoo-community.org/page/development-status
|
||||||
|
:alt: Beta
|
||||||
|
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
.. |badge3| image:: https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github
|
||||||
|
:target: https://github.com/akretion/odoo-usability/tree/12.0/base_usability
|
||||||
|
:alt: akretion/odoo-usability
|
||||||
|
|
||||||
|
|badge1| |badge2| |badge3|
|
||||||
|
|
||||||
|
This module adds the following functions:
|
||||||
|
|
||||||
|
* Adds *track_visibility='onchange'* on all the important fields of the Partner object
|
||||||
|
* By default, Odoo doesn't display the title field on all the partner form views. This module fixes it (it replaces the module base_title_on_partner)
|
||||||
|
* Adds a log message at INFO level when sending an email via SMTP
|
||||||
|
* Displays the local modules with installable filter
|
||||||
|
* A group by "State" is added to module search view
|
||||||
|
* Provides a _display_report_header method on the res.company object and _display_full_address on res.partner which are useful for reporting.
|
||||||
|
* Add model in cron tree view
|
||||||
|
* Add prefix field in sequence search view
|
||||||
|
* Better search and form view for country and state
|
||||||
|
* Display technical name of modules in kanban view
|
||||||
|
* Change module filter to `installable`
|
||||||
|
* Add widget=handle on sequence of res.partner.bank
|
||||||
|
* Add city and country in partner tree view
|
||||||
|
* Add widget="email" on email of contacts
|
||||||
|
* Add script to fix partners related to users in multi-company setup
|
||||||
|
* Add methods for py3o reports
|
||||||
|
* Add name_get() on ir.model
|
||||||
|
* Language wizard defaults to ".po"
|
||||||
|
* Add tracking on partner fields
|
||||||
|
* Handle lang in name_title field
|
||||||
|
* Remove empty lines in address
|
||||||
|
* Add bank Name field on res.partner.bank
|
||||||
|
* Partners auto-created for users are Suppliers and not Customers
|
||||||
|
|
||||||
|
**Table of contents**
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
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.
|
||||||
|
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_usability%0Aversion:%2012.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.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Akretion
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
* David Beal <david.beal@akretion.com>
|
||||||
|
|
||||||
|
Maintainers
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
This module is part of the `akretion/odoo-usability <https://github.com/akretion/odoo-usability/tree/12.0/base_usability>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute.
|
||||||
@@ -1,40 +1,24 @@
|
|||||||
# © 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',
|
||||||
'description': """
|
|
||||||
Base Usability
|
|
||||||
==============
|
|
||||||
|
|
||||||
This module adds *track_visibility='onchange'* on all the important fields of the Partner object.
|
|
||||||
|
|
||||||
By default, Odoo doesn't display the title field on all the partner form views. This module fixes it (it replaces the module base_title_on_partner).
|
|
||||||
|
|
||||||
It also adds a log message at INFO level when sending an email via SMTP.
|
|
||||||
|
|
||||||
It displays the local modules with installable filter.
|
|
||||||
A group by 'State' is added to module search view.
|
|
||||||
|
|
||||||
It provides a _display_report_header method on the res.company object and
|
|
||||||
_display_full_address on res.partner which are useful for reporting.
|
|
||||||
""",
|
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'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
|
|
||||||
3
base_usability/readme/CONTRIBUTORS.rst
Normal file
3
base_usability/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
* Raphaël Valyi <rvalyi@akretion.com>
|
||||||
|
* David Beal <david.beal@akretion.com>
|
||||||
25
base_usability/readme/DESCRIPTION.rst
Normal file
25
base_usability/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
This module adds the following functions:
|
||||||
|
|
||||||
|
* Adds *track_visibility='onchange'* on all the important fields of the Partner object
|
||||||
|
* By default, Odoo doesn't display the title field on all the partner form views. This module fixes it (it replaces the module base_title_on_partner)
|
||||||
|
* Adds a log message at INFO level when sending an email via SMTP
|
||||||
|
* Displays the local modules with installable filter
|
||||||
|
* A group by "State" is added to module search view
|
||||||
|
* Provides a _display_report_header method on the res.company object and _display_full_address on res.partner which are useful for reporting.
|
||||||
|
* Add model in cron tree view
|
||||||
|
* Add prefix field in sequence search view
|
||||||
|
* Better search and form view for country and state
|
||||||
|
* Display technical name of modules in kanban view
|
||||||
|
* Change module filter to `installable`
|
||||||
|
* Add widget=handle on sequence of res.partner.bank
|
||||||
|
* Add city and country in partner tree view
|
||||||
|
* Add widget="email" on email of contacts
|
||||||
|
* Add script to fix partners related to users in multi-company setup
|
||||||
|
* Add methods for py3o reports
|
||||||
|
* Add name_get() on ir.model
|
||||||
|
* Language wizard defaults to ".po"
|
||||||
|
* Add tracking on partner fields
|
||||||
|
* Handle lang in name_title field
|
||||||
|
* Remove empty lines in address
|
||||||
|
* Add bank Name field on res.partner.bank
|
||||||
|
* Partners auto-created for users are Suppliers and not Customers
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
Copyright 2016-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).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|||||||
2
base_usability/security/ir.model.access.csv
Normal file
2
base_usability/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
base.access_res_partner_title_group_user,Full access on res.partner.title to Settings group (instead of partner manager by default),base.model_res_partner_title,base.group_system,1,1,1,1
|
||||||
|
440
base_usability/static/description/index.html
Normal file
440
base_usability/static/description/index.html
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||||
|
<title>Base Usability</title>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
/*
|
||||||
|
:Author: David Goodger (goodger@python.org)
|
||||||
|
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||||
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
|
Default cascading style sheet for the HTML output of Docutils.
|
||||||
|
|
||||||
|
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||||
|
customize this style sheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* used to remove borders from tables and images */
|
||||||
|
.borderless, table.borderless td, table.borderless th {
|
||||||
|
border: 0 }
|
||||||
|
|
||||||
|
table.borderless td, table.borderless th {
|
||||||
|
/* Override padding for "table.docutils td" with "! important".
|
||||||
|
The right padding separates the table cells. */
|
||||||
|
padding: 0 0.5em 0 0 ! important }
|
||||||
|
|
||||||
|
.first {
|
||||||
|
/* Override more specific margin styles with "! important". */
|
||||||
|
margin-top: 0 ! important }
|
||||||
|
|
||||||
|
.last, .with-subtitle {
|
||||||
|
margin-bottom: 0 ! important }
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none }
|
||||||
|
|
||||||
|
.subscript {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
.superscript {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
a.toc-backref {
|
||||||
|
text-decoration: none ;
|
||||||
|
color: black }
|
||||||
|
|
||||||
|
blockquote.epigraph {
|
||||||
|
margin: 2em 5em ; }
|
||||||
|
|
||||||
|
dl.docutils dd {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||||
|
dl.docutils dt {
|
||||||
|
font-weight: bold }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.abstract {
|
||||||
|
margin: 2em 5em }
|
||||||
|
|
||||||
|
div.abstract p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||||
|
div.hint, div.important, div.note, div.tip, div.warning {
|
||||||
|
margin: 2em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||||
|
div.important p.admonition-title, div.note p.admonition-title,
|
||||||
|
div.tip p.admonition-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||||
|
div.danger p.admonition-title, div.error p.admonition-title,
|
||||||
|
div.warning p.admonition-title, .code .error {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||||
|
compound paragraphs.
|
||||||
|
div.compound .compound-first, div.compound .compound-middle {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
div.compound .compound-last, div.compound .compound-middle {
|
||||||
|
margin-top: 0.5em }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.dedication {
|
||||||
|
margin: 2em 5em ;
|
||||||
|
text-align: center ;
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
div.dedication p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-style: normal }
|
||||||
|
|
||||||
|
div.figure {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
div.footer, div.header {
|
||||||
|
clear: both;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
div.line-block {
|
||||||
|
display: block ;
|
||||||
|
margin-top: 1em ;
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
div.line-block div.line-block {
|
||||||
|
margin-top: 0 ;
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-left: 1.5em }
|
||||||
|
|
||||||
|
div.sidebar {
|
||||||
|
margin: 0 0 0.5em 1em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em ;
|
||||||
|
background-color: #ffffee ;
|
||||||
|
width: 40% ;
|
||||||
|
float: right ;
|
||||||
|
clear: right }
|
||||||
|
|
||||||
|
div.sidebar p.rubric {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-size: medium }
|
||||||
|
|
||||||
|
div.system-messages {
|
||||||
|
margin: 5em }
|
||||||
|
|
||||||
|
div.system-messages h1 {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
div.system-message {
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.system-message p.system-message-title {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
margin: 2em }
|
||||||
|
|
||||||
|
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||||
|
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||||
|
margin-top: 0.4em }
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
h2.subtitle {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
hr.docutils {
|
||||||
|
width: 75% }
|
||||||
|
|
||||||
|
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||||
|
clear: left ;
|
||||||
|
float: left ;
|
||||||
|
margin-right: 1em }
|
||||||
|
|
||||||
|
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||||
|
clear: right ;
|
||||||
|
float: right ;
|
||||||
|
margin-left: 1em }
|
||||||
|
|
||||||
|
img.align-center, .figure.align-center, object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.align-center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left }
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
clear: both ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right }
|
||||||
|
|
||||||
|
/* reset inner alignment in figures */
|
||||||
|
div.align-right {
|
||||||
|
text-align: inherit }
|
||||||
|
|
||||||
|
/* div.align-center * { */
|
||||||
|
/* text-align: left } */
|
||||||
|
|
||||||
|
.align-top {
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
.align-middle {
|
||||||
|
vertical-align: middle }
|
||||||
|
|
||||||
|
.align-bottom {
|
||||||
|
vertical-align: bottom }
|
||||||
|
|
||||||
|
ol.simple, ul.simple {
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
ol.arabic {
|
||||||
|
list-style: decimal }
|
||||||
|
|
||||||
|
ol.loweralpha {
|
||||||
|
list-style: lower-alpha }
|
||||||
|
|
||||||
|
ol.upperalpha {
|
||||||
|
list-style: upper-alpha }
|
||||||
|
|
||||||
|
ol.lowerroman {
|
||||||
|
list-style: lower-roman }
|
||||||
|
|
||||||
|
ol.upperroman {
|
||||||
|
list-style: upper-roman }
|
||||||
|
|
||||||
|
p.attribution {
|
||||||
|
text-align: right ;
|
||||||
|
margin-left: 50% }
|
||||||
|
|
||||||
|
p.caption {
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
p.credits {
|
||||||
|
font-style: italic ;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
p.label {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger ;
|
||||||
|
color: maroon ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
p.sidebar-title {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger }
|
||||||
|
|
||||||
|
p.sidebar-subtitle {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
p.topic-title {
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
pre.address {
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-top: 0 ;
|
||||||
|
font: inherit }
|
||||||
|
|
||||||
|
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
pre.code .ln { color: grey; } /* line numbers */
|
||||||
|
pre.code, code { background-color: #eeeeee }
|
||||||
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
|
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||||
|
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||||
|
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||||
|
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||||
|
|
||||||
|
span.classifier {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-style: oblique }
|
||||||
|
|
||||||
|
span.classifier-delimiter {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
span.interpreted {
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
span.option {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
span.pre {
|
||||||
|
white-space: pre }
|
||||||
|
|
||||||
|
span.problematic {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
span.section-subtitle {
|
||||||
|
/* font-size relative to parent (h1..h6 element) */
|
||||||
|
font-size: 80% }
|
||||||
|
|
||||||
|
table.citation {
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docinfo {
|
||||||
|
margin: 2em 4em }
|
||||||
|
|
||||||
|
table.docutils {
|
||||||
|
margin-top: 0.5em ;
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
table.footnote {
|
||||||
|
border-left: solid 1px black;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docutils td, table.docutils th,
|
||||||
|
table.docinfo td, table.docinfo th {
|
||||||
|
padding-left: 0.5em ;
|
||||||
|
padding-right: 0.5em ;
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: left ;
|
||||||
|
white-space: nowrap ;
|
||||||
|
padding-left: 0 }
|
||||||
|
|
||||||
|
/* "booktabs" style (no vertical lines) */
|
||||||
|
table.docutils.booktabs {
|
||||||
|
border: 0px;
|
||||||
|
border-top: 2px solid;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs * {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs th {
|
||||||
|
border-bottom: thin solid;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||||
|
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||||
|
font-size: 100% }
|
||||||
|
|
||||||
|
ul.auto-toc {
|
||||||
|
list-style-type: none }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="document" id="base-usability">
|
||||||
|
<h1 class="title">Base Usability</h1>
|
||||||
|
|
||||||
|
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_usability"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p>
|
||||||
|
<p>This module adds the following functions:</p>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Adds <em>track_visibility=’onchange’</em> on all the important fields of the Partner object</li>
|
||||||
|
<li>By default, Odoo doesn’t display the title field on all the partner form views. This module fixes it (it replaces the module base_title_on_partner)</li>
|
||||||
|
<li>Adds a log message at INFO level when sending an email via SMTP</li>
|
||||||
|
<li>Displays the local modules with installable filter</li>
|
||||||
|
<li>A group by “State” is added to module search view</li>
|
||||||
|
<li>Provides a _display_report_header method on the res.company object and _display_full_address on res.partner which are useful for reporting.</li>
|
||||||
|
<li>Add model in cron tree view</li>
|
||||||
|
<li>Add prefix field in sequence search view</li>
|
||||||
|
<li>Better search and form view for country and state</li>
|
||||||
|
<li>Display technical name of modules in kanban view</li>
|
||||||
|
<li>Change module filter to <cite>installable</cite></li>
|
||||||
|
<li>Add widget=handle on sequence of res.partner.bank</li>
|
||||||
|
<li>Add city and country in partner tree view</li>
|
||||||
|
<li>Add widget=”email” on email of contacts</li>
|
||||||
|
<li>Add script to fix partners related to users in multi-company setup</li>
|
||||||
|
<li>Add methods for py3o reports</li>
|
||||||
|
<li>Add name_get() on ir.model</li>
|
||||||
|
<li>Language wizard defaults to “.po”</li>
|
||||||
|
<li>Add tracking on partner fields</li>
|
||||||
|
<li>Handle lang in name_title field</li>
|
||||||
|
<li>Remove empty lines in address</li>
|
||||||
|
<li>Add bank Name field on res.partner.bank</li>
|
||||||
|
<li>Partners auto-created for users are Suppliers and not Customers</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Table of contents</strong></p>
|
||||||
|
<div class="contents local topic" id="contents">
|
||||||
|
<ul class="simple">
|
||||||
|
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li>
|
||||||
|
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul>
|
||||||
|
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li>
|
||||||
|
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li>
|
||||||
|
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="bug-tracker">
|
||||||
|
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1>
|
||||||
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/akretion/odoo-usability/issues">GitHub Issues</a>.
|
||||||
|
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
|
||||||
|
<a class="reference external" href="https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_usability%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||||
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="credits">
|
||||||
|
<h1><a class="toc-backref" href="#id2">Credits</a></h1>
|
||||||
|
<div class="section" id="authors">
|
||||||
|
<h2><a class="toc-backref" href="#id3">Authors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Akretion</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="contributors">
|
||||||
|
<h2><a class="toc-backref" href="#id4">Contributors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Alexis de Lattre <<a class="reference external" href="mailto:alexis.delattre@akretion.com">alexis.delattre@akretion.com</a>></li>
|
||||||
|
<li>David Beal <<a class="reference external" href="mailto:david.beal@akretion.com">david.beal@akretion.com</a>></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="maintainers">
|
||||||
|
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2>
|
||||||
|
<p>This module is part of the <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_usability">akretion/odoo-usability</a> project on GitHub.</p>
|
||||||
|
<p>You are welcome to contribute.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
<record id="ir_cron_view_tree" model="ir.ui.view">
|
|
||||||
<field name="model">ir.cron</field>
|
|
||||||
<field name="inherit_id" ref="base.ir_cron_view_tree"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="name" position="after">
|
|
||||||
<field name="model_id"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,22 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2015-2019 Akretion France (http://www.akretion.com/)
|
Copyright 2015-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).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="module_view_kanban" model="ir.ui.view">
|
|
||||||
<field name="name">Better display of module technical name</field>
|
|
||||||
<field name="model">ir.module.module</field>
|
|
||||||
<field name="inherit_id" ref="base.module_view_kanban"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//h4[@class='o_kanban_record_title']/code[@groups='base.group_no_one']" position="before">
|
|
||||||
<br/>
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<record id="view_module_filter" model="ir.ui.view">
|
<record id="view_module_filter" model="ir.ui.view">
|
||||||
<field name="model">ir.module.module</field>
|
<field name="model">ir.module.module</field>
|
||||||
@@ -25,9 +15,6 @@
|
|||||||
<xpath expr="//filter[@name='extra']" position="after">
|
<xpath expr="//filter[@name='extra']" position="after">
|
||||||
<filter name="installable" string="Installable" domain="[('state', '!=', 'uninstallable')]"/>
|
<filter name="installable" string="Installable" domain="[('state', '!=', 'uninstallable')]"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<group expand="0" position="inside">
|
|
||||||
<filter name="state_groupby" string="State" context="{'group_by': 'state'}"/>
|
|
||||||
</group>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
15
base_usability/views/ir_sequence.xml
Normal file
15
base_usability/views/ir_sequence.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_sequence_search" model="ir.ui.view">
|
||||||
|
<field name="model">ir.sequence</field>
|
||||||
|
<field name="inherit_id" ref="base.view_sequence_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="prefix"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user