Compare commits
36 Commits
16.0-no_de
...
14-adapt-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ee69f3bff | ||
|
|
46fcf5c0ef | ||
|
|
2c794033b0 | ||
|
|
2f784eb1a0 | ||
|
|
b019baed6e | ||
|
|
5532a8e165 | ||
|
|
a4c2a43cf7 | ||
|
|
f9246c6988 | ||
|
|
8364b42930 | ||
|
|
df370f4948 | ||
|
|
282f48db3e | ||
|
|
73c3c1a664 | ||
|
|
ac0afd6e2b | ||
|
|
a61d1458d8 | ||
|
|
0756beb8f5 | ||
|
|
0656d319ce | ||
|
|
a517fe67a8 | ||
|
|
2953baba22 | ||
|
|
5ea3f5adfd | ||
|
|
92a1b511c1 | ||
|
|
5ad925cee1 | ||
|
|
b8e335fb10 | ||
|
|
c693ccc62c | ||
|
|
983a25367f | ||
|
|
e36f88e23a | ||
|
|
18e9ed99ff | ||
|
|
dea732e270 | ||
|
|
150916200d | ||
|
|
48247b6d06 | ||
|
|
1971808c7d | ||
|
|
5724d31ac1 | ||
|
|
3ecfde15f0 | ||
|
|
44fedbf72e | ||
|
|
5f731d18f1 | ||
|
|
7477deeae3 | ||
|
|
2905b5f426 |
@@ -18,9 +18,10 @@
|
||||
'views/account_account.xml',
|
||||
'views/account_group.xml',
|
||||
# 'views/account_bank_statement.xml',
|
||||
# 'views/account_invoice_report.xml',
|
||||
'views/account_invoice_report.xml',
|
||||
'views/account_journal.xml',
|
||||
'views/account_move.xml',
|
||||
'views/account_analytic_line.xml',
|
||||
'views/account_menu.xml',
|
||||
'views/account_tax.xml',
|
||||
# 'views/product.xml', # TODO
|
||||
|
||||
@@ -36,6 +36,34 @@ class AccountMove(models.Model):
|
||||
compute="_compute_sales_dates",
|
||||
help="This information appear on invoice qweb report "
|
||||
"(you may use it for your own report)")
|
||||
# There is a native "blocked" field (bool) on account.move.line
|
||||
# We want to have that field on invoices to improve usability
|
||||
# while keeping compatibility with the standard Odoo datamodel
|
||||
blocked = fields.Boolean(
|
||||
compute="_compute_blocked",
|
||||
inverse="_inverse_blocked",
|
||||
store=True,
|
||||
string="Dispute",
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
@api.depends("line_ids", "line_ids.blocked")
|
||||
def _compute_blocked(self):
|
||||
for move in self:
|
||||
move.blocked = any(
|
||||
[
|
||||
l.blocked
|
||||
for l in move.line_ids
|
||||
if l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
]
|
||||
)
|
||||
|
||||
def _inverse_blocked(self):
|
||||
for move in self:
|
||||
for line in move.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
):
|
||||
line.blocked = move.blocked
|
||||
|
||||
def _compute_has_discount(self):
|
||||
prec = self.env['decimal.precision'].precision_get('Discount')
|
||||
|
||||
22
account_usability_akretion/views/account_analytic_line.xml
Normal file
22
account_usability_akretion/views/account_analytic_line.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Akretion France (http://www.akretion.com/)
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="view_account_analytic_line_tree_inherit_account" model="ir.ui.view">
|
||||
<field name="model">account.analytic.line</field>
|
||||
<field name="inherit_id" ref="account.view_account_analytic_line_tree_inherit_account"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="general_account_id" position="attributes">
|
||||
<attribute name="optional">show</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2018-2020 Akretion (http://www.akretion.com/)
|
||||
Copyright 2018-2024 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,56 +8,31 @@
|
||||
<odoo>
|
||||
|
||||
|
||||
<record id="account_invoice_report_tree" model="ir.ui.view">
|
||||
<record id="account_invoice_report_view_tree" model="ir.ui.view">
|
||||
<field name="name">usability.account.invoice.report.tree</field>
|
||||
<field name="model">account.invoice.report</field>
|
||||
<field name="inherit_id" ref="account.account_invoice_report_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Invoices Analysis">
|
||||
<field name="move_id"/>
|
||||
<field name="journal_id" optional="hide"/>
|
||||
<field name="company_id" optional="hide" groups="base.group_multi_company"/>
|
||||
<field name="invoice_date"/>
|
||||
<field name="invoice_date_due"/>
|
||||
<field name="move_type"/>
|
||||
<field name="commercial_partner_id"/>
|
||||
<field name="partner_id" optional="hide"/>
|
||||
<field name="country_id" optional="hide"/>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="commercial_partner_id" optional="hide"/>
|
||||
<field name="country_id" optional="hide"/>
|
||||
<field name="industry_id" optional="hide"/>
|
||||
<field name="invoice_user_id"/>
|
||||
<field name="fiscal_position_id" optional="hide"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_categ_id" optional="hide"/>
|
||||
<field name="account_id" optional="hide"/>
|
||||
<field name="analytic_account_id" optional="hide" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="quantity" sum="1"/>
|
||||
<field name="product_uom_id" groups="uom.group_uom"/>
|
||||
<field name="price_subtotal" sum="1"/>
|
||||
<field name="state"/>
|
||||
<field name="payment_state" optional="hide"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="quantity" position="after">
|
||||
<field name="product_uom_id" groups="uom.group_uom" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="account.action_account_invoice_report_all_supp" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_current': 1, 'search_default_supplier': 1, 'group_by': ['invoice_date']}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
||||
<field name="view_mode">pivot,graph</field>
|
||||
</record>
|
||||
|
||||
<record id="account.action_account_invoice_report_all" model="ir.actions.act_window">
|
||||
<field name="context">{'search_default_current': 1, 'search_default_customer': 1, 'group_by': ['invoice_date']}</field> <!-- Remove group_by_no_leaf, which breaks tree view -->
|
||||
<field name="view_mode">pivot,graph</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_invoice_report_pivot" model="ir.ui.view">
|
||||
<field name="name">usability.account.invoice.report</field>
|
||||
<record id="view_account_invoice_report_search" model="ir.ui.view">
|
||||
<field name="model">account.invoice.report</field>
|
||||
<field name="inherit_id" ref="account.view_account_invoice_report_pivot"/>
|
||||
<field name="inherit_id" ref="account.view_account_invoice_report_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<pivot position="attributes">
|
||||
<attribute name="disable_linking"></attribute>
|
||||
</pivot>
|
||||
</field>
|
||||
<filter name="category_product" position="after">
|
||||
<filter string="Product" name="product_groupby" context="{'group_by': 'product_id', 'residual_invisible':True}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -43,6 +43,16 @@
|
||||
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
|
||||
<field name="product_barcode" optional="hide"/>
|
||||
</xpath>
|
||||
<field name="invoice_source_email" position="after">
|
||||
<field name="blocked"/>
|
||||
</field>
|
||||
<div role="alert" position="after">
|
||||
<div id="warn_blocked" groups="account.group_account_invoice,account.group_account_readonly"
|
||||
class="alert alert-warning" role="alert" style="margin-bottom:0px;"
|
||||
attrs="{'invisible': ['|', ('move_type', 'not in', ('in_invoice', 'in_refund', 'out_invoice', 'out_refund')), ('blocked', '=', False)]}">
|
||||
This <field name="move_type"/> is marked as <b>disputed</b>.
|
||||
</div>
|
||||
</div>
|
||||
<xpath expr="//button[@name='open_duplicated_ref_bill_view']/.." position="attributes">
|
||||
<!-- show duplicate warning not only in draft state, but also in posted state -->
|
||||
<attribute name="attrs">{'invisible': ['|', ('state', '=', 'cancel'), ('duplicated_ref_ids', '=', [])]}</attribute>
|
||||
@@ -77,6 +87,8 @@
|
||||
<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)]"/>
|
||||
<separator/>
|
||||
<filter name="dispute" string="Dispute" domain="[('blocked', '=', True)]"/>
|
||||
</filter>
|
||||
<filter name="salesperson" position="before">
|
||||
<filter name="commercial_partner_groupby" string="Commercial Partner" context="{'group_by': 'commercial_partner_id'}"/>
|
||||
@@ -87,6 +99,23 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_line_form" model="ir.ui.view">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit_id" ref="account.view_move_line_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- The field 'blocked' is alone in it's block
|
||||
We don't want to display an empty block, so we put the attrs on the group
|
||||
The drawback of this is that, if someone added a field in that group,
|
||||
he won't see the field when internal_type is not payable/receivable -->
|
||||
<xpath expr="//field[@name='blocked']/.." position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('account_type', 'not in', ('liability_payable', 'asset_receivable'))]}</attribute>
|
||||
</xpath>
|
||||
<field name="account_id" position="after">
|
||||
<field name="account_type" invisible="1"/>
|
||||
</field>
|
||||
</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"/>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright 2020-2021 Akretion France (http://www.akretion.com)
|
||||
# Copyright 2020-2023 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',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Tools',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Dynamic lists',
|
||||
@@ -58,5 +58,5 @@ Limitation: when you want to have different access rights on these lists dependi
|
||||
'security/ir.model.access.csv',
|
||||
'views/dynamic_list.xml',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2020-2021 Akretion France (http://www.akretion.com)
|
||||
# Copyright 2020-2023 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).
|
||||
|
||||
@@ -63,18 +63,15 @@ class DynamicListCode(models.Model):
|
||||
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):
|
||||
def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
if args is None:
|
||||
args = []
|
||||
ids = []
|
||||
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)
|
||||
ids = list(self._search([('code', '=', name)] + args, limit=limit))
|
||||
if ids:
|
||||
return ids
|
||||
return super()._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
|
||||
|
||||
|
||||
class DynamicListCodeTranslate(models.Model):
|
||||
@@ -101,15 +98,12 @@ class DynamicListCodeTranslate(models.Model):
|
||||
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):
|
||||
def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
if args is None:
|
||||
args = []
|
||||
ids = []
|
||||
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)
|
||||
ids = list(self._search([('code', '=', name)] + args, limit=limit))
|
||||
if ids:
|
||||
return ids
|
||||
return super()._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2020-2021 Akretion France (http://www.akretion.com/)
|
||||
Copyright 2020-2023 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).
|
||||
-->
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
from . import partner_phone
|
||||
from . import models
|
||||
from .post_install import migrate_to_partner_phone
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Copyright 2014-2020 Abbaye du Barroux (http://www.barroux.org)
|
||||
# Copyright 2014-2020 Akretion (http://www.akretion.com>)
|
||||
# Copyright 2014-2023 Abbaye du Barroux (http://www.barroux.org)
|
||||
# Copyright 2014-2023 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',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Phone',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'One2many link between partners and phone numbers/emails',
|
||||
@@ -19,13 +19,14 @@ With this module, one partner can have several phone numbers and several emails.
|
||||
It has been developped by brother Bernard from Barroux Abbey and Alexis de Lattre from Akretion.
|
||||
""",
|
||||
'author': 'Akretion',
|
||||
'website': 'https://akretion.com/',
|
||||
'website': 'https://github.com/akretion/odoo-usability',
|
||||
'depends': ['contacts', 'base_usability', 'phone_validation'],
|
||||
'excludes': ['sms'], # because sms introduces big changes in partner form view
|
||||
'data': [
|
||||
'partner_phone_view.xml',
|
||||
'views/res_partner_phone.xml',
|
||||
'views/res_partner.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
'post_init_hook': 'migrate_to_partner_phone',
|
||||
}
|
||||
|
||||
2
base_partner_one2many_phone/models/__init__.py
Normal file
2
base_partner_one2many_phone/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import res_partner_phone
|
||||
from . import res_partner
|
||||
94
base_partner_one2many_phone/models/res_partner.py
Normal file
94
base_partner_one2many_phone/models/res_partner.py
Normal file
@@ -0,0 +1,94 @@
|
||||
# Copyright 2014-2023 Abbaye du Barroux (http://www.barroux.org)
|
||||
# Copyright 2016-2023 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, Command
|
||||
|
||||
|
||||
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)
|
||||
mobile = fields.Char(compute='_compute_partner_phone', store=True)
|
||||
email = fields.Char(compute='_compute_partner_phone', store=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(
|
||||
Command.create({'type': type, partner_phone_field: vals[partner_field]}))
|
||||
if partner_field in vals:
|
||||
vals.pop(partner_field)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
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')
|
||||
return super().create(vals_list)
|
||||
|
||||
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(Command.update(pphone.id, {
|
||||
partner_phone_field: vals[partner_field]}))
|
||||
else:
|
||||
vals['phone_ids'].append(Command.create({
|
||||
'type': type,
|
||||
partner_phone_field: vals[partner_field],
|
||||
}))
|
||||
else:
|
||||
if pphone:
|
||||
vals['phone_ids'].append(Command.delete(pphone.id))
|
||||
vals.pop(partner_field)
|
||||
|
||||
def write(self, vals):
|
||||
if 'phone_ids' not in vals:
|
||||
for rec in self:
|
||||
cvals = dict(vals, phone_ids=[])
|
||||
rec._update_write_vals(cvals, '1_email_primary', 'email', 'email')
|
||||
rec._update_write_vals(cvals, '3_phone_primary', 'phone', 'phone')
|
||||
rec._update_write_vals(cvals, '5_mobile_primary', 'mobile', 'phone')
|
||||
super(ResPartner, rec).write(cvals)
|
||||
return True
|
||||
else:
|
||||
return super().write(vals)
|
||||
108
base_partner_one2many_phone/models/res_partner_phone.py
Normal file
108
base_partner_one2many_phone/models/res_partner_phone.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# Copyright 2014-2023 Abbaye du Barroux (http://www.barroux.org)
|
||||
# Copyright 2016-2023 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
|
||||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
|
||||
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
|
||||
_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:
|
||||
country = self.partner_id.country_id
|
||||
self.phone = phone_validation.phone_format(
|
||||
self.phone,
|
||||
country.code or None,
|
||||
country.phone_code or None,
|
||||
force_format='INTERNATIONAL',
|
||||
raise_exception=False)
|
||||
|
||||
@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')
|
||||
''')
|
||||
@@ -1,193 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2017-2020 Akretion France (http://www.akretion.com/)
|
||||
# Copyright 2017-2023 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).
|
||||
|
||||
@@ -36,14 +36,12 @@ def create_partner_email(cr):
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -8,8 +8,9 @@ from odoo.tests.common import TransactionCase
|
||||
|
||||
class TestPartnerPhone(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPartnerPhone, self).setUp()
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
def _check_result(self, partner, result):
|
||||
rppo = self.env['res.partner.phone']
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?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>)
|
||||
Copyright 2014-2023 Abbaye du Barroux (http://www.barroux.org)
|
||||
Copyright 2016-2023 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).
|
||||
@@ -9,65 +9,6 @@
|
||||
|
||||
<odoo>
|
||||
|
||||
<!-- Partner phones -->
|
||||
<record id="res_partner_phone_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.tree</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||
<field name="type"/>
|
||||
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'readonly': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="email" widget="email" attrs="{'readonly': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="note"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.form</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group name="main">
|
||||
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||
<field name="type"/>
|
||||
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'invisible': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="email" widget="email" attrs="{'invisible': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="note"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_search" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.search</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="phone" />
|
||||
<field name="email" />
|
||||
<group name="groupby">
|
||||
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_action" model="ir.actions.act_window">
|
||||
<field name="name">Phones/E-mails</field>
|
||||
<field name="res_model">res.partner.phone</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="context">{'partner_phone_main_view': True}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="res_partner_phone_menu" action="res_partner_phone_action"
|
||||
parent="contacts.menu_contacts" sequence="10"/>
|
||||
|
||||
<record id="contacts.res_partner_menu_config" model="ir.ui.menu">
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
<!-- PARTNER views -->
|
||||
<record id="view_partner_form" model="ir.ui.view">
|
||||
<field name="name">add.phone_ids.on.partner.form</field>
|
||||
@@ -159,7 +100,7 @@
|
||||
<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>
|
||||
<attribute name="filter_domain">['|', '|', '|', '|', ('display_name', 'ilike', self), ('ref', '=ilike', self + '%'), ('phone_ids.email', 'ilike', self), ('vat', 'ilike', self), ('company_registry', 'ilike', self)]</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
71
base_partner_one2many_phone/views/res_partner_phone.xml
Normal file
71
base_partner_one2many_phone/views/res_partner_phone.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2014-2020 Abbaye du Barroux (http://www.barroux.org)
|
||||
Copyright 2016-2020 Akretion (http://www.akretion.com>)
|
||||
@author: Frère Bernard <informatique@barroux.org>
|
||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<!-- Partner phones -->
|
||||
<record id="res_partner_phone_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.tree</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||
<field name="type"/>
|
||||
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'readonly': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="email" widget="email" attrs="{'readonly': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="note"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.form</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group name="main">
|
||||
<field name="partner_id" invisible="not context.get('partner_phone_main_view')"/>
|
||||
<field name="type"/>
|
||||
<field name="phone" widget="phone" options="{'enable_sms': false}" attrs="{'required': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'invisible': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="email" widget="email" attrs="{'invisible': [('type', 'not in', ('1_email_primary', '2_email_secondary'))], 'required': [('type', 'in', ('1_email_primary', '2_email_secondary'))]}"/>
|
||||
<field name="note"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_search" model="ir.ui.view">
|
||||
<field name="name">res.partner.phone.search</field>
|
||||
<field name="model">res.partner.phone</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="phone" />
|
||||
<field name="email" />
|
||||
<group name="groupby">
|
||||
<filter name="type_groupby" string="Type" context="{'group_by': 'type'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_phone_action" model="ir.actions.act_window">
|
||||
<field name="name">Phones/E-mails</field>
|
||||
<field name="res_model">res.partner.phone</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="context">{'partner_phone_main_view': True}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="res_partner_phone_menu" action="res_partner_phone_action"
|
||||
parent="contacts.menu_contacts" sequence="10"/>
|
||||
|
||||
<record id="contacts.res_partner_menu_config" model="ir.ui.menu">
|
||||
<field name="sequence">20</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -4,10 +4,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 14.0\n"
|
||||
"Project-Id-Version: Odoo Server 16.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"
|
||||
"POT-Creation-Date: 2024-03-27 10:15+0000\n"
|
||||
"PO-Revision-Date: 2024-03-27 10:15+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -15,6 +15,20 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "%s with a capital of"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "APE:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_partner_bank
|
||||
msgid "Bank Accounts"
|
||||
@@ -25,11 +39,23 @@ msgstr ""
|
||||
msgid "Bank Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Capital:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.ir_property_view_search
|
||||
msgid "Company"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_partner
|
||||
msgid "Contact"
|
||||
@@ -41,6 +67,7 @@ msgid "Currency"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
@@ -48,61 +75,42 @@ 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
|
||||
#. odoo-python
|
||||
#: 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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "EORI:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.ir_property_view_search
|
||||
msgid "Field"
|
||||
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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
msgid "Mobile:"
|
||||
@@ -120,7 +128,7 @@ msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.view_res_partner_filter
|
||||
msgid "Name or Email or Reference"
|
||||
msgid "Name or Email or VAT or Reference"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
@@ -134,11 +142,6 @@ msgstr ""
|
||||
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
|
||||
@@ -146,11 +149,21 @@ msgid "Reference"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||
msgid "Search Countries"
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "SIREN:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "SIRET:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
@@ -158,11 +171,7 @@ 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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
@@ -171,22 +180,25 @@ msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_users
|
||||
msgid "Users"
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
msgid "VAT Number:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "VAT:"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
|
||||
@@ -4,17 +4,31 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 14.0\n"
|
||||
"Project-Id-Version: Odoo Server 16.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"
|
||||
"POT-Creation-Date: 2024-03-27 10:15+0000\n"
|
||||
"PO-Revision-Date: 2024-03-27 10:15+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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "%s with a capital of"
|
||||
msgstr "%s au capital de"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "APE:"
|
||||
msgstr "APE :"
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_partner_bank
|
||||
msgid "Bank Accounts"
|
||||
@@ -25,15 +39,27 @@ msgstr "Comptes bancaires"
|
||||
msgid "Bank Name"
|
||||
msgstr "Nom de la banque"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "Capital:"
|
||||
msgstr "Capital :"
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_company
|
||||
msgid "Companies"
|
||||
msgstr "Sociétés"
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.ir_property_view_search
|
||||
msgid "Company"
|
||||
msgstr "Société"
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_partner
|
||||
msgid "Contact"
|
||||
msgstr "Contact"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||
@@ -41,67 +67,50 @@ msgid "Currency"
|
||||
msgstr "Devise"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: 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 :"
|
||||
|
||||
#. 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
|
||||
#. odoo-python
|
||||
#: 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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "EORI:"
|
||||
msgstr "EORI :"
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.ir_property_view_search
|
||||
msgid "Field"
|
||||
msgstr "Champ"
|
||||
|
||||
#. 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"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_ir_mail_server
|
||||
msgid "Mail Server"
|
||||
msgstr "Serveur mail"
|
||||
msgstr "Serveur de messagerie"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
msgid "Mobile:"
|
||||
@@ -110,7 +119,7 @@ msgstr "Portable :"
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_ir_model
|
||||
msgid "Models"
|
||||
msgstr "Modèles"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#: model_terms:ir.ui.view,arch_db:base_usability.res_country_search
|
||||
@@ -119,8 +128,8 @@ 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"
|
||||
msgid "Name or Email or VAT or Reference"
|
||||
msgstr "Nom ou e-mail ou n°TVA ou référence"
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model.fields,field_description:base_usability.field_res_partner__name_title
|
||||
@@ -133,11 +142,6 @@ msgstr "Nom avec titre"
|
||||
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
|
||||
@@ -145,22 +149,29 @@ 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 ""
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "SIREN:"
|
||||
msgstr "SIREN :"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "SIRET:"
|
||||
msgstr "SIRET :"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: 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 :"
|
||||
|
||||
#. 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
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
@@ -169,22 +180,25 @@ msgstr "Tél :"
|
||||
|
||||
#. module: base_usability
|
||||
#: model:ir.model,name:base_usability.model_res_users
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
msgid "VAT Number:"
|
||||
msgstr "N° TVA :"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#, python-format
|
||||
msgid "VAT:"
|
||||
msgstr "TVA :"
|
||||
|
||||
#. module: base_usability
|
||||
#. odoo-python
|
||||
#: code:addons/base_usability/models/res_company.py:0
|
||||
#: code:addons/base_usability/models/res_partner.py:0
|
||||
#, python-format
|
||||
|
||||
@@ -4,3 +4,4 @@ from . import res_partner_bank
|
||||
from . import res_company
|
||||
from . import ir_mail_server
|
||||
from . import ir_model
|
||||
from . import ir_model_fields
|
||||
|
||||
16
base_usability/models/ir_model_fields.py
Normal file
16
base_usability/models/ir_model_fields.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2024 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
|
||||
|
||||
|
||||
class IrModelFields(models.Model):
|
||||
_inherit = 'ir.model.fields'
|
||||
|
||||
@api.depends('name', 'field_description')
|
||||
def name_get(self):
|
||||
res = []
|
||||
for rec in self:
|
||||
res.append((rec.id, '%s (%s)' % (rec.field_description, rec.name)))
|
||||
return res
|
||||
@@ -3,6 +3,7 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, models, _
|
||||
from odoo.tools.misc import format_amount
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
@@ -39,32 +40,77 @@ class ResCompany(models.Model):
|
||||
'value': self.phone,
|
||||
# http://www.fileformat.info/info/unicode/char/1f4de/index.htm
|
||||
'icon': '\U0001F4DE',
|
||||
'label': _('Tel:')},
|
||||
'label': _('Tel:'),
|
||||
},
|
||||
'email': {
|
||||
'value': self.email,
|
||||
# http://www.fileformat.info/info/unicode/char/2709/index.htm
|
||||
'icon': '\u2709',
|
||||
'label': _('E-mail:')},
|
||||
'label': _('E-mail:'),
|
||||
},
|
||||
'website': {
|
||||
'value': self.website,
|
||||
'icon': '\U0001f310',
|
||||
'label': _('Website:')},
|
||||
'label': _('Website:'),
|
||||
},
|
||||
'vat': {
|
||||
'value': self.vat,
|
||||
'label': _('VAT:')},
|
||||
'label': _('VAT:'),
|
||||
},
|
||||
'ape': {
|
||||
'value': hasattr(self, 'ape') and self.ape or False,
|
||||
'label': _('APE:'),
|
||||
},
|
||||
'siret': {
|
||||
'value': hasattr(self, 'siret') and self.siret or False,
|
||||
'label': _('SIRET:'),
|
||||
},
|
||||
'siren': {
|
||||
'value': hasattr(self, 'siren') and self.siren or False,
|
||||
'label': _('SIREN:'),
|
||||
},
|
||||
'eori': {
|
||||
'value': self._get_eori(),
|
||||
'label': _('EORI:'),
|
||||
},
|
||||
'capital': {
|
||||
# 'capital_amount' added by base_company_extension
|
||||
'value': hasattr(self, 'capital_amount') and self.capital_amount and format_amount(self.env, self.capital_amount, self.currency_id) or False,
|
||||
'label': _('Capital:'),
|
||||
},
|
||||
}
|
||||
# 'legal_type' added by base_company_extension
|
||||
if hasattr(self, 'legal_type') and self.legal_type:
|
||||
options['capital']['label'] = _('%s with a capital of') % self.legal_type
|
||||
return options
|
||||
|
||||
def _get_eori(self):
|
||||
eori = False
|
||||
if self.partner_id.country_id.code == 'FR' and hasattr(self, 'siret') and self.siret:
|
||||
# Currently migrating from EORI-SIRET to EORI-SIREN :
|
||||
# https://www.pwcavocats.com/fr/ealertes/ealertes-france/2023/avril/reforme-numero-eori-siren-siret.html
|
||||
# But, for the moment, we continue to use EORI-SIRET
|
||||
eori = f'FR{self.siret}'
|
||||
return eori
|
||||
|
||||
def _report_company_legal_name(self):
|
||||
'''Method inherited in the module base_company_extension'''
|
||||
self.ensure_one()
|
||||
return self.name
|
||||
|
||||
def _report_header_line_details(self):
|
||||
"""This method is designed to be inherited"""
|
||||
# I decided not to put email in the default header because only a few very small
|
||||
# companies have a generic company email address
|
||||
line_details = [['phone', 'website', 'capital'], ['vat', 'siret', 'eori', 'ape']]
|
||||
return line_details
|
||||
|
||||
# for reports
|
||||
def _display_report_header(
|
||||
self, line_details=[['phone', 'website'], ['vat']],
|
||||
icon=True, line_separator=' - '):
|
||||
self, line_details=None, icon=True, line_separator=' - '):
|
||||
self.ensure_one()
|
||||
if line_details is None:
|
||||
line_details = self._report_header_line_details()
|
||||
res = ''
|
||||
address = self.partner_id._display_address(without_company=True)
|
||||
address = address.replace('\n', ' - ')
|
||||
|
||||
24
base_usability/web-advanced_search_startswith.diff
Normal file
24
base_usability/web-advanced_search_startswith.diff
Normal file
@@ -0,0 +1,24 @@
|
||||
diff --git a/addons/web/static/src/search/filter_menu/custom_filter_item.js b/addons/web/static/src/search/filter_menu/custom_filter_item.js
|
||||
index f67f5fb40af..22525b7cbfd 100644
|
||||
--- a/addons/web/static/src/search/filter_menu/custom_filter_item.js
|
||||
+++ b/addons/web/static/src/search/filter_menu/custom_filter_item.js
|
||||
@@ -46,6 +46,8 @@ const FIELD_OPERATORS = {
|
||||
char: [
|
||||
{ symbol: "ilike", description: _lt("contains") },
|
||||
{ symbol: "not ilike", description: _lt("doesn't contain") },
|
||||
+ { symbol: "startswith", description: _lt("starts with") },
|
||||
+ { symbol: "endswith", description: _lt("ends with") },
|
||||
{ symbol: "=", description: _lt("is equal to") },
|
||||
{ symbol: "!=", description: _lt("is not equal to") },
|
||||
{ symbol: "!=", description: _lt("is set"), value: false },
|
||||
@@ -257,6 +259,10 @@ export class CustomFilterItem extends Component {
|
||||
[field.name, ">=", domainValue[0]],
|
||||
[field.name, "<=", domainValue[1]]
|
||||
);
|
||||
+ } else if (operator.symbol === "startswith") {
|
||||
+ domainArray.push([field.name, '=ilike', domainValue[0] + '%']);
|
||||
+ } else if (operator.symbol === "endswith") {
|
||||
+ domainArray.push([field.name, '=ilike', '%' + domainValue[0]]);
|
||||
} else {
|
||||
domainArray.push([field.name, operator.symbol, domainValue[0]]);
|
||||
}
|
||||
21
base_usability/web-export-add_technical_field_name.diff
Normal file
21
base_usability/web-export-add_technical_field_name.diff
Normal file
@@ -0,0 +1,21 @@
|
||||
diff --git a/addons/web/controllers/export.py b/addons/web/controllers/export.py
|
||||
index 5a1bbcb6b02..04c70131660 100644
|
||||
--- a/addons/web/controllers/export.py
|
||||
+++ b/addons/web/controllers/export.py
|
||||
@@ -308,7 +308,6 @@ class Export(http.Controller):
|
||||
def get_fields(self, model, prefix='', parent_name='',
|
||||
import_compat=True, parent_field_type=None,
|
||||
parent_field=None, exclude=None):
|
||||
-
|
||||
fields = self.fields_get(model)
|
||||
if import_compat:
|
||||
if parent_field_type in ['many2one', 'many2many']:
|
||||
@@ -347,7 +346,7 @@ class Export(http.Controller):
|
||||
# Add name field when expand m2o and m2m fields in import-compatible mode
|
||||
val = prefix
|
||||
name = parent_name + (parent_name and '/' or '') + field['string']
|
||||
- record = {'id': ident, 'string': name,
|
||||
+ record = {'id': ident, 'string': name + f' ({field_name})',
|
||||
'value': val, 'children': False,
|
||||
'field_type': field.get('type'),
|
||||
'required': field.get('required'),
|
||||
@@ -3,19 +3,20 @@
|
||||
<odoo>
|
||||
|
||||
<menuitem id="conf_tech" parent="base.menu_administration" name="🧰" groups="base.group_erp_manager" sequence="1">
|
||||
<menuitem id="model" name="Model" action="base.action_model_model" sequence="10"/>
|
||||
<menuitem id="view" name="View" action="base.action_ui_view" sequence="20" />
|
||||
<menuitem id="rec_rule" name="Record Rule" action="base.action_rule" sequence="30" />
|
||||
<menuitem id="menu" name="Menu" action="base.grant_menu_access" sequence="100" />
|
||||
<menuitem id="seq" name="Sequence" action="base.ir_sequence_form" sequence="100" />
|
||||
<menuitem id="model_data" name="Model Data" action="base.action_model_data" sequence="100" />
|
||||
<menuitem id="param" name="Param" action="base.ir_config_list_action" sequence="100" />
|
||||
<menuitem id="cron" name="Cron" action="base.ir_cron_act" sequence="100" />
|
||||
<menuitem id="window" name="Act Window" action="base.ir_action_window" sequence="100" />
|
||||
<menuitem id="server" name="Act Server" action="base.action_server_action" sequence="100" />
|
||||
<menuitem id="report" name="Report" action="base.ir_action_report" sequence="100" />
|
||||
<menuitem id="mail_tmpl" name="Mail Tmpl" action="mail.action_email_template_tree_all" sequence="100" />
|
||||
<menuitem id="property" name="Property" action="base.ir_property_form" sequence="100" />
|
||||
<menuitem id="model" name="Models" action="base.action_model_model" sequence="10"/>
|
||||
<menuitem id="fields" name="Fields" action="base.action_model_fields" sequence="10"/>
|
||||
<menuitem id="rec_rule" name="📑 Record Rules" action="base.action_rule" sequence="20" />
|
||||
<menuitem id="view" name="Views" action="base.action_ui_view" sequence="30" />
|
||||
<menuitem id="menu" name="📃 Menus" action="base.grant_menu_access" sequence="40" />
|
||||
<menuitem id="model_data" name="Model Data" action="base.action_model_data" sequence="50" />
|
||||
<menuitem id="cron" name="📅 Crons" action="base.ir_cron_act" sequence="70" />
|
||||
<menuitem id="window" name="Act Windows" action="base.ir_action_window" sequence="80" />
|
||||
<menuitem id="server" name="⚙ Act Server" action="base.action_server_action" sequence="90" />
|
||||
<menuitem id="report" name="📄 Reports" action="base.ir_action_report" sequence="100" />
|
||||
<menuitem id="param" name="Params" action="base.ir_config_list_action" sequence="110" />
|
||||
<menuitem id="seq" name="🔢 Sequences" action="base.ir_sequence_form" sequence="115" />
|
||||
<menuitem id="property" name="Properties" action="base.ir_property_form" sequence="120" />
|
||||
<menuitem id="mail_tmpl" name="📧 Mail Tmpl" action="mail.action_email_template_tree_all" sequence="140" />
|
||||
</menuitem>
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright 2019-2021 Akretion France (http://www.akretion.com)
|
||||
# Copyright 2019-2023 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': 'Link Tracker Usability',
|
||||
'version': '14.0.1.0.0',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Marketing',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Improve usability for link tracker',
|
||||
@@ -18,10 +18,10 @@ This module has been written by Alexis de Lattre from Akretion
|
||||
<alexis.delattre@akretion.com>.
|
||||
""",
|
||||
'author': 'Akretion',
|
||||
'website': 'http://www.akretion.com',
|
||||
'website': 'https://github.com/akretion/odoo-usability',
|
||||
'depends': ['link_tracker'],
|
||||
'data': [
|
||||
'views/link_tracker_click.xml',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2019-2021 Akretion France (http://www.akretion.com/)
|
||||
Copyright 2019-2023 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).
|
||||
-->
|
||||
|
||||
0
mrp_subcontracting_usability/__init__.py
Normal file
0
mrp_subcontracting_usability/__init__.py
Normal file
18
mrp_subcontracting_usability/__manifest__.py
Normal file
18
mrp_subcontracting_usability/__manifest__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2023 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': 'MRP Subcontracting Usability',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Manufacturing',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Usability improvements on mrp_subcontracting',
|
||||
'author': 'Akretion',
|
||||
'website': 'https://github.com/akretion/odoo-usability',
|
||||
'depends': ['mrp_subcontracting'],
|
||||
'data': [
|
||||
'views/mrp_bom.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
22
mrp_subcontracting_usability/views/mrp_bom.xml
Normal file
22
mrp_subcontracting_usability/views/mrp_bom.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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_mrp_bom_filter" model="ir.ui.view">
|
||||
<field name="model">mrp.bom</field>
|
||||
<field name="inherit_id" ref="mrp.view_mrp_bom_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="phantom" position="after">
|
||||
<filter name="subcontract" domain="[('type', '=', 'subcontract')]" string="Subcontracting"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
@@ -3,12 +3,26 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
|
||||
from odoo import api, models
|
||||
from odoo import fields, models, Command
|
||||
|
||||
|
||||
class MrpProduction(models.Model):
|
||||
_inherit = 'mrp.production'
|
||||
|
||||
location_dest_id = fields.Many2one(tracking=True)
|
||||
|
||||
# Target: allow to modify location_dest_id until the button 'Mark as done' is pushed
|
||||
# I didn't find a better implementation... feel free to improve if you find one
|
||||
def _compute_move_finished_ids(self):
|
||||
for prod in self:
|
||||
if prod.state not in ('draft', 'done') and prod.location_dest_id:
|
||||
vals = {'location_dest_id': prod.location_dest_id.id}
|
||||
prod.move_finished_ids = [
|
||||
Command.update(m.id, vals) for m in prod.move_finished_ids
|
||||
if m.state != 'done'
|
||||
]
|
||||
super()._compute_move_finished_ids()
|
||||
|
||||
# Method used by the report, inherited in this module
|
||||
# @api.model
|
||||
# def get_stock_move_sold_out_report(self, move):
|
||||
|
||||
@@ -13,11 +13,17 @@
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!--
|
||||
<label for="product_qty" position="before">
|
||||
<field name="location_src_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', '!=', 'draft')]}" position="move"/>
|
||||
<field name="location_dest_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', '!=', 'draft')]}" position="move"/>
|
||||
</label> -->
|
||||
<field name="user_id" position="before">
|
||||
<!-- I can't use position="move" because it would match another field in an embedded tree view -->
|
||||
<field name="location_src_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="location_dest_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}"/>
|
||||
</field>
|
||||
<!-- It is important to remove the original field location_dest_id
|
||||
and not just set it as invisible because it cancels the changes -->
|
||||
<xpath expr="//page[@name='miscellaneous']/group/group/field[@name='location_dest_id']" position="replace"/>
|
||||
<xpath expr="//page[@name='miscellaneous']/group/group/field[@name='location_src_id']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='miscellaneous']/group/group/field[@name='date_deadline']" position="after">
|
||||
<field name="date_start"/>
|
||||
<field name="date_finished"/>
|
||||
|
||||
@@ -31,6 +31,8 @@ Akretion:
|
||||
"views/report_pos_order.xml",
|
||||
"views/pos_category.xml",
|
||||
"views/pos_session.xml",
|
||||
"views/pos_payment_method.xml",
|
||||
"views/pos_order.xml",
|
||||
"views/product.xml",
|
||||
],
|
||||
"installable": True,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from . import product
|
||||
from . import pos_category
|
||||
from . import pos_payment_method
|
||||
from . import pos_order
|
||||
from . import pos_session
|
||||
|
||||
23
pos_usability/models/pos_order.py
Normal file
23
pos_usability/models/pos_order.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2024 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 PosOrder(models.Model):
|
||||
_inherit = 'pos.order'
|
||||
|
||||
# field displayed in pos.order list view
|
||||
payments_char = fields.Char(
|
||||
string="Payment Methods", compute="_compute_payments_char", store=True)
|
||||
|
||||
@api.depends('payment_ids')
|
||||
def _compute_payments_char(self):
|
||||
for order in self:
|
||||
payments = set()
|
||||
for pay in order.payment_ids:
|
||||
if pay.payment_method_id.name:
|
||||
# unfortunately, 'name' of pos.payment.method is translate=True
|
||||
payments.add(pay.payment_method_id.name)
|
||||
order.payments_char = ', '.join(payments)
|
||||
@@ -8,7 +8,9 @@ from odoo import fields, models
|
||||
class PosPaymentMethod(models.Model):
|
||||
_inherit = 'pos.payment.method'
|
||||
_check_company_auto = True
|
||||
_order = 'sequence, id'
|
||||
|
||||
outstanding_account_id = fields.Many2one(check_company=True)
|
||||
receivable_account_id = fields.Many2one(check_company=True)
|
||||
journal_id = fields.Many2one(check_company=True)
|
||||
sequence = fields.Integer(default=10)
|
||||
|
||||
14
pos_usability/models/pos_session.py
Normal file
14
pos_usability/models/pos_session.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright 2023 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
|
||||
|
||||
|
||||
class PosSession(models.Model):
|
||||
_inherit = 'pos.session'
|
||||
|
||||
def _loader_params_pos_payment_method(self):
|
||||
res = super()._loader_params_pos_payment_method()
|
||||
res['search_params']['order'] = "sequence, id"
|
||||
return res
|
||||
15
pos_usability/pos-always_open_cashbox.diff
Normal file
15
pos_usability/pos-always_open_cashbox.diff
Normal file
@@ -0,0 +1,15 @@
|
||||
diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js
|
||||
index e8ddb4d173c..684be2484a2 100644
|
||||
--- a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js
|
||||
+++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js
|
||||
@@ -190,7 +190,9 @@ odoo.define('point_of_sale.PaymentScreen', function (require) {
|
||||
}
|
||||
}
|
||||
async _finalizeValidation() {
|
||||
- if ((this.currentOrder.is_paid_with_cash() || this.currentOrder.get_change()) && this.env.pos.config.iface_cashdrawer && this.env.proxy && this.env.proxy.printer) {
|
||||
+ //if ((this.currentOrder.is_paid_with_cash() || this.currentOrder.get_change()) && this.env.pos.config.iface_cashdrawer && this.env.proxy && this.env.proxy.printer) {
|
||||
+ // Always open cashbox (by default, Odoo only opens cashbox for cash payments)
|
||||
+ if (this.env.pos.config.iface_cashdrawer && this.env.proxy && this.env.proxy.printer) {
|
||||
this.env.proxy.printer.open_cashbox();
|
||||
}
|
||||
|
||||
32
pos_usability/views/pos_order.xml
Normal file
32
pos_usability/views/pos_order.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
Copyright 2023 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_pos_pos_form" model="ir.ui.view">
|
||||
<field name="model">pos.order</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='lines']/form//field[@name='full_product_name']" position="before">
|
||||
<field name="product_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_pos_order_tree" model="ir.ui.view">
|
||||
<field name="model">pos.order</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_order_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="amount_total" position="after">
|
||||
<field name="payments_char" optional="show"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
32
pos_usability/views/pos_payment_method.xml
Normal file
32
pos_usability/views/pos_payment_method.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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="pos_payment_method_view_tree" model="ir.ui.view">
|
||||
<field name="model">pos.payment.method</field>
|
||||
<field name="inherit_id" ref="point_of_sale.pos_payment_method_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="before">
|
||||
<field name="sequence" widget="handle"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pos_payment_method_view_form" model="ir.ui.view">
|
||||
<field name="model">pos.payment.method</field>
|
||||
<field name="inherit_id" ref="point_of_sale.pos_payment_method_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<!-- company_id without groups="base.group_multi_company" is missing -->
|
||||
<field name="company_id" invisible="1"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright 2014-2020 Akretion (http://www.akretion.com/)
|
||||
# Copyright 2014-2023 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': 'Product Manager Group',
|
||||
'version': '14.0.1.0.0',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Hidden',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Add a group Product Manager',
|
||||
@@ -23,5 +23,5 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
||||
'security/product_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Copyright 2014-2020 Akretion (http://www.akretion.com)
|
||||
# Copyright 2014-2023 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': 'Product Manager Group Stock',
|
||||
'version': '14.0.1.0.0',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Hidden',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Extend the group Product Manager to Stock',
|
||||
@@ -22,6 +22,6 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
@@ -39,13 +39,14 @@ This module has been written by Alexis de Lattre from Akretion
|
||||
'depends': [
|
||||
'point_of_sale',
|
||||
'barcodes',
|
||||
'base_report_to_printer',
|
||||
],
|
||||
'external_dependencies': {'python': ['python-barcode>=0.14.0']},
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/product_print_zpl_barcode_view.xml',
|
||||
'wizard/res_config_settings_view.xml',
|
||||
'views/product.xml',
|
||||
'views/stock_picking.xml',
|
||||
'data/barcode_sequence.xml',
|
||||
],
|
||||
'installable': True,
|
||||
|
||||
412
product_print_zpl_barcode/i18n/fr.po
Normal file
412
product_print_zpl_barcode/i18n/fr.po
Normal file
@@ -0,0 +1,412 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * product_print_zpl_barcode
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-15 13:39+0000\n"
|
||||
"PO-Revision-Date: 2023-07-15 13:39+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: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__copies
|
||||
msgid "# Labels"
|
||||
msgstr "Nb étiquettes"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__label_size__38x25
|
||||
msgid "38x25 mm"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__barcode
|
||||
msgid "Barcode"
|
||||
msgstr "Code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__nomenclature_id
|
||||
msgid "Barcode Nomenclature"
|
||||
msgstr "Nomenclature des codes-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__rule_id
|
||||
msgid "Barcode Rule"
|
||||
msgstr "Règle de codes-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__barcode_type
|
||||
msgid "Barcode Type"
|
||||
msgstr "Type de code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__company_id
|
||||
msgid "Company"
|
||||
msgstr "Société"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__create_uid
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Créé par"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__create_date
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Créé le"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__currency_id
|
||||
msgid "Currency"
|
||||
msgstr "Devise"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_print_zpl_barcode_line__uom_id
|
||||
msgid "Default unit of measure used for all stock operations."
|
||||
msgstr ""
|
||||
"Unité de mesure par défaut utilisée pour toutes les opérations de stock."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__display_name
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nom affiché"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_product__must_print_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_template__must_print_barcode
|
||||
msgid ""
|
||||
"Enable that option for products for which you must print a barcode upon "
|
||||
"reception in stock."
|
||||
msgstr ""
|
||||
"Activez cette option sur les articles pour lesquels vous devez imprimer un "
|
||||
"code-barres dès leur réception en stock."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.actions.act_window,name:product_print_zpl_barcode.product_print_zpl_barcode_action
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_normal_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_only_form_view
|
||||
msgid "Generate Barcode"
|
||||
msgstr "Générer un code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Generate Labels"
|
||||
msgstr "Générer les étiquettes"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_print_zpl_barcode
|
||||
msgid "Generate and print product barcodes in ZPL"
|
||||
msgstr "Générer et imprimer des codes-barres d'articles en ZPL"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__id
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__label_size
|
||||
msgid "Label Size"
|
||||
msgstr "Taille de l'étiquette"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode____last_update
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Dernière modification le"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__write_uid
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Dernière mise à jour par"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__write_date
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Dernière mise à jour le"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Line '%s': barcode '%s' has %d digits. This wizard only supports EAN8 and "
|
||||
"EAN13 for the moment."
|
||||
msgstr ""
|
||||
"Ligne '%s' : le code-barres '%s' comporte %d chiffres. Cet assistant ne "
|
||||
"prend en charge que les EAN8 et EAN13 pour le moment."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Line '%s': barcode type '%s' is not supported for the moment"
|
||||
msgstr ""
|
||||
"Ligne '%s' : le type de code-barres '%s' n'est pas supporté pour le moment"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Line '%s': the barcode '%s' is not a valid EAN barcode (wrong checksum)."
|
||||
msgstr ""
|
||||
"Ligne '%s' : le code-barres '%s' n'est pas un code-barres EAN valide "
|
||||
"(mauvaise somme de contrôle)."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_print_zpl_barcode_line
|
||||
msgid "Line of the print ZPL barcode wizard"
|
||||
msgstr "Ligne de l'assistant d'impression du code-barres ZPL"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__line_ids
|
||||
msgid "Lines"
|
||||
msgstr "Lignes"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Missing Products"
|
||||
msgstr "Produits manquants"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__must_print_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_template__must_print_barcode
|
||||
msgid "Must Print Barcode"
|
||||
msgstr "Code-barres à imprimer"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "On line '%s', the number of copies must be strictly positive."
|
||||
msgstr ""
|
||||
"Sur la ligne '%s', le nombre d'étiquettes doit être strictement positif."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__barcode_image_png
|
||||
msgid "PNG Barcode Image"
|
||||
msgstr "Image PNG du code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__parent_id
|
||||
msgid "Parent"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__price
|
||||
msgid "Price"
|
||||
msgstr "Prix"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__price_uom
|
||||
msgid "Price/UoM"
|
||||
msgstr "Prix/unité"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__pricelist_id
|
||||
msgid "Pricelist"
|
||||
msgstr "Liste de prix"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Print"
|
||||
msgstr "Imprimer"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_normal_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_only_form_view
|
||||
msgid "Print Barcode"
|
||||
msgstr "Imprimer le code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_product_tree_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_tree_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.view_picking_form
|
||||
msgid "Print Barcodes"
|
||||
msgstr "Imprimer les code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_template
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__product_id
|
||||
msgid "Product"
|
||||
msgstr "Article"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__product_name
|
||||
msgid "Product Label"
|
||||
msgstr "Étiquette de l'article"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_product
|
||||
msgid "Product Variant"
|
||||
msgstr "Variante d'article"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__quantity
|
||||
msgid "Qty"
|
||||
msgstr "Qté"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__barcode_image_svg
|
||||
msgid "SVG Barcode Image"
|
||||
msgstr "Image SVG du code-barres"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_stock_picking__show_print_zpl_barcode
|
||||
msgid "Show Print Zpl Barcode"
|
||||
msgstr "Afficher le bouton imprimer le code-barres ZPL"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__state
|
||||
msgid "State"
|
||||
msgstr "État"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__state__step1
|
||||
msgid "Step1"
|
||||
msgstr "Étape 1"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__state__step2
|
||||
msgid "Step2"
|
||||
msgstr "Étape 2"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The barcode of the product (%s) has %d characters, which is smaller than the"
|
||||
" %d characters of the prefix of the barcode pattern (%s)."
|
||||
msgstr ""
|
||||
"Le code-barres de l'article (%s) comporte %d caractères, ce qui est plus "
|
||||
"petit que les %d caractères du préfixe du modèle de code-barres (%s)."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The barcode rule '%s' has a pattern '%s' which doesn't contain a integer and"
|
||||
" decimal part between '{}'."
|
||||
msgstr ""
|
||||
"La règle de code-barres '%s' a un motif '%s' qui ne contient pas de partie "
|
||||
"entière et décimale entre '{}'."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid "The product '%s' already has a barcode."
|
||||
msgstr "L'article '%s' a déjà un code-barres."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "The quantity (%s) must be positive !"
|
||||
msgstr "La quantité (%s) doit être positive !"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The sequence 'private.product.barcode' is not properly configured. The "
|
||||
"generated sequence should have 7 digits (for EAN-8) or 12 digits (for "
|
||||
"EAN-13). It currently has %d digits."
|
||||
msgstr ""
|
||||
"La séquence 'private.product.barcode' n'est pas correctement configurée. La "
|
||||
"séquence générée devrait avoir 7 chiffres (pour EAN-8) ou 12 chiffres (pour "
|
||||
"EAN-13). Elle comporte actuellement %d chiffres."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The value to encode in the barcode (%s) is superior to the maximum value "
|
||||
"allowed by the barcode pattern (%s)."
|
||||
msgstr ""
|
||||
"La valeur à encoder dans le code-barres (%s) est supérieure à la valeur "
|
||||
"maximale autorisée par le motif du code-barres (%s)."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "There are no pricelist in company '%s'."
|
||||
msgstr "Il n'y a pas de liste de prix dans la société '%s'."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_stock_picking
|
||||
msgid "Transfer"
|
||||
msgstr "Transfert"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__uom_id
|
||||
msgid "UoM"
|
||||
msgstr "Unité"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Wrong active_model in context (%s)."
|
||||
msgstr "Mauvais active_model dans le contexte (%s)."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot call the method generate_barcode_from_product_template on product"
|
||||
" '%s' because it has %d variants and not just one."
|
||||
msgstr ""
|
||||
"Vous ne pouvez pas appeler la méthode generate_barcode_from_product_template"
|
||||
" sur l'article '%s' parce qu'il a %d variantes et non une seule."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "You must select a ZPL Printer."
|
||||
msgstr "Vous devez sélectionner une imprimante ZPL."
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_file
|
||||
msgid "ZPL File"
|
||||
msgstr "Fichier ZPL"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_filename
|
||||
msgid "ZPL Filename"
|
||||
msgstr "Nom du fichier ZPL"
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_printer_id
|
||||
msgid "ZPL Printer"
|
||||
msgstr "Imprimante ZPL"
|
||||
392
product_print_zpl_barcode/i18n/product_print_zpl_barcode.pot
Normal file
392
product_print_zpl_barcode/i18n/product_print_zpl_barcode.pot
Normal file
@@ -0,0 +1,392 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * product_print_zpl_barcode
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-15 13:39+0000\n"
|
||||
"PO-Revision-Date: 2023-07-15 13:39+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: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__copies
|
||||
msgid "# Labels"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__label_size__38x25
|
||||
msgid "38x25 mm"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__barcode
|
||||
msgid "Barcode"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__nomenclature_id
|
||||
msgid "Barcode Nomenclature"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__rule_id
|
||||
msgid "Barcode Rule"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__barcode_type
|
||||
msgid "Barcode Type"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__company_id
|
||||
msgid "Company"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__create_uid
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__create_date
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__currency_id
|
||||
msgid "Currency"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_print_zpl_barcode_line__uom_id
|
||||
msgid "Default unit of measure used for all stock operations."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__display_name
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_product__must_print_barcode
|
||||
#: model:ir.model.fields,help:product_print_zpl_barcode.field_product_template__must_print_barcode
|
||||
msgid ""
|
||||
"Enable that option for products for which you must print a barcode upon "
|
||||
"reception in stock."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.actions.act_window,name:product_print_zpl_barcode.product_print_zpl_barcode_action
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_normal_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_only_form_view
|
||||
msgid "Generate Barcode"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Generate Labels"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_print_zpl_barcode
|
||||
msgid "Generate and print product barcodes in ZPL"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__id
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__label_size
|
||||
msgid "Label Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode____last_update
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__write_uid
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__write_date
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Line '%s': barcode '%s' has %d digits. This wizard only supports EAN8 and "
|
||||
"EAN13 for the moment."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Line '%s': barcode type '%s' is not supported for the moment"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Line '%s': the barcode '%s' is not a valid EAN barcode (wrong checksum)."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_print_zpl_barcode_line
|
||||
msgid "Line of the print ZPL barcode wizard"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__line_ids
|
||||
msgid "Lines"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Missing Products"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__must_print_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_template__must_print_barcode
|
||||
msgid "Must Print Barcode"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "On line '%s', the number of copies must be strictly positive."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__barcode_image_png
|
||||
msgid "PNG Barcode Image"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__parent_id
|
||||
msgid "Parent"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__price
|
||||
msgid "Price"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__price_uom
|
||||
msgid "Price/UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__pricelist_id
|
||||
msgid "Pricelist"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_print_zpl_barcode_form
|
||||
msgid "Print"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_normal_form_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_only_form_view
|
||||
msgid "Print Barcode"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_product_tree_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.product_template_tree_view
|
||||
#: model_terms:ir.ui.view,arch_db:product_print_zpl_barcode.view_picking_form
|
||||
msgid "Print Barcodes"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_template
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__product_id
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__product_name
|
||||
msgid "Product Label"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_product_product
|
||||
msgid "Product Variant"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__quantity
|
||||
msgid "Qty"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_product__barcode_image_svg
|
||||
msgid "SVG Barcode Image"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_stock_picking__show_print_zpl_barcode
|
||||
msgid "Show Print Zpl Barcode"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__state
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__state__step1
|
||||
msgid "Step1"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields.selection,name:product_print_zpl_barcode.selection__product_print_zpl_barcode__state__step2
|
||||
msgid "Step2"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The barcode of the product (%s) has %d characters, which is smaller than the"
|
||||
" %d characters of the prefix of the barcode pattern (%s)."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The barcode rule '%s' has a pattern '%s' which doesn't contain a integer and"
|
||||
" decimal part between '{}'."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid "The product '%s' already has a barcode."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "The quantity (%s) must be positive !"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The sequence 'private.product.barcode' is not properly configured. The "
|
||||
"generated sequence should have 7 digits (for EAN-8) or 12 digits (for "
|
||||
"EAN-13). It currently has %d digits."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The value to encode in the barcode (%s) is superior to the maximum value "
|
||||
"allowed by the barcode pattern (%s)."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "There are no pricelist in company '%s'."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model,name:product_print_zpl_barcode.model_stock_picking
|
||||
msgid "Transfer"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode_line__uom_id
|
||||
msgid "UoM"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "Wrong active_model in context (%s)."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/models/product.py:0
|
||||
#, python-format
|
||||
msgid ""
|
||||
"You cannot call the method generate_barcode_from_product_template on product"
|
||||
" '%s' because it has %d variants and not just one."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#. odoo-python
|
||||
#: code:addons/product_print_zpl_barcode/wizard/product_print_zpl_barcode.py:0
|
||||
#, python-format
|
||||
msgid "You must select a ZPL Printer."
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_file
|
||||
msgid "ZPL File"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_filename
|
||||
msgid "ZPL Filename"
|
||||
msgstr ""
|
||||
|
||||
#. module: product_print_zpl_barcode
|
||||
#: model:ir.model.fields,field_description:product_print_zpl_barcode.field_product_print_zpl_barcode__zpl_printer_id
|
||||
msgid "ZPL Printer"
|
||||
msgstr ""
|
||||
@@ -1 +1,2 @@
|
||||
from . import product
|
||||
from . import stock_picking
|
||||
|
||||
@@ -29,22 +29,6 @@ class ProductTemplate(models.Model):
|
||||
% (self.display_name, self.product_variant_count))
|
||||
return self.product_variant_ids[0].generate_barcode_from_product_product()
|
||||
|
||||
def print_zpl_barcode_from_product_template(self):
|
||||
self.ensure_one()
|
||||
if self.product_variant_count != 1:
|
||||
raise UserError(_(
|
||||
"You cannot call the method "
|
||||
"print_zpl_barcode_from_product_template on product '%s' "
|
||||
"because it has %d variants and not just one.")
|
||||
% (self.display_name, self.product_variant_count))
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
||||
action['context'] = {
|
||||
'active_id': self.product_variant_ids[0].id,
|
||||
'active_model': 'product.product',
|
||||
}
|
||||
return action
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
25
product_print_zpl_barcode/models/stock_picking.py
Normal file
25
product_print_zpl_barcode/models/stock_picking.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2023 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
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
show_print_zpl_barcode = fields.Boolean(compute='_compute_show_print_zpl_barcode')
|
||||
|
||||
@api.depends('state')
|
||||
def _compute_show_print_zpl_barcode(self):
|
||||
prec = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
for picking in self:
|
||||
show = False
|
||||
if picking.state == 'done' and picking.picking_type_code != 'outgoing':
|
||||
for line in picking.move_line_ids:
|
||||
if (
|
||||
line.product_id.must_print_barcode and
|
||||
float_compare(line.qty_done, 0, precision_digits=prec) > 0):
|
||||
show = True
|
||||
picking.show_print_zpl_barcode = show
|
||||
@@ -1,2 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_product_print_zpl_barcode,Full access to product.print.zpl.barcode wizard,model_product_print_zpl_barcode,base_report_to_printer.printing_group_user,1,1,1,1
|
||||
access_product_print_zpl_barcode,Full access to product.print.zpl.barcode wizard,model_product_print_zpl_barcode,base.group_user,1,1,1,1
|
||||
access_product_print_zpl_barcode_line,Full access to product.print.zpl.barcode.line wizard,model_product_print_zpl_barcode_line,base.group_user,1,1,1,1
|
||||
|
||||
|
@@ -27,7 +27,17 @@
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="generate_barcode_from_product_template" type="object" string="Generate Barcode" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '!=', False)]}"/>
|
||||
<button name="print_zpl_barcode_from_product_template" type="object" string="Print Barcode" groups="base_report_to_printer.printing_group_user" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '=', False)]}"/>
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcode" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '=', False)]}"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_tree_view" model="ir.ui.view">
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcodes"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
@@ -39,10 +49,19 @@
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="generate_barcode_from_product_product" type="object" string="Generate Barcode" attrs="{'invisible': [('barcode', '!=', False)]}"/>
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcode" groups="base_report_to_printer.printing_group_user" attrs="{'invisible': [('barcode', '=', False)]}"/>
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcode" attrs="{'invisible': [('barcode', '=', False)]}"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_product_tree_view" model="ir.ui.view">
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_product_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_open_label_layout" position="after">
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcodes"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
21
product_print_zpl_barcode/views/stock_picking.xml
Normal file
21
product_print_zpl_barcode/views/stock_picking.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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_picking_form" model="ir.ui.view">
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_toggle_is_locked" position="after">
|
||||
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcodes" attrs="{'invisible': [('show_print_zpl_barcode', '=', False)]}"/>
|
||||
<field name="show_print_zpl_barcode" invisible="1"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -1 +1,2 @@
|
||||
from . import product_print_zpl_barcode
|
||||
from . import res_config_settings
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2016-2020 Akretion France (http://www.akretion.com/)
|
||||
# Copyright 2016-2023 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).
|
||||
|
||||
@@ -8,24 +8,23 @@ from odoo.tools import float_compare, float_is_zero
|
||||
from stdnum.ean import is_valid, calc_check_digit
|
||||
import base64
|
||||
import re
|
||||
import socket
|
||||
import ipaddress
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
TIMEOUT = 5
|
||||
PRINTER_PORT = 9100
|
||||
|
||||
|
||||
class ProductPrintZplBarcode(models.TransientModel):
|
||||
_name = 'product.print.zpl.barcode'
|
||||
_description = 'Generate and print product barcodes in ZPL'
|
||||
_check_company_auto = True
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
assert self._context.get('active_model') == 'product.product',\
|
||||
'wrong active_model, should be product.product'
|
||||
product_id = self._context.get('active_id')
|
||||
product = self.env['product.product'].browse(product_id)
|
||||
if not product:
|
||||
raise UserError(_('Missing Product'))
|
||||
if not product.barcode:
|
||||
raise UserError(_(
|
||||
"Product '%s' doesn't have a barcode") % product.display_name)
|
||||
nomenclature = self.env.ref('barcodes.default_barcode_nomenclature')
|
||||
company = self.env.company
|
||||
posconfig = self.env['pos.config'].sudo().search(
|
||||
@@ -39,82 +38,219 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
], limit=1)
|
||||
if not pricelist:
|
||||
raise UserError(_(
|
||||
"There are no pricelist in company %s ?") % company.name)
|
||||
"There are no pricelist in company '%s'.") % company.name)
|
||||
|
||||
printer = self.env['printing.printer'].get_default()
|
||||
printer_ip = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'product_print_zpl_barcode.printer_ip')
|
||||
|
||||
line_ids = []
|
||||
if self._context.get('active_model') == 'product.product':
|
||||
product_ids = self._context.get('active_ids')
|
||||
products = self.env['product.product'].browse(product_ids)
|
||||
if not products:
|
||||
raise UserError(_('Missing Products'))
|
||||
for product in products:
|
||||
self._update_line_ids(line_ids, product)
|
||||
elif self._context.get('active_model') == 'product.template':
|
||||
product_tmpl_ids = self._context.get('active_ids')
|
||||
product_tmpls = self.env['product.template'].browse(product_tmpl_ids)
|
||||
for product_tmpl in product_tmpls:
|
||||
for product in product_tmpl.product_variant_ids:
|
||||
self._update_line_ids(line_ids, product)
|
||||
elif self._context.get('active_model') == 'stock.picking':
|
||||
prec = self.env['decimal.precision'].precision_get(
|
||||
'Product Unit of Measure')
|
||||
picking = self.env['stock.picking'].browse(self._context['active_id'])
|
||||
for ml in picking.move_line_ids:
|
||||
if (
|
||||
ml.product_id and
|
||||
ml.product_id.must_print_barcode and
|
||||
float_compare(ml.qty_done, 0, precision_digits=prec) > 0):
|
||||
self._update_line_ids(
|
||||
line_ids, ml.product_id, int(round(ml.qty_done)))
|
||||
else:
|
||||
raise UserError(_(
|
||||
"Wrong active_model in context (%s).")
|
||||
% self._context.get('active_model'))
|
||||
res.update({
|
||||
'company_id': company.id,
|
||||
'nomenclature_id': nomenclature.id,
|
||||
'pricelist_id': pricelist.id,
|
||||
'currency_id': pricelist.currency_id.id,
|
||||
'barcode': product.barcode,
|
||||
'product_name': product.name,
|
||||
'product_id': product_id,
|
||||
'zpl_printer_id': printer and printer.id or False,
|
||||
'zpl_printer_ip': printer_ip,
|
||||
'line_ids': line_ids,
|
||||
})
|
||||
return res
|
||||
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', required=True, readonly=True)
|
||||
uom_id = fields.Many2one(related='product_id.uom_id')
|
||||
# 1 line = un peu moins de 30
|
||||
product_name = fields.Char('Product Label', required=True, size=56)
|
||||
@api.model
|
||||
def _update_line_ids(self, line_ids, product, copies=1):
|
||||
if product.barcode:
|
||||
line_ids.append((0, 0, {
|
||||
'barcode': product.barcode,
|
||||
'product_name': product.name,
|
||||
'product_id': product.id,
|
||||
'copies': copies,
|
||||
}))
|
||||
else:
|
||||
logger.warning("Product '%s' doesn't have a barcode", product.display_name)
|
||||
|
||||
company_id = fields.Many2one( # default value set by default_get
|
||||
'res.company', required=True, ondelete='cascade')
|
||||
nomenclature_id = fields.Many2one(
|
||||
'barcode.nomenclature', 'Barcode Nomenclature', required=True)
|
||||
rule_id = fields.Many2one(
|
||||
'barcode.rule', string='Barcode Rule', readonly=True,
|
||||
compute='_compute_rule_id')
|
||||
barcode_type = fields.Selection(related='rule_id.type', string="Barcode Type")
|
||||
'barcode.nomenclature', 'Barcode Nomenclature', required=True,
|
||||
states={'step2': [('readonly', True)]})
|
||||
# label_size: remove readonly=True when we will support more labels
|
||||
label_size = fields.Selection([
|
||||
('38x25', '38x25 mm'),
|
||||
], required=True, default='38x25')
|
||||
], required=True, default='38x25', readonly=True)
|
||||
pricelist_id = fields.Many2one(
|
||||
'product.pricelist', string='Pricelist', required=True)
|
||||
currency_id = fields.Many2one(related='pricelist_id.currency_id')
|
||||
# TODO: for the moment, we only support weight, but...
|
||||
quantity = fields.Float(digits='Stock Weight')
|
||||
price_uom = fields.Monetary(
|
||||
readonly=True, string="Price per Unit of Measure",
|
||||
compute='_compute_price') # given by pricelist
|
||||
price = fields.Monetary(compute='_compute_price', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency')
|
||||
'product.pricelist', string='Pricelist', required=True,
|
||||
states={'step2': [('readonly', True)]}, check_company=True,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
|
||||
)
|
||||
state = fields.Selection([
|
||||
('step1', 'Step1'),
|
||||
('step2', 'Step2'),
|
||||
], default='step1', readonly=True)
|
||||
zpl_file = fields.Binary(string='ZPL File', readonly=True)
|
||||
zpl_filename = fields.Char('ZPL Filename')
|
||||
barcode = fields.Char(readonly=True)
|
||||
copies = fields.Integer(
|
||||
string='Number of Labels', default=1, required=True)
|
||||
zpl_printer_id = fields.Many2one(
|
||||
'printing.printer', string='ZPL Printer')
|
||||
zpl_printer_ip = fields.Char(string='ZPL Printer IP Address')
|
||||
line_ids = fields.One2many(
|
||||
'product.print.zpl.barcode.line', 'parent_id',
|
||||
string='Lines', states={'step2': [('readonly', True)]})
|
||||
|
||||
@api.depends('pricelist_id', 'quantity', 'product_id')
|
||||
def generate(self):
|
||||
"""Called by button for the wizard, 1st step"""
|
||||
self.ensure_one()
|
||||
zpl_strings = []
|
||||
for line in self.line_ids:
|
||||
barcode = line.barcode
|
||||
product_name = line.product_name
|
||||
assert barcode
|
||||
barcode_len = len(barcode)
|
||||
if barcode_len not in (8, 13):
|
||||
raise UserError(_(
|
||||
"Line '%s': barcode '%s' has %d digits. "
|
||||
"This wizard only supports EAN8 and EAN13 for the moment.")
|
||||
% (product_name, barcode, barcode_len))
|
||||
if not is_valid(barcode):
|
||||
raise UserError(_(
|
||||
"Line '%s': the barcode '%s' is not a valid EAN barcode "
|
||||
"(wrong checksum).") % (product_name, barcode))
|
||||
if line.copies <= 0:
|
||||
raise UserError(_(
|
||||
"On line '%s', the number of copies must be strictly positive."
|
||||
) % product_name)
|
||||
if line.barcode_type in ('price', 'weight'):
|
||||
barcode, zpl_str = line._prepare_price_weight_barcode_type()
|
||||
elif line.barcode_type == 'product':
|
||||
barcode, zpl_str = line._prepare_product_barcode_type()
|
||||
else:
|
||||
raise UserError(_(
|
||||
"Line '%s': barcode type '%s' is not supported for the moment")
|
||||
% (product_name, line.barcode_type))
|
||||
line.write({'barcode': barcode})
|
||||
zpl_strings.append(zpl_str)
|
||||
|
||||
zpl_filename = "barcodes.zpl"
|
||||
if len(self.line_ids) == 1:
|
||||
zpl_filename = "barcode_%s.zpl" % self.line_ids[0].barcode
|
||||
|
||||
zpl_str = '\n'.join(zpl_strings)
|
||||
zpl_bytes = zpl_str.encode('utf-8')
|
||||
vals = {
|
||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
||||
'state': 'step2',
|
||||
'zpl_filename': zpl_filename,
|
||||
}
|
||||
self.write(vals)
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
||||
action.update({
|
||||
'res_id': self.id,
|
||||
'context': self._context,
|
||||
'views': False})
|
||||
return action
|
||||
|
||||
def print_zpl(self):
|
||||
if not self.zpl_printer_ip:
|
||||
raise UserError(_(
|
||||
"You must configure the IP address of the ZPL Printer."))
|
||||
try:
|
||||
ip = ipaddress.ip_address(self.zpl_printer_ip)
|
||||
except Exception as e:
|
||||
raise UserError(str(e))
|
||||
version = ip.version
|
||||
# TODO works with DNS ?
|
||||
if version == 6: # IPv6
|
||||
socket_inet = socket.AF_INET6
|
||||
else: # IPv4
|
||||
socket_inet = socket.AF_INET
|
||||
with socket.socket(socket_inet, socket.SOCK_STREAM) as s:
|
||||
s.settimeout(TIMEOUT)
|
||||
try:
|
||||
s.connect((str(ip), PRINTER_PORT))
|
||||
except Exception as e:
|
||||
raise UserError(_(
|
||||
"Cannot connect to ZPL printer on %(ip)s. Error: %(error)s",
|
||||
ip=ip, error=e))
|
||||
zpl_file_bytes = base64.decodebytes(self.zpl_file)
|
||||
s.send(zpl_file_bytes)
|
||||
s.close()
|
||||
|
||||
|
||||
class ProductPrintZplBarcodeLine(models.TransientModel):
|
||||
_name = 'product.print.zpl.barcode.line'
|
||||
_description = 'Line of the print ZPL barcode wizard'
|
||||
|
||||
parent_id = fields.Many2one(
|
||||
'product.print.zpl.barcode', ondelete='cascade')
|
||||
product_id = fields.Many2one(
|
||||
'product.product', string='Product', readonly=True)
|
||||
uom_id = fields.Many2one(related='product_id.uom_id', string='UoM')
|
||||
# 1 line = a bit less than 30
|
||||
# I don't make product_name a stored computed field because I'm afraid
|
||||
# that we may not take the lang of the user
|
||||
product_name = fields.Char('Product Label', required=True, size=56)
|
||||
rule_id = fields.Many2one(
|
||||
'barcode.rule', string='Barcode Rule', compute='_compute_rule_id')
|
||||
barcode_type = fields.Selection(related='rule_id.type', string="Barcode Type")
|
||||
currency_id = fields.Many2one(related='parent_id.pricelist_id.currency_id')
|
||||
# TODO: for the moment, we only support weight, but...
|
||||
quantity = fields.Float(digits='Stock Weight', string='Qty')
|
||||
price_uom = fields.Monetary(
|
||||
string="Price/UoM", compute='_compute_price') # given by pricelist
|
||||
price = fields.Monetary(compute='_compute_price')
|
||||
barcode = fields.Char(readonly=True)
|
||||
copies = fields.Integer(string='# Labels', default=1, required=True)
|
||||
|
||||
@api.depends('parent_id.pricelist_id', 'quantity', 'product_id')
|
||||
def _compute_price(self):
|
||||
# for regular barcodes
|
||||
for wiz in self:
|
||||
if wiz.pricelist_id and wiz.product_id:
|
||||
price_uom = wiz.pricelist_id._get_product_price(
|
||||
wiz.product_id, 1, False)
|
||||
wiz.price_uom = price_uom
|
||||
wiz.price = price_uom * wiz.quantity
|
||||
for line in self:
|
||||
pricelist = line.parent_id.pricelist_id
|
||||
price_uom = price = 0.0
|
||||
if pricelist and line.product_id:
|
||||
price_uom = pricelist._get_product_price(line.product_id, 1, False)
|
||||
price = price_uom * line.quantity
|
||||
line.price_uom = price_uom
|
||||
line.price = price
|
||||
|
||||
@api.depends('nomenclature_id')
|
||||
@api.depends('parent_id.nomenclature_id')
|
||||
def _compute_rule_id(self):
|
||||
for wiz in self:
|
||||
for line in self:
|
||||
nomenclature = line.parent_id.nomenclature_id
|
||||
match_rule = False
|
||||
if wiz.nomenclature_id and wiz.barcode:
|
||||
for rule in wiz.nomenclature_id.rule_ids:
|
||||
match = wiz.nomenclature_id.match_pattern(
|
||||
wiz.barcode, rule.pattern)
|
||||
if nomenclature and line.barcode:
|
||||
for rule in nomenclature.rule_ids:
|
||||
match = nomenclature.match_pattern(
|
||||
line.barcode, rule.pattern)
|
||||
if match.get('match'):
|
||||
match_rule = rule.id
|
||||
break
|
||||
wiz.rule_id = match_rule
|
||||
line.rule_id = match_rule
|
||||
|
||||
def _prepare_price_weight_barcode_type(self):
|
||||
dpo = self.env['decimal.precision']
|
||||
bno = self.env['barcode.nomenclature']
|
||||
prec = dpo.precision_get('Stock Weight')
|
||||
value = self.quantity
|
||||
pbarcode = self.barcode
|
||||
@@ -139,7 +275,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
barcode = pbarcode[0:len(prefix)]
|
||||
# print("barcode=", barcode)
|
||||
# print("pattern=", pattern)
|
||||
m = re.search('\{N+D+\}', pattern)
|
||||
m = re.search(r'\{N+D+\}', pattern)
|
||||
# print("m=", m)
|
||||
assert m
|
||||
pattern_val = m.group(0)
|
||||
@@ -172,7 +308,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
assert len(barcode) == 13
|
||||
assert is_valid(barcode)
|
||||
# print("barcode FINAL=", barcode)
|
||||
zpl_unicode = self._price_weight_barcode_type_zpl() % {
|
||||
zpl_str = self._price_weight_barcode_type_zpl() % {
|
||||
'product_name': self.product_name,
|
||||
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
||||
'ean_no_checksum': barcode[:-1],
|
||||
@@ -183,12 +319,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
'quantity': value,
|
||||
'uom_name': self.uom_id.name,
|
||||
}
|
||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
||||
vals = {
|
||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
||||
'barcode': barcode,
|
||||
}
|
||||
return vals
|
||||
return (barcode, zpl_str)
|
||||
|
||||
@api.model
|
||||
def _price_weight_barcode_type_zpl(self):
|
||||
@@ -229,7 +360,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
return label
|
||||
|
||||
def _prepare_product_barcode_type(self):
|
||||
zpl_unicode = self._product_barcode_type_zpl() % {
|
||||
zpl_str = self._product_barcode_type_zpl() % {
|
||||
'product_name': self.product_name,
|
||||
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
||||
'ean_no_checksum': self.barcode[:-1],
|
||||
@@ -237,60 +368,4 @@ class ProductPrintZplBarcode(models.TransientModel):
|
||||
'currency_symbol': self.currency_id.symbol, # symbol is a required field
|
||||
'copies': self.copies,
|
||||
}
|
||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
||||
vals = {
|
||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
||||
'barcode': self.barcode, # unchanged
|
||||
}
|
||||
return vals
|
||||
|
||||
def generate(self):
|
||||
assert self.barcode
|
||||
if len(self.barcode) not in (8, 13):
|
||||
raise UserError(_(
|
||||
"This wizard only supports EAN8 and EAN13 for the moment. "
|
||||
"Barcode '%s' has %d digits.") % (
|
||||
self.barcode,
|
||||
len(self.barcode)))
|
||||
if not is_valid(self.barcode):
|
||||
raise UserError(_(
|
||||
"The barcode '%s' is not a valid EAN barcode "
|
||||
"(wrong checksum).") % self.barcode)
|
||||
if not self.copies:
|
||||
raise UserError(_("The number of copies cannot be 0"))
|
||||
if self.barcode_type in ('price', 'weight'):
|
||||
vals = self._prepare_price_weight_barcode_type()
|
||||
elif self.barcode_type == 'product':
|
||||
vals = self._prepare_product_barcode_type()
|
||||
else:
|
||||
raise UserError(_(
|
||||
"Barcode Type %s is not supported for the moment")
|
||||
% self.barcode_type)
|
||||
vals.update({
|
||||
'state': 'step2',
|
||||
'zpl_filename': 'barcode_%s.zpl' % vals['barcode'],
|
||||
})
|
||||
self.write(vals)
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
||||
action.update({
|
||||
'res_id': self.id,
|
||||
'context': self._context,
|
||||
'views': False})
|
||||
return action
|
||||
|
||||
def print_zpl(self):
|
||||
if not self.zpl_printer_id:
|
||||
raise UserError(_(
|
||||
"You must select a ZPL Printer."))
|
||||
self.zpl_printer_id.print_document(
|
||||
self.zpl_filename, base64.decodebytes(self.zpl_file), format='raw')
|
||||
action = True
|
||||
if self._context.get('print_and_new'):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
||||
action.update({
|
||||
'views': False,
|
||||
'context': self._context,
|
||||
})
|
||||
return action
|
||||
return (self.barcode, zpl_str)
|
||||
|
||||
@@ -11,38 +11,41 @@
|
||||
<field name="name">product_print_zpl_barcode.form</field>
|
||||
<field name="model">product.print.zpl.barcode</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Generate and Print Product Barcode">
|
||||
<group name="step1" string="Configuration">
|
||||
<form>
|
||||
<group name="step1">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_name" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="pricelist_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="price_uom"/>
|
||||
<field name="label_size" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="nomenclature_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
<field name="rule_id"/>
|
||||
<field name="barcode_type"/>
|
||||
<field name="barcode"/>
|
||||
<field name="copies" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||
</group>
|
||||
<group string="Enter Quantity" attrs="{'invisible': [('barcode_type', '=', 'product')]}">
|
||||
<label for="quantity"/>
|
||||
<div name="qty_uom" class="o_row">
|
||||
<field name="quantity" attrs="{'readonly': [('state', '=', 'step2')]}" class="oe_inline"/>
|
||||
<field name="uom_id" class="oe_inline" style="margin-left: 5px"/> </div>
|
||||
</group>
|
||||
<group name="step2" states="step2" string="Label">
|
||||
<field name="price" attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"/>
|
||||
<group name="step2" states="step2">
|
||||
<field name="zpl_file" filename="zpl_filename" />
|
||||
<field name="zpl_filename" invisible="1"/>
|
||||
<field name="zpl_printer_id" attrs="{'required': [('state', '=', 'step2')]}"/>
|
||||
<field name="zpl_printer_ip" attrs="{'required': [('state', '=', 'step2')]}"/>
|
||||
</group>
|
||||
<group name="lines">
|
||||
<field name="line_ids" colspan="2" nolabel="1">
|
||||
<tree editable="bottom">
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="product_id" optional="hide" force_save="1"/>
|
||||
<field name="product_name"/>
|
||||
<field name="price_uom"/>
|
||||
<field name="rule_id" optional="show"/>
|
||||
<field name="barcode_type" optional="hide"/>
|
||||
<field name="barcode" force_save="1"/>
|
||||
<field name="price" attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"/>
|
||||
<field name="quantity" attrs="{'invisible': [('barcode_type', '=', 'product')]}" optional="show"/>
|
||||
<field name="uom_id" attrs="{'invisible': [('barcode_type', '=', 'product')]}" optional="show"/>
|
||||
<field name="copies" />
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="generate" type="object" string="Generate Label" class="btn-primary" states="step1"/>
|
||||
<button name="generate" type="object" string="Generate Labels" class="btn-primary" states="step1"/>
|
||||
<button special="cancel" string="Cancel" class="btn-default" states="step1"/>
|
||||
<button name="print_zpl" type="object" string="Print" class="btn-primary" states="step2"/>
|
||||
<button name="print_zpl" type="object" string="Print and New" class="btn-primary" context="{'print_and_new': True}" attrs="{'invisible': ['|', ('state', '!=', 'step2'), ('barcode_type', '=', 'product')]}"/>
|
||||
<button special="cancel" string="Close" class="btn-default" states="step2"/>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
24
product_print_zpl_barcode/wizard/res_config_settings.py
Normal file
24
product_print_zpl_barcode/wizard/res_config_settings.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright 2023 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
|
||||
from odoo.exceptions import ValidationError
|
||||
import ipaddress
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
zpl_printer_ip = fields.Char(
|
||||
config_parameter="product_print_zpl_barcode.printer_ip",
|
||||
string="ZPL Printer IP Address")
|
||||
|
||||
@api.constrains('zpl_printer_ip')
|
||||
def _check_zpl_printer_ip(self):
|
||||
for wiz in self:
|
||||
if wiz.zpl_printer_ip:
|
||||
try:
|
||||
ipaddress.ip_address(wiz.zpl_printer_ip)
|
||||
except Exception as e:
|
||||
raise ValidationError(str(e))
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 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="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@id='companies']" position='after'>
|
||||
<h2>Barcode printing</h2>
|
||||
<div class="row mt16 o_settings_container" name="zpl_printer">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_right_pane" id="zpl_printer_ip">
|
||||
<div class="row">
|
||||
<label for="zpl_printer_ip" class="col-md-5" />
|
||||
<field name="zpl_printer_ip" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -24,6 +24,9 @@ This module has been written by Alexis de Lattre from Akretion France.
|
||||
],
|
||||
'data': [
|
||||
'views/stock_picking.xml',
|
||||
'views/purchase_order.xml',
|
||||
'views/stock_move.xml',
|
||||
'views/stock_move_line.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
from . import purchase
|
||||
from . import stock_move
|
||||
from . import stock_move_line
|
||||
|
||||
33
purchase_stock_usability/models/stock_move.py
Normal file
33
purchase_stock_usability/models/stock_move.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright 2023 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 StockMove(models.Model):
|
||||
_inherit = 'stock.move'
|
||||
|
||||
# for optional display in tree view
|
||||
product_supplier_code = fields.Char(
|
||||
compute='_compute_product_supplier_code', string="Vendor Product Code")
|
||||
|
||||
def _compute_product_supplier_code(self):
|
||||
pso = self.env['product.supplierinfo']
|
||||
for move in self:
|
||||
code = False
|
||||
if move.purchase_line_id and move.purchase_line_id.order_id:
|
||||
po = move.purchase_line_id.order_id
|
||||
partner_id = po.partner_id.commercial_partner_id.id
|
||||
if partner_id:
|
||||
sinfo = pso.search_read([
|
||||
('product_tmpl_id', '=', move.product_id.product_tmpl_id.id),
|
||||
('product_id', 'in', (False, move.product_id.id)),
|
||||
('partner_id', '=', partner_id),
|
||||
('product_code', '!=', False),
|
||||
('company_id', 'in', (False, move.company_id.id)),
|
||||
], ['product_code'], limit=1, order='product_id')
|
||||
# if I order by product_id, I get the null values at the end
|
||||
if sinfo:
|
||||
code = sinfo[0]['product_code']
|
||||
move.product_supplier_code = code
|
||||
34
purchase_stock_usability/models/stock_move_line.py
Normal file
34
purchase_stock_usability/models/stock_move_line.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# Copyright 2023 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 StockMoveLine(models.Model):
|
||||
_inherit = 'stock.move.line'
|
||||
|
||||
# for optional display in tree view
|
||||
product_supplier_code = fields.Char(
|
||||
compute='_compute_product_supplier_code', string="Vendor Product Code")
|
||||
|
||||
def _compute_product_supplier_code(self):
|
||||
pso = self.env['product.supplierinfo']
|
||||
for mline in self:
|
||||
code = False
|
||||
move = mline.move_id
|
||||
if move and move.purchase_line_id and move.purchase_line_id.order_id:
|
||||
po = move.purchase_line_id.order_id
|
||||
partner_id = po.partner_id.commercial_partner_id.id
|
||||
if partner_id:
|
||||
sinfo = pso.search_read([
|
||||
('product_tmpl_id', '=', mline.product_id.product_tmpl_id.id),
|
||||
('product_id', 'in', (False, mline.product_id.id)),
|
||||
('partner_id', '=', partner_id),
|
||||
('product_code', '!=', False),
|
||||
('company_id', 'in', (False, mline.company_id.id)),
|
||||
], ['product_code'], limit=1, order='product_id')
|
||||
# if I order by product_id, I get the null values at the end
|
||||
if sinfo:
|
||||
code = sinfo[0]['product_code']
|
||||
mline.product_supplier_code = code
|
||||
43
purchase_stock_usability/views/purchase_order.xml
Normal file
43
purchase_stock_usability/views/purchase_order.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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="purchase_order_tree" model="ir.ui.view">
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="picking_type_id" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_order_view_tree" model="ir.ui.view">
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase_stock.purchase_order_view_tree_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="picking_type_id" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_order_kpis_tree" model="ir.ui.view">
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_kpis_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="after">
|
||||
<field name="picking_type_id" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
</odoo>
|
||||
21
purchase_stock_usability/views/stock_move.xml
Normal file
21
purchase_stock_usability/views/stock_move.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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_tree" model="ir.ui.view">
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit_id" ref="stock.view_move_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<!-- picking.purchase_id is a native field ; it is added to the picking form view in this module -->
|
||||
<field name="product_id" position="after">
|
||||
<field name="product_supplier_code" optional="hide" attrs="{'column_invisible': [('parent.purchase_id', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
31
purchase_stock_usability/views/stock_move_line.xml
Normal file
31
purchase_stock_usability/views/stock_move_line.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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_line_tree" model="ir.ui.view">
|
||||
<field name="model">stock.move.line</field>
|
||||
<field name="inherit_id" ref="stock.view_move_line_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="product_id" position="after">
|
||||
<field name="product_supplier_code" 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_supplier_code" optional="hide" attrs="{'column_invisible': [('parent.purchase_id', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -16,6 +16,9 @@
|
||||
<field name="origin" position="after">
|
||||
<field name="purchase_id" attrs="{'invisible': [('purchase_id', '=', False)]}"/>
|
||||
</field>
|
||||
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='product_id']" position="after">
|
||||
<field name="product_supplier_code" optional="hide" attrs="{'column_invisible': [('parent.purchase_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -79,15 +79,22 @@ class PurchaseOrderLine(models.Model):
|
||||
compute='_compute_product_supplier_code', string='Vendor Product Code')
|
||||
|
||||
def _compute_product_supplier_code(self):
|
||||
pso = self.env['product.supplierinfo']
|
||||
for line in self:
|
||||
code = False
|
||||
if not line.display_type and line.product_id and line.order_id:
|
||||
partner_id = line.order_id.partner_id.commercial_partner_id.id
|
||||
if partner_id:
|
||||
for supplier_info in line.product_id.seller_ids:
|
||||
if supplier_info.partner_id.id == partner_id:
|
||||
code = supplier_info.product_code
|
||||
break
|
||||
sinfo = pso.search_read([
|
||||
('product_tmpl_id', '=', line.product_id.product_tmpl_id.id),
|
||||
('product_id', 'in', (False, line.product_id.id)),
|
||||
('partner_id', '=', partner_id),
|
||||
('product_code', '!=', False),
|
||||
('company_id', 'in', (False, line.order_id.company_id.id)),
|
||||
], ['product_code'], limit=1, order='product_id')
|
||||
# if I order by product_id, I get the null values at the end
|
||||
if sinfo:
|
||||
code = sinfo[0]['product_code']
|
||||
line.product_supplier_code = code
|
||||
|
||||
def _get_product_purchase_description(self, product_lang):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{
|
||||
'name': 'Sale Order Route',
|
||||
'version': '14.0.1.0.0',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Sales',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Set route on sale order',
|
||||
@@ -17,6 +17,6 @@ This module has been written by Alexis de Lattre from Akretion
|
||||
'author': 'Akretion',
|
||||
'website': 'http://www.akretion.com',
|
||||
'depends': ['sale_stock'],
|
||||
'data': ['views/sale_order.xml'],
|
||||
'installable': False,
|
||||
'data': ['views/sale_order.xml', 'views/sale_report.xml'],
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from . import sale_order
|
||||
from . import sale_report
|
||||
|
||||
@@ -2,14 +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 fields, models
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
route_id = fields.Many2one(
|
||||
'stock.location.route', string='Route',
|
||||
'stock.route', string='Route',
|
||||
ondelete='restrict', readonly=True, tracking=True,
|
||||
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
check_company=True,
|
||||
@@ -24,3 +24,18 @@ class SaleOrder(models.Model):
|
||||
lambda l:
|
||||
l.product_id and l.product_id.type in ('product', 'consu')).write(vals)
|
||||
return super()._action_confirm()
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
# It's important when you add a line AFTER order confirmation
|
||||
route_id = fields.Many2one(compute='_compute_route_id', readonly=False, store=True, precompute=True)
|
||||
|
||||
@api.depends('display_type', 'product_id')
|
||||
def _compute_route_id(self):
|
||||
for line in self:
|
||||
if not line.display_type and line.product_id and line.product_id.type in ('product', 'consu'):
|
||||
line.route_id = line.order_id.route_id or False
|
||||
else:
|
||||
line.route_id = False
|
||||
|
||||
21
sale_order_route/models/sale_report.py
Normal file
21
sale_order_route/models/sale_report.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Copyright 2024 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 fields, models
|
||||
|
||||
|
||||
class SaleReport(models.Model):
|
||||
_inherit = "sale.report"
|
||||
|
||||
route_id = fields.Many2one('stock.route', string='Route', readonly=True)
|
||||
|
||||
def _select_additional_fields(self):
|
||||
res = super()._select_additional_fields()
|
||||
res['route_id'] = "s.route_id"
|
||||
return res
|
||||
|
||||
def _group_by_sale(self):
|
||||
res = super()._group_by_sale()
|
||||
res += ', s.route_id'
|
||||
return res
|
||||
20
sale_order_route/views/sale_report.xml
Normal file
20
sale_order_route/views/sale_report.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 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_order_product_search" model="ir.ui.view">
|
||||
<field name="model">sale.report</field>
|
||||
<field name="inherit_id" ref="sale.view_order_product_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="status" position="after">
|
||||
<filter string="Route" name="route_id_groupby" context="{'group_by':'route_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
18
sale_remove_my_quotation_filter/README.rst
Normal file
18
sale_remove_my_quotation_filter/README.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
===============================
|
||||
Sale Remove My Quotation Filter
|
||||
===============================
|
||||
|
||||
This module removes the default filter **My Quotations** in the menu entry *Sales > Orders > Quotations*.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Akretion
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
0
sale_remove_my_quotation_filter/__init__.py
Normal file
0
sale_remove_my_quotation_filter/__init__.py
Normal file
16
sale_remove_my_quotation_filter/__manifest__.py
Normal file
16
sale_remove_my_quotation_filter/__manifest__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2023 Akretion France (https://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 remove My Quotations filter',
|
||||
'version': '16.0.1.0.0',
|
||||
'category': 'Sales',
|
||||
'license': 'AGPL-3',
|
||||
'summary': 'Remove default filter My Quotations',
|
||||
'author': 'Akretion',
|
||||
'website': 'https://github.com/akretion/odoo-usability',
|
||||
'depends': ['sale'],
|
||||
'data': ['views/sale_order.xml'],
|
||||
'installable': True,
|
||||
}
|
||||
18
sale_remove_my_quotation_filter/views/sale_order.xml
Normal file
18
sale_remove_my_quotation_filter/views/sale_order.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2023 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="sale.action_quotations_with_onboarding" model="ir.actions.act_window">
|
||||
<field name="context"></field>
|
||||
</record>
|
||||
|
||||
<record id="sale.action_quotations" model="ir.actions.act_window">
|
||||
<field name="context"></field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -24,6 +24,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
||||
'depends': ['sale_stock'],
|
||||
'data': [
|
||||
'views/sale_order.xml',
|
||||
'views/sale_report.xml',
|
||||
'views/procurement_group.xml',
|
||||
'views/stock_move.xml',
|
||||
'views/stock_picking.xml',
|
||||
|
||||
20
sale_stock_usability/views/sale_report.xml
Normal file
20
sale_stock_usability/views/sale_report.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 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_order_product_search" model="ir.ui.view">
|
||||
<field name="model">sale.report</field>
|
||||
<field name="inherit_id" ref="sale.view_order_product_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="status" position="after">
|
||||
<filter string="Warehouse" name="warehouse_id_groupby" context="{'group_by':'warehouse_id'}"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -73,6 +73,9 @@
|
||||
<field name="state" position="attributes">
|
||||
<attribute name="invisible">0</attribute>
|
||||
<attribute name="optional">hide</attribute>
|
||||
<attribute name="widget">badge</attribute>
|
||||
<attribute name="decoration-success">state == 'done'</attribute>
|
||||
<attribute name="decoration-info">state == 'sale'</attribute>
|
||||
</field>
|
||||
<field name="partner_id" position="after">
|
||||
<field name="client_order_ref" optional="show"/>
|
||||
|
||||
@@ -81,6 +81,7 @@ class StockQuant(models.Model):
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"picking_id": picking_id,
|
||||
"product_id": product_id,
|
||||
"product_uom_id": uom_id,
|
||||
"qty_done": qty,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from . import stock_move
|
||||
from . import stock_move_line
|
||||
from . import stock_picking
|
||||
from . import stock_picking_type
|
||||
from . import stock_warehouse_orderpoint
|
||||
from . import stock_quant
|
||||
from . import procurement_group
|
||||
|
||||
26
stock_usability/models/stock_picking_type.py
Normal file
26
stock_usability/models/stock_picking_type.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright 2023 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 StockPickingType(models.Model):
|
||||
_inherit = 'stock.picking.type'
|
||||
|
||||
is_dropship = fields.Boolean(compute="_compute_is_dropship", store=True)
|
||||
|
||||
@api.depends("code", "warehouse_id", "default_location_src_id", "default_location_dest_id")
|
||||
def _compute_is_dropship(self):
|
||||
supplier_loc_id = self.env.ref("stock.stock_location_suppliers").id
|
||||
customer_loc_id = self.env.ref("stock.stock_location_customers").id
|
||||
for picktype in self:
|
||||
is_dropship = False
|
||||
if (
|
||||
picktype.code == 'incoming'
|
||||
and not picktype.warehouse_id
|
||||
and picktype.default_location_src_id.id == supplier_loc_id
|
||||
and picktype.default_location_dest_id.id == customer_loc_id
|
||||
):
|
||||
is_dropship = True
|
||||
picktype.is_dropship = is_dropship
|
||||
@@ -38,3 +38,14 @@ class StockQuant(models.Model):
|
||||
len(self._context['search_location']) == 1):
|
||||
res['location_id'] = self._context['search_location'][0]
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def action_view_inventory(self):
|
||||
action = super().action_view_inventory()
|
||||
# Remove filter 'My Counts' set by default for Stock Users
|
||||
if (
|
||||
action.get('context') and
|
||||
isinstance(action['context'], dict) and
|
||||
action['context'].get('search_default_my_count')):
|
||||
action['context'].pop('search_default_my_count')
|
||||
return action
|
||||
|
||||
@@ -15,7 +15,19 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="responsible_id" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</field>
|
||||
</field>
|
||||
<field name="virtual_available" position="before">
|
||||
<field name="incoming_qty" optional="hide" sum="1"/>
|
||||
<field name="outgoing_qty" optional="hide" sum="1"/>
|
||||
<!-- we would like to also have free_qty, as on product.product, but this field doesn't exist on product.template -->
|
||||
</field>
|
||||
<!-- we have sum=1 on product.product qty fields, but not on product.template...so I add it on product.template -->
|
||||
<field name="virtual_available" position="attributes">
|
||||
<attribute name="sum">1</attribute>
|
||||
</field>
|
||||
<field name="qty_available" position="attributes">
|
||||
<attribute name="sum">1</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
<field name="product_id" position="after">
|
||||
<field name="product_barcode" optional="hide"/>
|
||||
</field>
|
||||
<field name="reference" position="after">
|
||||
<field name="origin" optional="hide"/>
|
||||
<field name="partner_id" optional="hide"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2014-2022 Akretion (http://www.akretion.com/)
|
||||
Copyright 2014-2023 Akretion (http://www.akretion.com/)
|
||||
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
-->
|
||||
|
||||
<odoo>
|
||||
|
||||
<record id="view_picking_type_form" model="ir.ui.view">
|
||||
<field name="model">stock.picking.type</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_type_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="return_picking_type_id" position="before">
|
||||
<field name="is_dropship"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_picking_type_tree" model="ir.ui.view">
|
||||
<field name="name">usability.stock.picking.type.tree</field>
|
||||
<field name="model">stock.picking.type</field>
|
||||
|
||||
Reference in New Issue
Block a user