Merge PR #1091 into 14.0

Signed-off-by simahawk
This commit is contained in:
OCA-git-bot
2021-09-20 09:35:41 +00:00
19 changed files with 1118 additions and 0 deletions

View File

@@ -0,0 +1 @@
../../../../stock_vertical_lift_kardex

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@@ -0,0 +1,96 @@
======================
Vertical Lift - Kardex
======================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_vertical_lift_kardex
:alt: OCA/stock-logistics-warehouse
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_vertical_lift_kardex
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/153/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
Add support for Kardex Remstar vertical lifts to the Vertical Lift
module.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_
**Table of contents**
.. contents::
:local:
Known issues / Roadmap
======================
* Add support of the hardware
* handle multiple gates for one lift
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_vertical_lift_kardex%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Camptocamp
Contributors
~~~~~~~~~~~~
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
Trobz
* Dung Tran <dungtd@trobz.com>
Other credits
~~~~~~~~~~~~~
The development of this module has been financially supported by:
* Camptocamp
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_vertical_lift_kardex>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

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

View File

@@ -0,0 +1,15 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Vertical Lift - Kardex",
"summary": "Integrate with Kardex Remstar Vertical Lifts",
"version": "14.0.1.0.0",
"category": "Stock",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"depends": ["stock_vertical_lift", "stock_location_position"],
"website": "https://github.com/OCA/stock-logistics-warehouse",
"data": [],
"installable": True,
"development_status": "Alpha",
}

View File

@@ -0,0 +1,38 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_vertical_lift_kardex
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \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: stock_vertical_lift_kardex
#: code:addons/stock_vertical_lift_kardex/models/stock_location.py:0
#, python-format
msgid ""
"Cell location %s has no position. Check if the dimensions of tray %s are "
"properly set in the tray type."
msgstr ""
#. module: stock_vertical_lift_kardex
#: model:ir.model,name:stock_vertical_lift_kardex.model_stock_location
msgid "Inventory Locations"
msgstr ""
#. module: stock_vertical_lift_kardex
#: code:addons/stock_vertical_lift_kardex/models/stock_location.py:0
#, python-format
msgid "Shuttle tray %s has no level. Please fix the configuration"
msgstr ""
#. module: stock_vertical_lift_kardex
#: model:ir.model,name:stock_vertical_lift_kardex.model_vertical_lift_shuttle
msgid "Vertical Lift Shuttle"
msgstr ""

View File

@@ -0,0 +1,2 @@
from . import stock_location
from . import vertical_lift_shuttle

View File

@@ -0,0 +1,98 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import _, exceptions, models
_logger = logging.getLogger(__name__)
class StockLocation(models.Model):
_inherit = "stock.location"
def _hardware_kardex_prepare_fetch_payload(self, cell_location=None):
if self.level is False:
raise exceptions.UserError(
_("Shuttle tray %s has no level. " "Please fix the configuration")
% self.display_name
)
shuttle = self.vertical_lift_shuttle_id
if cell_location:
x, y = cell_location.tray_cell_center_position()
if x == 0 and y == 0:
raise exceptions.UserError(
_(
"Cell location %s has no position. "
"Check if the dimensions of tray %s "
"are properly set in the tray type."
)
% (cell_location.display_name, self.name)
)
x, y = int(x), int(y)
else:
x, y = "", ""
subst = {
"code": shuttle._kardex_shuttle_code(),
"hostId": self.env["ir.sequence"].next_by_code("vertical.lift.command"),
# hard code the gate for now.
# TODO proper handling of multiple gates for 1 lift.
"addr": shuttle.name + "-1",
"carrier": self.level,
"carrierNext": "0",
"x": x,
"y": y,
"boxType": "",
"Q": "",
"order": "",
"part": "",
"desc": "",
}
return shuttle._hardware_kardex_format_template(subst)
def _hardware_vertical_lift_fetch_tray_payload(self, cell_location=None):
"""Prepare "fetch" message to be sent to the vertical lift hardware
Private method, this is where the implementation actually happens.
Addons can add their instructions based on the hardware used for
this location.
The hardware used for a location can be found in:
``self.vertical_lift_shuttle_id.hardware``
Each addon can implement its own mechanism depending of this value
and must call ``super``.
The method must send the command to the vertical lift to fetch / open
the tray. If a ``cell_location`` is passed and if the hardware supports
a way to show a cell (such as a laser pointer), it should send this
command as well.
Useful information that could be needed for the drivers:
* Any field of `self` (name, barcode, ...) which is the current tray.
* Any field of `cell_location` (name, barcode, ...) which is the cell
in the tray.
* ``self.vertical_lift_shuttle_id`` is the current Shuttle, where we
find details about the hardware, the current mode (pick, put, ...).
* ``self.tray_type_id`` is the kind of tray.
* ``self.tray_type_id.width_per_cell`` and
``self.tray_type_id.depth_per_cell`` return the size of a cell in mm.
* ``cell_location.posx`` and ``posy`` are the coordinate from the
bottom-left of the tray.
* ``cell_location.tray_cell_center_position()`` returns the central
position of the cell in mm from the bottom-left of a tray. (distance
from left, distance from bottom). Can be used for instance for
highlighting the cell using a laser pointer.
"""
if self.vertical_lift_shuttle_id.hardware == "kardex":
payload = self._hardware_kardex_prepare_fetch_payload(
cell_location=cell_location
)
_logger.debug("Sending to kardex (fetch): {}", payload)
else:
payload = super()._hardware_vertical_lift_fetch_tray_payload(
cell_location=cell_location
)
return payload

View File

@@ -0,0 +1,123 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import models
_logger = logging.getLogger(__name__)
JMIF_STATUS = {
0: "success",
101: "common error",
102: "sequence number invalid",
103: "machine busy",
104: "timeout",
105: "max retry reached",
106: "carrier in use or undefined",
107: "cancelled",
108: "invalid user input data",
201: "request accepted and queued",
202: "request processing started / request active",
203: "carrier arrived, maybe overwritten by code 0",
301: "AO occupied with other try on move back (store / put)",
302: "AO occupied with other try on fetch (pick)",
}
class VerticalLiftShuttle(models.Model):
_inherit = "vertical.lift.shuttle"
def _selection_hardware(self):
values = super()._selection_hardware()
values += [("kardex", "Kardex")]
return values
_kardex_message_template = (
"{code}|{hostId}|{addr}|{carrier}|{carrierNext}|"
"{x}|{y}|{boxType}|{Q}|{order}|{part}|{desc}|\r\n"
)
def _hardware_kardex_format_template(self, values):
payload = self._kardex_message_template.format(**values)
return payload.encode("iso-8859-1", "replace")
def _kardex_shuttle_code(self):
mapping = {"pick": "1", "put": "2", "inventory": "5"}
ping = "61"
return mapping.get(self.mode, ping)
def _hardware_kardex_prepare_release_payload(self):
subst = {
"code": self._kardex_shuttle_code(),
"hostId": self.env["ir.sequence"].next_by_code("vertical.lift.command"),
# hard code the gate for now.
"addr": self.name + "-1",
"carrier": "0",
"carrierNext": "0",
"x": "0",
"y": "0",
"boxType": "",
"Q": "",
"order": "",
"part": "",
"desc": "",
}
return self._hardware_kardex_format_template(subst)
def _hardware_vertical_lift_release_tray_payload(self):
"""Prepare "release" message to be sent to the vertical lift hardware
Private method, this is where the implementation actually happens.
Addons can add their instructions based on the hardware used for
this location.
The hardware used for a location can be found in:
``self.vertical_lift_shuttle_id.hardware``
Each addon can implement its own mechanism depending of this value
and must call ``super``.
The method must send the command to the vertical lift to release (close)
the tray.
Returns a message in bytes, that will be sent through
``VerticalLiftShuttle._hardware_send_message()``.
"""
if self.hardware == "kardex":
payload = self._hardware_kardex_prepare_release_payload()
_logger.debug("Sending to kardex (release): {}", payload)
else:
payload = super()._hardware_vertical_lift_release_tray_payload()
return payload
def _check_server_response(self, command):
response = command.answer
code, sep, remaining = response.partition("|")
code = int(code)
if code == 0:
return True
elif 1 <= code <= 99:
command.error = "interface error %d" % code
return False
elif code in JMIF_STATUS and code < 200:
command.error = "%d: %s" % (code, JMIF_STATUS[code])
return False
elif code in JMIF_STATUS and code < 300:
command.error = "%d: %s" % (code, JMIF_STATUS[code])
return True
elif code in JMIF_STATUS:
command.error = "%d: %s" % (code, JMIF_STATUS[code])
elif 501 <= code <= 999:
command.error = "%d: %s" % (code, "MM260 Error")
elif 1000 <= code <= 32767:
command.error = "%d: %s" % (code, "C2000TCP/C3000CGI machine error")
elif 0xFF0 <= code == 0xFFF:
command.error = "{:x}: {}".format(
code, "C3000CGI machine error (global short)"
)
elif 0xFFF < code:
command.error = "{:x}: {}".format(code, "C3000CGI machine error (long)")
return False

View File

@@ -0,0 +1,172 @@
#!/usr/bin/python3
import argparse
import asyncio
import logging
import os
import ssl
import time
import aiohttp
_logger = logging.getLogger(__name__)
class KardexProxyProtocol(asyncio.Protocol):
def __init__(self, loop, queue, args):
_logger.info("Proxy created")
self.transport = None
self.buffer = b""
self.queue = queue
self.loop = loop
self.args = args
def connection_made(self, transport):
_logger.info("Proxy incoming cnx")
self.transport = transport
self.buffer = b""
def data_received(self, data):
self.buffer += data
_logger.info("Proxy: received %s", data)
if len(self.buffer) > 65535:
# prevent buffer overflow
self.transport.close()
def eof_received(self):
_logger.info("Proxy: received EOF")
if self.buffer[-1] != b"\n":
# bad format -> close
self.transport.close()
data = (
self.buffer.replace(b"\r\n", b"\n")
.replace(b"\n", b"\r\n")
.decode("iso-8859-1", "replace")
)
self.loop.create_task(self.queue.put(data))
self.buffer = b""
def connection_lost(self, exc):
self.transport = None
self.buffer = b""
class KardexClientProtocol(asyncio.Protocol):
def __init__(self, loop, queue, args):
_logger.info("started kardex client")
self.loop = loop
self.queue = queue
self.args = args
self.transport = None
self.buffer = b""
def connection_made(self, transport):
self.transport = transport
_logger.info("connected to kardex server %r", transport)
async def keepalive(self):
while True:
t = int(time.time())
msg = "61|ping%d|SH1-1|0|0||||||||\r\n" % t
await self.send_message(msg)
await asyncio.sleep(20)
async def send_message(self, message):
_logger.info("SEND %r", message)
message = message.encode("iso-8859-1")
self.transport.write(message)
async def process_queue(self):
while True:
message = await self.queue.get()
await self.send_message(message)
def data_received(self, data):
data = data.replace(b"\0", b"")
_logger.info("RECV %s", data)
self.buffer += data
if b"\r\n" in self.buffer:
msg, sep, rem = self.buffer.partition(b"\r\n")
self.buffer = rem
msg = msg.decode("iso-8859-1", "replace").strip()
if msg.startswith("0|ping"):
_logger.info("ping ok")
else:
_logger.info("notify odoo: %s", msg)
self.loop.create_task(self.notify_odoo(msg))
def connection_lost(self, exc):
self.loop.stop()
async def notify_odoo(self, msg):
url = self.args.odoo_url + "/vertical-lift"
async with aiohttp.ClientSession() as session:
params = {"answer": msg, "secret": self.args.secret}
async with session.post(url, data=params) as resp:
resp_text = await resp.text()
_logger.info("Reponse from Odoo: %s %s", resp.status, resp_text)
def main(args, ssl_context=None):
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
loop = asyncio.get_event_loop()
queue = asyncio.Queue(loop=loop)
# create the main server
coro = loop.create_server(
lambda: KardexProxyProtocol(loop, queue, args), host=args.host, port=args.port
)
loop.run_until_complete(coro)
# create the connection to the JMIF client
if args.kardex_use_tls:
if ssl_context is None:
ssl_context = ssl.create_default_context()
else:
ssl_context = None
coro = loop.create_connection(
lambda: KardexClientProtocol(loop, queue, args),
host=args.kardex_host,
port=args.kardex_port,
ssl=ssl_context,
)
transport, client = loop.run_until_complete(coro)
loop.create_task(client.keepalive())
loop.create_task(client.process_queue())
loop.run_forever()
loop.close()
def make_parser():
listen_address = os.environ.get("INTERFACE", "0.0.0.0")
listen_port = int(os.environ.get("PORT", "7654"))
secret = os.environ.get("ODOO_CALLBACK_SECRET", "")
odoo_url = os.environ.get("ODOO_URL", "http://localhost:8069")
odoo_db = os.environ.get("ODOO_DB", "odoodb")
kardex_host = os.environ.get("KARDEX_HOST", "kardex")
kardex_port = int(os.environ.get("KARDEX_PORT", "9600"))
kardex_use_tls = (
False
if os.environ.get("KARDEX_TLS", "") in ("", "0", "False", "FALSE")
else True
)
parser = argparse.ArgumentParser()
arguments = [
("--host", listen_address, str),
("--port", listen_port, int),
("--odoo-url", odoo_url, str),
("--odoo-db", odoo_db, str),
("--secret", secret, str),
("--kardex-host", kardex_host, str),
("--kardex-port", kardex_port, str),
("--kardex-use-tls", kardex_use_tls, bool),
]
for name, default, type_ in arguments:
parser.add_argument(name, default=default, action="store", type=type_)
return parser
if __name__ == "__main__":
parser = make_parser()
args = parser.parse_args()
main(args)

View File

@@ -0,0 +1 @@
aiohttp

View File

@@ -0,0 +1,106 @@
# pylint: disable=W8116
import asyncio
import logging
import socket
import time
_logger = logging.getLogger("kardex.proxy")
logging.basicConfig(level=logging.DEBUG)
class KardexProxyProtocol(asyncio.Protocol):
def __init__(self, loop, queue):
_logger.info("Proxy created")
self.transport = None
self.buffer = b""
self.queue = queue
self.loop = loop
def connection_made(self, transport):
_logger.info("Proxy incoming cnx")
self.transport = transport
self.buffer = b""
def data_received(self, data):
self.buffer += data
_logger.info("Proxy: received %s", data)
if len(self.buffer) > 65535:
# prevent buffer overflow
self.transport.close()
def eof_received(self):
_logger.info("Proxy: received EOF")
if self.buffer[-1] != b"\n":
# bad format -> close
self.transport.close()
data = (
self.buffer.replace(b"\r\n", b"\n")
.replace(b"\n", b"\r\n")
.decode("iso-8859-1", "replace")
)
task = self.loop.create_task(self.queue.put(data))
self.buffer = b""
print("toto", task)
def connection_lost(self, exc):
self.transport = None
self.buffer = b""
class KardexClientProtocol(asyncio.Protocol):
def __init__(self, loop, queue):
_logger.info("started kardex client")
self.loop = loop
self.queue = queue
self.transport = None
self.buffer = b""
def connection_made(self, transport):
self.transport = transport
_logger.info("connected to kardex server %r", transport)
async def keepalive(self):
while True:
t = int(time.time())
msg = "61|ping%d|SH1-1|0|0||||||||\r\n" % t
await self.send_message(msg)
await asyncio.sleep(5)
async def send_message(self, message):
_logger.info("SEND %s", message)
message = message.encode("iso-8859-1").ljust(1024, b"\0")
self.transport.write(message)
async def process_queue(self):
while True:
message = await self.queue.get()
await self.send_message(message)
def data_received(self, data):
data = data.replace(b"\0", b"")
_logger.info("RECV %s", data)
self.buffer += data
def connection_lost(self, exc):
self.loop.stop()
if __name__ == "__main__":
_logger.info("starting")
loop = asyncio.get_event_loop()
loop.set_debug(1)
queue = asyncio.Queue(loop=loop)
coro = loop.create_server(
lambda: KardexProxyProtocol(loop, queue), port=3000, family=socket.AF_INET
)
server = loop.run_until_complete(coro)
coro = loop.create_connection(
lambda: KardexClientProtocol(loop, queue), "localhost", 9600
)
transport, client = loop.run_until_complete(coro)
print("%r" % transport)
loop.create_task(client.keepalive())
loop.create_task(client.process_queue())
_logger.info("run loop")
loop.run_forever()
loop.close()

View File

@@ -0,0 +1,5 @@
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
Trobz
* Dung Tran <dungtd@trobz.com>

View File

@@ -0,0 +1,3 @@
The development of this module has been financially supported by:
* Camptocamp

View File

@@ -0,0 +1,2 @@
Add support for Kardex Remstar vertical lifts to the Vertical Lift
module.

View File

@@ -0,0 +1,2 @@
* Add support of the hardware
* handle multiple gates for one lift

View File

@@ -0,0 +1 @@
aiohttp

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,446 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Vertical Lift - Kardex</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="vertical-lift-kardex">
<h1 class="title">Vertical Lift - Kardex</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_vertical_lift_kardex"><img alt="OCA/stock-logistics-warehouse" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_vertical_lift_kardex"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/153/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>Add support for Kardex Remstar vertical lifts to the Vertical Lift
module.</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#known-issues-roadmap" id="id1">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#other-credits" id="id6">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id1">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Add support of the hardware</li>
<li>handle multiple gates for one lift</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_vertical_lift_kardex%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Guewen Baconnier &lt;<a class="reference external" href="mailto:guewen.baconnier&#64;camptocamp.com">guewen.baconnier&#64;camptocamp.com</a>&gt;</li>
</ul>
<p>Trobz</p>
<ul class="simple">
<li>Dung Tran &lt;<a class="reference external" href="mailto:dungtd&#64;trobz.com">dungtd&#64;trobz.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#id6">Other credits</a></h2>
<p>The development of this module has been financially supported by:</p>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_vertical_lift_kardex">OCA/stock-logistics-warehouse</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>