Compare commits
41 Commits
14-fix-mig
...
14.0-stock
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61818437b3 | ||
|
|
f2172a5f06 | ||
|
|
e81e8e5a93 | ||
|
|
a7b0210a90 | ||
|
|
cd30298f88 | ||
|
|
5039e56417 | ||
|
|
3c338c9c78 | ||
|
|
2c87670281 | ||
|
|
59e34d0166 | ||
|
|
0e87b385d4 | ||
|
|
4a434d69f9 | ||
|
|
bade674c41 | ||
|
|
def89c0a5d | ||
|
|
88b12d86a2 | ||
|
|
e3c55047b3 | ||
|
|
b701586edc | ||
|
|
aae3ac2898 | ||
|
|
8a2e23cff5 | ||
|
|
0781cb7ba7 | ||
|
|
a7022e899f | ||
|
|
068509066d | ||
|
|
e594d15417 | ||
|
|
4121d8466d | ||
|
|
6df9756479 | ||
|
|
0331cc8eda | ||
|
|
54bc3d8af8 | ||
|
|
15edfe5603 | ||
|
|
8bf2116630 | ||
|
|
6b5282994a | ||
|
|
993aab7d18 | ||
|
|
73137ee1fb | ||
|
|
c4ec388380 | ||
|
|
8afcd49bc3 | ||
|
|
1e8f00d4e1 | ||
|
|
061efb2197 | ||
|
|
e698746dd1 | ||
|
|
d2bc7be9fe | ||
|
|
5f6b731e50 | ||
|
|
6c81ade7d0 | ||
|
|
209de4f6db | ||
|
|
2d3a792ce8 |
@@ -6,7 +6,7 @@ from datetime import timedelta
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.osv import expression
|
from odoo.osv import expression
|
||||||
from odoo.tools import float_is_zero
|
from odoo.tools import float_is_zero
|
||||||
from odoo.tools.misc import format_date
|
from odoo.tools.misc import format_date
|
||||||
@@ -40,6 +40,34 @@ class AccountMove(models.Model):
|
|||||||
compute="_compute_sales_dates", readonly=True,
|
compute="_compute_sales_dates", readonly=True,
|
||||||
help="This information appear on invoice qweb report "
|
help="This information appear on invoice qweb report "
|
||||||
"(you may use it for your own 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.internal_type in ("payable", "receivable")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _inverse_blocked(self):
|
||||||
|
for move in self:
|
||||||
|
for line in move.line_ids.filtered(
|
||||||
|
lambda l: l.account_id.internal_type in ("payable", "receivable")
|
||||||
|
):
|
||||||
|
line.blocked = move.blocked
|
||||||
|
|
||||||
def _compute_has_discount(self):
|
def _compute_has_discount(self):
|
||||||
prec = self.env['decimal.precision'].precision_get('Discount')
|
prec = self.env['decimal.precision'].precision_get('Discount')
|
||||||
@@ -235,6 +263,40 @@ class AccountMove(models.Model):
|
|||||||
date = invoice_date
|
date = invoice_date
|
||||||
return date
|
return date
|
||||||
|
|
||||||
|
# I don't use account_invoice_supplier_ref_unique because it adds
|
||||||
|
# a field supplier_invoice_number on account.move instead of using the native field
|
||||||
|
# cf https://github.com/OCA/account-invoicing/issues/1484
|
||||||
|
# So I take inspiration from the code of account_invoice_supplier_ref_unique
|
||||||
|
# but I use the native "ref" field
|
||||||
|
@api.constrains("ref", "partner_id")
|
||||||
|
def _check_in_invoice_ref_unique_insensitive(self):
|
||||||
|
for move in self:
|
||||||
|
if move.ref and move.is_purchase_document(
|
||||||
|
include_receipts=True
|
||||||
|
):
|
||||||
|
in_invoice_same_ref = self.search(
|
||||||
|
[
|
||||||
|
("commercial_partner_id", "=", move.commercial_partner_id.id),
|
||||||
|
("move_type", "in", ("in_invoice", "in_refund")),
|
||||||
|
("company_id", "=", move.company_id.id),
|
||||||
|
("ref", "=ilike", move.ref),
|
||||||
|
("id", "!=", move.id),
|
||||||
|
],
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
if in_invoice_same_ref:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"An invoice already exists in Odoo with the same "
|
||||||
|
"bill reference '%s' for the same supplier '%s': %s."
|
||||||
|
)
|
||||||
|
% (
|
||||||
|
in_invoice_same_ref.ref,
|
||||||
|
in_invoice_same_ref.partner_id.display_name,
|
||||||
|
in_invoice_same_ref.display_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccountMoveLine(models.Model):
|
class AccountMoveLine(models.Model):
|
||||||
_inherit = 'account.move.line'
|
_inherit = 'account.move.line'
|
||||||
|
|||||||
@@ -47,6 +47,16 @@
|
|||||||
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
|
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
|
||||||
<field name="product_barcode" optional="hide"/>
|
<field name="product_barcode" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<field name="auto_post" position="before">
|
||||||
|
<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>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -71,6 +81,8 @@
|
|||||||
<filter name="sent" string="Sent" domain="[('is_move_sent', '=', True), ('move_type', 'in', ('out_invoice', 'out_refund'))]"/>
|
<filter name="sent" string="Sent" domain="[('is_move_sent', '=', True), ('move_type', 'in', ('out_invoice', 'out_refund'))]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
|
<filter name="no_attachment" string="Missing Attachment" domain="[('has_attachment', '=', False)]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter name="dispute" string="Dispute" domain="[('blocked', '=', True)]"/>
|
||||||
</filter>
|
</filter>
|
||||||
<filter name="salesperson" position="before">
|
<filter name="salesperson" position="before">
|
||||||
<filter name="commercial_partner_groupby" string="Commercial Partner" context="{'group_by': 'commercial_partner_id'}"/>
|
<filter name="commercial_partner_groupby" string="Commercial Partner" context="{'group_by': 'commercial_partner_id'}"/>
|
||||||
@@ -81,6 +93,23 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</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_internal_type', 'not in', ('payable', 'receivable'))]}</attribute>
|
||||||
|
</xpath>
|
||||||
|
<field name="company_id" position="after">
|
||||||
|
<field name="account_internal_type" invisible="1"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="view_move_line_tree" model="ir.ui.view">
|
<record id="view_move_line_tree" model="ir.ui.view">
|
||||||
<field name="model">account.move.line</field>
|
<field name="model">account.move.line</field>
|
||||||
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
<field name="inherit_id" ref="account.view_move_line_tree"/>
|
||||||
@@ -126,6 +155,18 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_move_line_pivot" model="ir.ui.view">
|
||||||
|
<field name="model">account.move.line</field>
|
||||||
|
<field name="inherit_id" ref="account.view_move_line_pivot"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- By default, date is split by month... but if you've been using Odoo for several years,
|
||||||
|
the pivot table becomes very big by default: so we split by year -->
|
||||||
|
<field name="date" position="attributes">
|
||||||
|
<attribute name="interval">year</attribute>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="account.action_move_journal_line" model="ir.actions.act_window">
|
<record id="account.action_move_journal_line" model="ir.actions.act_window">
|
||||||
<field name="context">{'default_move_type': 'entry', 'view_no_maturity': True}</field>
|
<field name="context">{'default_move_type': 'entry', 'view_no_maturity': True}</field>
|
||||||
<!-- Remove 'search_default_misc_filter': 1 -->
|
<!-- Remove 'search_default_misc_filter': 1 -->
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class ResPartnerPhone(models.Model):
|
|||||||
_name = 'res.partner.phone'
|
_name = 'res.partner.phone'
|
||||||
_order = 'partner_id, type'
|
_order = 'partner_id, type'
|
||||||
_phone_name_sequence = 8
|
_phone_name_sequence = 8
|
||||||
|
_phone_name_fields = ["phone"]
|
||||||
_inherit = ['phone.validation.mixin']
|
_inherit = ['phone.validation.mixin']
|
||||||
_description = 'Multiple emails and phones for partners'
|
_description = 'Multiple emails and phones for partners'
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ class ResPartnerPhone(models.Model):
|
|||||||
if self._context.get('callerid'):
|
if self._context.get('callerid'):
|
||||||
name = pphone.partner_id.display_name
|
name = pphone.partner_id.display_name
|
||||||
else:
|
else:
|
||||||
name = u'%s (%s)' % (pphone.phone, pphone.partner_id.name)
|
name = '%s (%s)' % (pphone.phone, pphone.partner_id.name)
|
||||||
else:
|
else:
|
||||||
name = pphone.phone
|
name = pphone.phone
|
||||||
res.append((pphone.id, name))
|
res.append((pphone.id, name))
|
||||||
@@ -104,6 +105,7 @@ class ResPartnerPhone(models.Model):
|
|||||||
|
|
||||||
class ResPartner(models.Model):
|
class ResPartner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
_phone_name_fields = []
|
||||||
|
|
||||||
# in v10, we are supposed to have in DB E.164 format
|
# in v10, we are supposed to have in DB E.164 format
|
||||||
# with the current implementation, we have:
|
# with the current implementation, we have:
|
||||||
@@ -147,6 +149,8 @@ class ResPartner(models.Model):
|
|||||||
if vals.get(partner_field):
|
if vals.get(partner_field):
|
||||||
vals['phone_ids'].append(
|
vals['phone_ids'].append(
|
||||||
(0, 0, {'type': type, partner_phone_field: vals[partner_field]}))
|
(0, 0, {'type': type, partner_phone_field: vals[partner_field]}))
|
||||||
|
if partner_field in vals:
|
||||||
|
vals.pop(partner_field)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
@@ -155,7 +159,6 @@ class ResPartner(models.Model):
|
|||||||
self._update_create_vals(vals, '1_email_primary', 'email', 'email')
|
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, '3_phone_primary', 'phone', 'phone')
|
||||||
self._update_create_vals(vals, '5_mobile_primary', 'mobile', '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)
|
return super().create(vals)
|
||||||
|
|
||||||
def _update_write_vals(
|
def _update_write_vals(
|
||||||
@@ -178,16 +181,16 @@ class ResPartner(models.Model):
|
|||||||
else:
|
else:
|
||||||
if pphone:
|
if pphone:
|
||||||
vals['phone_ids'].append((2, pphone.id))
|
vals['phone_ids'].append((2, pphone.id))
|
||||||
|
vals.pop(partner_field)
|
||||||
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
if 'phone_ids' not in vals:
|
if 'phone_ids' not in vals:
|
||||||
for rec in self:
|
for rec in self:
|
||||||
vals['phone_ids'] = []
|
cvals = dict(vals, phone_ids=[])
|
||||||
rec._update_write_vals(vals, '1_email_primary', 'email', 'email')
|
rec._update_write_vals(cvals, '1_email_primary', 'email', 'email')
|
||||||
rec._update_write_vals(vals, '3_phone_primary', 'phone', 'phone')
|
rec._update_write_vals(cvals, '3_phone_primary', 'phone', 'phone')
|
||||||
rec._update_write_vals(vals, '5_mobile_primary', 'mobile', 'phone')
|
rec._update_write_vals(cvals, '5_mobile_primary', 'mobile', 'phone')
|
||||||
rec._update_write_vals(vals, '7_fax_primary', 'fax', 'phone')
|
super(ResPartner, rec).write(cvals)
|
||||||
super(ResPartner, rec).write(vals)
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return super().write(vals)
|
return super().write(vals)
|
||||||
|
|||||||
@@ -161,6 +161,12 @@
|
|||||||
<field name="name" position="attributes">
|
<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)]</attribute>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="email" position="attributes">
|
||||||
|
<attribute name="filter_domain">[('phone_ids.email', 'ilike', self)]</attribute>
|
||||||
|
</field>
|
||||||
|
<field name="phone" position="attributes">
|
||||||
|
<attribute name="filter_domain">[('phone_ids.phone', 'ilike', self)]</attribute>
|
||||||
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
'summary': 'Better usability in base module',
|
'summary': 'Better usability in base module',
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'depends': ['base'],
|
'depends': ['web'],
|
||||||
'data': [
|
'data': [
|
||||||
'security/group.xml',
|
'security/group.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
'views/ir_module.xml',
|
'views/ir_module.xml',
|
||||||
'views/ir_sequence.xml',
|
'views/ir_sequence.xml',
|
||||||
'views/ir_property.xml',
|
'views/ir_property.xml',
|
||||||
|
'views/assets.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
10
base_usability/static/src/scss/form_view.scss
Normal file
10
base_usability/static/src/scss/form_view.scss
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.o_form_view {
|
||||||
|
.o_address_format {
|
||||||
|
.o_address_state {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.o_address_zip {
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
base_usability/views/assets.xml
Normal file
15
base_usability/views/assets.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<template
|
||||||
|
id="assets_backend"
|
||||||
|
name="web_sheet_full_width"
|
||||||
|
inherit_id="web.assets_backend"
|
||||||
|
>
|
||||||
|
<xpath expr="." position="inside">
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="/base_usability/static/src/scss/form_view.scss"
|
||||||
|
/>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
<div attrs="{'invisible': [('same_vat_partner_id', '=', False)]}" position="attributes">
|
<div attrs="{'invisible': [('same_vat_partner_id', '=', False)]}" position="attributes">
|
||||||
<attribute name="class">alert alert-warning</attribute>
|
<attribute name="class">alert alert-warning</attribute>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Native order: city / state_id / zip. New order: zip / city / state_id -->
|
||||||
|
<xpath expr="//div[hasclass('o_address_format')]/field[@name='city']" position="before">
|
||||||
|
<field name="zip" position="move"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
28
base_usability/web-advanced_search_startswith.diff
Normal file
28
base_usability/web-advanced_search_startswith.diff
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
diff --git a/addons/web/static/src/js/control_panel/custom_filter_item.js b/addons/web/static/src/js/control_panel/custom_filter_item.js
|
||||||
|
index 6682b660b78..b2ff23344d9 100644
|
||||||
|
--- a/addons/web/static/src/js/control_panel/custom_filter_item.js
|
||||||
|
+++ b/addons/web/static/src/js/control_panel/custom_filter_item.js
|
||||||
|
@@ -180,6 +180,10 @@ odoo.define('web.CustomFilterItem', function (require) {
|
||||||
|
[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]]);
|
||||||
|
}
|
||||||
|
diff --git a/addons/web/static/src/js/control_panel/search_utils.js b/addons/web/static/src/js/control_panel/search_utils.js
|
||||||
|
index 8fce5b23ef6..d240d2e1fb2 100644
|
||||||
|
--- a/addons/web/static/src/js/control_panel/search_utils.js
|
||||||
|
+++ b/addons/web/static/src/js/control_panel/search_utils.js
|
||||||
|
@@ -18,6 +18,8 @@ odoo.define('web.searchUtils', function (require) {
|
||||||
|
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 },
|
||||||
@@ -1,4 +1 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import intrastat_product_type
|
from . import intrastat_product_type
|
||||||
from .post_install import set_intrastat_type_on_products
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Intrastat Product Type',
|
'name': 'Intrastat Product Type',
|
||||||
'version': '12.0.1.0.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Accounting',
|
'category': 'Accounting',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'summary': 'Adds a special field Intrastat Type on Products',
|
'summary': 'Adds a special field Intrastat Type on Products',
|
||||||
@@ -19,9 +19,8 @@ This module adds a field *Intrastat Type* on the Product Form with 2 possible op
|
|||||||
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
||||||
""",
|
""",
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'https://github.com/akretion/odoo-usability',
|
||||||
'depends': ['intrastat_product', 'l10n_fr_intrastat_service'],
|
'depends': ['intrastat_product', 'l10n_fr_intrastat_service'],
|
||||||
'data': ['product_view.xml'],
|
'data': ['product_view.xml'],
|
||||||
'post_init_hook': 'set_intrastat_type_on_products',
|
'installable': True,
|
||||||
'installable': False,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Copyright 2016-2023 Akretion (http://www.akretion.com)
|
||||||
# Copyright 2016-2019 Akretion (http://www.akretion.com)
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
|
||||||
@@ -14,50 +13,36 @@ class ProductTemplate(models.Model):
|
|||||||
intrastat_type = fields.Selection([
|
intrastat_type = fields.Selection([
|
||||||
('product', 'Product'),
|
('product', 'Product'),
|
||||||
('service', 'Service'),
|
('service', 'Service'),
|
||||||
], string='Intrastat Type', default='product', required=True,
|
],
|
||||||
|
compute='_compute_intrastat_type', readonly=False, store=True,
|
||||||
|
required=True, string='Intrastat Type',
|
||||||
help="Type of product used for the intrastat declarations. "
|
help="Type of product used for the intrastat declarations. "
|
||||||
"For example, you can configure a product with "
|
"For example, you can configure a product with "
|
||||||
"'Product Type' = 'Consumable' and 'Intrastat Type' = 'Service'.")
|
"'Product Type' = 'Consumable' and 'Intrastat Type' = 'Service'.")
|
||||||
|
|
||||||
@api.multi
|
|
||||||
@api.constrains('type', 'intrastat_type')
|
@api.constrains('type', 'intrastat_type')
|
||||||
def check_intrastat_type(self):
|
def check_intrastat_type(self):
|
||||||
for pt in self:
|
for pt in self:
|
||||||
if pt.intrastat_type == 'product' and pt.type == 'service':
|
if pt.intrastat_type == 'product' and pt.type == 'service':
|
||||||
raise ValidationError(_(
|
raise ValidationError(_(
|
||||||
"On the product %s, you cannot set Product Type to "
|
"On the product '%s', you cannot set Product Type to "
|
||||||
"'Service' and Intrastat Type to 'Product'.") % pt.name)
|
"'Service' and Intrastat Type to 'Product'.")
|
||||||
|
% pt.display_name)
|
||||||
if pt.intrastat_type == 'service' and pt.type == 'product':
|
if pt.intrastat_type == 'service' and pt.type == 'product':
|
||||||
raise ValidationError(_(
|
raise ValidationError(_(
|
||||||
"On the product %s, you cannot set Intrastat Type to "
|
"On the product '%s', you cannot set Intrastat Type to "
|
||||||
"'Service' and Product Type to 'Stockable product' "
|
"'Service' and Product Type to 'Stockable product' "
|
||||||
"(but you can set Product Type to 'Consumable' or "
|
"(but you can set Product Type to 'Consumable' or "
|
||||||
"'Service').") % pt.name)
|
"'Service').") % pt.display_name)
|
||||||
|
|
||||||
@api.onchange('type')
|
@api.depends('type')
|
||||||
def intrastat_type_onchange(self):
|
def _compute_intrastat_type(self):
|
||||||
if self.type in ('product', 'consu'):
|
for pt in self:
|
||||||
self.intrastat_type = 'product'
|
if pt.type in ('product', 'consu'):
|
||||||
elif self.type == 'service':
|
intrastat_type = 'product'
|
||||||
self.intrastat_type = 'service'
|
else:
|
||||||
|
intrastat_type = 'service'
|
||||||
@api.model
|
pt.intrastat_type = intrastat_type
|
||||||
def create(self, vals):
|
|
||||||
if vals.get('type'):
|
|
||||||
if not vals.get('intrastat_type'):
|
|
||||||
if vals['type'] in ('product', 'consu'):
|
|
||||||
vals['intrastat_type'] = 'product'
|
|
||||||
elif vals['type'] == 'service':
|
|
||||||
vals['intrastat_type'] = 'service'
|
|
||||||
elif (
|
|
||||||
vals.get('intrastat_type') == 'product' and
|
|
||||||
vals['type'] == 'service'):
|
|
||||||
# usefull because intrastat_type = 'product' by default and
|
|
||||||
# wizards in other modules that don't depend on this module
|
|
||||||
# (e.g. sale_rental) may create a product with only
|
|
||||||
# {'type': 'service'} and no 'intrastat_type'
|
|
||||||
vals['intrastat_type'] = 'service'
|
|
||||||
return super(ProductTemplate, self).create(vals)
|
|
||||||
|
|
||||||
|
|
||||||
class L10nFrIntrastatServiceDeclaration(models.Model):
|
class L10nFrIntrastatServiceDeclaration(models.Model):
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2016-2019 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
|
|
||||||
def set_intrastat_type_on_products(cr, registry):
|
|
||||||
cr.execute(
|
|
||||||
"UPDATE product_template SET intrastat_type='service' "
|
|
||||||
"WHERE type='service'")
|
|
||||||
return
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright 2016-2019 Akretion (http://www.akretion.com/)
|
Copyright 2016-2023 Akretion France (http://www.akretion.com/)
|
||||||
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
|
|||||||
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': '14.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>
|
||||||
@@ -31,7 +31,10 @@ Akretion:
|
|||||||
"views/report_pos_order.xml",
|
"views/report_pos_order.xml",
|
||||||
"views/pos_category.xml",
|
"views/pos_category.xml",
|
||||||
"views/pos_session.xml",
|
"views/pos_session.xml",
|
||||||
|
"views/pos_order.xml",
|
||||||
|
"views/pos_config.xml",
|
||||||
"views/product.xml",
|
"views/product.xml",
|
||||||
|
"views/pos_payment_method.xml",
|
||||||
],
|
],
|
||||||
"installable": True,
|
"installable": True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
from . import product
|
from . import product
|
||||||
from . import pos_category
|
from . import pos_category
|
||||||
|
from . import pos_config
|
||||||
from . import pos_payment_method
|
from . import pos_payment_method
|
||||||
|
|||||||
12
pos_usability/models/pos_config.py
Normal file
12
pos_usability/models/pos_config.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# 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 PosConfig(models.Model):
|
||||||
|
_inherit = 'pos.config'
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
@@ -8,5 +8,7 @@ from odoo import fields, models
|
|||||||
class PosPaymentMethod(models.Model):
|
class PosPaymentMethod(models.Model):
|
||||||
_inherit = 'pos.payment.method'
|
_inherit = 'pos.payment.method'
|
||||||
_check_company_auto = True
|
_check_company_auto = True
|
||||||
|
_order = 'sequence, id'
|
||||||
|
|
||||||
cash_journal_id = fields.Many2one(check_company=True)
|
cash_journal_id = fields.Many2one(check_company=True)
|
||||||
|
sequence = fields.Integer(default=10)
|
||||||
|
|||||||
35
pos_usability/pos-payment_terminal-auto_validate.diff
Normal file
35
pos_usability/pos-payment_terminal-auto_validate.diff
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
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 55aa635aa10..d91ea933a71 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
|
||||||
|
@@ -8,6 +8,7 @@ odoo.define('point_of_sale.PaymentScreen', function (require) {
|
||||||
|
const { useListener } = require('web.custom_hooks');
|
||||||
|
const Registries = require('point_of_sale.Registries');
|
||||||
|
const { onChangeOrder } = require('point_of_sale.custom_hooks');
|
||||||
|
+ const utils = require('web.utils');
|
||||||
|
|
||||||
|
class PaymentScreen extends PosComponent {
|
||||||
|
constructor() {
|
||||||
|
@@ -20,6 +21,7 @@ odoo.define('point_of_sale.PaymentScreen', function (require) {
|
||||||
|
useListener('send-payment-cancel', this._sendPaymentCancel);
|
||||||
|
useListener('send-payment-reverse', this._sendPaymentReverse);
|
||||||
|
useListener('send-force-done', this._sendForceDone);
|
||||||
|
+ useListener('validate-order', () => this.validateOrder(false));
|
||||||
|
this.lockedValidateOrder = useAsyncLockedMethod(this.validateOrder);
|
||||||
|
NumberBuffer.use(this._getNumberBufferConfig);
|
||||||
|
onChangeOrder(this._onPrevOrder, this._onNewOrder);
|
||||||
|
@@ -333,6 +335,14 @@ odoo.define('point_of_sale.PaymentScreen', function (require) {
|
||||||
|
if (isPaymentSuccessful) {
|
||||||
|
line.set_payment_status('done');
|
||||||
|
line.can_be_reversed = payment_terminal.supports_reversals;
|
||||||
|
+ // Automatically validate the order if, after an electronic payment,
|
||||||
|
+ // the current order is fully paid (BACKPORT FROM v16)
|
||||||
|
+ if (
|
||||||
|
+ this.currentOrder.is_paid() &&
|
||||||
|
+ utils.float_is_zero(this.currentOrder.get_due(), this.env.pos.currency.decimals)
|
||||||
|
+ ) {
|
||||||
|
+ this.trigger('validate-order');
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
line.set_payment_status('retry');
|
||||||
|
}
|
||||||
22
pos_usability/pos_payment_method-sort_by_sequence.diff
Normal file
22
pos_usability/pos_payment_method-sort_by_sequence.diff
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
|
||||||
|
index 86a7b44bcfb..06b46ba5645 100644
|
||||||
|
--- a/addons/point_of_sale/static/src/js/models.js
|
||||||
|
+++ b/addons/point_of_sale/static/src/js/models.js
|
||||||
|
@@ -514,16 +514,7 @@ exports.PosModel = Backbone.Model.extend({
|
||||||
|
fields: ['name', 'is_cash_count', 'use_payment_terminal'],
|
||||||
|
domain: function(self){return ['|',['active', '=', false], ['active', '=', true]]; },
|
||||||
|
loaded: function(self, payment_methods) {
|
||||||
|
- self.payment_methods = payment_methods.sort(function(a,b){
|
||||||
|
- // prefer cash payment_method to be first in the list
|
||||||
|
- if (a.is_cash_count && !b.is_cash_count) {
|
||||||
|
- return -1;
|
||||||
|
- } else if (!a.is_cash_count && b.is_cash_count) {
|
||||||
|
- return 1;
|
||||||
|
- } else {
|
||||||
|
- return a.id - b.id;
|
||||||
|
- }
|
||||||
|
- });
|
||||||
|
+ self.payment_methods = payment_methods;
|
||||||
|
self.payment_methods_by_id = {};
|
||||||
|
_.each(self.payment_methods, function(payment_method) {
|
||||||
|
self.payment_methods_by_id[payment_method.id] = payment_method;
|
||||||
21
pos_usability/views/pos_config.xml
Normal file
21
pos_usability/views/pos_config.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_pos_config_tree" model="ir.ui.view">
|
||||||
|
<field name="model">pos.config</field>
|
||||||
|
<field name="inherit_id" ref="point_of_sale.view_pos_config_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="before">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
||||||
21
pos_usability/views/pos_order.xml
Normal file
21
pos_usability/views/pos_order.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_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>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
20
pos_usability/views/pos_payment_method.xml
Normal file
20
pos_usability/views/pos_payment_method.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -1,20 +1,50 @@
|
|||||||
# Translation of Odoo Server.
|
# Translation of Odoo Server.
|
||||||
# This file contains the translation of the following modules:
|
# This file contains the translation of the following modules:
|
||||||
# * product_category_tax
|
# * product_category_tax
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Odoo Server 8.0-20150727\n"
|
"Project-Id-Version: Odoo Server 14.0\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2015-12-04 17:16+0000\n"
|
"POT-Creation-Date: 2023-10-16 20:34+0000\n"
|
||||||
"PO-Revision-Date: 2015-12-04 17:16+0000\n"
|
"PO-Revision-Date: 2023-10-16 20:34+0000\n"
|
||||||
"Last-Translator: <>\n"
|
"Last-Translator: Alexis de Lattre <alexis.delattre@akretion.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: \n"
|
"Content-Transfer-Encoding: \n"
|
||||||
"Plural-Forms: \n"
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: product_category_tax
|
||||||
|
#: model:ir.model,name:product_category_tax.model_product_categ_tax_mixin
|
||||||
|
msgid "Common code for taxes on product categories"
|
||||||
|
msgstr "Code commun pour les taxes sur les catégories d'articles"
|
||||||
|
|
||||||
|
#. module: product_category_tax
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_categ_tax_mixin__display_name
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_category__display_name
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_product__display_name
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_template__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "Nom affiché"
|
||||||
|
|
||||||
|
#. module: product_category_tax
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_categ_tax_mixin__id
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_category__id
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_product__id
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_template__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr "ID"
|
||||||
|
|
||||||
|
#. module: product_category_tax
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_categ_tax_mixin____last_update
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_category____last_update
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_product____last_update
|
||||||
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_template____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "Dernière modification le"
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: model:ir.model,name:product_category_tax.model_product_product
|
#: model:ir.model,name:product_category_tax.model_product_product
|
||||||
msgid "Product"
|
msgid "Product"
|
||||||
@@ -23,7 +53,7 @@ msgstr "Article"
|
|||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: model:ir.model,name:product_category_tax.model_product_category
|
#: model:ir.model,name:product_category_tax.model_product_category
|
||||||
msgid "Product Category"
|
msgid "Product Category"
|
||||||
msgstr "Catégorie d'articles"
|
msgstr "Catégorie d'article"
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: model:ir.model,name:product_category_tax.model_product_template
|
#: model:ir.model,name:product_category_tax.model_product_template
|
||||||
@@ -31,23 +61,31 @@ msgid "Product Template"
|
|||||||
msgstr "Modèle d'article"
|
msgstr "Modèle d'article"
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: field:product.category,purchase_tax_ids:0
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_category__purchase_tax_ids
|
||||||
msgid "Purchase Taxes"
|
msgid "Purchase Taxes"
|
||||||
msgstr "Taxes fournisseurs"
|
msgstr "Taxes à l'achat"
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: field:product.category,sale_tax_ids:0
|
#: model:ir.model.fields,field_description:product_category_tax.field_product_category__sale_tax_ids
|
||||||
msgid "Sale Taxes"
|
msgid "Sale Taxes"
|
||||||
msgstr "Taxes à la vente"
|
msgstr "Taxes à la vente"
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: code:addons/product_category_tax/product.py:57
|
#: code:addons/product_category_tax/product.py:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The purchase taxes configured on the product '%s' are not the same as the purchase taxes configured on it's related product category '%s'."
|
msgid ""
|
||||||
msgstr "Les taxes fournisseurs paramétrées sur l'article '%s' ne sont pas les mêmes que les taxes fournisseurs paramétrées sur la catégorie interne '%s'."
|
"The purchase taxes configured on the product '%s' are not the same as the "
|
||||||
|
"purchase taxes configured on it's related internal category '%s'."
|
||||||
|
msgstr ""
|
||||||
|
"Les taxes à l'achat configurées sur l'article '%s' ne sont pas les mêmes que "
|
||||||
|
"les taxes à l'achat configurées sur sa catégorie interne '%s'."
|
||||||
|
|
||||||
#. module: product_category_tax
|
#. module: product_category_tax
|
||||||
#: code:addons/product_category_tax/product.py:49
|
#: code:addons/product_category_tax/product.py:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The sale taxes configured on the product '%s' are not the same as the sale taxes configured on it's related product category '%s'."
|
msgid ""
|
||||||
msgstr "Les taxes à la vente paramétrées sur l'article '%s' ne sont pas les mêmes que les taxes à la vente paramétrées sur la catégorie interne '%s'."
|
"The sale taxes configured on the product '%s' are not the same as the sale "
|
||||||
|
"taxes configured on it's related internal category '%s'."
|
||||||
|
msgstr ""
|
||||||
|
"Les taxes à la vente configurées sur l'article '%s' ne sont pas les mêmes que "
|
||||||
|
"les taxes à la vente configurées sur sa catégorie interne '%s'."
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ This module has been written by Alexis de Lattre from Akretion
|
|||||||
<alexis.delattre@akretion.com>.
|
<alexis.delattre@akretion.com>.
|
||||||
""",
|
""",
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'https://github.com/akretion/odoo-usability',
|
||||||
# We depend on point_of_sale and not only 'product'
|
# We depend on point_of_sale and not only 'product'
|
||||||
# because the price barcode rule is added by the point_of_sale module
|
# because the price barcode rule is added by the point_of_sale module
|
||||||
# (the weight barcode rule is added by the stock module)
|
# (the weight barcode rule is added by the stock module)
|
||||||
@@ -46,6 +46,7 @@ This module has been written by Alexis de Lattre from Akretion
|
|||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'wizard/product_print_zpl_barcode_view.xml',
|
'wizard/product_print_zpl_barcode_view.xml',
|
||||||
'views/product.xml',
|
'views/product.xml',
|
||||||
|
'views/stock_picking.xml',
|
||||||
'data/barcode_sequence.xml',
|
'data/barcode_sequence.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'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 product
|
||||||
|
from . import stock_picking
|
||||||
|
|||||||
@@ -29,22 +29,6 @@ class ProductTemplate(models.Model):
|
|||||||
% (self.display_name, self.product_variant_count))
|
% (self.display_name, self.product_variant_count))
|
||||||
return self.product_variant_ids[0].generate_barcode_from_product_product()
|
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):
|
class ProductProduct(models.Model):
|
||||||
_inherit = 'product.product'
|
_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
|
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_report_to_printer.printing_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_report_to_printer.printing_group_user,1,1,1,1
|
||||||
|
|||||||
|
@@ -27,11 +27,23 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<header position="inside">
|
<header position="inside">
|
||||||
<button name="generate_barcode_from_product_template" type="object" string="Generate Barcode" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '!=', False)]}"/>
|
<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" groups="base_report_to_printer.printing_group_user" attrs="{'invisible': ['|', ('product_variant_count', '>', 1), ('barcode', '=', False)]}"/>
|
||||||
</header>
|
</header>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
||||||
|
<field name="product_variant_count" position="before">
|
||||||
|
<header>
|
||||||
|
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcodes" groups="base_report_to_printer.printing_group_user"/>
|
||||||
|
</header>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="product_normal_form_view" model="ir.ui.view">
|
<record id="product_normal_form_view" model="ir.ui.view">
|
||||||
<field name="model">product.product</field>
|
<field name="model">product.product</field>
|
||||||
@@ -44,5 +56,16 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
||||||
|
<field name="default_code" position="before">
|
||||||
|
<header>
|
||||||
|
<button name="%(product_print_zpl_barcode.product_print_zpl_barcode_action)d" type="action" string="Print Barcodes" groups="base_report_to_printer.printing_group_user"/>
|
||||||
|
</header>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</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,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>
|
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
@@ -9,23 +9,18 @@ from stdnum.ean import is_valid, calc_check_digit
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ProductPrintZplBarcode(models.TransientModel):
|
class ProductPrintZplBarcode(models.TransientModel):
|
||||||
_name = 'product.print.zpl.barcode'
|
_name = 'product.print.zpl.barcode'
|
||||||
_description = 'Generate and print product barcodes in ZPL'
|
_description = 'Generate and print product barcodes in ZPL'
|
||||||
|
_check_company_auto = True
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def default_get(self, fields_list):
|
def default_get(self, fields_list):
|
||||||
res = super().default_get(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')
|
nomenclature = self.env.ref('barcodes.default_barcode_nomenclature')
|
||||||
company = self.env.company
|
company = self.env.company
|
||||||
posconfig = self.env['pos.config'].sudo().search(
|
posconfig = self.env['pos.config'].sudo().search(
|
||||||
@@ -39,82 +34,200 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
], limit=1)
|
], limit=1)
|
||||||
if not pricelist:
|
if not pricelist:
|
||||||
raise UserError(_(
|
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 = self.env['printing.printer'].get_default()
|
||||||
|
|
||||||
|
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({
|
res.update({
|
||||||
|
'company_id': company.id,
|
||||||
'nomenclature_id': nomenclature.id,
|
'nomenclature_id': nomenclature.id,
|
||||||
'pricelist_id': pricelist.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_id': printer and printer.id or False,
|
||||||
|
'line_ids': line_ids,
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
|
|
||||||
product_id = fields.Many2one(
|
@api.model
|
||||||
'product.product', string='Product', required=True, readonly=True)
|
def _update_line_ids(self, line_ids, product, copies=1):
|
||||||
uom_id = fields.Many2one(related='product_id.uom_id')
|
if product.barcode:
|
||||||
# 1 line = un peu moins de 30
|
line_ids.append((0, 0, {
|
||||||
product_name = fields.Char('Product Label', required=True, size=56)
|
'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(
|
nomenclature_id = fields.Many2one(
|
||||||
'barcode.nomenclature', 'Barcode Nomenclature', required=True)
|
'barcode.nomenclature', 'Barcode Nomenclature', required=True,
|
||||||
rule_id = fields.Many2one(
|
states={'step2': [('readonly', True)]})
|
||||||
'barcode.rule', string='Barcode Rule', readonly=True,
|
# label_size: remove readonly=True when we will support more labels
|
||||||
compute='_compute_rule_id')
|
|
||||||
barcode_type = fields.Selection(related='rule_id.type', string="Barcode Type")
|
|
||||||
label_size = fields.Selection([
|
label_size = fields.Selection([
|
||||||
('38x25', '38x25 mm'),
|
('38x25', '38x25 mm'),
|
||||||
], required=True, default='38x25')
|
], required=True, default='38x25', readonly=True)
|
||||||
pricelist_id = fields.Many2one(
|
pricelist_id = fields.Many2one(
|
||||||
'product.pricelist', string='Pricelist', required=True)
|
'product.pricelist', string='Pricelist', required=True,
|
||||||
currency_id = fields.Many2one(related='pricelist_id.currency_id')
|
states={'step2': [('readonly', True)]}, check_company=True,
|
||||||
# TODO: for the moment, we only support weight, but...
|
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
|
||||||
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')
|
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('step1', 'Step1'),
|
('step1', 'Step1'),
|
||||||
('step2', 'Step2'),
|
('step2', 'Step2'),
|
||||||
], default='step1', readonly=True)
|
], default='step1', readonly=True)
|
||||||
zpl_file = fields.Binary(string='ZPL File', readonly=True)
|
zpl_file = fields.Binary(string='ZPL File', readonly=True)
|
||||||
zpl_filename = fields.Char('ZPL Filename')
|
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(
|
zpl_printer_id = fields.Many2one(
|
||||||
'printing.printer', string='ZPL Printer')
|
'printing.printer', string='ZPL Printer')
|
||||||
|
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_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')
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
def _compute_price(self):
|
||||||
# for regular barcodes
|
# for regular barcodes
|
||||||
for wiz in self:
|
for line in self:
|
||||||
if wiz.pricelist_id and wiz.product_id:
|
pricelist = line.parent_id.pricelist_id
|
||||||
price_uom = wiz.pricelist_id.get_product_price(
|
price_uom = price = 0.0
|
||||||
wiz.product_id, 1, False)
|
if pricelist and line.product_id:
|
||||||
wiz.price_uom = price_uom
|
price_uom = pricelist.get_product_price(line.product_id, 1, False)
|
||||||
wiz.price = price_uom * wiz.quantity
|
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):
|
def _compute_rule_id(self):
|
||||||
for wiz in self:
|
for line in self:
|
||||||
|
nomenclature = line.parent_id.nomenclature_id
|
||||||
match_rule = False
|
match_rule = False
|
||||||
if wiz.nomenclature_id and wiz.barcode:
|
if nomenclature and line.barcode:
|
||||||
for rule in wiz.nomenclature_id.rule_ids:
|
for rule in nomenclature.rule_ids:
|
||||||
match = wiz.nomenclature_id.match_pattern(
|
match = nomenclature.match_pattern(
|
||||||
wiz.barcode, rule.pattern)
|
line.barcode, rule.pattern)
|
||||||
if match.get('match'):
|
if match.get('match'):
|
||||||
match_rule = rule.id
|
match_rule = rule.id
|
||||||
break
|
break
|
||||||
wiz.rule_id = match_rule
|
line.rule_id = match_rule
|
||||||
|
|
||||||
def _prepare_price_weight_barcode_type(self):
|
def _prepare_price_weight_barcode_type(self):
|
||||||
dpo = self.env['decimal.precision']
|
dpo = self.env['decimal.precision']
|
||||||
bno = self.env['barcode.nomenclature']
|
|
||||||
prec = dpo.precision_get('Stock Weight')
|
prec = dpo.precision_get('Stock Weight')
|
||||||
value = self.quantity
|
value = self.quantity
|
||||||
pbarcode = self.barcode
|
pbarcode = self.barcode
|
||||||
@@ -139,7 +252,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
barcode = pbarcode[0:len(prefix)]
|
barcode = pbarcode[0:len(prefix)]
|
||||||
# print("barcode=", barcode)
|
# print("barcode=", barcode)
|
||||||
# print("pattern=", pattern)
|
# print("pattern=", pattern)
|
||||||
m = re.search('\{N+D+\}', pattern)
|
m = re.search(r'\{N+D+\}', pattern)
|
||||||
# print("m=", m)
|
# print("m=", m)
|
||||||
assert m
|
assert m
|
||||||
pattern_val = m.group(0)
|
pattern_val = m.group(0)
|
||||||
@@ -172,7 +285,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
assert len(barcode) == 13
|
assert len(barcode) == 13
|
||||||
assert is_valid(barcode)
|
assert is_valid(barcode)
|
||||||
# print("barcode FINAL=", 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,
|
'product_name': self.product_name,
|
||||||
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
||||||
'ean_no_checksum': barcode[:-1],
|
'ean_no_checksum': barcode[:-1],
|
||||||
@@ -183,12 +296,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
'quantity': value,
|
'quantity': value,
|
||||||
'uom_name': self.uom_id.name,
|
'uom_name': self.uom_id.name,
|
||||||
}
|
}
|
||||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
return (barcode, zpl_str)
|
||||||
vals = {
|
|
||||||
'zpl_file': base64.encodebytes(zpl_bytes),
|
|
||||||
'barcode': barcode,
|
|
||||||
}
|
|
||||||
return vals
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _price_weight_barcode_type_zpl(self):
|
def _price_weight_barcode_type_zpl(self):
|
||||||
@@ -229,7 +337,7 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
def _prepare_product_barcode_type(self):
|
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,
|
'product_name': self.product_name,
|
||||||
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
|
||||||
'ean_no_checksum': self.barcode[:-1],
|
'ean_no_checksum': self.barcode[:-1],
|
||||||
@@ -237,60 +345,4 @@ class ProductPrintZplBarcode(models.TransientModel):
|
|||||||
'currency_symbol': self.currency_id.symbol, # symbol is a required field
|
'currency_symbol': self.currency_id.symbol, # symbol is a required field
|
||||||
'copies': self.copies,
|
'copies': self.copies,
|
||||||
}
|
}
|
||||||
zpl_bytes = zpl_unicode.encode('utf-8')
|
return (self.barcode, zpl_str)
|
||||||
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
|
|
||||||
|
|||||||
@@ -11,38 +11,41 @@
|
|||||||
<field name="name">product_print_zpl_barcode.form</field>
|
<field name="name">product_print_zpl_barcode.form</field>
|
||||||
<field name="model">product.print.zpl.barcode</field>
|
<field name="model">product.print.zpl.barcode</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Generate and Print Product Barcode">
|
<form>
|
||||||
<group name="step1" string="Configuration">
|
<group name="step1">
|
||||||
<field name="state" invisible="1"/>
|
<field name="state" invisible="1"/>
|
||||||
<field name="currency_id" invisible="1"/>
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
<field name="product_id"/>
|
<field name="company_id" invisible="1"/>
|
||||||
<field name="product_name" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
|
||||||
<field name="pricelist_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
<field name="pricelist_id" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||||
<field name="price_uom"/>
|
|
||||||
<field name="label_size" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
<field name="label_size" attrs="{'readonly': [('state', '=', 'step2')]}"/>
|
||||||
<field name="nomenclature_id" 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>
|
||||||
<group string="Enter Quantity" attrs="{'invisible': [('barcode_type', '=', 'product')]}">
|
<group name="step2" states="step2">
|
||||||
<div name="qty_uom">
|
|
||||||
<field name="quantity" attrs="{'readonly': [('state', '=', 'step2')]}" class="oe_inline"/>
|
|
||||||
<field name="uom_id" class="oe_inline"/>
|
|
||||||
</div>
|
|
||||||
</group>
|
|
||||||
<group name="step2" states="step2" string="Label">
|
|
||||||
<field name="price" attrs="{'invisible': [('barcode_type', 'not in', ('price', 'weight'))]}"/>
|
|
||||||
<field name="zpl_file" filename="zpl_filename" />
|
<field name="zpl_file" filename="zpl_filename" />
|
||||||
<field name="zpl_filename" invisible="1"/>
|
<field name="zpl_filename" invisible="1"/>
|
||||||
<field name="zpl_printer_id" attrs="{'required': [('state', '=', 'step2')]}"/>
|
<field name="zpl_printer_id" attrs="{'required': [('state', '=', 'step2')]}"/>
|
||||||
</group>
|
</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>
|
<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 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" 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"/>
|
<button special="cancel" string="Close" class="btn-default" states="step2"/>
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Product Priority Star
|
|||||||
|
|
||||||
This module adds a priority star on products (like on pickings and manufacturing order). If the star is yellow, the product will be displayed at the top.
|
This module adds a priority star on products (like on pickings and manufacturing order). If the star is yellow, the product will be displayed at the top.
|
||||||
|
|
||||||
|
This feature is native in Odoo 16.0.
|
||||||
|
|
||||||
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
|
||||||
""",
|
""",
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
|||||||
'views/product_product.xml',
|
'views/product_product.xml',
|
||||||
'views/product_config_menu.xml',
|
'views/product_config_menu.xml',
|
||||||
'views/product_category_view.xml',
|
'views/product_category_view.xml',
|
||||||
|
'views/product_attribute_view.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ from . import product_template
|
|||||||
from . import product_supplierinfo
|
from . import product_supplierinfo
|
||||||
from . import product_pricelist
|
from . import product_pricelist
|
||||||
from . import product_category
|
from . import product_category
|
||||||
|
from . import product_attribute
|
||||||
|
|||||||
27
product_usability/models/product_attribute.py
Normal file
27
product_usability/models/product_attribute.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Copyright (C) 2022 Akretion (<http://www.akretion.com>).
|
||||||
|
# @author Kévin Roche <kevin.roche@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
class ProductAttribute(models.Model):
|
||||||
|
_inherit = "product.attribute"
|
||||||
|
|
||||||
|
values_count = fields.Integer(compute="_compute_values_count")
|
||||||
|
|
||||||
|
@api.depends("value_ids")
|
||||||
|
def _compute_values_count(self):
|
||||||
|
for attr in self:
|
||||||
|
attr.values_count = len(attr.value_ids)
|
||||||
|
|
||||||
|
def show_values_ids(self):
|
||||||
|
return {
|
||||||
|
"name": "Attributes Lines",
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"res_id": self.id,
|
||||||
|
"view_mode": "tree",
|
||||||
|
"res_model": "product.attribute.value",
|
||||||
|
"view_id":self.env.ref("product_usability.product_attribute_value_view_tree").id,
|
||||||
|
"target": "current",
|
||||||
|
"domain": [("id", "in", self.value_ids.ids)],
|
||||||
|
}
|
||||||
38
product_usability/views/product_attribute_view.xml
Normal file
38
product_usability/views/product_attribute_view.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="product_attribute_values_button" model="ir.ui.view">
|
||||||
|
<field name="model">product.attribute</field>
|
||||||
|
<field name="inherit_id" ref="product.product_attribute_view_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<group name="main_fields" position='before'>
|
||||||
|
<div class="oe_button_box" name="button_box">
|
||||||
|
<button
|
||||||
|
name="show_values_ids"
|
||||||
|
type="object"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-tasks"
|
||||||
|
>
|
||||||
|
<field
|
||||||
|
name="values_count"
|
||||||
|
widget="statinfo"
|
||||||
|
string="Attribute Values"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="product_attribute_value_view_tree">
|
||||||
|
<field name="name">product.attribute.value.view.tree</field>
|
||||||
|
<field name="model">product.attribute.value</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Attributes Values">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="is_custom"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -32,11 +32,13 @@
|
|||||||
<field name="model">product.pricelist.item</field>
|
<field name="model">product.pricelist.item</field>
|
||||||
<field name="inherit_id" ref="product.product_pricelist_item_form_view"/>
|
<field name="inherit_id" ref="product.product_pricelist_item_form_view"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<field name="applied_on" position="before">
|
||||||
|
<field name="pricelist_id" position="move"/>
|
||||||
|
</field>
|
||||||
<field name="pricelist_id" position="attributes">
|
<field name="pricelist_id" position="attributes">
|
||||||
<attribute name="invisible">not context.get('product_pricelist_item_main_view')</attribute>
|
<attribute name="invisible">context.get('from_product_pricelist_view')</attribute>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<field name="res_model">product.pricelist.item</field>
|
<field name="res_model">product.pricelist.item</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="domain">[('pricelist_id', '=', active_id)]</field>
|
<field name="domain">[('pricelist_id', '=', active_id)]</field>
|
||||||
<field name="context">{'product_pricelist_item_main_view': True}</field>
|
<field name="context">{'product_pricelist_item_main_view': True, 'default_pricelist_id': active_id}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="product_pricelist_view" model="ir.ui.view">
|
<record id="product_pricelist_view" model="ir.ui.view">
|
||||||
@@ -34,6 +34,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<field name="item_ids" position="attributes">
|
||||||
|
<attribute name="context">{'from_product_pricelist_view': True, 'default_base': 'list_price'}</attribute>
|
||||||
|
</field>
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -32,5 +32,16 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">usability.product.product.tree</field>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="inherit_id" ref="product.product_product_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="company_id" position="before">
|
||||||
|
<field name="seller_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<field name="inherit_id" ref="product.product_template_search_view" />
|
<field name="inherit_id" ref="product.product_template_search_view" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="categ_id" position="after">
|
<field name="categ_id" position="after">
|
||||||
<field name="seller_ids" string="Supplier" filter_domain="[('seller_ids.name', 'ilike', self)]"/>
|
<field name="seller_id" string="Main Supplier"/>
|
||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -32,4 +32,14 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</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">
|
||||||
|
<field name="barcode" position="after">
|
||||||
|
<field name="seller_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
0
project_usability/__init__.py
Normal file
0
project_usability/__init__.py
Normal file
20
project_usability/__manifest__.py
Normal file
20
project_usability/__manifest__.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Project Usability',
|
||||||
|
'version': '14.0.1.0.0',
|
||||||
|
'category': 'Services/Project',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'summary': 'Usability improvements on project module',
|
||||||
|
'author': 'Akretion',
|
||||||
|
'website': 'https://github.com/akretion/odoo-usability',
|
||||||
|
'depends': [
|
||||||
|
'project',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/project_project.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
}
|
||||||
26
project_usability/views/project_project.xml
Normal file
26
project_usability/views/project_project.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<!-- user_id is not displayed by default in project kanban view (it is native in v16) -->
|
||||||
|
<record id="view_project_kanban" model="ir.ui.view">
|
||||||
|
<field name="model">project.project</field>
|
||||||
|
<field name="inherit_id" ref="project.view_project_kanban"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="user_id"/>
|
||||||
|
</field>
|
||||||
|
<xpath expr="//div[hasclass('o_project_kanban_boxes')]" position="inside">
|
||||||
|
<a t-if="record.user_id.raw_value" class="o_project_kanban_box">
|
||||||
|
<field name="user_id" widget="many2one_avatar_user"/>
|
||||||
|
</a>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -23,7 +23,9 @@ Please contact Alexis de Lattre from Akretion <alexis.delattre@akretion.com> for
|
|||||||
'purchase_usability',
|
'purchase_usability',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
|
'views/purchase_order.xml',
|
||||||
'views/stock_picking.xml',
|
'views/stock_picking.xml',
|
||||||
|
'views/stock_warehouse_orderpoint.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
from . import purchase
|
from . import purchase
|
||||||
|
from . import stock_warehouse_orderpoint
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Copyright 2015-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockWarehouseOrderpoint(models.Model):
|
||||||
|
_inherit = 'stock.warehouse.orderpoint'
|
||||||
|
|
||||||
|
# Field needed to be able to search on supplier in the "Replenish" tree view
|
||||||
|
# I put it in purchase_stock_usability and not stock_usability
|
||||||
|
# because I wanted to use the field 'show_supplier' defined in purchase_stock
|
||||||
|
# (but I don't use it in the end because its computation returns False even
|
||||||
|
# on products with a Buy route) and I may also
|
||||||
|
# one day interact with supplier_id (M2O product.supplierinfo) defined in
|
||||||
|
# purchase_stock
|
||||||
|
seller_id = fields.Many2one(
|
||||||
|
related='product_id.product_tmpl_id.seller_ids.name',
|
||||||
|
store=True, string='Supplier',
|
||||||
|
)
|
||||||
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="date_planned" 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.purchase_order_view_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="date_planned" 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="date_planned" position="after">
|
||||||
|
<field name="picking_type_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2014-2020 Akretion (http://www.akretion.com/)
|
||||||
|
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
-->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="view_warehouse_orderpoint_tree_editable" model="ir.ui.view">
|
||||||
|
<field name="model">stock.warehouse.orderpoint</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="product_id" position="after">
|
||||||
|
<field name="seller_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_warehouse_orderpoint_tree_editable_config" model="ir.ui.view">
|
||||||
|
<field name="model">stock.warehouse.orderpoint</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable_config" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="product_id" position="after">
|
||||||
|
<field name="seller_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="stock_reorder_report_search" model="ir.ui.view">
|
||||||
|
<field name="model">stock.warehouse.orderpoint</field>
|
||||||
|
<field name="inherit_id" ref="stock.stock_reorder_report_search"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="product_id" position="after">
|
||||||
|
<field name="seller_id" domain="[('parent_id', '=', False)]"/>
|
||||||
|
</field>
|
||||||
|
<filter name="groupby_category" position="after">
|
||||||
|
<filter string="Supplier" name="seller_id_groupby" context="{'group_by': 'seller_id'}"/>
|
||||||
|
</filter>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -68,6 +68,13 @@ class PurchaseOrder(models.Model):
|
|||||||
# ]
|
# ]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def _prepare_invoice(self):
|
||||||
|
# Don't write self.partner_ref on 'ref' of invoice... ref is for the
|
||||||
|
# supplier invoice number !
|
||||||
|
vals = super()._prepare_invoice()
|
||||||
|
vals["ref"] = ''
|
||||||
|
return vals
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLine(models.Model):
|
class PurchaseOrderLine(models.Model):
|
||||||
_inherit = 'purchase.order.line'
|
_inherit = 'purchase.order.line'
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from odoo import api, models
|
|||||||
class CrmLead(models.Model):
|
class CrmLead(models.Model):
|
||||||
_inherit = 'crm.lead'
|
_inherit = 'crm.lead'
|
||||||
|
|
||||||
|
# not needed in v16 because it has been fixed in sale_crm
|
||||||
def action_view_sale_quotation(self):
|
def action_view_sale_quotation(self):
|
||||||
action = super().action_view_sale_quotation()
|
action = super().action_view_sale_quotation()
|
||||||
if 'search_default_partner_id' in action['context']:
|
if 'search_default_partner_id' in action['context']:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ This module has been written by Alexis de Lattre from Akretion
|
|||||||
""",
|
""",
|
||||||
'author': 'Akretion',
|
'author': 'Akretion',
|
||||||
'website': 'http://www.akretion.com',
|
'website': 'http://www.akretion.com',
|
||||||
'depends': ['sale_stock'],
|
'depends': ['sale_stock', 'base_view_inheritance_extension'],
|
||||||
'data': ['views/sale_order.xml'],
|
'data': ['views/sale_order.xml'],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,11 @@
|
|||||||
<field name="partner_shipping_id" position="after">
|
<field name="partner_shipping_id" position="after">
|
||||||
<field name="route_id" options="{'no_create_edit': True}"/>
|
<field name="route_id" options="{'no_create_edit': True}"/>
|
||||||
</field>
|
</field>
|
||||||
|
<!-- propagate route_id to lines: it's important when you add an order line AFTER
|
||||||
|
order confirmation -->
|
||||||
|
<field name="order_line" position="attributes">
|
||||||
|
<attribute name="context" operation="python_dict" key="default_route_id">route_id</attribute>
|
||||||
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -399,11 +399,6 @@ msgstr ""
|
|||||||
"Sélectionner l'option 'Avertissement' informera l'utilisateur du Message. Sélectionner 'Message Bloquant' lancera une "
|
"Sélectionner l'option 'Avertissement' informera l'utilisateur du Message. Sélectionner 'Message Bloquant' lancera une "
|
||||||
"exception avec le message et bloquera le flux. Le message doit être écrit dans le champ suivant."
|
"exception avec le message et bloquera le flux. Le message doit être écrit dans le champ suivant."
|
||||||
|
|
||||||
#. module: sale_usability
|
|
||||||
#: model_terms:ir.ui.view,arch_db:sale_usability.view_order_form
|
|
||||||
msgid "Send Order Acknowledgement"
|
|
||||||
msgstr "Confirmation de commande"
|
|
||||||
|
|
||||||
#. module: sale_usability
|
#. module: sale_usability
|
||||||
#: model_terms:ir.ui.view,arch_db:sale_usability.view_sales_order_filter
|
#: model_terms:ir.ui.view,arch_db:sale_usability.view_sales_order_filter
|
||||||
msgid "State"
|
msgid "State"
|
||||||
|
|||||||
@@ -32,9 +32,6 @@
|
|||||||
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="after">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="after">
|
||||||
<field name="product_barcode" optional="hide"/>
|
<field name="product_barcode" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<button name="action_quotation_send" attrs="{'invisible': ['|', ('state', '=', 'draft'), ('invoice_count','>=',1)]}" position="before">
|
|
||||||
<button name="action_quotation_send" type="object" string="Send Order Acknowledgement" attrs="{'invisible': ['|', ('state', 'not in', ('sale', 'done')), ('invoice_count','>=',1)]}"/>
|
|
||||||
</button>
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class StockQuant(models.Model):
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
|
"picking_id": picking_id,
|
||||||
"product_id": product_id,
|
"product_id": product_id,
|
||||||
"product_uom_id": uom_id,
|
"product_uom_id": uom_id,
|
||||||
"qty_done": qty,
|
"qty_done": qty,
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from . import stock_move
|
from . import stock_move
|
||||||
|
from . import stock_picking
|
||||||
|
from . import stock_picking_type
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# @author Kévin Roche <kevin.roche@akretion.com>
|
# @author Kévin Roche <kevin.roche@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
@@ -9,28 +10,35 @@ class StockMove(models.Model):
|
|||||||
_inherit = "stock.move"
|
_inherit = "stock.move"
|
||||||
|
|
||||||
location_dest_list = fields.Text(
|
location_dest_list = fields.Text(
|
||||||
string="Locations", compute="_compute_locations_dest_list"
|
string="Dest. Locations", compute="_compute_locations_list"
|
||||||
|
)
|
||||||
|
|
||||||
|
location_src_list = fields.Text(
|
||||||
|
string="Src. Locations", compute="_compute_locations_list"
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"move_line_ids", "move_line_ids.location_dest_id", "move_line_ids.qty_done"
|
"move_line_ids.location_dest_id",
|
||||||
|
"move_line_ids.location_id",
|
||||||
|
"move_line_ids.qty_done",
|
||||||
)
|
)
|
||||||
def _compute_locations_dest_list(self):
|
def _compute_locations_list(self):
|
||||||
|
def format_loc(data):
|
||||||
|
return "\n ".join([
|
||||||
|
f"{int(qty) if qty.is_integer() else qty}: {location.name}"
|
||||||
|
for location, qty in data.items()
|
||||||
|
])
|
||||||
for move in self:
|
for move in self:
|
||||||
data = []
|
dest = defaultdict(int)
|
||||||
separator = ", "
|
src = defaultdict(int)
|
||||||
dest_list = move.move_line_ids.location_dest_id
|
for line in move.move_line_ids:
|
||||||
for dest in dest_list:
|
dest[line.location_dest_id] += line.qty_done
|
||||||
lines_qty = move.move_line_ids.search(
|
src[line.location_id] += line.qty_done
|
||||||
[("move_id", "=", move.id), ("location_dest_id", "=", dest.id)]
|
move.location_src_list = format_loc(src)
|
||||||
).mapped("qty_done")
|
move.location_dest_list = format_loc(dest)
|
||||||
quantity = int(sum(lines_qty))
|
|
||||||
location = dest.name
|
|
||||||
data.append("{}: {}".format(quantity, location))
|
|
||||||
move.location_dest_list = separator.join(data)
|
|
||||||
|
|
||||||
def _compute_is_quantity_done_editable(self):
|
def _compute_is_quantity_done_editable(self):
|
||||||
super()._compute_is_quantity_done_editable()
|
super()._compute_is_quantity_done_editable()
|
||||||
for move in self:
|
for move in self:
|
||||||
if len(move.move_line_ids) == 1 and move.show_details_visible:
|
if len(move.move_line_ids) <= 1:
|
||||||
move.is_quantity_done_editable = True
|
move.is_quantity_done_editable = True
|
||||||
|
|||||||
26
stock_reception_usability/models/stock_picking.py
Normal file
26
stock_reception_usability/models/stock_picking.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2024 Akretion (https://www.akretion.com).
|
||||||
|
# @author Sébastien BEAU <sebastien.beau@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class StockPicking(models.Model):
|
||||||
|
_inherit = 'stock.picking'
|
||||||
|
|
||||||
|
show_src_location = fields.Boolean(compute="_compute_show_location")
|
||||||
|
show_dest_location = fields.Boolean(compute="_compute_show_location")
|
||||||
|
|
||||||
|
@api.depends("picking_type_id.code", "location_id", "location_dest_id")
|
||||||
|
def _compute_show_location(self):
|
||||||
|
for picking in self:
|
||||||
|
picking.show_src_location = (
|
||||||
|
picking.picking_type_id.code != "incoming"
|
||||||
|
and picking.location_id.child_ids
|
||||||
|
)
|
||||||
|
picking.show_dest_location = (
|
||||||
|
picking.picking_type_id.code != "outgoing"
|
||||||
|
and picking.location_dest_id.child_ids
|
||||||
|
)
|
||||||
17
stock_reception_usability/models/stock_picking_type.py
Normal file
17
stock_reception_usability/models/stock_picking_type.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Copyright 2024 Akretion (https://www.akretion.com).
|
||||||
|
# @author Sébastien BEAU <sebastien.beau@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockPickingType(models.Model):
|
||||||
|
_inherit = 'stock.picking.type'
|
||||||
|
|
||||||
|
def _default_show_operations(self):
|
||||||
|
# super is not called as we want to always force the value to False
|
||||||
|
# super()._default_show_operations()
|
||||||
|
return False
|
||||||
|
|
||||||
|
show_operations = fields.Boolean(default=lambda s: s._default_show_operations())
|
||||||
@@ -4,8 +4,13 @@
|
|||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
<field name="inherit_id" ref="stock.view_picking_form" />
|
<field name="inherit_id" ref="stock.view_picking_form" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<field name="picking_type_code" position="after">
|
||||||
|
<field name="show_src_location" invisible="1"/>
|
||||||
|
<field name="show_dest_location" invisible="1"/>
|
||||||
|
</field>
|
||||||
<xpath expr="//field[@name='product_uom']" position="after">
|
<xpath expr="//field[@name='product_uom']" position="after">
|
||||||
<field name="location_dest_list" />
|
<field name="location_src_list" attrs="{'column_invisible': [('parent.show_src_location', '=', False)]}"/>
|
||||||
|
<field name="location_dest_list" attrs="{'column_invisible': [('parent.show_dest_location', '=', False)]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='product_uom_qty']" position="attributes">
|
<xpath expr="//field[@name='product_uom_qty']" position="attributes">
|
||||||
<attribute
|
<attribute
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ This module has been written by Alexis de Lattre from Akretion <alexis.delattre@
|
|||||||
'views/stock_location.xml',
|
'views/stock_location.xml',
|
||||||
'views/stock_move.xml',
|
'views/stock_move.xml',
|
||||||
'views/stock_picking.xml',
|
'views/stock_picking.xml',
|
||||||
|
'views/stock_picking_type.xml',
|
||||||
'views/stock_warehouse.xml',
|
'views/stock_warehouse.xml',
|
||||||
'views/stock_warehouse_orderpoint.xml',
|
'views/stock_warehouse_orderpoint.xml',
|
||||||
'views/product.xml',
|
'views/product.xml',
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from . import stock_move
|
from . import stock_move
|
||||||
from . import stock_picking
|
from . import stock_picking
|
||||||
|
from . import stock_picking_type
|
||||||
|
from . import stock_location
|
||||||
from . import stock_location_route
|
from . import stock_location_route
|
||||||
from . import stock_warehouse_orderpoint
|
from . import stock_warehouse_orderpoint
|
||||||
from . import stock_quant
|
from . import stock_quant
|
||||||
|
|||||||
14
stock_usability/models/stock_location.py
Normal file
14
stock_usability/models/stock_location.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 fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockLocation(models.Model):
|
||||||
|
_inherit = 'stock.location'
|
||||||
|
|
||||||
|
def action_show_quants(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env['stock.quant']._get_quants_action(domain=[('location_id', 'child_of', self.id)])
|
||||||
|
return action
|
||||||
@@ -3,9 +3,6 @@
|
|||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import fields, models, _
|
from odoo import fields, models, _
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class StockPicking(models.Model):
|
class StockPicking(models.Model):
|
||||||
@@ -24,9 +21,3 @@ class StockPicking(models.Model):
|
|||||||
for pick in self:
|
for pick in self:
|
||||||
pick.message_post(body=_("Picking <b>unreserved</b>."))
|
pick.message_post(body=_("Picking <b>unreserved</b>."))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
class StockPickingType(models.Model):
|
|
||||||
_inherit = 'stock.picking.type'
|
|
||||||
|
|
||||||
name = fields.Char(translate=False)
|
|
||||||
|
|||||||
27
stock_usability/models/stock_picking_type.py
Normal file
27
stock_usability/models/stock_picking_type.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Copyright 2014-2020 Akretion (http://www.akretion.com)
|
||||||
|
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockPickingType(models.Model):
|
||||||
|
_inherit = 'stock.picking.type'
|
||||||
|
|
||||||
|
name = fields.Char(translate=False)
|
||||||
|
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
|
||||||
@@ -24,5 +24,5 @@ class StockQuant(models.Model):
|
|||||||
('package_id', '=', self.package_id.id or False),
|
('package_id', '=', self.package_id.id or False),
|
||||||
('result_package_id', '=', self.package_id.id or False),
|
('result_package_id', '=', self.package_id.id or False),
|
||||||
]
|
]
|
||||||
action['context'] = {'create': 0}
|
action['context'] = {'create': 0, 'stock_move_line_main_view': True}
|
||||||
return action
|
return action
|
||||||
|
|||||||
@@ -40,6 +40,10 @@
|
|||||||
string="Reordering Rules"
|
string="Reordering Rules"
|
||||||
class="oe_stat_button" icon="fa-refresh"/>
|
class="oe_stat_button" icon="fa-refresh"/>
|
||||||
</div>
|
</div>
|
||||||
|
<button name="%(stock.location_open_quants)d" position="attributes">
|
||||||
|
<attribute name="type">object</attribute>
|
||||||
|
<attribute name="name">action_show_quants</attribute>
|
||||||
|
</button>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,10 @@
|
|||||||
<field name="product_id" position="after">
|
<field name="product_id" position="after">
|
||||||
<field name="product_barcode" optional="hide"/>
|
<field name="product_barcode" optional="hide"/>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="reference" position="after">
|
||||||
|
<field name="origin" optional="hide"/>
|
||||||
|
<field name="partner_id" optional="hide"/>
|
||||||
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -69,6 +73,18 @@
|
|||||||
<field name="context">{'search_default_done': 1}</field>
|
<field name="context">{'search_default_done': 1}</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="view_move_line_form" model="ir.ui.view">
|
||||||
|
<field name="name">stock_usability.stock.move.line.form</field>
|
||||||
|
<field name="model">stock.move.line</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_move_line_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="reference" position="before">
|
||||||
|
<field name="picking_id" attrs="{'invisible': [('picking_id', '=', False)]}"/>
|
||||||
|
<field name="production_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="view_move_line_tree" model="ir.ui.view">
|
<record id="view_move_line_tree" model="ir.ui.view">
|
||||||
<field name="name">stock_usability.stock.move.line.tree</field>
|
<field name="name">stock_usability.stock.move.line.tree</field>
|
||||||
<field name="model">stock.move.line</field>
|
<field name="model">stock.move.line</field>
|
||||||
|
|||||||
22
stock_usability/views/stock_picking_type.xml
Normal file
22
stock_usability/views/stock_picking_type.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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="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="code" position="after">
|
||||||
|
<field name="is_dropship"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -25,6 +25,14 @@
|
|||||||
<field name="trigger" position="attributes">
|
<field name="trigger" position="attributes">
|
||||||
<attribute name="optional">show</attribute>
|
<attribute name="optional">show</attribute>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="route_id" position="attributes">
|
||||||
|
<attribute name="optional">show</attribute>
|
||||||
|
</field>
|
||||||
|
<!-- Button 'Automate Orders' is not needed because the user
|
||||||
|
can manually change the 'Trigger' field. And it takes a
|
||||||
|
lot of room in the tree view -->
|
||||||
|
<!-- TODO I tried many things to avoid a replace... but it didn't work -->
|
||||||
|
<button name="action_replenish_auto" position="replace"/>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -39,5 +47,4 @@
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
required=True)
|
required=True)
|
||||||
warehouse_id = fields.Many2one(
|
warehouse_id = fields.Many2one(
|
||||||
'stock.warehouse', string='Warehouse', check_company=True,
|
'stock.warehouse', string='Warehouse', check_company=True,
|
||||||
domain="[('company_id', '=', company_id)]")
|
domain="[('company_id', 'in', [False, company_id])]")
|
||||||
location_id = fields.Many2one(
|
location_id = fields.Many2one(
|
||||||
'stock.location', string='Root Stock Location', required=True,
|
'stock.location', string='Root Stock Location', required=True,
|
||||||
domain="[('usage', 'in', ('view', 'internal')), ('company_id', '=', company_id)]",
|
domain="[('usage', 'in', ('view', 'internal')), ('company_id', 'in', [False, company_id])]",
|
||||||
default=lambda self: self._default_location(), check_company=True,
|
default=lambda self: self._default_location(), check_company=True,
|
||||||
help="The childen locations of the selected locations will "
|
help="The childen locations of the selected locations will "
|
||||||
"be taken in the valuation.")
|
"be taken in the valuation.")
|
||||||
@@ -75,7 +75,7 @@ class StockValuationXlsx(models.TransientModel):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_location(self):
|
def _default_location(self):
|
||||||
wh = self.env.ref('stock.warehouse0')
|
wh = self.env["stock.warehouse"].search([], limit=1)
|
||||||
return wh.lot_stock_id
|
return wh.lot_stock_id
|
||||||
|
|
||||||
@api.onchange('warehouse_id')
|
@api.onchange('warehouse_id')
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
required=True)
|
required=True)
|
||||||
warehouse_id = fields.Many2one(
|
warehouse_id = fields.Many2one(
|
||||||
'stock.warehouse', string='Warehouse', check_company=True,
|
'stock.warehouse', string='Warehouse', check_company=True,
|
||||||
domain="[('company_id', '=', company_id)]")
|
domain="[('company_id', 'in', [False, company_id])]")
|
||||||
location_id = fields.Many2one(
|
location_id = fields.Many2one(
|
||||||
'stock.location', string='Root Stock Location', required=True,
|
'stock.location', string='Root Stock Location', required=True,
|
||||||
domain="[('usage', 'in', ('view', 'internal')), ('company_id', '=', company_id)]",
|
domain="[('usage', 'in', ('view', 'internal')), ('company_id', 'in', [False, company_id])]",
|
||||||
default=lambda self: self._default_location(), check_company=True,
|
default=lambda self: self._default_location(), check_company=True,
|
||||||
help="The childen locations of the selected locations will "
|
help="The childen locations of the selected locations will "
|
||||||
"be taken in the valuation.")
|
"be taken in the valuation.")
|
||||||
@@ -58,7 +58,7 @@ class StockVariationXlsx(models.TransientModel):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_location(self):
|
def _default_location(self):
|
||||||
wh = self.env.ref('stock.warehouse0')
|
wh = self.env["stock.warehouse"].search([], limit=1)
|
||||||
return wh.lot_stock_id
|
return wh.lot_stock_id
|
||||||
|
|
||||||
@api.onchange('warehouse_id')
|
@api.onchange('warehouse_id')
|
||||||
|
|||||||
Reference in New Issue
Block a user