mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
1
setup/stock_request/odoo/addons/stock_request
Symbolic link
1
setup/stock_request/odoo/addons/stock_request
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../stock_request
|
||||
6
setup/stock_request/setup.py
Normal file
6
setup/stock_request/setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
135
stock_request/README.rst
Normal file
135
stock_request/README.rst
Normal file
@@ -0,0 +1,135 @@
|
||||
=============
|
||||
Stock Request
|
||||
=============
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |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/13.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-13-0/stock-logistics-warehouse-13-0-stock_request
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/153/13.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
|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 smashing it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_request%0Aversion:%2013.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>
|
||||
|
||||
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/13.0/stock_request>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
1
stock_request/__init__.py
Normal file
1
stock_request/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
27
stock_request/__manifest__.py
Normal file
27
stock_request/__manifest__.py
Normal file
@@ -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.0.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,
|
||||
}
|
||||
17
stock_request/data/stock_request_sequence_data.xml
Normal file
17
stock_request/data/stock_request_sequence_data.xml
Normal file
@@ -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>
|
||||
1096
stock_request/i18n/ca.po
Normal file
1096
stock_request/i18n/ca.po
Normal file
File diff suppressed because it is too large
Load Diff
1142
stock_request/i18n/de.po
Normal file
1142
stock_request/i18n/de.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/el_GR.po
Normal file
1097
stock_request/i18n/el_GR.po
Normal file
File diff suppressed because it is too large
Load Diff
1157
stock_request/i18n/es.po
Normal file
1157
stock_request/i18n/es.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/es_ES.po
Normal file
1097
stock_request/i18n/es_ES.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/eu.po
Normal file
1096
stock_request/i18n/eu.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/fi.po
Normal file
1096
stock_request/i18n/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/fr.po
Normal file
1096
stock_request/i18n/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/fr_CH.po
Normal file
1097
stock_request/i18n/fr_CH.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/fr_FR.po
Normal file
1097
stock_request/i18n/fr_FR.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/gl.po
Normal file
1096
stock_request/i18n/gl.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/hr.po
Normal file
1097
stock_request/i18n/hr.po
Normal file
File diff suppressed because it is too large
Load Diff
1098
stock_request/i18n/hr_HR.po
Normal file
1098
stock_request/i18n/hr_HR.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/it.po
Normal file
1096
stock_request/i18n/it.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/nl.po
Normal file
1096
stock_request/i18n/nl.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/nl_NL.po
Normal file
1097
stock_request/i18n/nl_NL.po
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/pt.po
Normal file
1096
stock_request/i18n/pt.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/pt_BR.po
Normal file
1097
stock_request/i18n/pt_BR.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/ro.po
Normal file
1097
stock_request/i18n/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
1098
stock_request/i18n/ru.po
Normal file
1098
stock_request/i18n/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/sl.po
Normal file
1097
stock_request/i18n/sl.po
Normal file
File diff suppressed because it is too large
Load Diff
1092
stock_request/i18n/stock_request.pot
Normal file
1092
stock_request/i18n/stock_request.pot
Normal file
File diff suppressed because it is too large
Load Diff
1096
stock_request/i18n/tr.po
Normal file
1096
stock_request/i18n/tr.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/tr_TR.po
Normal file
1097
stock_request/i18n/tr_TR.po
Normal file
File diff suppressed because it is too large
Load Diff
1097
stock_request/i18n/vi_VN.po
Normal file
1097
stock_request/i18n/vi_VN.po
Normal file
File diff suppressed because it is too large
Load Diff
1121
stock_request/i18n/zh_CN.po
Normal file
1121
stock_request/i18n/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
14
stock_request/models/__init__.py
Normal file
14
stock_request/models/__init__.py
Normal file
@@ -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
|
||||
29
stock_request/models/procurement_group.py
Normal file
29
stock_request/models/procurement_group.py
Normal file
@@ -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)
|
||||
14
stock_request/models/res_company.py
Normal file
14
stock_request/models/res_company.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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"
|
||||
)
|
||||
40
stock_request/models/res_config_settings.py
Normal file
40
stock_request/models/res_config_settings.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# 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_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
|
||||
45
stock_request/models/stock_location.py
Normal file
45
stock_request/models/stock_location.py
Normal file
@@ -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."
|
||||
)
|
||||
)
|
||||
27
stock_request/models/stock_location_route.py
Normal file
27
stock_request/models/stock_location_route.py
Normal file
@@ -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."
|
||||
)
|
||||
)
|
||||
78
stock_request/models/stock_move.py
Normal file
78
stock_request/models/stock_move.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# 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):
|
||||
res = super()._action_cancel()
|
||||
self.mapped("allocation_ids.stock_request_id").check_done()
|
||||
return res
|
||||
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
res = super()._action_done(cancel_backorder=cancel_backorder)
|
||||
self.mapped("allocation_ids.stock_request_id").check_done()
|
||||
return res
|
||||
70
stock_request/models/stock_move_line.py
Normal file
70
stock_request/models/stock_move_line.py
Normal file
@@ -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
|
||||
39
stock_request/models/stock_picking.py
Normal file
39
stock_request/models/stock_picking.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# 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.ref("stock_request.action_stock_request_form").read()[0]
|
||||
|
||||
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
|
||||
379
stock_request/models/stock_request.py
Normal file
379
stock_request/models/stock_request.py
Normal file
@@ -0,0 +1,379 @@
|
||||
# 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 UserError, ValidationError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
REQUEST_STATES = [
|
||||
("draft", "Draft"),
|
||||
("open", "In progress"),
|
||||
("done", "Done"),
|
||||
("cancel", "Cancelled"),
|
||||
]
|
||||
|
||||
|
||||
class StockRequest(models.Model):
|
||||
_name = "stock.request"
|
||||
_description = "Stock Request"
|
||||
_inherit = "stock.request.abstract"
|
||||
_order = "id desc"
|
||||
|
||||
def __get_request_states(self):
|
||||
return REQUEST_STATES
|
||||
|
||||
def _get_request_states(self):
|
||||
return self.__get_request_states()
|
||||
|
||||
def _get_default_requested_by(self):
|
||||
return self.env["res.users"].browse(self.env.uid)
|
||||
|
||||
@staticmethod
|
||||
def _get_expected_date():
|
||||
return fields.Datetime.now()
|
||||
|
||||
def _get_default_expected_date(self):
|
||||
if self.order_id:
|
||||
res = self.order_id.expected_date
|
||||
else:
|
||||
res = self._get_expected_date()
|
||||
return res
|
||||
|
||||
name = fields.Char(states={"draft": [("readonly", False)]})
|
||||
state = fields.Selection(
|
||||
selection=_get_request_states,
|
||||
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",
|
||||
default=lambda s: s._get_default_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)
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique(name, company_id)", "Stock Request name must be unique")
|
||||
]
|
||||
|
||||
@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.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"})
|
||||
self.mapped("order_id").check_done()
|
||||
return True
|
||||
|
||||
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_done_allocation():
|
||||
request.action_done()
|
||||
return True
|
||||
|
||||
def _check_done_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": self.expected_date,
|
||||
"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 _action_launch_procurement_rule(self):
|
||||
"""
|
||||
Launch procurement group 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
|
||||
|
||||
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"].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.ref("stock.action_picking_tree_all").read()[0]
|
||||
|
||||
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")
|
||||
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()
|
||||
265
stock_request/models/stock_request_abstract.py
Normal file
265
stock_request/models/stock_request_abstract.py
Normal file
@@ -0,0 +1,265 @@
|
||||
# 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(
|
||||
"stock.warehouse", "Warehouse", ondelete="cascade", required=True
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
"stock.location",
|
||||
"Location",
|
||||
domain=[("usage", "in", ["internal", "transit"])],
|
||||
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
|
||||
)
|
||||
product_uom_id = fields.Many2one(
|
||||
"uom.uom",
|
||||
"Product Unit of Measure",
|
||||
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.constrains("product_qty")
|
||||
def _check_qty(self):
|
||||
for rec in self:
|
||||
if rec.product_qty <= 0:
|
||||
raise ValidationError(
|
||||
_("Stock Request product quantity has to be strictly positive.")
|
||||
)
|
||||
|
||||
@api.onchange("warehouse_id")
|
||||
def onchange_warehouse_id(self):
|
||||
""" Finds location id for changed warehouse. """
|
||||
res = {"domain": {}}
|
||||
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 res
|
||||
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
|
||||
return res
|
||||
|
||||
@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("allow_virtual_location")
|
||||
def onchange_allow_virtual_location(self):
|
||||
if self.allow_virtual_location:
|
||||
return {"domain": {"location_id": []}}
|
||||
|
||||
@api.onchange("company_id")
|
||||
def onchange_company_id(self):
|
||||
"""Sets a default warehouse when the company is changed and limits
|
||||
the user selection of warehouses."""
|
||||
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.onchange_warehouse_id()
|
||||
|
||||
return {"domain": {"warehouse_id": [("company_id", "=", self.company_id.id)]}}
|
||||
|
||||
@api.onchange("product_id")
|
||||
def onchange_product_id(self):
|
||||
res = {"domain": {}}
|
||||
if self.product_id:
|
||||
self.product_uom_id = self.product_id.uom_id.id
|
||||
res["domain"]["product_uom_id"] = [
|
||||
("category_id", "=", self.product_id.uom_id.category_id.id)
|
||||
]
|
||||
return res
|
||||
res["domain"]["product_uom_id"] = []
|
||||
return res
|
||||
90
stock_request/models/stock_request_allocation.py
Normal file
90
stock_request/models/stock_request_allocation.py
Normal file
@@ -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
|
||||
361
stock_request/models/stock_request_order.py
Normal file
361
stock_request/models/stock_request_order.py
Normal file
@@ -0,0 +1,361 @@
|
||||
# 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"]._get_request_states()
|
||||
|
||||
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,
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
"res.users",
|
||||
"Requested by",
|
||||
required=True,
|
||||
tracking=True,
|
||||
default=lambda s: s._get_default_requested_by(),
|
||||
)
|
||||
warehouse_id = fields.Many2one(
|
||||
"stock.warehouse",
|
||||
"Warehouse",
|
||||
readonly=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
"stock.location",
|
||||
"Location",
|
||||
readonly=True,
|
||||
domain=[("usage", "in", ["internal", "transit"])],
|
||||
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.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("allow_virtual_location")
|
||||
def onchange_allow_virtual_location(self):
|
||||
if self.allow_virtual_location:
|
||||
return {"domain": {"location_id": []}}
|
||||
|
||||
@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()
|
||||
return {"domain": {"warehouse_id": [("company_id", "=", self.company_id.id)]}}
|
||||
|
||||
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):
|
||||
self.mapped("stock_request_ids").action_confirm()
|
||||
self.write({"state": "open"})
|
||||
return True
|
||||
|
||||
def action_draft(self):
|
||||
self.mapped("stock_request_ids").action_draft()
|
||||
self.write({"state": "draft"})
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
self.mapped("stock_request_ids").action_cancel()
|
||||
self.write({"state": "cancel"})
|
||||
return True
|
||||
|
||||
def action_done(self):
|
||||
self.write({"state": "done"})
|
||||
return True
|
||||
|
||||
def check_done(self):
|
||||
for rec in self:
|
||||
if not rec.stock_request_ids.filtered(lambda r: r.state != "done"):
|
||||
rec.action_done()
|
||||
return
|
||||
|
||||
def action_view_transfer(self):
|
||||
action = self.env.ref("stock.action_picking_tree_all").read()[0]
|
||||
|
||||
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.ref("stock_request.action_stock_request_form").read()[0]
|
||||
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.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.ref("stock_request.stock_request_order_action").read()[0]
|
||||
action["views"] = [
|
||||
(self.env.ref("stock_request.stock_request_order_form").id, "form")
|
||||
]
|
||||
action["res_id"] = order.id
|
||||
return action
|
||||
42
stock_request/models/stock_rule.py
Normal file
42
stock_request/models/stock_rule.py
Normal file
@@ -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
|
||||
46
stock_request/models/stock_warehouse.py
Normal file
46
stock_request/models/stock_warehouse.py
Normal file
@@ -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."
|
||||
)
|
||||
)
|
||||
17
stock_request/readme/CONFIGURE.rst
Normal file
17
stock_request/readme/CONFIGURE.rst
Normal file
@@ -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
|
||||
13
stock_request/readme/CONTRIBUTORS.rst
Normal file
13
stock_request/readme/CONTRIBUTORS.rst
Normal file
@@ -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>
|
||||
2
stock_request/readme/DESCRIPTION.rst
Normal file
2
stock_request/readme/DESCRIPTION.rst
Normal file
@@ -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.
|
||||
5
stock_request/readme/ROADMAP.rst
Normal file
5
stock_request/readme/ROADMAP.rst
Normal file
@@ -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.
|
||||
16
stock_request/readme/USAGE.rst
Normal file
16
stock_request/readme/USAGE.rst
Normal file
@@ -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.
|
||||
17
stock_request/security/ir.model.access.csv
Normal file
17
stock_request/security/ir.model.access.csv
Normal file
@@ -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,
|
||||
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
|
||||
|
123
stock_request/security/stock_request_security.xml
Normal file
123
stock_request/security/stock_request_security.xml
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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_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>
|
||||
<record id="stock.group_stock_user" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('group_stock_request_user'))]" />
|
||||
</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','child_of',[user.company_id.id])]</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','child_of',[user.company_id.id])]</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>
|
||||
BIN
stock_request/static/description/icon.png
Normal file
BIN
stock_request/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
172
stock_request/static/description/icon.svg
Normal file
172
stock_request/static/description/icon.svg
Normal file
@@ -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 |
476
stock_request/static/description/index.html
Normal file
476
stock_request/static/description/index.html
Normal file
@@ -0,0 +1,476 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
||||
<title>Stock Request</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: grey; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="stock-request">
|
||||
<h1 class="title">Stock Request</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" 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" href="https://github.com/OCA/stock-logistics-warehouse/tree/13.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" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-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" href="https://runbot.odoo-community.org/runbot/153/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-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="id1">Configuration</a></li>
|
||||
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
|
||||
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h1><a class="toc-backref" href="#id1">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="#id2">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="#id3">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="#id4">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_request%0Aversion:%2013.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="#id5">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>ForgeFlow</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#id7">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>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#id8">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/13.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>
|
||||
1
stock_request/tests/__init__.py
Normal file
1
stock_request/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_stock_request
|
||||
1104
stock_request/tests/test_stock_request.py
Normal file
1104
stock_request/tests/test_stock_request.py
Normal file
File diff suppressed because it is too large
Load Diff
26
stock_request/views/product.xml
Normal file
26
stock_request/views/product.xml
Normal file
@@ -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>
|
||||
153
stock_request/views/res_config_settings_views.xml
Normal file
153
stock_request/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,153 @@
|
||||
<?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>
|
||||
<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>
|
||||
27
stock_request/views/stock_move_views.xml
Normal file
27
stock_request/views/stock_move_views.xml
Normal file
@@ -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>
|
||||
27
stock_request/views/stock_picking_views.xml
Normal file
27
stock_request/views/stock_picking_views.xml
Normal file
@@ -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>
|
||||
59
stock_request/views/stock_request_allocation_views.xml
Normal file
59
stock_request/views/stock_request_allocation_views.xml
Normal file
@@ -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 string="Stock Request Allocations">
|
||||
<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>
|
||||
51
stock_request/views/stock_request_menu.xml
Normal file
51
stock_request/views/stock_request_menu.xml
Normal file
@@ -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>
|
||||
204
stock_request/views/stock_request_order_views.xml
Normal file
204
stock_request/views/stock_request_order_views.xml
Normal file
@@ -0,0 +1,204 @@
|
||||
<?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 string="Stock requests">
|
||||
<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 string="Stock request">
|
||||
<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" />
|
||||
<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="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="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>
|
||||
251
stock_request/views/stock_request_views.xml
Normal file
251
stock_request/views/stock_request_views.xml
Normal file
@@ -0,0 +1,251 @@
|
||||
<?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
|
||||
string="Stock Requests"
|
||||
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="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="location_id"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"
|
||||
/>
|
||||
<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="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>
|
||||
Reference in New Issue
Block a user