Compare commits

..

30 Commits

Author SHA1 Message Date
clementmbr
45500f5bd8 product_usability: Improve filter domain on supplier product names 2021-09-03 15:25:55 +02:00
Alexis de Lattre
a28a853f45 base_usabilty: translate=False on print_report_name 2021-08-28 00:00:07 +02:00
Alexis de Lattre
82b9a1e502 Move tracking=True for res.partner fields to mail_usability 2021-08-27 12:10:34 +02:00
Alexis de Lattre
b05abba064 [MIG] sale_order_route to v14 2021-08-26 23:18:37 +02:00
Alexis de Lattre
75e3463a76 Add name_search in base_partner_ref 2021-08-26 23:06:38 +02:00
Alexis de Lattre
3066c0545d Add print buttons in direct access on sale.order and account.invoice 2021-08-26 12:44:50 +02:00
Alexis de Lattre
62a84469c8 [FIX] account_usability: line order in py3o report 2021-08-26 10:45:59 +02:00
Alexis de Lattre
00339e44b6 MIG delivery_usability to v14 2021-08-25 21:57:43 +02:00
Alexis de Lattre
d1ae620079 Add product_barcode with optional=hide in tree view of inventory lines and quants 2021-08-25 20:29:46 +02:00
Alexis de Lattre
3db9f0f096 MIG eradicate_quick_create to v14 2021-08-25 18:25:17 +02:00
Alexis de Lattre
732ee2c55b base_dynamic_list: update module desc 2021-08-24 22:59:25 +02:00
Alexis de Lattre
e08d658b25 [MIG] base_dynamic_list from v10 to v14 2021-08-24 21:54:06 +02:00
Alexis de Lattre
2a7ec92a37 Add archive filter 2021-08-24 21:43:14 +02:00
Alexis de Lattre
dc30ce4e69 Add index=True on domain 2021-08-24 21:43:14 +02:00
Alexis de Lattre
32d45d228b Add new module base_dynamic_list 2021-08-24 21:43:14 +02:00
Alexis de Lattre
939de0c9bd base_partner_one2many_phone: improve country handling in phone reformat 2021-08-23 18:50:43 +02:00
Alexis de Lattre
48a19b8f97 stock_usability: remove unnecessary inherit (native colors ok) 2021-07-20 13:09:11 +02:00
Alexis de Lattre
8a2e662d43 base_usability: update FR translation 2021-07-01 19:20:59 +02:00
Alexis de Lattre
de2e5f2121 stock_account_usability: show to_refund on stock.move form 2021-05-21 11:29:18 +02:00
Alexis de Lattre
42e014bcb1 sale_usability: add no-attachment filter on sale.order
account_usability: small code improvement on has_attachment
2021-05-20 19:29:31 +02:00
Alexis de Lattre
e70e3b23cf Improve picking view 2021-04-28 11:51:30 +02:00
Alexis de Lattre
50b4944c8b Always show location_id on stock inventory line 2021-04-28 11:34:28 +02:00
Alexis de Lattre
96bfda6e1b Add product_barcode in SO lines, PO lines, stock move lines and invoice lines with optional="hide"
Show warning about double VAT partner even when not in editable mode
2021-04-28 11:27:04 +02:00
Alexis de Lattre
cfb58ed80f Add inherit of account.move.line filter view 2021-04-27 16:25:50 +02:00
Alexis de Lattre
91e9c1fe33 account_usability: allow to manually create a journal item in cash and check journals 2021-04-22 23:58:03 +02:00
Alexis de Lattre
dff4e47cf5 Add sudo() to access ir.actions.act_windows... stupid v14! 2021-04-14 08:13:53 +02:00
Alexis de Lattre
452cc399c5 stock_usability: add button to access stock moves before button to access product moves, because stock moves are more important than product moves 2021-04-12 21:29:23 +02:00
Clément Mombereau
f4c22501a7 Merge pull request #144 from akretion/14.0-fix-message-post-picking
[FIX] picking.message_post() needs explicit 'body' argument
2021-04-07 00:53:22 +02:00
clementmbr
9e8874eb4b [FIX] picking.message_post() needs explicit 'body' argument 2021-04-07 00:04:02 +02:00
Alexis de Lattre
fc6c0384ed stock_usability: create ir.config_parameter stock.no_default_immediate_tranfer=True upon install 2021-04-01 11:47:12 +02:00
60 changed files with 852 additions and 187 deletions

