new module for request
This commit is contained in:
parent
c7e0f3a4c4
commit
21709501c8
|
|
@ -0,0 +1,128 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
.history
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
parts/
|
||||
sdist/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# github action
|
||||
.github/workflows/*yaml
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
=============
|
||||
Stock Request
|
||||
=============
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:7d0cfabe2f67cd7a6c7fbde532efbb15414351ea68ed6ebb68c3e3747e2b97c3
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-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_request
|
||||
: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_request
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=14.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
This module was written to allow users to request products that are
|
||||
frequently stocked by the company, to be transferred to their chosen location.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
To configure this module:
|
||||
|
||||
* Go to Stock Requests > Settings
|
||||
|
||||
Users should be assigned to the groups 'Stock Request / User' or 'Stock
|
||||
Request / Manager'.
|
||||
|
||||
## Group Stock Request / User
|
||||
|
||||
* Can see her/his own Stock Requests, and others that she/he's been granted
|
||||
permission to follow.
|
||||
|
||||
* Can create/update only her/his Stock Requests.
|
||||
|
||||
## Group Stock Request / Manager
|
||||
|
||||
* Can fully manage all Stock Requests
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
## Creation
|
||||
|
||||
* Go to 'Stock Requests / Stock Requests' and create a new Request.
|
||||
* Indicate a product, quantity and location.
|
||||
* Press 'Confirm'.
|
||||
|
||||
Upon confirmation the request will be evaluated using the procurement rules
|
||||
for the selected location.
|
||||
|
||||
In case that transfers are created, the user will be able to access to them
|
||||
from the button 'Transfers' available in the Stock Request.
|
||||
|
||||
## Cancel
|
||||
|
||||
When the user cancels a Stock Request, the related pending stock moves will be
|
||||
also cancelled.
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
There is no way to achieve Storck Request and Stock Request Orders.
|
||||
It should be developed taking into account that only Cancel and Done
|
||||
stock request can be archived.
|
||||
|
||||
It is also required to manage active field logically from Orders to SRs.
|
||||
|
||||
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 to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_request%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
|
||||
~~~~~~~
|
||||
|
||||
* ForgeFlow
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Jordi Ballester (EFICENT) <jordi.ballester@forgeflow.com>.
|
||||
* Enric Tobella <etobella@creublanca.es>
|
||||
* Atte Isopuro <atte.isopuro@avoin.systems>
|
||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||
* Raul Martin <raul.martin@braintec-group.com>
|
||||
* Serpent Consulting Services Pvt. Ltd. <support@serpentcs.com>
|
||||
* `Open Source Integrators <https://www.opensourceintegrators.com>`_
|
||||
|
||||
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
|
||||
* Steve Campbell <scampbell@opensourceintegrators.com>
|
||||
|
||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||
* Kitti U. <kittiu@ecosoft.co.th>
|
||||
|
||||
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_request>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
{
|
||||
"name": "Stock Request",
|
||||
"summary": "Internal request for stock",
|
||||
"version": "14.0.1.9.0",
|
||||
"license": "LGPL-3",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
"author": "ForgeFlow, Odoo Community Association (OCA)",
|
||||
"category": "Warehouse Management",
|
||||
"depends": ["stock"],
|
||||
"data": [
|
||||
"security/stock_request_security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/product.xml",
|
||||
"views/stock_request_views.xml",
|
||||
"views/stock_request_allocation_views.xml",
|
||||
"views/stock_move_views.xml",
|
||||
"views/stock_picking_views.xml",
|
||||
"views/stock_request_order_views.xml",
|
||||
"views/res_config_settings_views.xml",
|
||||
"views/stock_request_menu.xml",
|
||||
"data/stock_request_sequence_data.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="seq_stock_request" model="ir.sequence">
|
||||
<field name="name">Stock Request</field>
|
||||
<field name="code">stock.request</field>
|
||||
<field name="prefix">SR/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
<record id="seq_stock_request_order" model="ir.sequence">
|
||||
<field name="name">Stock Request Order</field>
|
||||
<field name="code">stock.request.order</field>
|
||||
<field name="prefix">SRO/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.rule" id="stock_picking_rule">
|
||||
<field name="name">stock_request multi-company</field>
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record model="ir.rule" id="stock_request_order_picking_rule">
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from openupgradelib import openupgrade
|
||||
|
||||
|
||||
@openupgrade.migrate()
|
||||
def migrate(env, version):
|
||||
openupgrade.load_data(
|
||||
env.cr, "stock_request", "migrations/14.0.1.3.0/noupdate_changes.xml"
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from . import procurement_group
|
||||
from . import stock_request_abstract
|
||||
from . import stock_request
|
||||
from . import stock_request_allocation
|
||||
from . import stock_request_order
|
||||
from . import stock_move
|
||||
from . import stock_picking
|
||||
from . import stock_rule
|
||||
from . import stock_move_line
|
||||
from . import res_config_settings
|
||||
from . import stock_warehouse
|
||||
from . import stock_location
|
||||
from . import stock_location_route
|
||||
from . import res_company
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (C) 2019 Open Source Integrators
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = "procurement.group"
|
||||
|
||||
@api.model
|
||||
def run(self, procurements, raise_user_error=True):
|
||||
indexes_to_pop = []
|
||||
new_procs = []
|
||||
for i, procurement in enumerate(procurements):
|
||||
if "stock_request_id" in procurement.values and procurement.values.get(
|
||||
"stock_request_id"
|
||||
):
|
||||
req = self.env["stock.request"].browse(
|
||||
procurement.values.get("stock_request_id")
|
||||
)
|
||||
if req.order_id:
|
||||
new_procs.append(procurement._replace(origin=req.order_id.name))
|
||||
indexes_to_pop.append(i)
|
||||
if new_procs:
|
||||
indexes_to_pop.reverse()
|
||||
for index in indexes_to_pop:
|
||||
procurements.pop(index)
|
||||
procurements.extend(new_procs)
|
||||
return super().run(procurements, raise_user_error=raise_user_error)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright 2018 ForgeFlow S.L.
|
||||
# (http://www.forgeflow.com)
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
stock_request_allow_virtual_loc = fields.Boolean(
|
||||
string="Allow Virtual locations on Stock Requests"
|
||||
)
|
||||
stock_request_check_available_first = fields.Boolean(
|
||||
string="Check available stock first"
|
||||
)
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# Copyright 2018 Creu Blanca
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
group_stock_request_order = fields.Boolean(
|
||||
implied_group="stock_request.group_stock_request_order"
|
||||
)
|
||||
|
||||
module_stock_request_purchase = fields.Boolean(
|
||||
string="Stock Requests for Purchases"
|
||||
)
|
||||
|
||||
module_stock_request_kanban = fields.Boolean(
|
||||
string="Stock Requests Kanban integration"
|
||||
)
|
||||
|
||||
stock_request_check_available_first = fields.Boolean(
|
||||
related="company_id.stock_request_check_available_first", readonly=False
|
||||
)
|
||||
stock_request_allow_virtual_loc = fields.Boolean(
|
||||
related="company_id.stock_request_allow_virtual_loc", readonly=False
|
||||
)
|
||||
|
||||
module_stock_request_analytic = fields.Boolean(
|
||||
string="Stock Requests Analytic integration"
|
||||
)
|
||||
|
||||
module_stock_request_submit = fields.Boolean(
|
||||
string="Submitted state in Stock Requests"
|
||||
)
|
||||
|
||||
module_stock_request_mrp = fields.Boolean(string="Stock Request for Manufacturing")
|
||||
|
||||
# Dependencies
|
||||
@api.onchange("stock_request_allow_virtual_loc")
|
||||
def _onchange_stock_request_allow_virtual_loc(self):
|
||||
if self.stock_request_allow_virtual_loc:
|
||||
self.group_stock_multi_locations = True
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright 2018 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockLocation(models.Model):
|
||||
_inherit = "stock.location"
|
||||
|
||||
@api.constrains("company_id")
|
||||
def _check_company_stock_request(self):
|
||||
if any(
|
||||
rec.company_id
|
||||
and self.env["stock.request"].search(
|
||||
[("company_id", "!=", rec.company_id.id), ("location_id", "=", rec.id)],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the company of the location, as it is "
|
||||
"already assigned to stock requests that belong to "
|
||||
"another company."
|
||||
)
|
||||
)
|
||||
if any(
|
||||
rec.company_id
|
||||
and self.env["stock.request.order"].search(
|
||||
[
|
||||
("company_id", "!=", rec.company_id.id),
|
||||
("warehouse_id", "=", rec.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the company of the location, as it is "
|
||||
"already assigned to stock request orders that belong to "
|
||||
"another company."
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright 2018 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockLocationRoute(models.Model):
|
||||
_inherit = "stock.location.route"
|
||||
|
||||
@api.constrains("company_id")
|
||||
def _check_company_stock_request(self):
|
||||
if any(
|
||||
rec.company_id
|
||||
and self.env["stock.request"].search(
|
||||
[("company_id", "!=", rec.company_id.id), ("route_id", "=", rec.id)],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the company of the route, as it is "
|
||||
"already assigned to stock requests that belong to "
|
||||
"another company."
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
allocation_ids = fields.One2many(
|
||||
comodel_name="stock.request.allocation",
|
||||
inverse_name="stock_move_id",
|
||||
string="Stock Request Allocation",
|
||||
)
|
||||
|
||||
stock_request_ids = fields.One2many(
|
||||
comodel_name="stock.request",
|
||||
string="Stock Requests",
|
||||
compute="_compute_stock_request_ids",
|
||||
)
|
||||
|
||||
@api.depends("allocation_ids")
|
||||
def _compute_stock_request_ids(self):
|
||||
for rec in self:
|
||||
rec.stock_request_ids = rec.allocation_ids.mapped("stock_request_id")
|
||||
|
||||
def _merge_moves_fields(self):
|
||||
res = super(StockMove, self)._merge_moves_fields()
|
||||
res["allocation_ids"] = [(4, m.id) for m in self.mapped("allocation_ids")]
|
||||
return res
|
||||
|
||||
@api.constrains("company_id")
|
||||
def _check_company_stock_request(self):
|
||||
if any(
|
||||
self.env["stock.request.allocation"].search(
|
||||
[
|
||||
("company_id", "!=", rec.company_id.id),
|
||||
("stock_move_id", "=", rec.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The company of the stock request must match with "
|
||||
"that of the location."
|
||||
)
|
||||
)
|
||||
|
||||
def copy_data(self, default=None):
|
||||
if not default:
|
||||
default = {}
|
||||
if "allocation_ids" not in default:
|
||||
default["allocation_ids"] = []
|
||||
for alloc in self.allocation_ids:
|
||||
default["allocation_ids"].append(
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"stock_request_id": alloc.stock_request_id.id,
|
||||
"requested_product_uom_qty": alloc.requested_product_uom_qty,
|
||||
},
|
||||
)
|
||||
)
|
||||
return super(StockMove, self).copy_data(default)
|
||||
|
||||
def _action_cancel(self):
|
||||
"""Apply sudo to prevent requests ACL errors if the user does not have
|
||||
permissions (example: productions)."""
|
||||
res = super()._action_cancel()
|
||||
self.mapped("allocation_ids.stock_request_id").sudo().check_cancel()
|
||||
return res
|
||||
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
"""Apply sudo to prevent requests ACL errors if the user does not have
|
||||
permissions (example: productions)."""
|
||||
res = super()._action_done(cancel_backorder=cancel_backorder)
|
||||
self.mapped("allocation_ids.stock_request_id").sudo().check_done()
|
||||
return res
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# Copyright 2017 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo import _, api, models
|
||||
|
||||
|
||||
class StockMoveLine(models.Model):
|
||||
_inherit = "stock.move.line"
|
||||
|
||||
@api.model
|
||||
def _stock_request_confirm_done_message_content(self, message_data):
|
||||
title = _("Receipt confirmation %s for your Request %s") % (
|
||||
message_data["picking_name"],
|
||||
message_data["request_name"],
|
||||
)
|
||||
message = "<h3>%s</h3>" % title
|
||||
message += _(
|
||||
"The following requested items from Stock Request %s "
|
||||
"have now been received in %s using Picking %s:"
|
||||
) % (
|
||||
message_data["request_name"],
|
||||
message_data["location_name"],
|
||||
message_data["picking_name"],
|
||||
)
|
||||
message += "<ul>"
|
||||
message += _("<li><b>%s</b>: Transferred quantity %s %s</li>") % (
|
||||
message_data["product_name"],
|
||||
message_data["product_qty"],
|
||||
message_data["product_uom"],
|
||||
)
|
||||
message += "</ul>"
|
||||
return message
|
||||
|
||||
def _prepare_message_data(self, ml, request, allocated_qty):
|
||||
return {
|
||||
"request_name": request.name,
|
||||
"picking_name": ml.picking_id.name,
|
||||
"product_name": ml.product_id.name_get()[0][1],
|
||||
"product_qty": allocated_qty,
|
||||
"product_uom": ml.product_uom_id.name,
|
||||
"location_name": ml.location_dest_id.name_get()[0][1],
|
||||
}
|
||||
|
||||
def _action_done(self):
|
||||
res = super(StockMoveLine, self)._action_done()
|
||||
for ml in self.filtered(lambda m: m.exists() and m.move_id.allocation_ids):
|
||||
qty_done = ml.product_uom_id._compute_quantity(
|
||||
ml.qty_done, ml.product_id.uom_id
|
||||
)
|
||||
|
||||
# We do sudo because potentially the user that completes the move
|
||||
# may not have permissions for stock.request.
|
||||
to_allocate_qty = qty_done
|
||||
for allocation in ml.move_id.allocation_ids.sudo():
|
||||
allocated_qty = 0.0
|
||||
if allocation.open_product_qty:
|
||||
allocated_qty = min(allocation.open_product_qty, to_allocate_qty)
|
||||
allocation.allocated_product_qty += allocated_qty
|
||||
to_allocate_qty -= allocated_qty
|
||||
if allocated_qty:
|
||||
request = allocation.stock_request_id
|
||||
message_data = self._prepare_message_data(
|
||||
ml, request, allocated_qty
|
||||
)
|
||||
message = self._stock_request_confirm_done_message_content(
|
||||
message_data
|
||||
)
|
||||
request.message_post(body=message, subtype_xmlid="mail.mt_comment")
|
||||
request.check_done()
|
||||
return res
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
stock_request_ids = fields.One2many(
|
||||
comodel_name="stock.request",
|
||||
string="Stock Requests",
|
||||
compute="_compute_stock_request_ids",
|
||||
)
|
||||
stock_request_count = fields.Integer(
|
||||
"Stock Request #", compute="_compute_stock_request_ids"
|
||||
)
|
||||
|
||||
@api.depends("move_lines")
|
||||
def _compute_stock_request_ids(self):
|
||||
for rec in self:
|
||||
rec.stock_request_ids = rec.move_lines.mapped("stock_request_ids")
|
||||
rec.stock_request_count = len(rec.stock_request_ids)
|
||||
|
||||
def action_view_stock_request(self):
|
||||
"""
|
||||
:return dict: dictionary value for created view
|
||||
"""
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(
|
||||
"stock_request.action_stock_request_form"
|
||||
)
|
||||
requests = self.mapped("stock_request_ids")
|
||||
if len(requests) > 1:
|
||||
action["domain"] = [("id", "in", requests.ids)]
|
||||
elif requests:
|
||||
action["views"] = [
|
||||
(self.env.ref("stock_request.view_stock_request_form").id, "form")
|
||||
]
|
||||
action["res_id"] = requests.id
|
||||
return action
|
||||
|
|
@ -0,0 +1,479 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from datetime import datetime, time
|
||||
|
||||
from dateutil import relativedelta
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class StockRequest(models.Model):
|
||||
_name = "stock.request"
|
||||
_description = "Stock Request"
|
||||
_inherit = "stock.request.abstract"
|
||||
_order = "id desc"
|
||||
|
||||
def _get_default_requested_by(self):
|
||||
return self.env["res.users"].browse(self.env.uid)
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_date():
|
||||
return fields.Datetime.now()
|
||||
|
||||
name = fields.Char(states={"draft": [("readonly", False)]})
|
||||
state = fields.Selection(
|
||||
selection=[
|
||||
("draft", "Draft"),
|
||||
("open", "In progress"),
|
||||
("done", "Done"),
|
||||
("cancel", "Cancelled"),
|
||||
],
|
||||
string="Status",
|
||||
copy=False,
|
||||
default="draft",
|
||||
index=True,
|
||||
readonly=True,
|
||||
tracking=True,
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
"res.users",
|
||||
"Requested by",
|
||||
required=True,
|
||||
tracking=True,
|
||||
default=lambda s: s._get_default_requested_by(),
|
||||
)
|
||||
expected_date = fields.Datetime(
|
||||
"Expected Date",
|
||||
index=True,
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="Date when you expect to receive the goods.",
|
||||
)
|
||||
picking_policy = fields.Selection(
|
||||
[
|
||||
("direct", "Receive each product when available"),
|
||||
("one", "Receive all products at once"),
|
||||
],
|
||||
string="Shipping Policy",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
default="direct",
|
||||
)
|
||||
move_ids = fields.One2many(
|
||||
comodel_name="stock.move",
|
||||
compute="_compute_move_ids",
|
||||
string="Stock Moves",
|
||||
readonly=True,
|
||||
)
|
||||
picking_ids = fields.One2many(
|
||||
"stock.picking",
|
||||
compute="_compute_picking_ids",
|
||||
string="Pickings",
|
||||
readonly=True,
|
||||
)
|
||||
qty_in_progress = fields.Float(
|
||||
"Qty In Progress",
|
||||
digits="Product Unit of Measure",
|
||||
readonly=True,
|
||||
compute="_compute_qty",
|
||||
store=True,
|
||||
help="Quantity in progress.",
|
||||
)
|
||||
qty_done = fields.Float(
|
||||
"Qty Done",
|
||||
digits="Product Unit of Measure",
|
||||
readonly=True,
|
||||
compute="_compute_qty",
|
||||
store=True,
|
||||
help="Quantity completed",
|
||||
)
|
||||
qty_cancelled = fields.Float(
|
||||
"Qty Cancelled",
|
||||
digits="Product Unit of Measure",
|
||||
readonly=True,
|
||||
compute="_compute_qty",
|
||||
store=True,
|
||||
help="Quantity cancelled",
|
||||
)
|
||||
picking_count = fields.Integer(
|
||||
string="Delivery Orders",
|
||||
compute="_compute_picking_ids",
|
||||
readonly=True,
|
||||
)
|
||||
allocation_ids = fields.One2many(
|
||||
comodel_name="stock.request.allocation",
|
||||
inverse_name="stock_request_id",
|
||||
string="Stock Request Allocation",
|
||||
)
|
||||
order_id = fields.Many2one("stock.request.order", readonly=True)
|
||||
warehouse_id = fields.Many2one(
|
||||
states={"draft": [("readonly", False)]}, readonly=True
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
states={"draft": [("readonly", False)]}, readonly=True
|
||||
)
|
||||
product_id = fields.Many2one(states={"draft": [("readonly", False)]}, readonly=True)
|
||||
product_uom_id = fields.Many2one(
|
||||
states={"draft": [("readonly", False)]}, readonly=True
|
||||
)
|
||||
product_uom_qty = fields.Float(
|
||||
states={"draft": [("readonly", False)]}, readonly=True
|
||||
)
|
||||
procurement_group_id = fields.Many2one(
|
||||
states={"draft": [("readonly", False)]}, readonly=True
|
||||
)
|
||||
company_id = fields.Many2one(states={"draft": [("readonly", False)]}, readonly=True)
|
||||
route_id = fields.Many2one(states={"draft": [("readonly", False)]}, readonly=True)
|
||||
rule_ids = fields.Many2many(
|
||||
"stock.rule", string="Rules used", compute="_compute_rules"
|
||||
)
|
||||
lead_days_date = fields.Date(compute="_compute_lead_days")
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique(name, company_id)", "Stock Request name must be unique")
|
||||
]
|
||||
|
||||
@api.constrains("state", "product_qty")
|
||||
def _check_qty(self):
|
||||
for rec in self:
|
||||
if rec.state == "draft" and rec.product_qty <= 0:
|
||||
raise ValidationError(
|
||||
_("Stock Request product quantity has to be strictly positive.")
|
||||
)
|
||||
elif rec.state != "draft" and rec.product_qty < 0:
|
||||
raise ValidationError(
|
||||
_("Stock Request product quantity cannot be negative.")
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"route_id",
|
||||
"product_id",
|
||||
"location_id",
|
||||
"company_id",
|
||||
"warehouse_id",
|
||||
"product_id.route_ids",
|
||||
)
|
||||
def _compute_rules(self):
|
||||
for request in self:
|
||||
if not request.product_id or not request.location_id:
|
||||
request.rule_ids = False
|
||||
continue
|
||||
request.rule_ids = request.product_id._get_rules_from_location(
|
||||
request.location_id, route_ids=request.route_id
|
||||
)
|
||||
|
||||
@api.depends("rule_ids", "product_id.seller_ids", "product_id.seller_ids.delay")
|
||||
def _compute_lead_days(self):
|
||||
for request in self.with_context(bypass_delay_description=True):
|
||||
if not request.product_id or not request.location_id:
|
||||
request.lead_days_date = False
|
||||
continue
|
||||
lead_days, dummy = request.rule_ids._get_lead_days(request.product_id)
|
||||
lead_days_date = fields.Date.today() + relativedelta.relativedelta(
|
||||
days=lead_days
|
||||
)
|
||||
request.lead_days_date = lead_days_date
|
||||
|
||||
@api.depends("allocation_ids", "allocation_ids.stock_move_id")
|
||||
def _compute_move_ids(self):
|
||||
for request in self:
|
||||
request.move_ids = request.allocation_ids.mapped("stock_move_id")
|
||||
|
||||
@api.depends(
|
||||
"allocation_ids",
|
||||
"allocation_ids.stock_move_id",
|
||||
"allocation_ids.stock_move_id.picking_id",
|
||||
)
|
||||
def _compute_picking_ids(self):
|
||||
for request in self:
|
||||
request.picking_count = 0
|
||||
request.picking_ids = self.env["stock.picking"]
|
||||
request.picking_ids = request.move_ids.filtered(
|
||||
lambda m: m.state != "cancel"
|
||||
).mapped("picking_id")
|
||||
request.picking_count = len(request.picking_ids)
|
||||
|
||||
@api.depends(
|
||||
"allocation_ids",
|
||||
"allocation_ids.stock_move_id.state",
|
||||
"allocation_ids.stock_move_id.move_line_ids",
|
||||
"allocation_ids.stock_move_id.move_line_ids.qty_done",
|
||||
)
|
||||
def _compute_qty(self):
|
||||
for request in self:
|
||||
incoming_qty = 0.0
|
||||
other_qty = 0.0
|
||||
for allocation in request.allocation_ids:
|
||||
if allocation.stock_move_id.picking_code == "incoming":
|
||||
incoming_qty += allocation.allocated_product_qty
|
||||
else:
|
||||
other_qty += allocation.allocated_product_qty
|
||||
done_qty = abs(other_qty - incoming_qty)
|
||||
open_qty = sum(request.allocation_ids.mapped("open_product_qty"))
|
||||
uom = request.product_id.uom_id
|
||||
request.qty_done = uom._compute_quantity(done_qty, request.product_uom_id)
|
||||
request.qty_in_progress = uom._compute_quantity(
|
||||
open_qty, request.product_uom_id
|
||||
)
|
||||
request.qty_cancelled = (
|
||||
max(
|
||||
0,
|
||||
uom._compute_quantity(
|
||||
request.product_qty - done_qty - open_qty,
|
||||
request.product_uom_id,
|
||||
),
|
||||
)
|
||||
if request.allocation_ids
|
||||
else 0
|
||||
)
|
||||
|
||||
@api.constrains("order_id", "requested_by")
|
||||
def check_order_requested_by(self):
|
||||
if self.order_id and self.order_id.requested_by != self.requested_by:
|
||||
raise ValidationError(_("Requested by must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "warehouse_id")
|
||||
def check_order_warehouse_id(self):
|
||||
if self.order_id and self.order_id.warehouse_id != self.warehouse_id:
|
||||
raise ValidationError(_("Warehouse must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "location_id")
|
||||
def check_order_location(self):
|
||||
if self.order_id and self.order_id.location_id != self.location_id:
|
||||
raise ValidationError(_("Location must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "procurement_group_id")
|
||||
def check_order_procurement_group(self):
|
||||
if (
|
||||
self.order_id
|
||||
and self.order_id.procurement_group_id != self.procurement_group_id
|
||||
):
|
||||
raise ValidationError(_("Procurement group must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "company_id")
|
||||
def check_order_company(self):
|
||||
if self.order_id and self.order_id.company_id != self.company_id:
|
||||
raise ValidationError(_("Company must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "expected_date")
|
||||
def check_order_expected_date(self):
|
||||
if self.order_id and self.order_id.expected_date != self.expected_date:
|
||||
raise ValidationError(_("Expected date must be equal to the order"))
|
||||
|
||||
@api.constrains("order_id", "picking_policy")
|
||||
def check_order_picking_policy(self):
|
||||
if self.order_id and self.order_id.picking_policy != self.picking_policy:
|
||||
raise ValidationError(_("The picking policy must be equal to the order"))
|
||||
|
||||
def _action_confirm(self):
|
||||
self._action_launch_procurement_rule()
|
||||
self.filtered(lambda x: x.state != "done").write({"state": "open"})
|
||||
|
||||
def action_confirm(self):
|
||||
self._action_confirm()
|
||||
return True
|
||||
|
||||
def action_draft(self):
|
||||
self.write({"state": "draft"})
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
self.sudo().mapped("move_ids")._action_cancel()
|
||||
self.write({"state": "cancel"})
|
||||
return True
|
||||
|
||||
def action_done(self):
|
||||
self.write({"state": "done"})
|
||||
return True
|
||||
|
||||
def check_cancel(self):
|
||||
for request in self:
|
||||
if request._check_cancel_allocation():
|
||||
request.write({"state": "cancel"})
|
||||
|
||||
def check_done(self):
|
||||
precision = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
for request in self:
|
||||
allocated_qty = sum(request.allocation_ids.mapped("allocated_product_qty"))
|
||||
qty_done = request.product_id.uom_id._compute_quantity(
|
||||
allocated_qty, request.product_uom_id
|
||||
)
|
||||
if (
|
||||
float_compare(
|
||||
qty_done, request.product_uom_qty, precision_digits=precision
|
||||
)
|
||||
>= 0
|
||||
):
|
||||
request.action_done()
|
||||
elif request._check_cancel_allocation():
|
||||
# If qty_done=0 and qty_cancelled>0 it's cancelled
|
||||
request.write({"state": "cancel"})
|
||||
return True
|
||||
|
||||
def _check_cancel_allocation(self):
|
||||
precision = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
self.ensure_one()
|
||||
return (
|
||||
self.allocation_ids
|
||||
and float_compare(self.qty_cancelled, 0, precision_digits=precision) > 0
|
||||
)
|
||||
|
||||
def _prepare_procurement_values(self, group_id=False):
|
||||
|
||||
"""Prepare specific key for moves or other components that
|
||||
will be created from a procurement rule
|
||||
coming from a stock request. This method could be override
|
||||
in order to add other custom key that could be used in
|
||||
move/po creation.
|
||||
"""
|
||||
return {
|
||||
"date_planned": max(
|
||||
[self.expected_date, datetime.combine(self.lead_days_date, time.min)]
|
||||
),
|
||||
"warehouse_id": self.warehouse_id,
|
||||
"stock_request_allocation_ids": self.id,
|
||||
"group_id": group_id or self.procurement_group_id.id or False,
|
||||
"route_ids": self.route_id,
|
||||
"stock_request_id": self.id,
|
||||
}
|
||||
|
||||
def _skip_procurement(self):
|
||||
return self.state != "draft" or self.product_id.type not in ("consu", "product")
|
||||
|
||||
def _prepare_stock_move(self, qty):
|
||||
return {
|
||||
"name": self.product_id.display_name,
|
||||
"company_id": self.company_id.id,
|
||||
"product_id": self.product_id.id,
|
||||
"product_uom_qty": qty,
|
||||
"product_uom": self.product_id.uom_id.id,
|
||||
"location_id": self.location_id.id,
|
||||
"location_dest_id": self.location_id.id,
|
||||
"state": "draft",
|
||||
"reference": self.name,
|
||||
}
|
||||
|
||||
def _prepare_stock_request_allocation(self, move):
|
||||
return {
|
||||
"stock_request_id": self.id,
|
||||
"stock_move_id": move.id,
|
||||
"requested_product_uom_qty": move.product_uom_qty,
|
||||
}
|
||||
|
||||
def _action_use_stock_available(self):
|
||||
"""Create a stock move with the necessary data and mark it as done."""
|
||||
allocation_model = self.env["stock.request.allocation"]
|
||||
stock_move_model = self.env["stock.move"].sudo()
|
||||
precision = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
quants = self.env["stock.quant"]._gather(self.product_id, self.location_id)
|
||||
pending_qty = self.product_uom_qty
|
||||
for quant in quants.filtered(lambda x: x.available_quantity >= 0):
|
||||
qty_move = min(pending_qty, quant.available_quantity)
|
||||
if float_compare(qty_move, 0, precision_digits=precision) > 0:
|
||||
move = stock_move_model.create(self._prepare_stock_move(qty_move))
|
||||
move._action_confirm()
|
||||
pending_qty -= qty_move
|
||||
# Create allocation + done move
|
||||
allocation_model.create(self._prepare_stock_request_allocation(move))
|
||||
move.quantity_done = move.product_uom_qty
|
||||
move._action_done()
|
||||
|
||||
def _action_launch_procurement_rule(self):
|
||||
"""
|
||||
Launch procurement group (if not enough stock is available) run method
|
||||
with required/custom fields genrated by a
|
||||
stock request. procurement group will launch '_run_move',
|
||||
'_run_buy' or '_run_manufacture'
|
||||
depending on the stock request product rule.
|
||||
"""
|
||||
precision = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
errors = []
|
||||
for request in self:
|
||||
if request._skip_procurement():
|
||||
continue
|
||||
qty = 0.0
|
||||
for move in request.move_ids.filtered(lambda r: r.state != "cancel"):
|
||||
qty += move.product_qty
|
||||
|
||||
if float_compare(qty, request.product_qty, precision_digits=precision) >= 0:
|
||||
continue
|
||||
|
||||
# If stock is available we use it and we do not execute rule
|
||||
if request.company_id.stock_request_check_available_first:
|
||||
if (
|
||||
float_compare(
|
||||
request.product_id.sudo()
|
||||
.with_context(location=request.location_id.id)
|
||||
.free_qty,
|
||||
request.product_uom_qty,
|
||||
precision_digits=precision,
|
||||
)
|
||||
>= 0
|
||||
):
|
||||
request._action_use_stock_available()
|
||||
continue
|
||||
|
||||
values = request._prepare_procurement_values(
|
||||
group_id=request.procurement_group_id
|
||||
)
|
||||
try:
|
||||
procurements = []
|
||||
procurements.append(
|
||||
self.env["procurement.group"].Procurement(
|
||||
request.product_id,
|
||||
request.product_uom_qty,
|
||||
request.product_uom_id,
|
||||
request.location_id,
|
||||
request.name,
|
||||
request.name,
|
||||
self.env.company,
|
||||
values,
|
||||
)
|
||||
)
|
||||
self.env["procurement.group"].sudo().run(procurements)
|
||||
except UserError as error:
|
||||
errors.append(error.name)
|
||||
if errors:
|
||||
raise UserError("\n".join(errors))
|
||||
return True
|
||||
|
||||
def action_view_transfer(self):
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(
|
||||
"stock.action_picking_tree_all"
|
||||
)
|
||||
pickings = self.mapped("picking_ids")
|
||||
if len(pickings) > 1:
|
||||
action["domain"] = [("id", "in", pickings.ids)]
|
||||
elif pickings:
|
||||
action["views"] = [(self.env.ref("stock.view_picking_form").id, "form")]
|
||||
action["res_id"] = pickings.id
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
upd_vals = vals.copy()
|
||||
if upd_vals.get("name", "/") == "/":
|
||||
upd_vals["name"] = self.env["ir.sequence"].next_by_code("stock.request")
|
||||
if "order_id" in upd_vals:
|
||||
order_id = self.env["stock.request.order"].browse(upd_vals["order_id"])
|
||||
upd_vals["expected_date"] = order_id.expected_date
|
||||
else:
|
||||
upd_vals["expected_date"] = self._get_expected_date()
|
||||
return super().create(upd_vals)
|
||||
|
||||
def unlink(self):
|
||||
if self.filtered(lambda r: r.state != "draft"):
|
||||
raise UserError(_("Only requests on draft state can be unlinked"))
|
||||
return super(StockRequest, self).unlink()
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockRequest(models.AbstractModel):
|
||||
_name = "stock.request.abstract"
|
||||
_description = "Stock Request Template"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(StockRequest, self).default_get(fields)
|
||||
warehouse = None
|
||||
if "warehouse_id" not in res and res.get("company_id"):
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", res["company_id"])], limit=1
|
||||
)
|
||||
if warehouse:
|
||||
res["warehouse_id"] = warehouse.id
|
||||
res["location_id"] = warehouse.lot_stock_id.id
|
||||
return res
|
||||
|
||||
@api.depends(
|
||||
"product_id",
|
||||
"product_uom_id",
|
||||
"product_uom_qty",
|
||||
"product_id.product_tmpl_id.uom_id",
|
||||
)
|
||||
def _compute_product_qty(self):
|
||||
for rec in self:
|
||||
rec.product_qty = rec.product_uom_id._compute_quantity(
|
||||
rec.product_uom_qty, rec.product_id.product_tmpl_id.uom_id
|
||||
)
|
||||
|
||||
name = fields.Char("Name", copy=False, required=True, readonly=True, default="/")
|
||||
warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Warehouse",
|
||||
check_company=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
string="Location",
|
||||
domain="not allow_virtual_location and "
|
||||
"[('usage', 'in', ['internal', 'transit'])] or []",
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
"product.product",
|
||||
"Product",
|
||||
domain=[("type", "in", ["product", "consu"])],
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
)
|
||||
allow_virtual_location = fields.Boolean(
|
||||
related="company_id.stock_request_allow_virtual_loc", readonly=True
|
||||
)
|
||||
allowed_uom_categ_id = fields.Many2one(related="product_id.uom_id.category_id")
|
||||
product_uom_id = fields.Many2one(
|
||||
comodel_name="uom.uom",
|
||||
string="Product Unit of Measure",
|
||||
domain="[('category_id', '=?', allowed_uom_categ_id)]",
|
||||
required=True,
|
||||
default=lambda self: self._context.get("product_uom_id", False),
|
||||
)
|
||||
product_uom_qty = fields.Float(
|
||||
"Quantity",
|
||||
digits="Product Unit of Measure",
|
||||
required=True,
|
||||
help="Quantity, specified in the unit of measure indicated in the request.",
|
||||
)
|
||||
product_qty = fields.Float(
|
||||
"Real Quantity",
|
||||
compute="_compute_product_qty",
|
||||
store=True,
|
||||
copy=False,
|
||||
digits="Product Unit of Measure",
|
||||
help="Quantity in the default UoM of the product",
|
||||
)
|
||||
procurement_group_id = fields.Many2one(
|
||||
"procurement.group",
|
||||
"Procurement Group",
|
||||
help="Moves created through this stock request will be put in this "
|
||||
"procurement group. If none is given, the moves generated by "
|
||||
"procurement rules will be grouped into one big picking.",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
"res.company", "Company", required=True, default=lambda self: self.env.company
|
||||
)
|
||||
route_id = fields.Many2one(
|
||||
"stock.location.route",
|
||||
string="Route",
|
||||
domain="[('id', 'in', route_ids)]",
|
||||
ondelete="restrict",
|
||||
)
|
||||
|
||||
route_ids = fields.Many2many(
|
||||
"stock.location.route",
|
||||
string="Routes",
|
||||
compute="_compute_route_ids",
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique(name, company_id)", "Name must be unique")
|
||||
]
|
||||
|
||||
@api.depends("product_id", "warehouse_id", "location_id")
|
||||
def _compute_route_ids(self):
|
||||
route_obj = self.env["stock.location.route"]
|
||||
routes = route_obj.search(
|
||||
[("warehouse_ids", "in", self.mapped("warehouse_id").ids)]
|
||||
)
|
||||
routes_by_warehouse = {}
|
||||
for route in routes:
|
||||
for warehouse in route.warehouse_ids:
|
||||
routes_by_warehouse.setdefault(
|
||||
warehouse.id, self.env["stock.location.route"]
|
||||
)
|
||||
routes_by_warehouse[warehouse.id] |= route
|
||||
for record in self:
|
||||
routes = route_obj
|
||||
if record.product_id:
|
||||
routes += record.product_id.mapped(
|
||||
"route_ids"
|
||||
) | record.product_id.mapped("categ_id").mapped("total_route_ids")
|
||||
if record.warehouse_id and routes_by_warehouse.get(record.warehouse_id.id):
|
||||
routes |= routes_by_warehouse[record.warehouse_id.id]
|
||||
parents = record.get_parents().ids
|
||||
record.route_ids = routes.filtered(
|
||||
lambda r: any(p.location_id.id in parents for p in r.rule_ids)
|
||||
)
|
||||
|
||||
def get_parents(self):
|
||||
location = self.location_id
|
||||
result = location
|
||||
while location.location_id:
|
||||
location = location.location_id
|
||||
result |= location
|
||||
return result
|
||||
|
||||
@api.constrains(
|
||||
"company_id", "product_id", "warehouse_id", "location_id", "route_id"
|
||||
)
|
||||
def _check_company_constrains(self):
|
||||
"""Check if the related models have the same company"""
|
||||
for rec in self:
|
||||
if (
|
||||
rec.product_id.company_id
|
||||
and rec.product_id.company_id != rec.company_id
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You have entered a product that is assigned "
|
||||
"to another company."
|
||||
)
|
||||
)
|
||||
if (
|
||||
rec.location_id.company_id
|
||||
and rec.location_id.company_id != rec.company_id
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You have entered a location that is "
|
||||
"assigned to another company."
|
||||
)
|
||||
)
|
||||
if rec.warehouse_id.company_id != rec.company_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You have entered a warehouse that is "
|
||||
"assigned to another company."
|
||||
)
|
||||
)
|
||||
if (
|
||||
rec.route_id
|
||||
and rec.route_id.company_id
|
||||
and rec.route_id.company_id != rec.company_id
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You have entered a route that is "
|
||||
"assigned to another company."
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("product_id")
|
||||
def _check_product_uom(self):
|
||||
"""Check if the UoM has the same category as the
|
||||
product standard UoM"""
|
||||
if any(
|
||||
request.product_id.uom_id.category_id != request.product_uom_id.category_id
|
||||
for request in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You have to select a product unit of measure in the "
|
||||
"same category than the default unit "
|
||||
"of measure of the product"
|
||||
)
|
||||
)
|
||||
|
||||
@api.onchange("warehouse_id")
|
||||
def onchange_warehouse_id(self):
|
||||
"""Finds location id for changed warehouse."""
|
||||
if self._name == "stock.request" and self.order_id:
|
||||
# When the stock request is created from an order the wh and
|
||||
# location are taken from the order and we rely on it to change
|
||||
# all request associated. Thus, no need to apply
|
||||
# the onchange, as it could lead to inconsistencies.
|
||||
return
|
||||
if self.warehouse_id:
|
||||
loc_wh = self.location_id.get_warehouse()
|
||||
if self.warehouse_id != loc_wh:
|
||||
self.location_id = self.warehouse_id.lot_stock_id.id
|
||||
if self.warehouse_id.company_id != self.company_id:
|
||||
self.company_id = self.warehouse_id.company_id
|
||||
|
||||
@api.onchange("location_id")
|
||||
def onchange_location_id(self):
|
||||
if self.location_id:
|
||||
loc_wh = self.location_id.get_warehouse()
|
||||
if loc_wh and self.warehouse_id != loc_wh:
|
||||
self.warehouse_id = loc_wh
|
||||
self.with_context(no_change_childs=True).onchange_warehouse_id()
|
||||
|
||||
@api.onchange("company_id")
|
||||
def onchange_company_id(self):
|
||||
"""Sets a default warehouse when the company is changed."""
|
||||
if self.company_id and (
|
||||
not self.warehouse_id or self.warehouse_id.company_id != self.company_id
|
||||
):
|
||||
self.warehouse_id = self.env["stock.warehouse"].search(
|
||||
[
|
||||
"|",
|
||||
("company_id", "=", False),
|
||||
("company_id", "=", self.company_id.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
self.onchange_warehouse_id()
|
||||
|
||||
@api.onchange("product_id")
|
||||
def onchange_product_id(self):
|
||||
if self.product_id:
|
||||
self.product_uom_id = self.product_id.uom_id
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockRequestAllocation(models.Model):
|
||||
_name = "stock.request.allocation"
|
||||
_description = "Stock Request Allocation"
|
||||
|
||||
stock_request_id = fields.Many2one(
|
||||
string="Stock Request",
|
||||
comodel_name="stock.request",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
string="Company",
|
||||
comodel_name="res.company",
|
||||
readonly=True,
|
||||
related="stock_request_id.company_id",
|
||||
store=True,
|
||||
)
|
||||
stock_move_id = fields.Many2one(
|
||||
string="Stock Move",
|
||||
comodel_name="stock.move",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
string="Product",
|
||||
comodel_name="product.product",
|
||||
related="stock_request_id.product_id",
|
||||
readonly=True,
|
||||
)
|
||||
product_uom_id = fields.Many2one(
|
||||
string="UoM",
|
||||
comodel_name="uom.uom",
|
||||
related="stock_request_id.product_uom_id",
|
||||
readonly=True,
|
||||
)
|
||||
requested_product_uom_qty = fields.Float(
|
||||
"Requested Quantity (UoM)",
|
||||
help="Quantity of the stock request allocated to the stock move, "
|
||||
"in the UoM of the Stock Request",
|
||||
)
|
||||
requested_product_qty = fields.Float(
|
||||
"Requested Quantity",
|
||||
help="Quantity of the stock request allocated to the stock move, "
|
||||
"in the default UoM of the product",
|
||||
compute="_compute_requested_product_qty",
|
||||
)
|
||||
allocated_product_qty = fields.Float(
|
||||
"Allocated Quantity",
|
||||
copy=False,
|
||||
help="Quantity of the stock request allocated to the stock move, "
|
||||
"in the default UoM of the product",
|
||||
)
|
||||
open_product_qty = fields.Float(
|
||||
"Open Quantity",
|
||||
compute="_compute_open_product_qty",
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"stock_request_id.product_id",
|
||||
"stock_request_id.product_uom_id",
|
||||
"requested_product_uom_qty",
|
||||
)
|
||||
def _compute_requested_product_qty(self):
|
||||
for rec in self:
|
||||
rec.requested_product_qty = rec.product_uom_id._compute_quantity(
|
||||
rec.requested_product_uom_qty, rec.product_id.uom_id
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"requested_product_qty",
|
||||
"allocated_product_qty",
|
||||
"stock_move_id",
|
||||
"stock_move_id.state",
|
||||
)
|
||||
def _compute_open_product_qty(self):
|
||||
for rec in self:
|
||||
if rec.stock_move_id.state in ["cancel", "done"]:
|
||||
rec.open_product_qty = 0.0
|
||||
else:
|
||||
rec.open_product_qty = (
|
||||
rec.requested_product_qty - rec.allocated_product_qty
|
||||
)
|
||||
if rec.open_product_qty < 0.0:
|
||||
rec.open_product_qty = 0.0
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
# Copyright 2018 Creu Blanca
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
|
||||
|
||||
class StockRequestOrder(models.Model):
|
||||
_name = "stock.request.order"
|
||||
_description = "Stock Request Order"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
_order = "id desc"
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super().default_get(fields)
|
||||
warehouse = None
|
||||
if "warehouse_id" not in res and res.get("company_id"):
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", res["company_id"])], limit=1
|
||||
)
|
||||
if warehouse:
|
||||
res["warehouse_id"] = warehouse.id
|
||||
res["location_id"] = warehouse.lot_stock_id.id
|
||||
return res
|
||||
|
||||
def __get_request_order_states(self):
|
||||
return self.env["stock.request"].fields_get(allfields=["state"])["state"][
|
||||
"selection"
|
||||
]
|
||||
|
||||
def _get_request_order_states(self):
|
||||
return self.__get_request_order_states()
|
||||
|
||||
def _get_default_requested_by(self):
|
||||
return self.env["res.users"].browse(self.env.uid)
|
||||
|
||||
name = fields.Char(
|
||||
"Name",
|
||||
copy=False,
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
default="/",
|
||||
)
|
||||
state = fields.Selection(
|
||||
selection=_get_request_order_states,
|
||||
string="Status",
|
||||
copy=False,
|
||||
default="draft",
|
||||
index=True,
|
||||
readonly=True,
|
||||
tracking=True,
|
||||
compute="_compute_state",
|
||||
store=True,
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
"res.users",
|
||||
"Requested by",
|
||||
required=True,
|
||||
tracking=True,
|
||||
default=lambda s: s._get_default_requested_by(),
|
||||
)
|
||||
warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Warehouse",
|
||||
check_company=True,
|
||||
readonly=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
string="Location",
|
||||
domain="not allow_virtual_location and "
|
||||
"[('usage', 'in', ['internal', 'transit'])] or []",
|
||||
readonly=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
allow_virtual_location = fields.Boolean(
|
||||
related="company_id.stock_request_allow_virtual_loc", readonly=True
|
||||
)
|
||||
procurement_group_id = fields.Many2one(
|
||||
"procurement.group",
|
||||
"Procurement Group",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="Moves created through this stock request will be put in this "
|
||||
"procurement group. If none is given, the moves generated by "
|
||||
"procurement rules will be grouped into one big picking.",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
"res.company",
|
||||
"Company",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
expected_date = fields.Datetime(
|
||||
"Expected Date",
|
||||
default=fields.Datetime.now,
|
||||
index=True,
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="Date when you expect to receive the goods.",
|
||||
)
|
||||
picking_policy = fields.Selection(
|
||||
[
|
||||
("direct", "Receive each product when available"),
|
||||
("one", "Receive all products at once"),
|
||||
],
|
||||
string="Shipping Policy",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
default="direct",
|
||||
)
|
||||
move_ids = fields.One2many(
|
||||
comodel_name="stock.move",
|
||||
compute="_compute_move_ids",
|
||||
string="Stock Moves",
|
||||
readonly=True,
|
||||
)
|
||||
picking_ids = fields.One2many(
|
||||
"stock.picking",
|
||||
compute="_compute_picking_ids",
|
||||
string="Pickings",
|
||||
readonly=True,
|
||||
)
|
||||
picking_count = fields.Integer(
|
||||
string="Delivery Orders", compute="_compute_picking_ids", readonly=True
|
||||
)
|
||||
stock_request_ids = fields.One2many(
|
||||
"stock.request", inverse_name="order_id", copy=True
|
||||
)
|
||||
stock_request_count = fields.Integer(
|
||||
string="Stock requests", compute="_compute_stock_request_count", readonly=True
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique(name, company_id)", "Stock Request name must be unique")
|
||||
]
|
||||
|
||||
@api.depends("stock_request_ids.state")
|
||||
def _compute_state(self):
|
||||
for item in self:
|
||||
states = item.stock_request_ids.mapped("state")
|
||||
if not item.stock_request_ids or all(x == "draft" for x in states):
|
||||
item.state = "draft"
|
||||
elif all(x == "cancel" for x in states):
|
||||
item.state = "cancel"
|
||||
elif all(x in ("done", "cancel") for x in states):
|
||||
item.state = "done"
|
||||
else:
|
||||
item.state = "open"
|
||||
|
||||
@api.depends("stock_request_ids.allocation_ids")
|
||||
def _compute_picking_ids(self):
|
||||
for record in self:
|
||||
record.picking_ids = record.stock_request_ids.mapped("picking_ids")
|
||||
record.picking_count = len(record.picking_ids)
|
||||
|
||||
@api.depends("stock_request_ids")
|
||||
def _compute_move_ids(self):
|
||||
for record in self:
|
||||
record.move_ids = record.stock_request_ids.mapped("move_ids")
|
||||
|
||||
@api.depends("stock_request_ids")
|
||||
def _compute_stock_request_count(self):
|
||||
for record in self:
|
||||
record.stock_request_count = len(record.stock_request_ids)
|
||||
|
||||
@api.onchange("requested_by")
|
||||
def onchange_requested_by(self):
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("expected_date")
|
||||
def onchange_expected_date(self):
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("picking_policy")
|
||||
def onchange_picking_policy(self):
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("location_id")
|
||||
def onchange_location_id(self):
|
||||
if self.location_id:
|
||||
loc_wh = self.location_id.get_warehouse()
|
||||
if loc_wh and self.warehouse_id != loc_wh:
|
||||
self.warehouse_id = loc_wh
|
||||
self.with_context(no_change_childs=True).onchange_warehouse_id()
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("warehouse_id")
|
||||
def onchange_warehouse_id(self):
|
||||
if self.warehouse_id:
|
||||
# search with sudo because the user may not have permissions
|
||||
loc_wh = self.location_id.get_warehouse()
|
||||
if self.warehouse_id != loc_wh:
|
||||
self.location_id = self.warehouse_id.lot_stock_id
|
||||
self.with_context(no_change_childs=True).onchange_location_id()
|
||||
if self.warehouse_id.company_id != self.company_id:
|
||||
self.company_id = self.warehouse_id.company_id
|
||||
self.with_context(no_change_childs=True).onchange_company_id()
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("procurement_group_id")
|
||||
def onchange_procurement_group_id(self):
|
||||
self.change_childs()
|
||||
|
||||
@api.onchange("company_id")
|
||||
def onchange_company_id(self):
|
||||
if self.company_id and (
|
||||
not self.warehouse_id or self.warehouse_id.company_id != self.company_id
|
||||
):
|
||||
self.warehouse_id = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", self.company_id.id)], limit=1
|
||||
)
|
||||
self.with_context(no_change_childs=True).onchange_warehouse_id()
|
||||
self.change_childs()
|
||||
|
||||
def change_childs(self):
|
||||
if not self._context.get("no_change_childs", False):
|
||||
for line in self.stock_request_ids:
|
||||
line.warehouse_id = self.warehouse_id
|
||||
line.location_id = self.location_id
|
||||
line.company_id = self.company_id
|
||||
line.picking_policy = self.picking_policy
|
||||
line.expected_date = self.expected_date
|
||||
line.requested_by = self.requested_by
|
||||
line.procurement_group_id = self.procurement_group_id
|
||||
|
||||
def action_confirm(self):
|
||||
if not self.stock_request_ids:
|
||||
raise UserError(
|
||||
_("There should be at least one request item for confirming the order.")
|
||||
)
|
||||
self.mapped("stock_request_ids").action_confirm()
|
||||
return True
|
||||
|
||||
def action_draft(self):
|
||||
self.mapped("stock_request_ids").action_draft()
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
self.mapped("stock_request_ids").action_cancel()
|
||||
return True
|
||||
|
||||
def action_done(self):
|
||||
return True
|
||||
|
||||
def action_view_transfer(self):
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(
|
||||
"stock.action_picking_tree_all"
|
||||
)
|
||||
|
||||
pickings = self.mapped("picking_ids")
|
||||
if len(pickings) > 1:
|
||||
action["domain"] = [("id", "in", pickings.ids)]
|
||||
elif pickings:
|
||||
action["views"] = [(self.env.ref("stock.view_picking_form").id, "form")]
|
||||
action["res_id"] = pickings.id
|
||||
return action
|
||||
|
||||
def action_view_stock_requests(self):
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(
|
||||
"stock_request.action_stock_request_form"
|
||||
)
|
||||
if len(self.stock_request_ids) > 1:
|
||||
action["domain"] = [("order_id", "in", self.ids)]
|
||||
elif self.stock_request_ids:
|
||||
action["views"] = [
|
||||
(self.env.ref("stock_request.view_stock_request_form").id, "form")
|
||||
]
|
||||
action["res_id"] = self.stock_request_ids.id
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
upd_vals = vals.copy()
|
||||
if upd_vals.get("name", "/") == "/":
|
||||
upd_vals["name"] = self.env["ir.sequence"].next_by_code(
|
||||
"stock.request.order"
|
||||
)
|
||||
return super().create(upd_vals)
|
||||
|
||||
def unlink(self):
|
||||
if self.filtered(lambda r: r.state != "draft"):
|
||||
raise UserError(_("Only orders on draft state can be unlinked"))
|
||||
return super().unlink()
|
||||
|
||||
@api.constrains("warehouse_id", "company_id")
|
||||
def _check_warehouse_company(self):
|
||||
if any(
|
||||
request.warehouse_id.company_id != request.company_id for request in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The company of the stock request must match with "
|
||||
"that of the warehouse."
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("location_id", "company_id")
|
||||
def _check_location_company(self):
|
||||
if any(
|
||||
request.location_id.company_id
|
||||
and request.location_id.company_id != request.company_id
|
||||
for request in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The company of the stock request must match with "
|
||||
"that of the location."
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("stock_request_ids")
|
||||
def _check_location_empty_stock_request_ids(self):
|
||||
if any(not request.stock_request_ids for request in self):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"It is not possible to set empty stock request orders (maybe "
|
||||
"you should cancel it)."
|
||||
)
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _create_from_product_multiselect(self, products):
|
||||
if not products:
|
||||
return False
|
||||
if products._name not in ("product.product", "product.template"):
|
||||
raise ValidationError(
|
||||
_("This action only works in the context of products")
|
||||
)
|
||||
if products._name == "product.template":
|
||||
# search instead of mapped so we don't include archived variants
|
||||
products = self.env["product.product"].search(
|
||||
[("product_tmpl_id", "in", products.ids)]
|
||||
)
|
||||
expected = self.default_get(["expected_date"])["expected_date"]
|
||||
try:
|
||||
order = self.env["stock.request.order"].create(
|
||||
dict(
|
||||
expected_date=expected,
|
||||
stock_request_ids=[
|
||||
(
|
||||
0,
|
||||
0,
|
||||
dict(
|
||||
product_id=product.id,
|
||||
product_uom_id=product.uom_id.id,
|
||||
product_uom_qty=1.0,
|
||||
expected_date=expected,
|
||||
),
|
||||
)
|
||||
for product in products
|
||||
],
|
||||
)
|
||||
)
|
||||
except AccessError:
|
||||
# TODO: if there is a nice way to hide the action from the
|
||||
# Action-menu if the user doesn't have the necessary rights,
|
||||
# that would be a better way of doing this
|
||||
raise UserError(
|
||||
_(
|
||||
"Unfortunately it seems you do not have the necessary rights "
|
||||
"for creating stock requests. Please contact your "
|
||||
"administrator."
|
||||
)
|
||||
)
|
||||
action = self.env["ir.actions.act_window"]._for_xml_id(
|
||||
"stock_request.stock_request_order_action"
|
||||
)
|
||||
action["views"] = [
|
||||
(self.env.ref("stock_request.stock_request_order_form").id, "form")
|
||||
]
|
||||
action["res_id"] = order.id
|
||||
return action
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Copyright 2017-2020 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockRule(models.Model):
|
||||
_inherit = "stock.rule"
|
||||
|
||||
def _get_stock_move_values(
|
||||
self,
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
company_id,
|
||||
values,
|
||||
):
|
||||
result = super(StockRule, self)._get_stock_move_values(
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
company_id,
|
||||
values,
|
||||
)
|
||||
if values.get("stock_request_id", False):
|
||||
result["allocation_ids"] = [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"stock_request_id": values.get("stock_request_id"),
|
||||
"requested_product_uom_qty": product_qty,
|
||||
},
|
||||
)
|
||||
]
|
||||
return result
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2018 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class StockWarehouse(models.Model):
|
||||
_inherit = "stock.warehouse"
|
||||
|
||||
@api.constrains("company_id")
|
||||
def _check_company_stock_request(self):
|
||||
if any(
|
||||
self.env["stock.request"].search(
|
||||
[
|
||||
("company_id", "!=", rec.company_id.id),
|
||||
("warehouse_id", "=", rec.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the company of the warehouse, as it is "
|
||||
"already assigned to stock requests that belong to "
|
||||
"another company."
|
||||
)
|
||||
)
|
||||
if any(
|
||||
self.env["stock.request.order"].search(
|
||||
[
|
||||
("company_id", "!=", rec.company_id.id),
|
||||
("warehouse_id", "=", rec.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
for rec in self
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You cannot change the company of the warehouse, as it is "
|
||||
"already assigned to stock request orders that belong to "
|
||||
"another company."
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
To configure this module:
|
||||
|
||||
* Go to Stock Requests > Settings
|
||||
|
||||
Users should be assigned to the groups 'Stock Request / User' or 'Stock
|
||||
Request / Manager'.
|
||||
|
||||
## Group Stock Request / User
|
||||
|
||||
* Can see her/his own Stock Requests, and others that she/he's been granted
|
||||
permission to follow.
|
||||
|
||||
* Can create/update only her/his Stock Requests.
|
||||
|
||||
## Group Stock Request / Manager
|
||||
|
||||
* Can fully manage all Stock Requests
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
* Jordi Ballester (EFICENT) <jordi.ballester@forgeflow.com>.
|
||||
* Enric Tobella <etobella@creublanca.es>
|
||||
* Atte Isopuro <atte.isopuro@avoin.systems>
|
||||
* Lois Rilo <lois.rilo@forgeflow.com>
|
||||
* Raul Martin <raul.martin@braintec-group.com>
|
||||
* Serpent Consulting Services Pvt. Ltd. <support@serpentcs.com>
|
||||
* `Open Source Integrators <https://www.opensourceintegrators.com>`_
|
||||
|
||||
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
|
||||
* Steve Campbell <scampbell@opensourceintegrators.com>
|
||||
|
||||
* Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||
* Kitti U. <kittiu@ecosoft.co.th>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
This module was written to allow users to request products that are
|
||||
frequently stocked by the company, to be transferred to their chosen location.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
There is no way to achieve Storck Request and Stock Request Orders.
|
||||
It should be developed taking into account that only Cancel and Done
|
||||
stock request can be archived.
|
||||
|
||||
It is also required to manage active field logically from Orders to SRs.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
## Creation
|
||||
|
||||
* Go to 'Stock Requests / Stock Requests' and create a new Request.
|
||||
* Indicate a product, quantity and location.
|
||||
* Press 'Confirm'.
|
||||
|
||||
Upon confirmation the request will be evaluated using the procurement rules
|
||||
for the selected location.
|
||||
|
||||
In case that transfers are created, the user will be able to access to them
|
||||
from the button 'Transfers' available in the Stock Request.
|
||||
|
||||
## Cancel
|
||||
|
||||
When the user cancels a Stock Request, the related pending stock moves will be
|
||||
also cancelled.
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_stock_request_user,stock request user,model_stock_request,group_stock_request_user,1,1,1,1
|
||||
access_stock_request_manager,stock request manager,model_stock_request,group_stock_request_manager,1,1,1,1
|
||||
access_stock_request_stock_user,stock.request stock user,model_stock_request,stock.group_stock_user,1,0,0,0
|
||||
access_stock_request_allocation_user,stock request allocation user,model_stock_request_allocation,group_stock_request_user,1,1,1,1
|
||||
access_stock_request_allocation_manager,stock request allocation manager,model_stock_request_allocation,group_stock_request_manager,1,1,1,1
|
||||
access_stock_request_allocation_stock_user,stock.request.allocation stock user,model_stock_request_allocation,base.group_user,1,0,0,0
|
||||
access_stock_location_user,stock.location.user,stock.model_stock_location,group_stock_request_user,1,0,0,0
|
||||
access_stock_location_request_manager,stock.location request manager,stock.model_stock_location,group_stock_request_manager,1,0,0,0
|
||||
access_stock_rule_request_manager,stock_rule_request_manager,stock.model_stock_rule,group_stock_request_manager,1,0,0,0
|
||||
access_stock_rule_user,stock_rule_user,stock.model_stock_rule,group_stock_request_user,1,0,0,0
|
||||
access_stock_request_order_user,stock request user,model_stock_request_order,group_stock_request_user,1,1,1,0
|
||||
access_stock_request_order_manager,stock request manager,model_stock_request_order,group_stock_request_manager,1,1,1,1
|
||||
access_stock_request_user_warehouse,stock request user Warehouse,stock.model_stock_warehouse,group_stock_request_user,1,0,0,0
|
||||
access_stock_request_manager_warehouse,stock request manager Warehouse,stock.model_stock_warehouse,group_stock_request_manager,1,0,0,0
|
||||
access_stock_request_user_stock_move,stock request user stock move,stock.model_stock_move,group_stock_request_user,1,0,0,0
|
||||
access_stock_request_manager_stock_move,stock request manager stock move,stock.model_stock_move,group_stock_request_manager,1,0,0,0
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.module.category" id="module_category_stock_request">
|
||||
<field name="name">Stock Request</field>
|
||||
<field name="parent_id" ref="base.module_category_inventory" />
|
||||
<field name="sequence">10</field>
|
||||
</record>
|
||||
<record id="group_stock_request_user" model="res.groups">
|
||||
<field name="name">Stock Request User</field>
|
||||
<field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
|
||||
<field name="category_id" ref="module_category_stock_request" />
|
||||
</record>
|
||||
<record id="group_stock_request_manager" model="res.groups">
|
||||
<field name="name">Stock Request Manager</field>
|
||||
<field
|
||||
name="users"
|
||||
eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"
|
||||
/>
|
||||
<field
|
||||
name="implied_ids"
|
||||
eval="[(4, ref('stock_request.group_stock_request_user')),(4, ref('stock.group_stock_user'))]"
|
||||
/>
|
||||
<field name="category_id" ref="module_category_stock_request" />
|
||||
</record>
|
||||
<record id="group_stock_request_order" model="res.groups">
|
||||
<field name="name">Stock Request Order</field>
|
||||
<field name="category_id" ref="base.module_category_hidden" />
|
||||
</record>
|
||||
<data noupdate="1">
|
||||
<record model="ir.rule" id="stock_picking_rule">
|
||||
<field name="name">stock_request multi-company</field>
|
||||
<field
|
||||
name="model_id"
|
||||
search="[('model','=','stock.request')]"
|
||||
model="ir.model"
|
||||
/>
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="stock_request_followers_rule" model="ir.rule">
|
||||
<field name="name">Follow Stock Request</field>
|
||||
<field name="model_id" ref="model_stock_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_stock_request_user')])]" />
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="False" />
|
||||
<field name="perm_create" eval="False" />
|
||||
<field name="perm_unlink" eval="False" />
|
||||
<field name="domain_force">['|',('requested_by','=',user.id),
|
||||
('message_partner_ids', 'in', [user.partner_id.id])]</field>
|
||||
</record>
|
||||
<record id="stock_request_rule" model="ir.rule">
|
||||
<field name="name">Stock Request User</field>
|
||||
<field name="model_id" ref="model_stock_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_stock_request_user')])]" />
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
<field name="domain_force">[('requested_by','=',user.id)]</field>
|
||||
</record>
|
||||
<record id="stock_request_manager_rule" model="ir.rule">
|
||||
<field name="name">Stock Request Manager</field>
|
||||
<field name="model_id" ref="model_stock_request" />
|
||||
<field name="groups" eval="[(6,0, [ref('group_stock_request_manager')])]" />
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
</record>
|
||||
<record model="ir.rule" id="stock_request_order_picking_rule">
|
||||
<field name="name">stock_request_order multi-company</field>
|
||||
<field name="model_id" ref="model_stock_request_order" />
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record id="stock_request_order_followers_rule" model="ir.rule">
|
||||
<field name="name">Follow Stock Request Order</field>
|
||||
<field name="model_id" ref="model_stock_request_order" />
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('stock_request.group_stock_request_user')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="False" />
|
||||
<field name="perm_create" eval="False" />
|
||||
<field name="perm_unlink" eval="False" />
|
||||
<field name="domain_force">['|',('requested_by','=',user.id),
|
||||
('message_partner_ids', 'in', [user.partner_id.id])]</field>
|
||||
</record>
|
||||
<record id="stock_request_order_rule" model="ir.rule">
|
||||
<field name="name">Stock Request Order User</field>
|
||||
<field name="model_id" ref="model_stock_request_order" />
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('stock_request.group_stock_request_user')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
<field name="domain_force">[('requested_by','=',user.id)]</field>
|
||||
</record>
|
||||
<record id="stock_request_order_manager_rule" model="ir.rule">
|
||||
<field name="name">Stock Request Manager</field>
|
||||
<field name="model_id" ref="model_stock_request_order" />
|
||||
<field
|
||||
name="groups"
|
||||
eval="[(6,0, [ref('stock_request.group_stock_request_manager')])]"
|
||||
/>
|
||||
<field name="perm_read" eval="True" />
|
||||
<field name="perm_write" eval="True" />
|
||||
<field name="perm_create" eval="True" />
|
||||
<field name="perm_unlink" eval="True" />
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -0,0 +1,172 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4562">
|
||||
<rect
|
||||
ry="4.1577382"
|
||||
y="13.040181"
|
||||
x="-124.92113"
|
||||
height="98.866074"
|
||||
width="99.811012"
|
||||
id="rect4564"
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
clip-path="none" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4578">
|
||||
<rect
|
||||
ry="4.1577382"
|
||||
y="1.5119057"
|
||||
x="-120.00744"
|
||||
height="97.930626"
|
||||
width="99.811012"
|
||||
id="rect4580"
|
||||
style="fill:#aa0000;stroke-width:0.26458332" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cx="35.432324"
|
||||
inkscape:cy="70.109427"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer2"
|
||||
showgrid="false"
|
||||
inkscape:snap-page="true"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1052"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false">
|
||||
<sodipodi:guide
|
||||
position="27.285156,77.130766"
|
||||
orientation="-0.70710678,0.70710678"
|
||||
id="guide4619"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="65.956845,10.583333"
|
||||
orientation="-0.70710678,0.70710678"
|
||||
id="guide4621"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="67.704985,78.09933"
|
||||
orientation="-0.70710678,0.70710678"
|
||||
id="guide4623"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="62.407434,24.722431"
|
||||
orientation="-0.70710678,0.70710678"
|
||||
id="guide4625"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-197)"
|
||||
style="display:inline" />
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2"
|
||||
style="display:inline">
|
||||
<rect
|
||||
y="0"
|
||||
x="0"
|
||||
height="25.164532"
|
||||
width="100"
|
||||
id="rect4535"
|
||||
style="fill:#de8787;stroke-width:0.13272627"
|
||||
ry="4.9136906"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
<rect
|
||||
ry="4.9136906"
|
||||
style="fill:#501616;stroke-width:0.13272627"
|
||||
id="rect4566"
|
||||
width="100"
|
||||
height="25.164532"
|
||||
x="0.18899068"
|
||||
y="74.958138"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
<rect
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
id="rect914"
|
||||
width="99.811012"
|
||||
height="97.930626"
|
||||
x="0.18898809"
|
||||
y="1.1339295"
|
||||
ry="4.1577382"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
<path
|
||||
id="path61"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 97.234379,149.70387 0.28348,94.58853 c 0,0 0.75595,3.1183 3.496281,3.87426 h 28.91518 c 0,0 3.30729,-0.94494 3.87426,-3.59077 v -7.74852 c 0,0 -0.94494,-3.1183 -3.68527,-3.59077 -2.74033,-0.47247 -18.23735,0 -18.23735,0 v -82.96577 c 0,0 -0.47247,-3.68526 -3.40179,-3.77976 h -7.46503 c 0,0 -2.834821,-0.0945 -3.779761,3.2128 z m -40.72672,-3.30742 -32.22284,0.0946 c 0,0 -11.24479,1.32285 -11.24479,11.15022 l 0.0946,25.22999 -10.866522,0.18914 c 0,0 -9.35491,-0.8503 -11.24479,10.77247 l 0.47232,47.05759 c 0,0 1.79532,21.82849 21.733562,21.63951 19.93824,-0.18899 21.73356,-21.54494 21.73356,-21.54494 l 14.45751,-0.0946 c 0,0 1.22835,21.734 21.73356,21.63951 0,0 21.1671,-0.28386 21.73407,-21.63951 0,0 -0.28348,-10.01622 -7.27604,-16.34732 v -24.28483 c 0,0 0.28326,-3.21309 -0.94516,-5.66993 -1.22842,-2.45685 -17.85938,-41.76593 -17.85938,-41.76593 0,0 -1.88966,-5.19754 -10.29963,-6.42596 z m -28.86542,14.55415 26.45989,0.13384 17.10489,39.28856 -0.13332,3.87521 h -21.91598 l -21.38164,-21.38112 z m -14.52107,69.22316 a 10.657372,10.657372 0 0 1 10.65722,10.65723 10.657372,10.657372 0 0 1 -10.65722,10.65723 10.657372,10.657372 0 0 1 -10.657752,-10.65723 10.657372,10.657372 0 0 1 10.657752,-10.65723 z m 58.20833,0 a 10.657372,10.657372 0 0 1 10.65723,10.65723 10.657372,10.657372 0 0 1 -10.65723,10.65723 10.657372,10.657372 0 0 1 -10.65775,-10.65723 10.657372,10.657372 0 0 1 10.65775,-10.65723 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -92.911273,23.247206 -27.285157,27.28811 v 49.842664 h 37.697532 17.674503 l 29.218074,-29.216604 -13.586893,-4.10161 -3.29823,-44.78112 -13.501505,13.50151 -6.362234,-9.16857 -20.55609,-3.36438"
|
||||
style="display:inline;opacity:0.476;fill:#550000;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="path4576"
|
||||
clip-path="url(#clipPath4578)"
|
||||
transform="translate(120.21264,-0.31639914)"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 65.712353,24.643945 0.14027,46.803935 c 0,0 0.374059,1.542981 1.730014,1.917051 h 14.307701 c 0,0 1.636494,-0.467579 1.917043,-1.776775 v -3.834094 c 0,0 -0.467568,-1.542985 -1.823534,-1.77677 -1.355955,-0.233787 -9.024128,0 -9.024128,0 V 24.924486 c 0,0 -0.23379,-1.823526 -1.683265,-1.870286 h -3.693817 c 0,0 -1.402715,-0.04676 -1.870284,1.589745 z M 45.560112,23.00738 29.615728,23.05418 c 0,0 -5.564103,0.654568 -5.564103,5.517309 l 0.04681,12.484207 -5.376931,0.09359 c 0,0 -4.628961,-0.420742 -5.564104,5.330392 l 0.233712,23.284857 c 0,0 0.888353,10.801089 10.754117,10.707579 9.865764,-0.09354 10.754117,-10.660784 10.754117,-10.660784 l 7.153819,-0.04682 c 0,0 0.607805,10.754338 10.75411,10.707578 0,0 10.473823,-0.140456 10.754372,-10.707578 0,0 -0.140269,-4.956187 -3.600297,-8.088919 V 49.659084 c 0,0 0.14016,-1.589888 -0.467689,-2.805573 -0.607837,-1.215689 -8.83711,-20.666457 -8.83711,-20.666457 0,0 -0.935026,-2.571826 -5.096422,-3.179669 z m -14.283078,7.201629 13.092779,0.06622 8.463775,19.440614 -0.06602,1.917516 H 41.923187 L 31.343205,41.053641 Z m -7.18526,34.252739 a 5.2734399,5.2734399 0 0 1 5.273364,5.27337 5.2734399,5.2734399 0 0 1 -5.273364,5.273369 5.2734399,5.2734399 0 0 1 -5.273628,-5.273369 5.2734399,5.2734399 0 0 1 5.273628,-5.27337 z m 28.802424,0 a 5.2734399,5.2734399 0 0 1 5.273363,5.27337 5.2734399,5.2734399 0 0 1 -5.273363,5.273369 5.2734399,5.2734399 0 0 1 -5.273626,-5.273369 5.2734399,5.2734399 0 0 1 5.273626,-5.27337 z"
|
||||
style="fill:#501616;stroke:none;stroke-width:0.1309201px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="path4582"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
<path
|
||||
id="path61-3"
|
||||
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.1309201px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 67.229989,22.903912 0.14027,46.803935 c 0,0 0.374059,1.542981 1.730014,1.917051 h 14.307701 c 0,0 1.636494,-0.467579 1.917043,-1.776775 v -3.834094 c 0,0 -0.467568,-1.542985 -1.823534,-1.77677 -1.355955,-0.233787 -9.024128,0 -9.024128,0 V 23.184453 c 0,0 -0.23379,-1.823526 -1.683265,-1.870286 h -3.693817 c 0,0 -1.402715,-0.04676 -1.870284,1.589745 z m -20.152241,-1.636565 -15.944384,0.0468 c 0,0 -5.564103,0.654568 -5.564103,5.517309 l 0.04681,12.484207 -5.376931,0.09359 c 0,0 -4.628961,-0.420742 -5.564104,5.330392 l 0.233712,23.284857 c 0,0 0.888353,10.801089 10.754117,10.707579 9.865764,-0.09354 10.754117,-10.660784 10.754117,-10.660784 l 7.153819,-0.04682 c 0,0 0.607805,10.754338 10.75411,10.707578 0,0 10.473823,-0.140456 10.754372,-10.707578 0,0 -0.140269,-4.956187 -3.600297,-8.088919 V 47.919051 c 0,0 0.14016,-1.589888 -0.467689,-2.805573 -0.607837,-1.215689 -8.83711,-20.666457 -8.83711,-20.666457 0,0 -0.935026,-2.571826 -5.096422,-3.179669 z m -14.283078,7.201629 13.092779,0.06622 8.463775,19.440614 -0.06602,1.917516 H 43.440823 L 32.860841,39.313608 Z m -7.18526,34.252739 a 5.2734399,5.2734399 0 0 1 5.273364,5.27337 5.2734399,5.2734399 0 0 1 -5.273364,5.273369 5.2734399,5.2734399 0 0 1 -5.273628,-5.273369 5.2734399,5.2734399 0 0 1 5.273628,-5.27337 z m 28.802424,0 a 5.2734399,5.2734399 0 0 1 5.273363,5.27337 5.2734399,5.2734399 0 0 1 -5.273363,5.273369 5.2734399,5.2734399 0 0 1 -5.273626,-5.273369 5.2734399,5.2734399 0 0 1 5.273626,-5.27337 z"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-xdpi="91.327972"
|
||||
inkscape:export-ydpi="91.327972" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
|
|
@ -0,0 +1,481 @@
|
|||
<!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: https://docutils.sourceforge.io/" />
|
||||
<title>Stock Request</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
Despite the name, some widely supported CSS2 features are used.
|
||||
|
||||
See https://docutils.sourceforge.io/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: gray; } /* 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, pre.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="stock-request">
|
||||
<h1 class="title">Stock Request</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:7d0cfabe2f67cd7a6c7fbde532efbb15414351ea68ed6ebb68c3e3747e2b97c3
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_request"><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 image-reference" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_request"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=14.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This module was written to allow users to request products that are
|
||||
frequently stocked by the company, to be transferred to their chosen location.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
|
||||
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-3">Known issues / Roadmap</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-4">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-5">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-6">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-7">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-8">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
|
||||
<p>To configure this module:</p>
|
||||
<ul class="simple">
|
||||
<li>Go to Stock Requests > Settings</li>
|
||||
</ul>
|
||||
<p>Users should be assigned to the groups ‘Stock Request / User’ or ‘Stock
|
||||
Request / Manager’.</p>
|
||||
<p>## Group Stock Request / User</p>
|
||||
<ul class="simple">
|
||||
<li>Can see her/his own Stock Requests, and others that she/he’s been granted
|
||||
permission to follow.</li>
|
||||
<li>Can create/update only her/his Stock Requests.</li>
|
||||
</ul>
|
||||
<p>## Group Stock Request / Manager</p>
|
||||
<ul class="simple">
|
||||
<li>Can fully manage all Stock Requests</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
|
||||
<p>## Creation</p>
|
||||
<ul class="simple">
|
||||
<li>Go to ‘Stock Requests / Stock Requests’ and create a new Request.</li>
|
||||
<li>Indicate a product, quantity and location.</li>
|
||||
<li>Press ‘Confirm’.</li>
|
||||
</ul>
|
||||
<p>Upon confirmation the request will be evaluated using the procurement rules
|
||||
for the selected location.</p>
|
||||
<p>In case that transfers are created, the user will be able to access to them
|
||||
from the button ‘Transfers’ available in the Stock Request.</p>
|
||||
<p>## Cancel</p>
|
||||
<p>When the user cancels a Stock Request, the related pending stock moves will be
|
||||
also cancelled.</p>
|
||||
</div>
|
||||
<div class="section" id="known-issues-roadmap">
|
||||
<h1><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h1>
|
||||
<p>There is no way to achieve Storck Request and Stock Request Orders.
|
||||
It should be developed taking into account that only Cancel and Done
|
||||
stock request can be archived.</p>
|
||||
<p>It is also required to manage active field logically from Orders to SRs.</p>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#toc-entry-4">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 to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_request%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="#toc-entry-5">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-6">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>ForgeFlow</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-7">Contributors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Jordi Ballester (EFICENT) <<a class="reference external" href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>>.</li>
|
||||
<li>Enric Tobella <<a class="reference external" href="mailto:etobella@creublanca.es">etobella@creublanca.es</a>></li>
|
||||
<li>Atte Isopuro <<a class="reference external" href="mailto:atte.isopuro@avoin.systems">atte.isopuro@avoin.systems</a>></li>
|
||||
<li>Lois Rilo <<a class="reference external" href="mailto:lois.rilo@forgeflow.com">lois.rilo@forgeflow.com</a>></li>
|
||||
<li>Raul Martin <<a class="reference external" href="mailto:raul.martin@braintec-group.com">raul.martin@braintec-group.com</a>></li>
|
||||
<li>Serpent Consulting Services Pvt. Ltd. <<a class="reference external" href="mailto:support@serpentcs.com">support@serpentcs.com</a>></li>
|
||||
<li><a class="reference external" href="https://www.opensourceintegrators.com">Open Source Integrators</a><ul>
|
||||
<li>Maxime Chambreuil <<a class="reference external" href="mailto:mchambreuil@opensourceintegrators.com">mchambreuil@opensourceintegrators.com</a>></li>
|
||||
<li>Steve Campbell <<a class="reference external" href="mailto:scampbell@opensourceintegrators.com">scampbell@opensourceintegrators.com</a>></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Héctor Villarreal <<a class="reference external" href="mailto:hector.villarreal@forgeflow.com">hector.villarreal@forgeflow.com</a>></li>
|
||||
<li>Kitti U. <<a class="reference external" href="mailto:kittiu@ecosoft.co.th">kittiu@ecosoft.co.th</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#toc-entry-8">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_request">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>
|
||||
|
|
@ -0,0 +1 @@
|
|||
from . import test_stock_request
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="action_variant_generate_stock_request_orders" model="ir.actions.server">
|
||||
<field name="name">Request Stock</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="product.model_product_product" />
|
||||
<field name="binding_model_id" ref="product.model_product_product" />
|
||||
<field name="code">
|
||||
action = records.env['stock.request.order']._create_from_product_multiselect(records)
|
||||
</field>
|
||||
</record>
|
||||
<record
|
||||
id="action_template_generate_stock_request_orders"
|
||||
model="ir.actions.server"
|
||||
>
|
||||
<field name="name">Request Stock</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="product.model_product_template" />
|
||||
<field name="binding_model_id" ref="product.model_product_template" />
|
||||
<field name="code">
|
||||
action = records.env['stock.request.order']._create_from_product_multiselect(records)
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
Copyright 2018 Creu Blanca
|
||||
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
-->
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.stock_request</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('settings')]" position="inside">
|
||||
<div
|
||||
class="app_settings_block"
|
||||
data-string="Stock Request"
|
||||
data-key="stock_request"
|
||||
groups="stock_request.group_stock_request_manager"
|
||||
>
|
||||
<h2>Orders & Configuration</h2>
|
||||
<div class="row mt16 o_settings_container" id="stock_request">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_stock_request_order" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Orders"
|
||||
for="group_stock_request_order"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Activates Stock Request Orders
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="stock_request_allow_virtual_loc" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Allow All Locations Types"
|
||||
for="stock_request_allow_virtual_loc"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
By default only internal and transit locations are allowed in Stock Request and Orders.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_stock_request_submit" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Submitted State"
|
||||
for="module_stock_request_purchase"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Add State to Stock Request and Stock Request Orders if activated.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="stock_request_check_available_first" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Check available stock first"
|
||||
for="stock_request_check_available_first"
|
||||
/>
|
||||
<div
|
||||
class="text-muted"
|
||||
>By default, available stock is not used</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Purchases</h2>
|
||||
<div
|
||||
class="row mt16 o_settings_container"
|
||||
id="stock_request_purchase"
|
||||
>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_stock_request_purchase" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Stock Requests for Purchases"
|
||||
for="module_stock_request_purchase"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Use Purchases with Stock Requests
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Manufacturing</h2>
|
||||
<div class="row mt16 o_settings_container" id="stock_request_mrp">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_stock_request_mrp" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Stock Requests for Manufacturing"
|
||||
for="module_stock_request_mrp"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Use Manufacturing Orders with Stock Requests
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Kanban</h2>
|
||||
<div
|
||||
class="row mt16 o_settings_container"
|
||||
id="stock_request_purchase"
|
||||
>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_stock_request_kanban" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Stock Requests Kanban cards"
|
||||
for="module_stock_request_purchase"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Use Kanban cards for consumable products
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Analytic</h2>
|
||||
<div
|
||||
class="row mt16 o_settings_container"
|
||||
id="stock_request_analytic"
|
||||
>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_stock_request_analytic" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label
|
||||
string="Enable Analytic Accounting in Stock Requests"
|
||||
for="module_stock_request_analytic"
|
||||
/>
|
||||
<div class="text-muted">
|
||||
Analytic accounting in Stock Requests
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_stock_request_config" model="ir.actions.act_window">
|
||||
<field name="name">Settings</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.config.settings</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">inline</field>
|
||||
<field name="context">{'module' : 'stock_request'}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="view_stock_move_operations" model="ir.ui.view">
|
||||
<field name="name">stock.move.operations.form</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit_id" ref="stock.view_stock_move_operations" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="move_line_ids" position="after">
|
||||
<newline />
|
||||
<field name="allocation_ids" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">stock.move.form</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit_id" ref="stock.view_move_form" />
|
||||
<field name="arch" type="xml">
|
||||
<group name="linked_group" position="after">
|
||||
<newline />
|
||||
<group name="allocations" string="Stock Request Allocations">
|
||||
<field name="allocation_ids" />
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="view_picking_form" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form" />
|
||||
<field eval="12" name="priority" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_stock_request"
|
||||
class="oe_stat_button"
|
||||
icon="fa-chain"
|
||||
attrs="{'invisible':[('stock_request_ids', '=', [])]}"
|
||||
>
|
||||
<field
|
||||
name="stock_request_count"
|
||||
widget="statinfo"
|
||||
string="Stock Requests"
|
||||
/>
|
||||
<field name="stock_request_ids" invisible="1" />
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2017 ForgeFlow
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="view_stock_request_allocation_tree" model="ir.ui.view">
|
||||
<field name="name">stock.request.allocation.tree</field>
|
||||
<field name="model">stock.request.allocation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="stock_request_id" />
|
||||
<field name="stock_move_id" />
|
||||
<field name="product_id" />
|
||||
<field name="requested_product_uom_qty" />
|
||||
<field
|
||||
name="product_uom_id"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
<field name="requested_product_qty" />
|
||||
<field name="allocated_product_qty" />
|
||||
<field name="open_product_qty" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_stock_request_allocation_form" model="ir.ui.view">
|
||||
<field name="name">stock.request.allocation.form</field>
|
||||
<field name="model">stock.request.allocation</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Request Allocations">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="stock_request_id" />
|
||||
<field name="stock_move_id" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="product_id" />
|
||||
<field name="requested_product_uom_qty" />
|
||||
<field
|
||||
name="product_uom_id"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
<field name="requested_product_qty" />
|
||||
<field name="allocated_product_qty" />
|
||||
<field name="open_product_qty" />
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
options="{'no_create': True}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<menuitem
|
||||
id="menu_stock_request_root"
|
||||
name="Stock Requests"
|
||||
groups="stock_request.group_stock_request_user,stock_request.group_stock_request_manager"
|
||||
web_icon="stock_request,static/description/icon.png"
|
||||
sequence="100"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_stock_request_operations"
|
||||
parent="menu_stock_request_root"
|
||||
name="Operations"
|
||||
sequence="10"
|
||||
/>
|
||||
<menuitem
|
||||
id="stock_request_order_menu"
|
||||
name="Stock Request Orders"
|
||||
parent="menu_stock_request_operations"
|
||||
action="stock_request_order_action"
|
||||
groups="group_stock_request_order"
|
||||
sequence="20"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_stock_request"
|
||||
action="action_stock_request_form"
|
||||
name="Stock Requests"
|
||||
parent="menu_stock_request_operations"
|
||||
sequence="30"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_stock_request_master_data"
|
||||
parent="menu_stock_request_root"
|
||||
name="Master Data"
|
||||
sequence="100"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_stock_request_master_data"
|
||||
parent="menu_stock_request_root"
|
||||
name="Master Data"
|
||||
sequence="100"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_stock_request_config"
|
||||
name="Settings"
|
||||
parent="menu_stock_request_root"
|
||||
sequence="999"
|
||||
action="action_stock_request_config"
|
||||
groups="base.group_system"
|
||||
/>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="stock_request_order_tree">
|
||||
<field name="name">stock.request.order.tree</field>
|
||||
<field name="model">stock.request.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="warehouse_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="stock_request_order_form">
|
||||
<field name="name">stock.request.order.form</field>
|
||||
<field name="model">stock.request.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button
|
||||
name="action_confirm"
|
||||
string="Confirm"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', 'not in', ['draft'])]}"
|
||||
/>
|
||||
<button
|
||||
name="action_cancel"
|
||||
states="draft,open"
|
||||
type="object"
|
||||
string="Cancel"
|
||||
/>
|
||||
<button
|
||||
name="action_draft"
|
||||
states="cancel"
|
||||
type="object"
|
||||
string="Set to Draft"
|
||||
/>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="picking_ids" invisible="1" />
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_transfer"
|
||||
class="oe_stat_button"
|
||||
icon="fa-truck"
|
||||
attrs="{'invisible': [('picking_count', '=', 0)]}"
|
||||
groups="stock.group_stock_user"
|
||||
>
|
||||
<field
|
||||
name="picking_count"
|
||||
widget="statinfo"
|
||||
string="Transfers"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_stock_requests"
|
||||
class="oe_stat_button"
|
||||
icon="fa-chain"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
groups="stock.group_stock_user"
|
||||
>
|
||||
<field
|
||||
name="stock_request_count"
|
||||
widget="statinfo"
|
||||
string="Stock Requests"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Stock request Order" />
|
||||
<h1>
|
||||
<field name="name" readonly="1" />
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="expected_date" />
|
||||
<field name="picking_policy" />
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="warehouse_id"
|
||||
widget="selection"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field
|
||||
name="location_id"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="allow_virtual_location" invisible="1" />
|
||||
<field
|
||||
name="procurement_group_id"
|
||||
groups="stock.group_adv_location"
|
||||
/>
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
options="{'no_create': True}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page name="items" string="Items">
|
||||
<field
|
||||
name="stock_request_ids"
|
||||
context="{
|
||||
'default_expected_date':expected_date,
|
||||
'default_picking_policy': picking_policy,
|
||||
'default_warehouse_id': warehouse_id,
|
||||
'default_location_id': location_id,
|
||||
'default_procurement_group_id': procurement_group_id,
|
||||
'default_company_id': company_id,
|
||||
'default_state': state,
|
||||
}"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
>
|
||||
<tree editable="bottom">
|
||||
<field name="name" readonly="1" />
|
||||
<field name="product_id" />
|
||||
<field name="allowed_uom_categ_id" invisible="1" />
|
||||
<field
|
||||
name="product_uom_id"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
<field
|
||||
name="route_id"
|
||||
options="{'no_create': True}"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="route_ids" invisible="1" />
|
||||
<field name="product_uom_qty" />
|
||||
<field name="qty_in_progress" />
|
||||
<field name="qty_done" />
|
||||
<field name="expected_date" invisible="1" />
|
||||
<field name="picking_policy" invisible="1" />
|
||||
<field name="warehouse_id" invisible="1" />
|
||||
<field
|
||||
name="allow_virtual_location"
|
||||
invisible="1"
|
||||
/>
|
||||
<field name="location_id" invisible="1" />
|
||||
<field name="procurement_group_id" invisible="1" />
|
||||
<field name="company_id" invisible="1" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="activity_ids" widget="mail_activity" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="stock_request_order_search">
|
||||
<field name="name">stock.request.order.search</field>
|
||||
<field name="model">stock.request.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Stock Requests Order Search">
|
||||
<field name="name" string="Stock Request Orders" />
|
||||
<field name="state" />
|
||||
<field name="warehouse_id" />
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<separator />
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" />
|
||||
<filter
|
||||
string="In Progress"
|
||||
name="open"
|
||||
domain="[('state','=','open')]"
|
||||
/>
|
||||
<filter string="Finished" name="done" domain="[('state','=','done')]" />
|
||||
<filter
|
||||
string="Cancelled"
|
||||
name="cancel"
|
||||
domain="[('state','=','cancel')]"
|
||||
/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter
|
||||
string="Warehouse"
|
||||
name="warehouse"
|
||||
domain="[]"
|
||||
context="{'group_by':'warehouse_id'}"
|
||||
/>
|
||||
<filter
|
||||
string="Location"
|
||||
name="location"
|
||||
domain="[]"
|
||||
context="{'group_by':'location_id'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="stock_request_order_action">
|
||||
<field name="name">Stock Request Orders</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">stock.request.order</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2017 ForgeFlow
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="view_stock_request_tree" model="ir.ui.view">
|
||||
<field name="name">stock.request.tree</field>
|
||||
<field name="model">stock.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree
|
||||
decoration-muted="state == 'cancel'"
|
||||
decoration-bf="message_needaction==True"
|
||||
>
|
||||
<field name="message_needaction" invisible="1" />
|
||||
<field name="name" />
|
||||
<field name="warehouse_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="allow_virtual_location" invisible="1" />
|
||||
<field
|
||||
name="route_id"
|
||||
options="{'no_create': True}"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="product_id" />
|
||||
<field name="allowed_uom_categ_id" invisible="1" />
|
||||
<field
|
||||
name="product_uom_id"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
<field name="product_uom_qty" />
|
||||
<field name="qty_in_progress" />
|
||||
<field name="qty_done" />
|
||||
<field name="qty_cancelled" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="stock_request_search">
|
||||
<field name="name">stock.request.search</field>
|
||||
<field name="model">stock.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Stock Requests Search">
|
||||
<field name="name" string="Stock Requests" />
|
||||
<field name="warehouse_id" />
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<field name="product_id" />
|
||||
<separator />
|
||||
<filter
|
||||
name="current_requests"
|
||||
string="Current requests"
|
||||
domain="['|', ('expected_date', '>', (datetime.date.today() - relativedelta(months=1)).strftime('%Y-%m-01')), ('state', '!=', 'done')]"
|
||||
/>
|
||||
<separator />
|
||||
<filter string="Draft" name="draft" domain="[('state','=','draft')]" />
|
||||
<filter
|
||||
string="In Progress"
|
||||
name="open"
|
||||
domain="[('state','=','open')]"
|
||||
/>
|
||||
<filter string="Finished" name="done" domain="[('state','=','done')]" />
|
||||
<filter
|
||||
string="Cancelled"
|
||||
name="cancel"
|
||||
domain="[('state','=','cancel')]"
|
||||
/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter
|
||||
name="warehouse"
|
||||
string="Warehouse"
|
||||
domain="[]"
|
||||
context="{'group_by':'warehouse_id'}"
|
||||
/>
|
||||
<filter
|
||||
name="location"
|
||||
string="Location"
|
||||
domain="[]"
|
||||
context="{'group_by':'location_id'}"
|
||||
/>
|
||||
<filter
|
||||
name="status"
|
||||
string="Status"
|
||||
domain="[]"
|
||||
context="{'group_by':'state'}"
|
||||
/>
|
||||
<filter
|
||||
name="route"
|
||||
string="Route"
|
||||
domain="[]"
|
||||
context="{'group_by':'route_id'}"
|
||||
/>
|
||||
<filter
|
||||
name="product"
|
||||
string="Product"
|
||||
domain="[]"
|
||||
context="{'group_by':'product_id'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_stock_request_form" model="ir.ui.view">
|
||||
<field name="name">stock.request.form</field>
|
||||
<field name="model">stock.request</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Requests">
|
||||
<header>
|
||||
<button
|
||||
name="action_confirm"
|
||||
string="Confirm"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', 'not in', ['draft'])]}"
|
||||
/>
|
||||
<button
|
||||
name="action_cancel"
|
||||
states="draft,open"
|
||||
type="object"
|
||||
string="Cancel"
|
||||
/>
|
||||
<button
|
||||
name="action_draft"
|
||||
states="cancel"
|
||||
type="object"
|
||||
string="Set to Draft"
|
||||
/>
|
||||
<button
|
||||
name="action_done"
|
||||
string="Done"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', 'not in', ['open'])]}"
|
||||
/>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="picking_ids" invisible="1" />
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_transfer"
|
||||
class="oe_stat_button"
|
||||
icon="fa-truck"
|
||||
attrs="{'invisible': [('picking_count', '=', 0)]}"
|
||||
groups="stock.group_stock_user"
|
||||
>
|
||||
<field
|
||||
name="picking_count"
|
||||
widget="statinfo"
|
||||
string="Transfers"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Stock Request" />
|
||||
<h1>
|
||||
<field name="name" readonly="1" />
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field
|
||||
name="order_id"
|
||||
readonly="1"
|
||||
groups="stock_request.group_stock_request_order"
|
||||
/>
|
||||
<field name="product_id" />
|
||||
<field name="expected_date" />
|
||||
<field name="picking_policy" />
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="warehouse_id"
|
||||
widget="selection"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="allow_virtual_location" invisible="1" />
|
||||
<field
|
||||
name="location_id"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field
|
||||
name="route_id"
|
||||
options="{'no_create': True}"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="route_ids" invisible="1" />
|
||||
<field
|
||||
name="procurement_group_id"
|
||||
groups="stock.group_adv_location"
|
||||
/>
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
options="{'no_create': True}"
|
||||
/>
|
||||
</group>
|
||||
<group name="quantities">
|
||||
<label for="product_uom_qty" />
|
||||
<div>
|
||||
<field name="product_uom_qty" class="oe_inline" />
|
||||
<field name="allowed_uom_categ_id" invisible="1" />
|
||||
<field
|
||||
name="product_uom_id"
|
||||
class="oe_inline"
|
||||
options="{'no_open': True, 'no_create': True}"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
</div>
|
||||
<field
|
||||
name="qty_in_progress"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
<field
|
||||
name="qty_done"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
<field
|
||||
name="qty_cancelled"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<!--Empty notebook to inherit pages from other related modules-->
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" />
|
||||
<field name="activity_ids" widget="mail_activity" />
|
||||
<field name="message_ids" widget="mail_thread" />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_stock_request_form" model="ir.actions.act_window">
|
||||
<field name="name">Stock Requests</field>
|
||||
<field name="res_model">stock.request</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">tree,form,pivot</field>
|
||||
<field name="context">{
|
||||
'search_default_current_requests': 1,
|
||||
'pivot_column_groupby': ['location_id'], 'pivot_row_groupby': ['product_id'], 'pivot_measures': ['product_uom_qty', 'qty_done'],
|
||||
'search_default_draft': 1, 'search_default_open': 1
|
||||
}</field>
|
||||
<field name="view_id" ref="view_stock_request_tree" />
|
||||
<field name="search_view_id" ref="stock_request_search" />
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a Stock Request.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue