mirror of
https://github.com/bringout/ventor.git
synced 2026-04-20 19:52:09 +02:00
Initial commit: Ventor Odoo packages (4 packages)
This commit is contained in:
commit
1f20ad87e6
190 changed files with 10375 additions and 0 deletions
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2020 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from . import stock_location
|
||||
from . import stock_picking
|
||||
from . import stock_quant
|
||||
from . import res_company
|
||||
from . import res_config
|
||||
from . import res_users
|
||||
from . import stock_warehouse
|
||||
from . import ventor_option_setting
|
||||
from . import pallet_transfer
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2022 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
# Odoo:
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class PalletTransfer(models.Model):
|
||||
_name = "pallet.transfer"
|
||||
_description = "All Pallet Transfers"
|
||||
|
||||
name = fields.Char(
|
||||
string="Transfer Name",
|
||||
required=True,
|
||||
default="New",
|
||||
)
|
||||
pallet_name = fields.Char(string="Pallet Name")
|
||||
pallet_id = fields.Many2one("stock.location", string="Pallets")
|
||||
source_location_id = fields.Many2one("stock.location", string="Source Location")
|
||||
destination_location_id = fields.Many2one("stock.location", string="Destination Location")
|
||||
|
||||
def update_value(self):
|
||||
self.write(
|
||||
{
|
||||
"name": self.env["ir.sequence"].next_by_code("pallet.transfer"),
|
||||
"pallet_name": self.pallet_id.name,
|
||||
"source_location_id": self.pallet_id.location_id,
|
||||
}
|
||||
)
|
||||
self.pallet_id.location_id = self.destination_location_id
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
if res:
|
||||
res.update_value()
|
||||
return res
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# Copyright 2020 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import _, models, fields
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import io
|
||||
import base64
|
||||
from PIL import Image
|
||||
|
||||
|
||||
LOGOTYPE_W = 500
|
||||
LOGOTYPE_H = 500
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
barcode_on_picking_document = fields.Boolean(
|
||||
string='Show Sales Order Barcode on Picking document',
|
||||
help='Showing a barcode of the related sales order on all printed picking documents',
|
||||
)
|
||||
|
||||
force_lot_validation_on_inventory_adjustment = fields.Boolean(
|
||||
string='Force Lot Validation on Inventory Adjustment',
|
||||
)
|
||||
|
||||
logotype_file = fields.Binary('Ventor Application Logo File', default=False)
|
||||
|
||||
def _validate_logotype(self, vals):
|
||||
if not vals.get('logotype_file'):
|
||||
return False
|
||||
|
||||
dat = base64.decodebytes(vals.get('logotype_file'))
|
||||
|
||||
image = Image.open(io.BytesIO(dat))
|
||||
if image.format.lower() != 'png':
|
||||
raise UserError(
|
||||
_(
|
||||
'Apparently, the logotype is not a .png file'
|
||||
' or the file was incorrectly converted to .png format'
|
||||
)
|
||||
)
|
||||
|
||||
if int(image.width) < LOGOTYPE_W or int(image.height) < LOGOTYPE_H:
|
||||
raise UserError(
|
||||
_(
|
||||
'The logotype can\'t be less than {}x{} px.'.format(LOGOTYPE_W, LOGOTYPE_H)
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
def write(self, vals):
|
||||
if 'logotype_file' in vals:
|
||||
self._validate_logotype(vals)
|
||||
res = super(Company, self).write(vals)
|
||||
return res
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
# Copyright 2020 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import models, fields, api
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VentorConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
logotype_file = fields.Binary(
|
||||
string='Ventor Application Logo File',
|
||||
related='company_id.logotype_file',
|
||||
readonly=False
|
||||
)
|
||||
|
||||
module_outgoing_routing = fields.Boolean(
|
||||
string='Outgoing Routing'
|
||||
)
|
||||
|
||||
add_barcode_on_view = fields.Boolean(
|
||||
string='Show the Location barcode field on the form',
|
||||
)
|
||||
|
||||
base_version = fields.Char(
|
||||
string='Base Module Version',
|
||||
compute='_compute_base_version',
|
||||
store=False,
|
||||
)
|
||||
|
||||
barcode_on_picking_document = fields.Boolean(
|
||||
string='Show Sales Order Barcode on Picking document',
|
||||
readonly=False,
|
||||
related='company_id.barcode_on_picking_document',
|
||||
)
|
||||
|
||||
force_lot_validation_on_inventory_adjustment = fields.Boolean(
|
||||
string='Force Lot Validation on Inventory Adjustment',
|
||||
readonly=False,
|
||||
related='company_id.force_lot_validation_on_inventory_adjustment',
|
||||
)
|
||||
|
||||
custom_package_name = fields.Char(
|
||||
string='Custom package name',
|
||||
config_parameter='ventor_base.custom_package_name',
|
||||
)
|
||||
|
||||
@api.depends('company_id')
|
||||
def _compute_base_version(self):
|
||||
self.env.cr.execute(
|
||||
"SELECT latest_version FROM ir_module_module WHERE name='ventor_base'"
|
||||
)
|
||||
result = self.env.cr.fetchone()
|
||||
full_version = result and result[0]
|
||||
split_value = full_version and full_version.split('.')
|
||||
self.base_version = split_value and '.'.join(split_value[-3:])
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
res = super(VentorConfigSettings, self).get_values()
|
||||
|
||||
view_with_barcode = self.env.ref(
|
||||
'ventor_base.view_location_form_inherit_additional_barcode',
|
||||
raise_if_not_found=False
|
||||
)
|
||||
if view_with_barcode:
|
||||
res['add_barcode_on_view'] = view_with_barcode.active
|
||||
|
||||
return res
|
||||
|
||||
def _set_apply_default_lots(self, previous_group):
|
||||
operation_type_ids = self.env['stock.picking.type'].search([])
|
||||
group_stock_production_lot = previous_group.get('group_stock_production_lot')
|
||||
|
||||
if (
|
||||
group_stock_production_lot != self.group_stock_production_lot
|
||||
and not self.group_stock_production_lot
|
||||
):
|
||||
operation_type_ids.apply_default_lots = False
|
||||
ventor_apply_default_lots = self.env['ventor.option.setting'].search(
|
||||
[
|
||||
('technical_name', '=', 'apply_default_lots'),
|
||||
]
|
||||
)
|
||||
ventor_apply_default_lots.with_context(
|
||||
disable_apply_default_lots=True
|
||||
).set_apply_default_lots_fields(self.group_stock_production_lot)
|
||||
|
||||
def _set_packages_fields(self, previous_group):
|
||||
operation_type_ids = self.env['stock.picking.type'].search([])
|
||||
group_stock_tracking_lot = previous_group.get('group_stock_tracking_lot')
|
||||
|
||||
if group_stock_tracking_lot != self.group_stock_tracking_lot:
|
||||
operation_type_ids.manage_packages = self.group_stock_tracking_lot
|
||||
operation_type_ids.show_put_in_pack_button = self.group_stock_tracking_lot
|
||||
if not self.group_stock_tracking_lot:
|
||||
operation_type_ids.show_put_in_pack_button = self.group_stock_tracking_lot
|
||||
operation_type_ids.scan_destination_package = self.group_stock_tracking_lot
|
||||
operation_type_ids.confirm_source_package = self.group_stock_tracking_lot
|
||||
operation_type_ids.allow_creating_new_packages = self.group_stock_tracking_lot
|
||||
|
||||
ventor_packages_settings = self.env['ventor.option.setting'].search(
|
||||
[
|
||||
(
|
||||
'technical_name',
|
||||
'in',
|
||||
(
|
||||
'confirm_source_package',
|
||||
'scan_destination_package',
|
||||
'manage_packages',
|
||||
'allow_creating_new_packages',
|
||||
'pack_all_items',
|
||||
'use_reusable_packages',
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
ventor_packages_settings.with_context(
|
||||
disable_package_fields=True
|
||||
).set_related_package_fields(self.group_stock_tracking_lot)
|
||||
if self.group_stock_tracking_lot:
|
||||
ventor_packages_settings = self.env['ventor.option.setting'].search(
|
||||
[
|
||||
('technical_name', '=', 'manage_packages'),
|
||||
]
|
||||
)
|
||||
ventor_packages_settings.value = self.env.ref('ventor_base.bool_true')
|
||||
|
||||
def _set_manage_product_owner(self, previous_group):
|
||||
operation_type_ids = self.env['stock.picking.type'].search([])
|
||||
group_stock_tracking_owner = previous_group.get('group_stock_tracking_owner')
|
||||
|
||||
if group_stock_tracking_owner != self.group_stock_tracking_owner:
|
||||
operation_type_ids.manage_product_owner = self.group_stock_tracking_owner
|
||||
|
||||
ventor_owner_settings = self.env['ventor.option.setting'].search(
|
||||
[
|
||||
('technical_name', '=', 'manage_product_owner'),
|
||||
]
|
||||
)
|
||||
ventor_owner_settings.value = self.env.ref('ventor_base.bool_true') if self.group_stock_tracking_owner else self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def _update_display_wave_picking_menu(self, previous_group):
|
||||
group_stock_picking_wave = previous_group.get('group_stock_picking_wave')
|
||||
|
||||
if group_stock_picking_wave != self.group_stock_picking_wave:
|
||||
merp_wave_picking_menu = self.env.ref('ventor_base.merp_wave_picking_menu')
|
||||
users = self.env['res.users'].with_context(active_test=False).search([
|
||||
('share', '=', False)
|
||||
])
|
||||
merp_wave_picking_menu.write(
|
||||
{
|
||||
'users': [(6, 0, users.ids)]
|
||||
if self.group_stock_picking_wave
|
||||
else [(5, 0, 0)],
|
||||
}
|
||||
)
|
||||
|
||||
def set_values(self):
|
||||
previous_group = self.default_get(
|
||||
[
|
||||
'group_stock_tracking_lot',
|
||||
'group_stock_tracking_owner',
|
||||
'group_stock_production_lot',
|
||||
'group_stock_picking_wave',
|
||||
]
|
||||
)
|
||||
res = super(VentorConfigSettings, self).set_values()
|
||||
|
||||
view_with_barcode = self.env.ref('ventor_base.view_location_form_inherit_additional_barcode')
|
||||
view_with_barcode.active = self.add_barcode_on_view
|
||||
|
||||
# Updating the values of dependent fields
|
||||
self.sudo()._set_apply_default_lots(previous_group)
|
||||
self.sudo()._set_packages_fields(previous_group)
|
||||
self.sudo()._set_manage_product_owner(previous_group)
|
||||
|
||||
# Updating the menu display in Ventor for users
|
||||
self.sudo()._update_display_wave_picking_menu(previous_group)
|
||||
return res
|
||||
131
odoo-bringout-ventor-ventor_base/ventor_base/models/res_users.py
Normal file
131
odoo-bringout-ventor-ventor_base/ventor_base/models/res_users.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# Copyright 2020 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
import json
|
||||
|
||||
from odoo import api, models, fields
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
allowed_warehouse_ids = fields.Many2many(
|
||||
comodel_name='stock.warehouse',
|
||||
string='Allowed Warehouses',
|
||||
help='List of all warehouses user has access to',
|
||||
)
|
||||
|
||||
custom_package_name = fields.Char(
|
||||
string='Custom Build Name',
|
||||
compute="_compute_custom_package_name",
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
ventor_base_version = fields.Char(
|
||||
compute="_compute_ventor_base_version",
|
||||
compute_sudo=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
ventor_global_settings = fields.Text(
|
||||
string='Global Settings',
|
||||
readonly=True,
|
||||
compute='_compute_global_settings'
|
||||
)
|
||||
|
||||
ventor_user_settings = fields.Text(
|
||||
string='User Settings'
|
||||
)
|
||||
|
||||
@property
|
||||
def SELF_READABLE_FIELDS(self):
|
||||
readable_fields = [
|
||||
'ventor_global_settings',
|
||||
'ventor_user_settings',
|
||||
'custom_package_name',
|
||||
'ventor_base_version',
|
||||
]
|
||||
return super().SELF_READABLE_FIELDS + readable_fields
|
||||
|
||||
@property
|
||||
def SELF_WRITEABLE_FIELDS(self):
|
||||
writable_fields = ['ventor_user_settings']
|
||||
return super().SELF_WRITEABLE_FIELDS + writable_fields
|
||||
|
||||
def _compute_custom_package_name(self):
|
||||
custom_package_name = (
|
||||
self.env["ir.config_parameter"]
|
||||
.get_param("ventor_base.custom_package_name", "")
|
||||
)
|
||||
self.custom_package_name = custom_package_name
|
||||
|
||||
def _compute_ventor_base_version(self):
|
||||
ventor_base_version = (
|
||||
self.env["ir.module.module"]
|
||||
.search([("name", "=", "ventor_base"), ("state", "=", "installed")])
|
||||
.latest_version
|
||||
)
|
||||
for user in self:
|
||||
if ventor_base_version:
|
||||
user.ventor_base_version = ventor_base_version
|
||||
else:
|
||||
user.ventor_base_version = ""
|
||||
|
||||
def _compute_global_settings(self):
|
||||
settings = []
|
||||
|
||||
for stock_picking_type in self.env['stock.picking.type'].search([]):
|
||||
stock_picking_type_settings = stock_picking_type.get_warehouse_operation_settings()
|
||||
if stock_picking_type.code != 'outgoing':
|
||||
stock_picking_type_settings['settings'].pop('check_shipping_information')
|
||||
if stock_picking_type.code != 'incoming':
|
||||
stock_picking_type_settings['settings'].pop('hide_qty_to_receive')
|
||||
settings.append(stock_picking_type_settings)
|
||||
|
||||
ventor_option_settings = self._get_ventor_option_settings()
|
||||
|
||||
obj = {'operation_types': settings}
|
||||
obj.update(ventor_option_settings)
|
||||
|
||||
self.ventor_global_settings = json.dumps(
|
||||
obj=obj,
|
||||
indent=' ',
|
||||
sort_keys=True
|
||||
)
|
||||
|
||||
def _get_ventor_option_settings(self):
|
||||
ventor_option_settings = self.env['ventor.option.setting'].sudo().get_general_settings()
|
||||
if self.env.ref('ventor_base.merp_wave_picking_menu') not in self.groups_id:
|
||||
ventor_option_settings.pop('wave_picking')
|
||||
return ventor_option_settings
|
||||
|
||||
def _update_group_picking_wave_menu(self, vals):
|
||||
vals = self._remove_reified_groups(vals)
|
||||
if 'groups_id' in vals:
|
||||
group_stock_picking_wave = self.env.ref('stock.group_stock_picking_wave')
|
||||
merp_wave_picking_menu = self.env.ref('ventor_base.merp_wave_picking_menu')
|
||||
for user in self:
|
||||
if group_stock_picking_wave not in user.groups_id and merp_wave_picking_menu in user.groups_id:
|
||||
merp_wave_picking_menu.write({'users': [(3, user.id)]})
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
result = super().create(vals_list)
|
||||
if not result.allowed_warehouse_ids:
|
||||
result.write(
|
||||
{
|
||||
'allowed_warehouse_ids': [
|
||||
(
|
||||
6, 0, self.env["stock.warehouse"].sudo().with_context(active_test=False).search([]).ids
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
def write(self, vals):
|
||||
result = super().write(vals)
|
||||
if result and 'allowed_warehouse_ids' in vals:
|
||||
self.env['ir.rule'].clear_caches()
|
||||
self._update_group_picking_wave_menu(vals)
|
||||
return result
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Copyright 2021 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
|
||||
# Odoo:
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockLocation(models.Model):
|
||||
_inherit = "stock.location"
|
||||
|
||||
# store attribute for Group By 'Warehouse', locations tree view
|
||||
warehouse_id = fields.Many2one(store=True)
|
||||
is_pallet = fields.Boolean(string="Is Pallet")
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
from odoo import fields, models, api, _
|
||||
|
||||
|
||||
class StockPickingType(models.Model):
|
||||
_inherit = "stock.picking.type"
|
||||
|
||||
allow_creating_new_packages = fields.Boolean(
|
||||
string="Allow creating new packages",
|
||||
help="User can create new packages by scanning a new barcode or create it manually"
|
||||
)
|
||||
|
||||
apply_default_lots = fields.Boolean(
|
||||
string="Apply default lots and serials",
|
||||
help="If it's on, you don't need to scan lot number to confirm it. "
|
||||
"On receipts the app will create default Odoo lots and apply them to the product. "
|
||||
"On delivery zone you don't need to confirm lots and "
|
||||
"they will be taken Odoo by default"
|
||||
)
|
||||
|
||||
apply_quantity_automatically = fields.Boolean(
|
||||
string="Apply quantity automatically",
|
||||
help="Automatically validate the line after scanning a destination location. "
|
||||
"Warning: you have to insert QTY first before destination location"
|
||||
)
|
||||
|
||||
autocomplete_the_item_quantity_field = fields.Boolean(
|
||||
string="Autocomplete item quantity",
|
||||
help="Automatically insert expected quantity. No need to enter the quantity "
|
||||
"of goods using the keyboard or using scanning"
|
||||
)
|
||||
|
||||
behavior_on_backorder_creation = fields.Selection(
|
||||
[
|
||||
("always_create_backorder", "Always Create Backorder"),
|
||||
("never_create_backorder", "Never Create Backorder"),
|
||||
("ask_me_every_time", "Ask Me Every Time"),
|
||||
],
|
||||
string="Behavior On Backorder Creation",
|
||||
default="ask_me_every_time",
|
||||
required=True,
|
||||
help="Choose how to process backorders. You can always create backorders, "
|
||||
"always ignore backorders or choose it all the time (default)"
|
||||
)
|
||||
|
||||
behavior_on_split_operation = fields.Selection(
|
||||
[
|
||||
("always_split_line", "Always Split the Line"),
|
||||
("always_move_less_items", "Always Move Less Items"),
|
||||
("ask_me_every_time", "Ask Me Every Time"),
|
||||
],
|
||||
string="Behavior On Split Operation",
|
||||
compute="_compute_behavior_on_split_operation",
|
||||
readonly=False,
|
||||
store=True,
|
||||
help="Choose how to process less product qty than initial. You can always split "
|
||||
"the line, always move less items or choose it all the time(default)"
|
||||
)
|
||||
|
||||
change_destination_location = fields.Boolean(
|
||||
string="Change destination location",
|
||||
help="If this setting is active a user can change destination location "
|
||||
"while receiving to be placed at any available location",
|
||||
)
|
||||
|
||||
change_source_location = fields.Boolean(
|
||||
string="Change source location",
|
||||
help="User can change default source location to pick item from another location. "
|
||||
"Works only if 'Confirm source location' setting is active",
|
||||
)
|
||||
|
||||
check_shipping_information = fields.Boolean(
|
||||
string="Check shipping information",
|
||||
help="If the setting is active the user can edit shipping information "
|
||||
"before validate OUT transfer",
|
||||
)
|
||||
|
||||
confirm_destination_location = fields.Boolean(
|
||||
string="Confirm destination location",
|
||||
help="The dot next to the field gets yellow color means user has to confirm it. "
|
||||
"User has to scan a barcode of destination location"
|
||||
)
|
||||
|
||||
confirm_product = fields.Boolean(
|
||||
string="Confirm product",
|
||||
help="The dot next to the field gets yellow color means user has to confirm it. "
|
||||
"User has to scan a barcode of product."
|
||||
)
|
||||
|
||||
confirm_source_location = fields.Boolean(
|
||||
string="Confirm source location",
|
||||
help="The dot next to the field gets yellow color means user has to confirm it. "
|
||||
"User has to scan a barcode of source location"
|
||||
)
|
||||
|
||||
confirm_source_package = fields.Boolean(
|
||||
string="Confirm source package",
|
||||
help="User has to scan a barcode of source package. "
|
||||
"The dot next to the field gets yellow color means user has to confirm it"
|
||||
)
|
||||
|
||||
hide_qty_to_receive = fields.Boolean(
|
||||
string="Hide QTYs to receive",
|
||||
help="Setting’s description: User will not see how many QTYs they need to receive."
|
||||
)
|
||||
|
||||
is_consignment_enabled = fields.Boolean(
|
||||
compute="_compute_is_consignment_enabled"
|
||||
)
|
||||
|
||||
is_package_tracking_enabled = fields.Boolean(
|
||||
compute="_compute_is_package_tracking_enabled"
|
||||
)
|
||||
|
||||
is_stock_production_lot_enabled = fields.Boolean(
|
||||
compute="_compute_is_stock_production_lot_enabled"
|
||||
)
|
||||
|
||||
manage_packages = fields.Boolean(
|
||||
string="Show packages fields",
|
||||
default=lambda self: self.env.ref("stock.group_tracking_lot")
|
||||
in self.env.ref("base.group_user").implied_ids,
|
||||
help="Scan source (destination) packages right after scanning source (destination) "
|
||||
"location. Use it if you move from one package to another or pick items from "
|
||||
"packages or pallets. Works only if package management settings is active on Odoo "
|
||||
"side.\n\n If you want to use Show packages fields, you must turn on setting "
|
||||
"'Packages' in inventory settings",
|
||||
)
|
||||
|
||||
manage_product_owner = fields.Boolean(
|
||||
string="Show Product Owner field",
|
||||
default=lambda self: self.env.ref("stock.group_tracking_owner")
|
||||
in self.env.ref("base.group_user").implied_ids,
|
||||
help="Allow scan product owner. You can specify product owner while moving items. "
|
||||
"Working only with 'Consignment' setting on Odoo side"
|
||||
)
|
||||
|
||||
scan_destination_location_once = fields.Boolean(
|
||||
string="Scan destination location once",
|
||||
help="Scan the destination location only once with the last item. "
|
||||
"The destination location will be applied to all lines."
|
||||
)
|
||||
|
||||
scan_destination_package = fields.Boolean(
|
||||
string="Confirm destination package",
|
||||
help="User has to scan a barcode of destination package. The dot next to the field "
|
||||
"gets yellow color means user has to confirm it"
|
||||
)
|
||||
|
||||
show_next_product = fields.Boolean(
|
||||
string="Show next product",
|
||||
help="Product field will show the next product to be picked. "
|
||||
"Use the setting during picking and delivery. "
|
||||
"It is recommended to disable the setting for the reception area",
|
||||
)
|
||||
|
||||
show_print_attachment_button = fields.Boolean(
|
||||
string="Show Print attachments button",
|
||||
default=True,
|
||||
help="Showing the Print attachments button in the toolbar instead of "
|
||||
"keeping it in the hidden menu"
|
||||
)
|
||||
|
||||
show_put_in_pack_button = fields.Boolean(
|
||||
string="Show Put in pack button",
|
||||
default=lambda self: self.env.ref("stock.group_tracking_lot")
|
||||
in self.env.ref("base.group_user").implied_ids,
|
||||
help="Showing the Put in pack button in the toolbar instead of "
|
||||
"keeping it in the hidden menu"
|
||||
)
|
||||
|
||||
transfer_more_items = fields.Boolean(
|
||||
string="Move more than planned",
|
||||
help="Allows moving more items than expected (for example kg of meat, etc)"
|
||||
)
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_behavior_on_split_operation(self):
|
||||
for operation_type in self:
|
||||
if operation_type.code == 'incoming':
|
||||
operation_type.behavior_on_split_operation = 'always_split_line'
|
||||
else:
|
||||
operation_type.behavior_on_split_operation = 'ask_me_every_time'
|
||||
|
||||
def _compute_is_consignment_enabled(self):
|
||||
internal_user_groups = self.env.ref('base.group_user').implied_ids
|
||||
group_tracking_owner = self.env.ref("stock.group_tracking_owner")
|
||||
for item in self:
|
||||
item.is_consignment_enabled = group_tracking_owner in internal_user_groups
|
||||
|
||||
def _compute_is_package_tracking_enabled(self):
|
||||
internal_user_groups = self.env.ref('base.group_user').implied_ids
|
||||
group_tracking_lot = self.env.ref("stock.group_tracking_lot")
|
||||
for item in self:
|
||||
item.is_package_tracking_enabled = group_tracking_lot in internal_user_groups
|
||||
|
||||
def _compute_is_stock_production_lot_enabled(self):
|
||||
internal_user_groups = self.env.ref('base.group_user').implied_ids
|
||||
group_production_lot = self.env.ref("stock.group_production_lot")
|
||||
for item in self:
|
||||
item.is_stock_production_lot_enabled = group_production_lot in internal_user_groups
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if 'code' in vals:
|
||||
vals['show_next_product'] = vals['code'] != "incoming"
|
||||
vals['change_destination_location'] = True
|
||||
|
||||
return super(StockPickingType, self).create(vals_list)
|
||||
|
||||
@api.onchange('confirm_source_location')
|
||||
def _onchange_confirm_source_location(self):
|
||||
if not self.confirm_source_location:
|
||||
self.change_source_location = False
|
||||
|
||||
@api.onchange('confirm_destination_location')
|
||||
def _onchange_confirm_destination_location(self):
|
||||
if not self.confirm_destination_location:
|
||||
self.apply_quantity_automatically = False
|
||||
|
||||
@api.onchange('change_source_location')
|
||||
def _onchange_change_source_location(self):
|
||||
if self.change_source_location and not self.confirm_source_location:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _("Warning"),
|
||||
'message': _("'Change source location' is available only "
|
||||
"if 'Confirm source location' is enabled")
|
||||
}
|
||||
}
|
||||
|
||||
@api.onchange('apply_quantity_automatically')
|
||||
def _onchange_apply_quantity_automatically(self):
|
||||
if self.apply_quantity_automatically and not self.confirm_destination_location:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _("Warning"),
|
||||
'message': _("'Apply quantity automatically' is available only "
|
||||
"if 'Confirm destination location' is enabled")
|
||||
}
|
||||
}
|
||||
|
||||
def write(self, vals):
|
||||
res = super(StockPickingType, self).write(vals)
|
||||
|
||||
if 'change_source_location' in vals or 'confirm_source_location' in vals:
|
||||
for stock_picking_type in self:
|
||||
if stock_picking_type.change_source_location:
|
||||
if not stock_picking_type.confirm_source_location:
|
||||
stock_picking_type.change_source_location = False
|
||||
|
||||
if 'apply_quantity_automatically' in vals or 'confirm_destination_location' in vals:
|
||||
for stock_picking_type in self:
|
||||
if stock_picking_type.apply_quantity_automatically:
|
||||
if not stock_picking_type.confirm_destination_location:
|
||||
stock_picking_type.apply_quantity_automatically = False
|
||||
|
||||
if 'manage_packages' in vals:
|
||||
for stock_picking_type in self:
|
||||
if not stock_picking_type.manage_packages:
|
||||
if stock_picking_type.scan_destination_package:
|
||||
stock_picking_type.scan_destination_package = False
|
||||
if stock_picking_type.confirm_source_package:
|
||||
stock_picking_type.confirm_source_package = False
|
||||
if stock_picking_type.allow_creating_new_packages:
|
||||
stock_picking_type.allow_creating_new_packages = False
|
||||
return res
|
||||
|
||||
def get_warehouse_operation_settings(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"wh_code": self.warehouse_id.code,
|
||||
"wh_name": self.warehouse_id.name,
|
||||
"settings": {
|
||||
"allow_creating_new_packages": self.allow_creating_new_packages,
|
||||
"confirm_source_location": self.confirm_source_location,
|
||||
"change_source_location": self.change_source_location,
|
||||
"show_next_product": self.show_next_product,
|
||||
"confirm_product": self.confirm_product,
|
||||
"apply_default_lots": self.apply_default_lots,
|
||||
"transfer_more_items": self.transfer_more_items,
|
||||
"confirm_destination_location": self.confirm_destination_location,
|
||||
"apply_quantity_automatically": self.apply_quantity_automatically,
|
||||
"change_destination_location": self.change_destination_location,
|
||||
"scan_destination_location_once": self.scan_destination_location_once,
|
||||
"autocomplete_the_item_quantity_field": self.autocomplete_the_item_quantity_field,
|
||||
"show_print_attachment_button": self.show_print_attachment_button,
|
||||
"show_put_in_pack_button": self.show_put_in_pack_button,
|
||||
"manage_packages": self.manage_packages,
|
||||
"manage_product_owner": self.manage_product_owner,
|
||||
"behavior_on_backorder_creation": self.behavior_on_backorder_creation,
|
||||
"behavior_on_split_operation": self.behavior_on_split_operation,
|
||||
"scan_destination_package": self.scan_destination_package,
|
||||
"confirm_source_package": self.confirm_source_package,
|
||||
"check_shipping_information": self.check_shipping_information,
|
||||
"hide_qty_to_receive": self.hide_qty_to_receive,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2021 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockQuant(models.Model):
|
||||
_inherit = 'stock.quant'
|
||||
|
||||
@api.constrains("lot_id", "inventory_quantity")
|
||||
def _check_product_lot(self):
|
||||
""" check product lot/serial except for stock_fix_lot """
|
||||
|
||||
for quant in self:
|
||||
if (
|
||||
not quant.inventory_quantity
|
||||
or not quant.company_id.force_lot_validation_on_inventory_adjustment
|
||||
or (
|
||||
quant.env.context.get("skip_product_lot_check") and not quant.inventory_quantity)
|
||||
):
|
||||
return
|
||||
if quant.tracking in ("lot", "serial") and not quant.lot_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You need to supply a Lot/Serial number for product: %s",
|
||||
quant.product_id.display_name,
|
||||
)
|
||||
)
|
||||
|
||||
@api.model
|
||||
def user_has_groups(self, groups):
|
||||
# we need to override method as we need different access group
|
||||
# to be allowed to validate inventory
|
||||
if (
|
||||
self.env.context.get("validate_inventory")
|
||||
and groups == "stock.group_stock_manager"
|
||||
):
|
||||
groups = "ventor_base.merp_user_validate_inventory_adjustment"
|
||||
res = super(StockQuant, self).user_has_groups(groups)
|
||||
return res
|
||||
|
||||
def _apply_inventory(self):
|
||||
res = super(StockQuant, self.with_context(validate_inventory=True))._apply_inventory()
|
||||
return res
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2020 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import api, models, fields
|
||||
|
||||
|
||||
class StockWarehouse(models.Model):
|
||||
_inherit = 'stock.warehouse'
|
||||
|
||||
is_internal = fields.Boolean(
|
||||
string='Is Internal Warehouse',
|
||||
)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super(StockWarehouse, self).create(vals_list)
|
||||
if res:
|
||||
res.update_users_calculated_warehouse()
|
||||
return res
|
||||
|
||||
def update_users_calculated_warehouse(self):
|
||||
for warehouse in self:
|
||||
users = self.env['res.users'].with_context(active_test=False).search([
|
||||
('share', '=', False)])
|
||||
wh_ids = self.env['stock.warehouse'].with_context(active_test=False).search([
|
||||
('id', '!=', warehouse.id)]).ids
|
||||
wh_ids.sort()
|
||||
modified_user_ids = []
|
||||
for user in users.with_context(active_test=False):
|
||||
# Because of specifics on how Odoo working with companies on first start, we have to filter by company
|
||||
user_wh_ids = user.allowed_warehouse_ids.filtered(
|
||||
lambda wh: wh.company_id.id == warehouse.env.company.id
|
||||
).ids
|
||||
user_wh_ids.sort()
|
||||
if wh_ids == user_wh_ids:
|
||||
user.allowed_warehouse_ids = [(4, warehouse.id, 0)]
|
||||
modified_user_ids.append(user.id)
|
||||
|
||||
# Because access rights are using this field, we need to invalidate cache
|
||||
if modified_user_ids:
|
||||
self.env['res.users'].browse(modified_user_ids).invalidate_recordset(
|
||||
[
|
||||
'allowed_warehouse_ids',
|
||||
]
|
||||
)
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
# Copyright 2022 VentorTech OU
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import _, api, models, fields
|
||||
|
||||
|
||||
class VentorOptionSetting(models.Model):
|
||||
_name = 'ventor.option.setting'
|
||||
_description = 'Ventor Option Setting'
|
||||
|
||||
name = fields.Char(required=True, index=True)
|
||||
action_type = fields.Selection(
|
||||
[
|
||||
('warehouse_opration', 'Warehouse Opration'),
|
||||
('package_management', 'Package Management'),
|
||||
('batch_picking', 'Batch Picking'),
|
||||
('wave_picking', 'Wave Picking'),
|
||||
('cluster_picking', 'Cluster Picking'),
|
||||
('internal_transfers', 'Internal Transfers'),
|
||||
('putaway', 'Putaway'),
|
||||
('instant_inventory', 'Instant Inventory'),
|
||||
('inventory_adjustments', 'Inventory Adjustments'),
|
||||
('quick_info', 'Quick Info'),
|
||||
('scrap_management', 'Scrap Management'),
|
||||
('create_so', 'Create SO'),
|
||||
('create_po', 'Create PO'),
|
||||
], required=True
|
||||
)
|
||||
description = fields.Text()
|
||||
technical_name = fields.Char(required=True)
|
||||
value = fields.Many2one(
|
||||
'ventor.setting.value',
|
||||
string='Value',
|
||||
required=True,
|
||||
domain="[('id', 'in', settings_dependency)]",
|
||||
)
|
||||
value_type = fields.Selection(
|
||||
[
|
||||
('bool', 'Boolean'),
|
||||
('select', 'Selection'),
|
||||
]
|
||||
)
|
||||
settings_dependency = fields.Many2many(
|
||||
comodel_name='ventor.setting.value'
|
||||
)
|
||||
|
||||
@api.onchange('value')
|
||||
def _onchange_value(self):
|
||||
if self.technical_name in ('confirm_source_location', 'change_source_location'):
|
||||
return self._set_change_source_location()
|
||||
elif self.technical_name in ('add_boxes_before_cluster', 'multiple_boxes_for_one_transfer'):
|
||||
return self._set_add_boxes_before_cluster()
|
||||
elif self.technical_name in (
|
||||
'manage_packages',
|
||||
'confirm_source_package',
|
||||
'scan_destination_package',
|
||||
'allow_creating_new_packages',
|
||||
'pack_all_items',
|
||||
'allow_validate_less',
|
||||
):
|
||||
return self.set_related_package_fields(self._get_group_settings_value('stock.group_tracking_lot'))
|
||||
elif self.technical_name in ('manage_product_owner'):
|
||||
self.set_manage_product_owner_fields(self._get_group_settings_value('stock.group_tracking_owner'))
|
||||
elif self.technical_name in ('apply_default_lots'):
|
||||
self.set_apply_default_lots_fields(self._get_group_settings_value('stock.group_production_lot'))
|
||||
elif self.technical_name in ('move_multiple_products', 'hold_destination_location'):
|
||||
return self.set_hold_destination_location_fields()
|
||||
elif self.technical_name in ('use_reusable_packages'):
|
||||
return self.set_reusable_packages_related_fields(self._get_group_settings_value('stock.group_tracking_lot'))
|
||||
elif self.technical_name in ('confirm_destination_location'):
|
||||
self._set_confirm_destination_location_cluster_picking_fields()
|
||||
|
||||
def _get_group_settings_value(self, key):
|
||||
internal_user_groups = self.env.ref('base.group_user').implied_ids
|
||||
group = self.env.ref(key)
|
||||
return group in internal_user_groups
|
||||
|
||||
def _get_warning(self, message):
|
||||
return {'warning': {
|
||||
'title': _('Another Settings were changed automatically!'),
|
||||
'message': message,
|
||||
}}
|
||||
|
||||
def get_setting_field(self, technical_name):
|
||||
if not isinstance(technical_name, list):
|
||||
technical_name = [technical_name]
|
||||
return self.env['ventor.option.setting'].search(
|
||||
[
|
||||
('action_type', '=', self.action_type),
|
||||
('technical_name', 'in', technical_name),
|
||||
]
|
||||
)
|
||||
|
||||
def get_general_settings(self):
|
||||
action_types = [
|
||||
'package_management',
|
||||
'batch_picking',
|
||||
'wave_picking',
|
||||
'cluster_picking',
|
||||
'internal_transfers',
|
||||
'putaway',
|
||||
'instant_inventory',
|
||||
'inventory_adjustments',
|
||||
'quick_info',
|
||||
'scrap_management',
|
||||
'create_so',
|
||||
'create_po',
|
||||
]
|
||||
ventor_option_settings = self.env['ventor.option.setting'].search([])
|
||||
|
||||
settings = {}
|
||||
for action_type in action_types:
|
||||
settings[action_type] = {
|
||||
set.technical_name: self.get_normalized_value(set.value.setting_value)
|
||||
for set in ventor_option_settings.filtered(lambda r: r.action_type == action_type)
|
||||
}
|
||||
return settings
|
||||
|
||||
def set_allow_validate_less(self):
|
||||
if self.technical_name == 'allow_validate_less':
|
||||
pack_all_items = self.get_setting_field('pack_all_items')
|
||||
if pack_all_items.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
elif self.technical_name == 'pack_all_items':
|
||||
allow_validate_less = self.get_setting_field('allow_validate_less')
|
||||
if allow_validate_less.value == self.env.ref('ventor_base.bool_true'):
|
||||
allow_validate_less.value = self.env.ref('ventor_base.bool_false')
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Force Pack" to True, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Validate uncompleted orders" was changed to False'
|
||||
))
|
||||
|
||||
def set_apply_default_lots_fields(self, group_stock_production_lot):
|
||||
if self.env.context.get('disable_apply_default_lots'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
elif not group_stock_production_lot and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def _set_add_boxes_before_cluster(self):
|
||||
if self.technical_name == 'add_boxes_before_cluster' and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
multiple_boxes_for_one_transfer = self.get_setting_field('multiple_boxes_for_one_transfer')
|
||||
use_reusable_packages = self.get_setting_field('use_reusable_packages')
|
||||
if multiple_boxes_for_one_transfer.value == self.env.ref('ventor_base.bool_true'):
|
||||
multiple_boxes_for_one_transfer.value = self.env.ref('ventor_base.bool_false')
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Add boxes before cluster" to True, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Multiple boxes for one transfer" was changed to False'
|
||||
))
|
||||
if use_reusable_packages.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
elif self.technical_name == 'multiple_boxes_for_one_transfer' and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
add_boxes_before_cluster = self.get_setting_field('add_boxes_before_cluster')
|
||||
if add_boxes_before_cluster.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def _set_change_source_location(self):
|
||||
if self.technical_name == 'confirm_source_location' and self.value == self.env.ref('ventor_base.bool_false'):
|
||||
change_source_location = self.get_setting_field('change_source_location')
|
||||
change_source_location.value = self.env.ref('ventor_base.bool_false')
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Confirm source location" to False, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Change source location" was changed to False'
|
||||
))
|
||||
elif self.technical_name == 'change_source_location' and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
confirm_source_location = self.get_setting_field('confirm_source_location')
|
||||
if confirm_source_location.value == self.env.ref('ventor_base.bool_false'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def _set_confirm_destination_location_cluster_picking_fields(self):
|
||||
if self.value == self.env.ref('ventor_base.bool_true') and self.action_type == 'cluster_picking':
|
||||
use_reusable_packages = self.get_setting_field('use_reusable_packages')
|
||||
if use_reusable_packages.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def set_hold_destination_location_fields(self):
|
||||
if self.technical_name == 'move_multiple_products' and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
hold_destination_location = self.get_setting_field('hold_destination_location')
|
||||
if hold_destination_location.value == self.env.ref('ventor_base.bool_true'):
|
||||
hold_destination_location.value = self.env.ref('ventor_base.bool_false')
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Move multiple items" to True, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Hold destination location" was changed to False'
|
||||
))
|
||||
elif self.technical_name == 'hold_destination_location' and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
move_multiple_products = self.get_setting_field('move_multiple_products')
|
||||
if move_multiple_products.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def set_manage_product_owner_fields(self, group_stock_tracking_owner):
|
||||
if not group_stock_tracking_owner and self.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
|
||||
def set_related_package_fields(self, group_stock_tracking_lot):
|
||||
if not group_stock_tracking_lot:
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
elif group_stock_tracking_lot:
|
||||
manage_packages = self.get_setting_field('manage_packages')
|
||||
if self.value.setting_value == 'False' and self.technical_name == 'manage_packages':
|
||||
relate_manage_packages_fields = self.get_setting_field(
|
||||
[
|
||||
'confirm_source_package',
|
||||
'scan_destination_package',
|
||||
]
|
||||
)
|
||||
relate_manage_packages_fields.value = self.env.ref('ventor_base.bool_false')
|
||||
if self.action_type in ('batch_picking', 'cluster_picking'):
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Show packages fields" to False, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Confirm source package" was changed to False'
|
||||
'\n- "Confirm destination package" was changed to False'
|
||||
))
|
||||
if self.technical_name != 'manage_packages' and manage_packages.value == self.env.ref('ventor_base.bool_false'):
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
if self.value.setting_value == 'True' and self.technical_name in ('pack_all_items', 'allow_validate_less'):
|
||||
return self.set_allow_validate_less()
|
||||
if self.technical_name == 'scan_destination_package' and self.value == self.env.ref('ventor_base.bool_false'):
|
||||
use_reusable_packages = self.get_setting_field('use_reusable_packages')
|
||||
if use_reusable_packages.value == self.env.ref('ventor_base.bool_true'):
|
||||
self.value = self.env.ref('ventor_base.bool_true')
|
||||
|
||||
def set_reusable_packages_related_fields(self, group_stock_tracking_lot):
|
||||
if not group_stock_tracking_lot:
|
||||
self.value = self.env.ref('ventor_base.bool_false')
|
||||
elif self.value == self.env.ref('ventor_base.bool_true'):
|
||||
related_settings_for_disabling = self.get_setting_field(
|
||||
[
|
||||
'confirm_destination_location',
|
||||
'add_boxes_before_cluster',
|
||||
]
|
||||
)
|
||||
related_settings_for_enabling = self.get_setting_field('scan_destination_package')
|
||||
if related_settings_for_disabling:
|
||||
related_settings_for_disabling.value = self.env.ref('ventor_base.bool_false')
|
||||
if related_settings_for_enabling:
|
||||
related_settings_for_enabling.value = self.env.ref('ventor_base.bool_true')
|
||||
return self._get_warning(_(
|
||||
'Because you changed "Use reusable packages" to True, '
|
||||
'automatically the following settings were also changed: '
|
||||
'\n- "Confirm destination location" was changed to False'
|
||||
'\n- "Add boxes before cluster" was changed to False'
|
||||
'\n- "Confirm destination package" was changed to True'
|
||||
))
|
||||
|
||||
def get_normalized_value(self, setting_value):
|
||||
normalized_settings = {
|
||||
'True': 'true',
|
||||
'False': 'false',
|
||||
'Always Create Backorder': 'always_create_backorder',
|
||||
'Never Create Backorder': 'never_create_backorder',
|
||||
'Always Split the Line': 'always_split_line',
|
||||
'Always Move Less Items': 'always_move_less_items',
|
||||
'Ask Me Every Time': 'ask_me_every_time',
|
||||
}
|
||||
return normalized_settings.get(setting_value)
|
||||
|
||||
|
||||
class VentorSettingValue(models.Model):
|
||||
_name = 'ventor.setting.value'
|
||||
_description = 'Ventor Setting Value'
|
||||
_rec_name = 'setting_value'
|
||||
|
||||
setting_type = fields.Char()
|
||||
setting_value = fields.Char()
|
||||
Loading…
Add table
Add a link
Reference in a new issue