View File

@@ -5,6 +5,7 @@
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):
@@ -49,11 +50,11 @@ class AccountMove(models.Model):
def _compute_has_attachment(self):
iao = self.env['ir.attachment']
for move in self:
if iao.search([
if iao.search_count([
('res_model', '=', 'account.move'),
('res_id', '=', move.id),
('type', '=', 'binary'),
('company_id', '=', move.company_id.id)], limit=1):
('company_id', '=', move.company_id.id)]):
move.has_attachment = True
else:
move.has_attachment = False
@@ -122,7 +123,11 @@ class AccountMove(models.Model):
has_sections = False
subtotal = 0.0
sign = self.move_type == 'out_refund' and -1 or 1
for line in self.invoice_line_ids:
# 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:
@@ -157,6 +162,24 @@ class AccountMove(models.Model):
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'
@@ -176,6 +199,8 @@ class AccountMoveLine(models.Model):
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()

View File

@@ -18,6 +18,12 @@
<field name="invoice_incoterm_id" position="attributes">
<attribute name="widget">selection</attribute>
</field>
<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="after">
@@ -30,6 +36,9 @@
<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>
@@ -58,6 +67,36 @@
</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', '&gt;=', (context_today().strftime('%Y-01-01'))), ('date', '&lt;=', (context_today().strftime('%Y-12-31')))]"/>
<filter name="previous_year" string="Previous Year" domain="[('date', '&gt;=', (context_today() + relativedelta(day=1, month=1, years=-1)).strftime('%Y-%m-%d')), ('date', '&lt;=', (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 -->

View File

@@ -0,0 +1 @@
from . import models

View 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,
}

View File

@@ -0,0 +1 @@
from . import dynamic_list

View 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)

View 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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_dynamic_list_read Read access on dynamic.list to employees model_dynamic_list base.group_user 1 0 0 0
3 access_dynamic_list_full Full access to dynamic.list to System group model_dynamic_list base.group_system 1 1 1 1
4 access_dynamic_list_translate_read Read access on dynamic.list.translate to employees model_dynamic_list_translate base.group_user 1 0 0 0
5 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
6 access_dynamic_list_code_read Read access on dynamic.list.code to employees model_dynamic_list_code base.group_user 1 0 0 0
7 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
8 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
9 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

View 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>

View File

@@ -46,7 +46,7 @@ class ResPartnerPhone(models.Model):
@api.onchange('phone', 'partner_id')
def _onchange_phone_validation(self):
if self.phone:
self.phone = self.phone_format(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):

View File

@@ -28,7 +28,7 @@ class ResPartner(models.Model):
# START modif of native method
if partner.ref:
name = u"[%s] %s" % (partner.ref, name)
name = "[%s] %s" % (partner.ref, name)
# END modif of native method
if partner.company_name or partner.parent_id:
if not name and partner.type in ['invoice', 'delivery', 'other']:
@@ -55,3 +55,13 @@ class ResPartner(models.Model):
if self._context.get('show_vat') and partner.vat:
name = "%s %s" % (name, partner.vat)
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)

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-17 16:55+0000\n"
"PO-Revision-Date: 2021-02-17 16:55+0000\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"

View File

@@ -6,9 +6,9 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-17 16:55+0000\n"
"PO-Revision-Date: 2021-02-17 16:55+0000\n"
"Last-Translator: \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"
@@ -33,16 +33,15 @@ msgstr "Sociétés"
#. module: base_usability
#: model:ir.model,name:base_usability.model_res_partner
msgid "Contact"
msgstr ""
msgstr "Contact"
#. module: base_usability
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
msgid "Currency"
msgstr "Monnaie"
msgstr "Devise"
#. 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 "N° client :"
@@ -68,7 +67,7 @@ msgstr "E-mail :"
#. module: base_usability
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
msgid "Group By"
msgstr ""
msgstr "Grouper par"
#. module: base_usability
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server__id
@@ -79,12 +78,12 @@ msgstr ""
#: 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 ""
msgstr "ID"
#. module: base_usability
#: model_terms:ir.ui.view,arch_db:base_usability.view_module_filter
msgid "Installable"
msgstr ""
msgstr "Installable"
#. module: base_usability
#: model:ir.model.fields,field_description:base_usability.field_ir_mail_server____last_update
@@ -100,7 +99,7 @@ msgstr "Dernière modification le"
#. module: base_usability
#: model:ir.model,name:base_usability.model_ir_mail_server
msgid "Mail Server"
msgstr "Serveur d'email"
msgstr "Serveur mail"
#. module: base_usability
#: code:addons/base_usability/models/res_partner.py:0
@@ -132,7 +131,7 @@ msgstr "Nom avec titre"
#. module: base_usability
#: model:res.groups,name:base_usability.group_nobody
msgid "Nobody (used to hide native menus)"
msgstr ""
msgstr "Personne (utilisé pour cacher des entrées de menu natifs)"
#. module: base_usability
#: model:ir.model,name:base_usability.model_res_partner_category
@@ -152,7 +151,6 @@ 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 "N° fournisseur :"

View File

@@ -4,4 +4,5 @@ from . import res_partner_bank
from . import res_partner_category
from . import res_company
from . import ir_mail_server
from . import ir_actions_report
from . import ir_model

View 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)

View File

@@ -8,25 +8,9 @@ from odoo import models, fields, api, _
class ResPartner(models.Model):
_inherit = 'res.partner'
# track_visibility is handled in the 'mail' module, and base_usability
# doesn't depend on 'mail', but that doesn't hurt, it will just be
# ignored if mail is not installed
# TODO move to mail module
# name = fields.Char(tracking=True)
# parent_id = fields.Many2one(tracking=True)
# ref = fields.Char(tracking=True)
# lang = fields.Selection(tracking=True)
# user_id = fields.Many2one(tracking=True)
# vat = fields.Char(tracking=True)
# street = fields.Char(tracking=True)
# street2 = fields.Char(tracking=True)
# zip = fields.Char(tracking=True)
# city = fields.Char(tracking=True)
# state_id = fields.Many2one(tracking=True)
# country_id = fields.Many2one(tracking=True)
# is_company = fields.Boolean(tracking=True)
# active = fields.Boolean(tracking=True)
# company_id = fields.Many2one(tracking=True)
# tracking=True is handled in the 'mail' module, and base_usability
# doesn't depend on 'mail', so adding tracking on res.partner fields
# has been moved to mail_usability
ref = fields.Char(copy=False)
# For reports
name_title = fields.Char(

View File

@@ -25,7 +25,7 @@
<field name="name">base_usability.res.country.search</field>
<field name="model">res.country</field>
<field name="arch" type="xml">
<search string="Search Countries">
<search>
<field name="name" filter_domain="['|', ('name', 'ilike', self), ('code', '=', self)]" string="Name or Code"/>
<field name="code"/>
<field name="currency_id"/>

View File

@@ -16,6 +16,10 @@
<xpath expr="//field[@name='child_ids']/form//field[@name='title']" position="attributes">
<attribute name="attrs"></attribute>
</xpath>
<!-- Show double VAT partner even when not in editable mode -->
<div attrs="{'invisible': [('same_vat_partner_id', '=', False)]}" position="attributes">
<attribute name="class">alert alert-warning</attribute>
</div>
</field>
</record>

View File

@@ -1 +1 @@
from . import stock
from . import models

View File

@@ -1,10 +1,10 @@
# Copyright 2018-2019 Akretion (http://www.akretion.com)
# Copyright 2018-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).
{
'name': 'Delivery Usability',
'version': '12.0.1.0.0',
'version': '14.0.1.0.0',
'category': 'Stock',
'license': 'AGPL-3',
'summary': 'Several usability enhancements in Delivery',
@@ -14,7 +14,6 @@ Delivery Usability
The usability enhancements include:
* allow modification of carrier and it's tracking ref. on a done picking
* display field 'invoice_shipping_on_delivery' on sale.order form view
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
@@ -22,8 +21,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'website': 'http://www.akretion.com',
'depends': ['delivery'],
'data': [
'sale_view.xml',
'stock_view.xml',
'views/stock_picking.xml',
],
'installable': False,
'installable': True,
}

View File

@@ -1,4 +1,4 @@
# Copyright 2018-2019 Akretion (http://www.akretion.com)
# Copyright 2018-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).
@@ -8,5 +8,5 @@ from odoo import fields, models
class StockPicking(models.Model):
_inherit = 'stock.picking'
carrier_id = fields.Many2one(track_visibility='onchange')
carrier_tracking_ref = fields.Char(track_visibility='onchange')
carrier_id = fields.Many2one(tracking=True)
carrier_tracking_ref = fields.Char(tracking=True)

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018-2019 Akretion
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_order_form_with_carrier" model="ir.ui.view">
<field name="name">delivery_usability.sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="delivery.view_order_form_with_carrier"/>
<field name="arch" type="xml">
<group name="sale_pay" position="inside">
<field name="invoice_shipping_on_delivery"/>
</group>
</field>
</record>
</odoo>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2018-2021 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->

View File

@@ -1,9 +1,9 @@
# Copyright 2014-2019 Akretion France (http://www.akretion.com)
# Copyright 2014-2021 Akretion France (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
{
'name': 'Eradicate Quick Create',
'version': '12.0.2.0.0',
'version': '14.0.1.0.0',
'category': 'Tools',
'license': 'AGPL-3',
'summary': 'Disable quick create on all objects',
@@ -13,7 +13,7 @@ Eradicate Quick Create
Disable quick create on all objects of Odoo.
This new version of the module uses the module *web_m2x_options* from the OCA *web* project instead of the module *base_optional_quick_create* from the OCA project *server-ux*.
This module uses the module *web_m2x_options* from the OCA *web* project (in v10 and lower, it was using the module *base_optional_quick_create* from the OCA project *server-ux*).
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
@@ -21,5 +21,5 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'website': 'http://www.akretion.com',
'depends': ['web_m2x_options'],
'post_init_hook': 'web_m2x_options_create',
'installable': False,
'installable': True,
}

View File

@@ -1,4 +1,4 @@
# Copyright 2019 Akretion France
# Copyright 2019-2021 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@@ -11,7 +11,7 @@ def web_m2x_options_create(cr, registry):
config_parameter = env['ir.config_parameter'].search(
[('key', '=', 'web_m2x_options.create')])
if config_parameter and config_parameter.value != 'False':
config_parameter.value = 'False'
config_parameter.write({'value': 'False'})
else:
env['ir.config_parameter'].create({
'key': 'web_m2x_options.create',

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,32 @@
# Copyright 2016-2021 Akretion France (http://www.akretion.com)
# @author Benoît Guillot <benoit.guillot@akretion.com>
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Mail Usability',
'version': '14.0.1.0.0',
'category': 'Productivity/Discuss',
'license': 'AGPL-3',
'summary': 'Usability improvements on mails',
'description': """
Mail Usability
==============
Small usability improvements on mails:
* remove link in mail footer (TODO mig v14)
* remove 'sent by' in notification footer (TODO mig v14)
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['mail'],
'data': [
#'views/mail_view.xml',
#'data/mail_data.xml',
#'wizard/email_template_preview_view.xml',
#'wizard/mail_compose_message_view.xml',
],
'installable': True,
}

View File

@@ -0,0 +1,2 @@
from . import res_partner
from . import mail_template

View File

@@ -0,0 +1,11 @@
# Copyright 2018-2021 Akretion France (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class MailTemplate(models.Model):
_inherit = 'mail.template'
auto_delete = fields.Boolean(default=False)

View File

@@ -0,0 +1,26 @@
# 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).
from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
# tracking=True is handled in the 'mail' module, so it's better
# to have this in mail_usability than in base_usability
name = fields.Char(tracking=True)
parent_id = fields.Many2one(tracking=True)
ref = fields.Char(tracking=True)
lang = fields.Selection(tracking=True)
vat = fields.Char(tracking=True)
street = fields.Char(tracking=True)
street2 = fields.Char(tracking=True)
zip = fields.Char(tracking=True)
city = fields.Char(tracking=True)
state_id = fields.Many2one(tracking=True)
country_id = fields.Many2one(tracking=True)
is_company = fields.Boolean(tracking=True)
active = fields.Boolean(tracking=True)
company_id = fields.Many2one(tracking=True)

View File

@@ -32,7 +32,6 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'views/product_pricelist_item.xml',
'views/product_template_view.xml',
'views/product_product.xml',
'views/product_category_view.xml',
],
'installable': True,
}

View File

@@ -2,4 +2,3 @@ from . import product_product
from . import product_template
from . import product_supplierinfo
from . import product_pricelist
from . import product_category

View File

@@ -1,13 +0,0 @@
# Copyright 2022 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductCategory(models.Model):
_inherit = ['product.category', "mail.thread", "mail.activity.mixin"]
_name = 'product.category'
name = fields.Char(tracking=10)
parent_id = fields.Many2one(tracking=20)

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="product_category_form_view" model="ir.ui.view">
<field name="model">product.category</field>
<field name="inherit_id" ref="product.product_category_form_view" />
<field name="arch" type="xml">
<sheet position="after">
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</sheet>
</field>
</record>
</odoo>

View File

@@ -12,7 +12,7 @@
<field name="inherit_id" ref="product.product_supplierinfo_search_view"/>
<field name="arch" type="xml">
<field name="product_tmpl_id" position="after">
<field name="product_code"/>
<field name="product_name" filter_domain="['|', ('product_code', 'ilike', self), ('product_name', 'ilike', self)]" />
</field>
</field>
</record>

View File

@@ -24,11 +24,6 @@ class PurchaseOrder(models.Model):
for order in self:
order.delivery_partner_id = order.dest_address_id
def print_order(self):
report = self.env.ref('purchase.action_report_purchase_order')
action = report.report_action(self)
return action
# Re-write native name_get() to use amount_untaxed instead of amount_total
@api.depends('name', 'partner_ref')
def name_get(self):
@@ -71,3 +66,10 @@ class PurchaseOrder(models.Model):
# {'subtotal': 8932.23},
# ]
return res
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
# for optional display in tree view
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")

View File

@@ -14,7 +14,7 @@
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<button name="action_rfq_send" states="purchase" position="after">
<button name="print_order" states="purchase" string="Print Order" type="object"/>
<button name="%(purchase.action_report_purchase_order)d" states="purchase,done" string="Print" type="action"/>
</button>
<field name="fiscal_position_id" position="attributes">
<attribute name="widget">selection</attribute>
@@ -34,6 +34,9 @@
<xpath expr="//field[@name='order_line']/form//field[@name='analytic_tag_ids']" position="attributes">
<attribute name="groups">analytic.group_analytic_tags</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after">
<field name="product_barcode" optional="hide"/>
</xpath>
</field>
</record>

View File

@@ -1,3 +1 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -1,10 +1,10 @@
# Copyright 2019 Akretion France (http://www.akretion.com/)
# Copyright 2019-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': 'Sale Order Route',
'version': '12.0.1.0.0',
'version': '14.0.1.0.0',
'category': 'Sales',
'license': 'AGPL-3',
'summary': 'Set route on sale order',
@@ -18,5 +18,5 @@ This module has been written by Alexis de Lattre from Akretion
'website': 'http://www.akretion.com',
'depends': ['sale_stock'],
'data': ['views/sale_order.xml'],
'installable': False,
'installable': True,
}

View File

@@ -1,3 +1 @@
# -*- coding: utf-8 -*-
from . import sale_order

View File

@@ -1,4 +1,4 @@
# Copyright 2019 Akretion France (http://www.akretion.com/)
# Copyright 2019-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).
@@ -10,17 +10,17 @@ class SaleOrder(models.Model):
route_id = fields.Many2one(
'stock.location.route', string='Route',
ondelete='restrict', readonly=True, track_visibility='onchange',
ondelete='restrict', readonly=True, tracking=True,
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
domain=[('sale_selectable', '=', True)])
check_company=True,
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False), ('sale_selectable', '=', True)]")
def _action_confirm(self):
# Caution: take into account the scenario where
# route_id has a value, then SO is cancelled+back to draft,
# then route_id = False and SO is confirmed again
# Takes into account the scenario where route_id has a value, then SO is
# cancelled+back to draft, then route_id = False and SO is confirmed again
for order in self:
vals = {'route_id': order.route_id.id or False}
order.order_line.filtered(
lambda l:
l.product_id.type in ('product', 'consu')).write(vals)
super(SaleOrder, self)._action_confirm()
l.product_id and l.product_id.type in ('product', 'consu')).write(vals)
return super()._action_confirm()

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 Akretion France (http://www.akretion.com/)
Copyright 2019-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).
-->

View File

@@ -21,6 +21,9 @@ class SaleOrder(models.Model):
fiscal_position_id = fields.Many2one(tracking=True)
# for reports
has_discount = fields.Boolean(compute='_compute_has_discount')
has_attachment = fields.Boolean(
compute='_compute_has_attachment',
search='_search_has_attachment')
@api.depends('order_line.discount')
def _compute_has_discount(self):
@@ -34,6 +37,30 @@ class SaleOrder(models.Model):
break
order.has_discount = has_discount
def _compute_has_attachment(self):
iao = self.env['ir.attachment']
for order in self:
if iao.search_count([
('res_model', '=', 'sale.order'),
('res_id', '=', order.id),
('type', '=', 'binary'),
('company_id', '=', order.company_id.id)]):
order.has_attachment = True
else:
order.has_attachment = False
def _search_has_attachment(self, operator, value):
att_order_ids = {}
if operator == '=':
search_res = self.env['ir.attachment'].search_read([
('res_model', '=', 'sale.order'),
('type', '=', 'binary'),
('res_id', '!=', False)], ['res_id'])
for att in search_res:
att_order_ids[att['res_id']] = True
res = [('id', value and 'in' or 'not in', list(att_order_ids))]
return res
# for report
def py3o_lines_layout(self):
self.ensure_one()
@@ -66,6 +93,10 @@ class SaleOrder(models.Model):
class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'
# for optional display in tree view
product_barcode = fields.Char(
related='product_id.barcode', string="Product Barcode")
@api.onchange('product_uom', 'product_uom_qty')
def product_uom_change(self):
# When the user has manually set a custom price

View File

@@ -26,6 +26,12 @@
<field name="date_order" position="after">
<field name="client_order_ref"/>
</field>
<button name="action_quotation_send" states="sent,sale" position="after">
<button name="%(sale.action_report_saleorder)d" type="action" string="Print" states="draft,sent,sale,done"/>
</button>
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="after">
<field name="product_barcode" optional="hide"/>
</xpath>
</field>
</record>
@@ -62,6 +68,10 @@
<filter name="order_month" position="after">
<filter string="State" name="state_groupby" context="{'group_by': 'state'}"/>
</filter>
<filter name="activities_upcoming_all" position="after">
<separator/>
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
</filter>
</field>
</record>

View File

@@ -1,4 +1,4 @@
# Copyright 2019-2020 Akretion France (http://www.akretion.com)
# Copyright 2019-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).
@@ -15,14 +15,13 @@ Stock Account Usability
The usability enhancements include:
NONE
- show to_refund on stock.move form view
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['stock_account'],
'data': [
],
'installable': False,
'depends': ['stock_account', 'stock_usability'],
'data': ['views/stock_move.xml'],
'installable': True,
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_move_form" model="ir.ui.view">
<field name="name">stock_account_usability.stock.move.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock_usability.view_move_form" />
<field name="arch" type="xml">
<field name="price_unit" position="after">
<field name="to_refund" readonly="1"/>
</field>
</field>
</record>
</odoo>

View File

@@ -1,2 +0,0 @@
from . import stock_return_picking
from . import stock_quantity_history

View File

@@ -1 +1,2 @@
from . import models
from .post_install import create_config_parameter_immediate_tranfer

View File

@@ -38,5 +38,6 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
'views/procurement_scheduler_log.xml',
'security/ir.model.access.csv',
],
'post_init_hook': 'create_config_parameter_immediate_tranfer',
'installable': True,
}

View File

@@ -3,7 +3,8 @@ from . import stock_picking
from . import stock_location_route
from . import stock_warehouse_orderpoint
from . import stock_quant
from . import stock_inventory
from . import procurement_group
from . import procurement_scheduler_log
from . import product_template
from . import product
from . import res_partner

View File

@@ -0,0 +1,31 @@
# Copyright 2016-2021 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
tracking = fields.Selection(tracking=True)
sale_delay = fields.Float(tracking=True)
# the 'stock' module adds 'product' in type...
# but forgets to make it the default
type = fields.Selection(default='product')
def action_view_stock_move(self):
action = self.env.ref('stock.stock_move_action').sudo().read()[0]
action['domain'] = [('product_id.product_tmpl_id', 'in', self.ids)]
action['context'] = {'search_default_done': True}
return action
class ProductProduct(models.Model):
_inherit = 'product.product'
def action_view_stock_move(self):
action = self.env.ref('stock.stock_move_action').sudo().read()[0]
action['domain'] = [('product_id', 'in', self.ids)]
action['context'] = {'search_default_done': True}
return action

View File

@@ -1,15 +0,0 @@
# Copyright 2016-2020 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
tracking = fields.Selection(tracking=True)
sale_delay = fields.Float(tracking=True)
# the 'stock' module adds 'product' in type...
# but forgets to make it the default
type = fields.Selection(default='product')

View File

@@ -0,0 +1,11 @@
# Copyright 2021 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class StockInventoryLine(models.Model):
_inherit = 'stock.inventory.line'
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")

View File

@@ -2,7 +2,7 @@
# @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 import fields, models, _
from odoo.exceptions import UserError
import logging
@@ -12,6 +12,9 @@ logger = logging.getLogger(__name__)
class StockMove(models.Model):
_inherit = 'stock.move'
# for optional display in tree view
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
# def name_get(self):
# '''name_get of stock_move is important for the reservation of the
# quants: so want to add the name of the customer and the expected date
@@ -37,7 +40,7 @@ class StockMove(models.Model):
picking = move.picking_id
if picking:
product = move.product_id
picking.message_post(_(
picking.message_post(body=_(
"Product <a href=# data-oe-model=product.product "
"data-oe-id=%d>%s</a> qty %s %s <b>unreserved</b>")
% (product.id, product.display_name,
@@ -49,6 +52,9 @@ class StockMove(models.Model):
class StockMoveLine(models.Model):
_inherit = 'stock.move.line'
# for optional display in tree view
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
# TODO: I think it's not complete
def button_do_unreserve(self):
for moveline in self:
@@ -60,7 +66,7 @@ class StockMoveLine(models.Model):
picking = moveline.move_id.picking_id
if picking:
product = moveline.product_id
picking.message_post(_(
picking.message_post(body=_(
"Product <a href=# data-oe-model=product.product "
"data-oe-id=%d>%s</a> qty %s %s <b>unreserved</b>")
% (product.id, product.display_name,

View File

@@ -2,12 +2,14 @@
# @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 import fields, models
class StockQuant(models.Model):
_inherit = 'stock.quant'
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
def action_stock_move_lines_reserved(self):
self.ensure_one()
action = self.action_view_stock_moves()

View File

@@ -0,0 +1,26 @@
# 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).
import logging
from odoo import SUPERUSER_ID, api
logger = logging.getLogger(__name__)
def create_config_parameter_immediate_tranfer(cr, registry):
with api.Environment.manage():
env = api.Environment(cr, SUPERUSER_ID, {})
ico = env["ir.config_parameter"]
conf_param = ico.search([('key', '=', 'stock.no_default_immediate_tranfer')])
if not conf_param:
ico.create({
'key': 'stock.no_default_immediate_tranfer',
'value': 'True',
})
logger.info(
'ir.config_parameter stock.no_default_immediate_tranfer created')
else:
logger.info(
'ir.config_parameter stock.no_default_immediate_tranfer '
'already exists')

View File

@@ -19,5 +19,38 @@
</field>
</record>
<record id="product_template_form_view_procurement_button" model="ir.ui.view">
<field name="name">stock_usability.product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.product_template_form_view_procurement_button"/>
<field name="arch" type="xml">
<button name="action_view_stock_move_lines" position="before">
<button string="Stock Moves"
type="object"
name= "action_view_stock_move"
attrs="{'invisible': [('type', 'not in', ('product', 'consu'))]}"
groups="stock.group_stock_user"
class="oe_stat_button" icon="fa-exchange"
/>
</button>
</field>
</record>
<record id="product_form_view_procurement_button" model="ir.ui.view">
<field name="name">stock_usability.product.product.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<button name="action_view_stock_move_lines" position="before">
<button string="Stock Moves"
type="object"
name= "action_view_stock_move"
attrs="{'invisible': [('type', 'not in', ('product', 'consu'))]}"
groups="stock.group_stock_user"
class="oe_stat_button" icon="fa-exchange"
/>
</button>
</field>
</record>
</odoo>

View File

@@ -12,6 +12,9 @@
<field name="model">stock.inventory.line</field>
<field name="inherit_id" ref="stock.stock_inventory_line_tree"/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
<tree position="attributes">
<!-- native :
decoration-danger="product_qty != theoretical_qty"
@@ -21,18 +24,9 @@
<attribute name="decoration-info">product_qty &gt; theoretical_qty</attribute>
<attribute name="decoration-danger">product_qty &lt; theoretical_qty</attribute>
</tree>
</field>
</record>
<record id="view_inventory_tree" model="ir.ui.view">
<field name="name">usability.stock.inventory.tree</field>
<field name="model">stock.inventory</field>
<field name="inherit_id" ref="stock.view_inventory_tree"/>
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="decoration-info">state == 'draft'</attribute>
<attribute name="decoration-warning">state == 'confirm'</attribute>
</tree>
<field name="location_id" position="attributes">
<attribute name="invisible">0</attribute>
</field>
</field>
</record>

View File

@@ -58,6 +58,9 @@
states="partially_available,assigned"
icon="fa-ban"/>
</field>
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
</field>
</record>
@@ -78,6 +81,20 @@
states="partially_available,assigned"
icon="fa-ban"/>
</field>
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
</field>
</record>
<!-- View embedded in picking -->
<record id="view_stock_move_line_detailed_operation_tree" model="ir.ui.view">
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree" />
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
</field>
</record>

View File

@@ -30,9 +30,12 @@
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='location_id']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='location_dest_id']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='name']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='product_id']" position="after">
<field name="location_id" groups="stock.group_stock_multi_locations"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations"/>
<field name="product_barcode" optional="hide"/>
<field name="name" optional="hide"/>
<field name="location_id" groups="stock.group_stock_multi_locations" optional="show"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations" optional="show"/>
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/tree/button[@name='action_assign_serial']" position="after">
<button type="object" name="button_do_unreserve" string="Unreserve"
@@ -40,15 +43,6 @@
states="partially_available,assigned"
icon="fa-ban"/>
</xpath>
<!-- STOCK MOVE LINE -->
<!--
<xpath expr="//field[@name='move_line_ids_without_package']/tree/field[@name='location_id']" position="attributes">
<attribute name="attrs">{}</attribute>
</xpath>
<xpath expr="//field[@name='move_line_ids_without_package']/tree/field[@name='location_dest_id']" position="attributes">
<attribute name="attrs">{}</attribute>
</xpath>
-->
</field>
</record>

View File

@@ -13,6 +13,9 @@
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree"/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
<field name="quantity" position="attributes">
<attribute name="sum">1</attribute>
</field>