Merge PR #839 into 13.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2020-03-12 12:49:23 +00:00
64 changed files with 30637 additions and 0 deletions

124
stock_request/README.rst Normal file
View File

@@ -0,0 +1,124 @@
=============
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/12.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-12-0/stock-logistics-warehouse-12-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/12.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.
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:%2012.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
~~~~~~~
* Eficent
Contributors
~~~~~~~~~~~~
* Jordi Ballester (EFICENT) <jordi.ballester@eficent.com>.
* Enric Tobella <etobella@creublanca.es>
* Atte Isopuro <atte.isopuro@avoin.systems>
* Lois Rilo <lois.rilo@eficent.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>
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/12.0/stock_request>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

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

View File

@@ -0,0 +1,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": "13.0.1.0.0",
"license": "LGPL-3",
"website": "https://github.com/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,
}

View File

@@ -0,0 +1,20 @@
<?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>

1020
stock_request/i18n/ca.po Normal file

File diff suppressed because it is too large Load Diff

1062
stock_request/i18n/de.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/el_GR.po Normal file

File diff suppressed because it is too large Load Diff

1073
stock_request/i18n/es.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/es_ES.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/eu.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/fi.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/fr.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/fr_CH.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/fr_FR.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/gl.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/hr.po Normal file

File diff suppressed because it is too large Load Diff

1022
stock_request/i18n/hr_HR.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/it.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/nl.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/nl_NL.po Normal file

File diff suppressed because it is too large Load Diff

1020
stock_request/i18n/pt.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/pt_BR.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/ro.po Normal file

File diff suppressed because it is too large Load Diff

1022
stock_request/i18n/ru.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/sl.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,992 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_request
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: stock_request
#: code:addons/stock_request/models/stock_move_line.py:20
#, python-format
msgid "<li><b>%s</b>: Transferred quantity %s %s</li>"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_needaction
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_needaction
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_needaction
msgid "Action Needed"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Activates Stock Request Orders"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_ids
msgid "Activities"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_state
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_state
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_state
msgid "Activity State"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Add State to Stock Request and Stock Request Orders if activated."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__allocated_product_qty
msgid "Allocated Quantity"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Allow All Locations Types"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_company__stock_request_allow_virtual_loc
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__stock_request_allow_virtual_loc
#: model:ir.model.fields,field_description:stock_request.field_stock_request__allow_virtual_location
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__allow_virtual_location
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__allow_virtual_location
msgid "Allow Virtual locations on Stock Requests"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Analytic"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Analytic accounting in Stock Requests"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
msgid "Archived"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_attachment_count
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_attachment_count
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_attachment_count
msgid "Attachment Count"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "By default only internal and transit locations are allowed in Stock Request and Orders."
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Cancel"
msgstr ""
#. module: stock_request
#: model_terms:ir.actions.act_window,help:stock_request.action_stock_request_form
msgid "Click to add a Stock Request."
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_res_company
msgid "Companies"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__company_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__company_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__company_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__company_id
msgid "Company"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:189
#, python-format
msgid "Company must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Confirm"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__create_uid
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__create_uid
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__create_uid
msgid "Created by"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__create_date
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__create_date
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__create_date
msgid "Created on"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__expected_date
#: model:ir.model.fields,help:stock_request.field_stock_request_order__expected_date
msgid "Date when you expect to receive the goods."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__picking_count
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__picking_count
msgid "Delivery Orders"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__display_name
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__display_name
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__display_name
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__display_name
msgid "Display Name"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Done"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Enable Analytic Accounting in Stock Requests"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Enable Orders"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Enable Stock Requests Kanban cards"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Enable Stock Requests for Purchases"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Enable Submitted State"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__expected_date
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__expected_date
msgid "Expected Date"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:196
#, python-format
msgid "Expected date must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_follower_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_follower_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_follower_ids
msgid "Followers"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_channel_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_channel_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_channel_ids
msgid "Followers (Channels)"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_partner_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_partner_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_partner_ids
msgid "Followers (Partners)"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
msgid "Group By"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__group_stock_request_order
msgid "Group Stock Request Order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__id
msgid "ID"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_unread
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_unread
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_unread
msgid "If checked new messages require your attention."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_needaction
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_needaction
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_needaction
msgid "If checked, new messages require your attention."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_has_error
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_has_error
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_location
msgid "Inventory Locations"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_location_route
msgid "Inventory Routes"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_is_follower
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_is_follower
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_is_follower
msgid "Is Follower"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
msgid "Items"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Kanban"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request____last_update
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract____last_update
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation____last_update
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__write_uid
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__write_uid
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__write_date
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__write_date
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__location_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__location_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__location_id
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
msgid "Location"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:172
#, python-format
msgid "Location must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_main_attachment_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_main_attachment_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_main_attachment_id
msgid "Main Attachment"
msgstr ""
#. module: stock_request
#: model:ir.ui.menu,name:stock_request.menu_stock_request_master_data
msgid "Master Data"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_has_error
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_has_error
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_has_error
msgid "Message Delivery error"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_ids
msgid "Messages"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__procurement_group_id
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__procurement_group_id
#: model:ir.model.fields,help:stock_request.field_stock_request_order__procurement_group_id
msgid "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."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__name
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__name
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__name
msgid "Name"
msgstr ""
#. module: stock_request
#: sql_constraint:stock.request:0
#: sql_constraint:stock.request.abstract:0
msgid "Name must be unique"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_date_deadline
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_date_deadline
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_date_deadline
msgid "Next Activity Deadline"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_summary
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_summary
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_summary
msgid "Next Activity Summary"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_type_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_type_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_type_id
msgid "Next Activity Type"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_needaction_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_needaction_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_needaction_counter
msgid "Number of Actions"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_has_error_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_has_error_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_has_error_counter
msgid "Number of error"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_needaction_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_needaction_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_needaction_counter
msgid "Number of messages which requires an action"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_has_error_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_has_error_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__message_unread_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__message_unread_counter
#: model:ir.model.fields,help:stock_request.field_stock_request_order__message_unread_counter
msgid "Number of unread messages"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_order.py:267
#, python-format
msgid "Only orders on draft state can be unlinked"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:334
#, python-format
msgid "Only requests on draft state can be unlinked"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__open_product_qty
msgid "Open Quantity"
msgstr ""
#. module: stock_request
#: model:ir.ui.menu,name:stock_request.menu_stock_request_operations
msgid "Operations"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__order_id
msgid "Order"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Orders &amp; Configuration"
msgstr ""
#. module: stock_request
#: selection:stock.request,activity_state:0
#: selection:stock.request.abstract,activity_state:0
#: selection:stock.request.order,activity_state:0
msgid "Overdue"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__picking_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__picking_ids
msgid "Pickings"
msgstr ""
#. module: stock_request
#: selection:stock.request,activity_state:0
#: selection:stock.request.abstract,activity_state:0
#: selection:stock.request.order,activity_state:0
msgid "Planned"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_procurement_group
#: model:ir.model.fields,field_description:stock_request.field_stock_request__procurement_group_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__procurement_group_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__procurement_group_id
msgid "Procurement Group"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:182
#, python-format
msgid "Procurement group must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__product_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__product_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__product_id
msgid "Product"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_move_line
msgid "Product Moves (Stock Move Line)"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__product_uom_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__product_uom_id
msgid "Product Unit of Measure"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Purchases"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__qty_done
msgid "Qty Done"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__qty_in_progress
msgid "Qty In Progress"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__product_uom_qty
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__product_uom_qty
msgid "Quantity"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__qty_done
msgid "Quantity completed"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__qty_in_progress
msgid "Quantity in progress."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__product_qty
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__product_qty
msgid "Quantity in the default UoM of the product"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request_allocation__requested_product_uom_qty
msgid "Quantity of the stock request allocated to the stock move, in the UoM of the Stock Request"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request_allocation__allocated_product_qty
#: model:ir.model.fields,help:stock_request.field_stock_request_allocation__requested_product_qty
msgid "Quantity of the stock request allocated to the stock move, in the default UoM of the product"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__product_uom_qty
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__product_uom_qty
msgid "Quantity, specified in the unit of measure indicated in the request."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__product_qty
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__product_qty
msgid "Real Quantity"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_move_line.py:12
#, python-format
msgid "Receipt confirmation %s for your Request %s"
msgstr ""
#. module: stock_request
#: selection:stock.request,picking_policy:0
#: selection:stock.request.order,picking_policy:0
msgid "Receive all products at once"
msgstr ""
#. module: stock_request
#: selection:stock.request,picking_policy:0
#: selection:stock.request.order,picking_policy:0
msgid "Receive each product when available"
msgstr ""
#. module: stock_request
#: model:ir.actions.server,name:stock_request.action_template_generate_stock_request_orders
#: model:ir.actions.server,name:stock_request.action_variant_generate_stock_request_orders
msgid "Request Stock"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__requested_product_qty
msgid "Requested Quantity"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__requested_product_uom_qty
msgid "Requested Quantity (UoM)"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__requested_by
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__requested_by
msgid "Requested by"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:158
#, python-format
msgid "Requested by must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__activity_user_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__activity_user_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__activity_user_id
msgid "Responsible User"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__route_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__route_id
msgid "Route"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__route_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__route_ids
msgid "Routes"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Set to Draft"
msgstr ""
#. module: stock_request
#: model:ir.actions.act_window,name:stock_request.action_stock_request_config
#: model:ir.ui.menu,name:stock_request.menu_stock_request_config
msgid "Settings"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__picking_policy
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__picking_policy
msgid "Shipping Policy"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__state
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__state
msgid "Status"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__activity_state
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__activity_state
#: model:ir.model.fields,help:stock_request.field_stock_request_order__activity_state
msgid "Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_move
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__stock_move_id
msgid "Stock Move"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__move_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__move_ids
msgid "Stock Moves"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__stock_request_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__stock_request_ids
#: model:ir.module.category,name:stock_request.module_category_stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Stock Request"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_picking__stock_request_count
msgid "Stock Request #"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_request_allocation
#: model:ir.model.fields,field_description:stock_request.field_stock_move__allocation_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request__allocation_ids
msgid "Stock Request Allocation"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.view_move_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_move_picking_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_allocation_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_allocation_tree
msgid "Stock Request Allocations"
msgstr ""
#. module: stock_request
#: model:res.groups,name:stock_request.group_stock_request_manager
msgid "Stock Request Manager"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_request_order
#: model:res.groups,name:stock_request.group_stock_request_order
msgid "Stock Request Order"
msgstr ""
#. module: stock_request
#: model:ir.actions.act_window,name:stock_request.stock_request_order_action
#: model:ir.ui.menu,name:stock_request.stock_request_order_menu
msgid "Stock Request Orders"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_request_abstract
msgid "Stock Request Template"
msgstr ""
#. module: stock_request
#: model:res.groups,name:stock_request.group_stock_request_user
msgid "Stock Request User"
msgstr ""
#. module: stock_request
#: sql_constraint:stock.request:0
#: sql_constraint:stock.request.order:0
msgid "Stock Request name must be unique"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:165
#, python-format
msgid "Stock Request product quantity has to be strictly positive."
msgstr ""
#. module: stock_request
#: model:ir.actions.act_window,name:stock_request.action_stock_request_form
#: model:ir.model.fields,field_description:stock_request.field_stock_move__stock_request_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_picking__stock_request_ids
#: model:ir.ui.menu,name:stock_request.menu_stock_request
#: model:ir.ui.menu,name:stock_request.menu_stock_request_root
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
#: model_terms:ir.ui.view,arch_db:stock_request.view_picking_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_tree
msgid "Stock Requests"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__module_stock_request_analytic
msgid "Stock Requests Analytic integration"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__module_stock_request_kanban
msgid "Stock Requests Kanban integration"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
msgid "Stock Requests Search"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__module_stock_request_purchase
msgid "Stock Requests for Purchases"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_rule
msgid "Stock Rule"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
msgid "Stock request"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__stock_request_count
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_tree
msgid "Stock requests"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_res_config_settings__module_stock_request_submit
msgid "Submitted state in Stock Requests"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_move.py:38
#: code:addons/stock_request/models/stock_request_order.py:284
#, python-format
msgid "The company of the stock request must match with that of the location."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_order.py:275
#, python-format
msgid "The company of the stock request must match with that of the warehouse."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_move_line.py:15
#, python-format
msgid "The following requested items from Stock Request %s have now been received in %s using Picking %s:"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:206
#, python-format
msgid "The picking policy must be equal to the order"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_order.py:293
#, python-format
msgid "This action only works in the context of products"
msgstr ""
#. module: stock_request
#: selection:stock.request,activity_state:0
#: selection:stock.request.abstract,activity_state:0
#: selection:stock.request.order,activity_state:0
msgid "Today"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_picking
msgid "Transfer"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_order_form
#: model_terms:ir.ui.view,arch_db:stock_request.view_stock_request_form
msgid "Transfers"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_order.py:314
#, python-format
msgid "Unfortunately it seems you do not have the necessary rights for creating stock requests. Please contact your administrator."
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_unread
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_unread
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_unread
msgid "Unread Messages"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__message_unread_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__message_unread_counter
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__message_unread_counter
msgid "Unread Messages Counter"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request_allocation__product_uom_id
msgid "UoM"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Use Kanban cards for consumable products"
msgstr ""
#. module: stock_request
#: model_terms:ir.ui.view,arch_db:stock_request.res_config_settings_view_form
msgid "Use Purchases with Stock Requests"
msgstr ""
#. module: stock_request
#: model:ir.model,name:stock_request.model_stock_warehouse
#: model:ir.model.fields,field_description:stock_request.field_stock_request__warehouse_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__warehouse_id
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__warehouse_id
#: model_terms:ir.ui.view,arch_db:stock_request.stock_request_search
msgid "Warehouse"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request.py:165
#, python-format
msgid "Warehouse must be equal to the order"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,field_description:stock_request.field_stock_request__website_message_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_abstract__website_message_ids
#: model:ir.model.fields,field_description:stock_request.field_stock_request_order__website_message_ids
msgid "Website Messages"
msgstr ""
#. module: stock_request
#: model:ir.model.fields,help:stock_request.field_stock_request__website_message_ids
#: model:ir.model.fields,help:stock_request.field_stock_request_abstract__website_message_ids
#: model:ir.model.fields,help:stock_request.field_stock_request_order__website_message_ids
msgid "Website communication history"
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_location.py:24
#, python-format
msgid "You cannot change the company of the location, as it is already assigned to stock request orders that belong to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_location.py:17
#, python-format
msgid "You cannot change the company of the location, as it is already assigned to stock requests that belong to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_location_route.py:17
#, python-format
msgid "You cannot change the company of the route, as it is already assigned to stock requests that belong to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_warehouse.py:26
#, python-format
msgid "You cannot change the company of the warehouse, as it is already assigned to stock request orders that belong to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_warehouse.py:18
#, python-format
msgid "You cannot change the company of the warehouse, as it is already assigned to stock requests that belong to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:138
#, python-format
msgid "You have entered a location that is assigned to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:133
#, python-format
msgid "You have entered a product that is assigned to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:147
#, python-format
msgid "You have entered a route that is assigned to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:142
#, python-format
msgid "You have entered a warehouse that is assigned to another company."
msgstr ""
#. module: stock_request
#: code:addons/stock_request/models/stock_request_abstract.py:157
#, python-format
msgid "You have to select a product unit of measure in the same category than the default unit of measure of the product"
msgstr ""

1020
stock_request/i18n/tr.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/tr_TR.po Normal file

File diff suppressed because it is too large Load Diff

1021
stock_request/i18n/vi_VN.po Normal file

File diff suppressed because it is too large Load Diff

1037
stock_request/i18n/zh_CN.po Normal file

File diff suppressed because it is too large Load Diff

View 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

View 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):
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)

View File

@@ -0,0 +1,14 @@
# Copyright 2018 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.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"
)

View File

@@ -0,0 +1,38 @@
# 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"
)
# 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

View File

@@ -0,0 +1,45 @@
# Copyright 2018 Eficent Business and IT Consulting Services, 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."
)
)

View File

@@ -0,0 +1,27 @@
# Copyright 2018 Eficent Business and IT Consulting Services, 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."
)
)

View File

@@ -0,0 +1,50 @@
# 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."
)
)

View File

@@ -0,0 +1,65 @@
# Copyright 2017 Eficent Business and IT Consulting Services 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 = ml.qty_done
for allocation in ml.move_id.allocation_ids:
allocated_qty = 0.0
if allocation.open_product_qty:
allocated_qty = min(allocation.open_product_qty, qty_done)
allocation.allocated_product_qty += allocated_qty
to_allocate_qty -= 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="mail.mt_comment")
request.check_done()
return res

View 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

View File

@@ -0,0 +1,337 @@
# 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,
track_visibility="onchange",
)
requested_by = fields.Many2one(
"res.users",
"Requested by",
required=True,
track_visibility="onchange",
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",
)
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")
def _compute_move_ids(self):
for request in self:
request.move_ids = request.allocation_ids.mapped("stock_move_id")
@api.depends("allocation_ids")
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:
done_qty = sum(request.allocation_ids.mapped("allocated_product_qty"))
open_qty = sum(request.allocation_ids.mapped("open_product_qty"))
request.qty_done = request.product_id.uom_id._compute_quantity(
done_qty, request.product_uom_id
)
request.qty_in_progress = request.product_id.uom_id._compute_quantity(
open_qty, request.product_uom_id
)
@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.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.mapped("move_ids")._action_cancel()
self.state = "cancel"
return True
def action_done(self):
self.state = "done"
if self.order_id:
self.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()
return True
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()

View File

@@ -0,0 +1,257 @@
# 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"]
for wh in self.mapped("warehouse_id"):
wh_routes = route_obj.search([("warehouse_ids", "=", wh.id)])
for record in self.filtered(lambda r: r.warehouse_id == wh):
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:
routes |= wh_routes
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

View File

@@ -0,0 +1,88 @@
# 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",
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 == "cancel":
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

View File

@@ -0,0 +1,363 @@
# 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,
track_visibility="onchange",
)
requested_by = fields.Many2one(
"res.users",
"Requested by",
required=True,
track_visibility="onchange",
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):
for line in self.stock_request_ids:
line.action_confirm()
self.state = "open"
return True
def action_draft(self):
for line in self.stock_request_ids:
line.action_draft()
self.state = "draft"
return True
def action_cancel(self):
for line in self.stock_request_ids:
line.action_cancel()
self.state = "cancel"
return True
def action_done(self):
self.state = "done"
return True
def check_done(self):
if not self.stock_request_ids.filtered(lambda r: r.state != "done"):
self.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

View 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

View File

@@ -0,0 +1,46 @@
# Copyright 2018 Eficent Business and IT Consulting Services, 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."
)
)

View 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

View File

@@ -0,0 +1,12 @@
* 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>

View 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.

View 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.

View 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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_stock_request_user stock request user model_stock_request group_stock_request_user 1 1 1
3 access_stock_request_manager stock request manager model_stock_request group_stock_request_manager 1 1 1 1
4 access_stock_request_stock_user stock.request stock user model_stock_request stock.group_stock_user 1 0 0 0
5 access_stock_request_allocation_user stock request allocation user model_stock_request_allocation group_stock_request_user 1 1 1 1
6 access_stock_request_allocation_manager stock request allocation manager model_stock_request_allocation group_stock_request_manager 1 1 1 1
7 access_stock_request_allocation_stock_user stock.request.allocation stock user model_stock_request_allocation base.group_user 1 0 0 0
8 access_stock_location_user stock.location.user stock.model_stock_location group_stock_request_user 1 0 0 0
9 access_stock_location_request_manager stock.location request manager stock.model_stock_location group_stock_request_manager 1 0 0 0
10 access_stock_rule_request_manager stock_rule_request_manager stock.model_stock_rule group_stock_request_manager 1 0 0 0
11 access_stock_rule_user stock_rule_user stock.model_stock_rule group_stock_request_user 1 0 0 0
12 access_stock_request_order_user stock request user model_stock_request_order group_stock_request_user 1 1 1 0
13 access_stock_request_order_manager stock request manager model_stock_request_order group_stock_request_manager 1 1 1 1
14 access_stock_request_user_warehouse stock request user Warehouse stock.model_stock_warehouse group_stock_request_user 1 0 0 0
15 access_stock_request_manager_warehouse stock request manager Warehouse stock.model_stock_warehouse group_stock_request_manager 1 0 0 0
16 access_stock_request_user_stock_move stock request user stock move stock.model_stock_move group_stock_request_user 1 0 0 0
17 access_stock_request_manager_stock_move stock request manager stock move stock.model_stock_move group_stock_request_manager 1 0 0 0

View File

@@ -0,0 +1,117 @@
<?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_warehouse_management"/>
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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

View File

@@ -0,0 +1,467 @@
<?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/12.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-12-0/stock-logistics-warehouse-12-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/12.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="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">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 &gt; 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/hes 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="bug-tracker">
<h1><a class="toc-backref" href="#id3">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:%2012.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="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Eficent</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Jordi Ballester (EFICENT) &lt;<a class="reference external" href="mailto:jordi.ballester&#64;eficent.com">jordi.ballester&#64;eficent.com</a>&gt;.</li>
<li>Enric Tobella &lt;<a class="reference external" href="mailto:etobella&#64;creublanca.es">etobella&#64;creublanca.es</a>&gt;</li>
<li>Atte Isopuro &lt;<a class="reference external" href="mailto:atte.isopuro&#64;avoin.systems">atte.isopuro&#64;avoin.systems</a>&gt;</li>
<li>Lois Rilo &lt;<a class="reference external" href="mailto:lois.rilo&#64;eficent.com">lois.rilo&#64;eficent.com</a>&gt;</li>
<li>Raul Martin &lt;<a class="reference external" href="mailto:raul.martin&#64;braintec-group.com">raul.martin&#64;braintec-group.com</a>&gt;</li>
<li>Serpent Consulting Services Pvt. Ltd. &lt;<a class="reference external" href="mailto:support&#64;serpentcs.com">support&#64;serpentcs.com</a>&gt;</li>
<li><a class="reference external" href="https://www.opensourceintegrators.com">Open Source Integrators</a><ul>
<li>Maxime Chambreuil &lt;<a class="reference external" href="mailto:mchambreuil&#64;opensourceintegrators.com">mchambreuil&#64;opensourceintegrators.com</a>&gt;</li>
<li>Steve Campbell &lt;<a class="reference external" href="mailto:scampbell&#64;opensourceintegrators.com">scampbell&#64;opensourceintegrators.com</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/12.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>

View File

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

View File

@@ -0,0 +1,995 @@
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0).
from collections import Counter
from datetime import datetime
from odoo import exceptions, fields
from odoo.tests import common
class TestStockRequest(common.TransactionCase):
def setUp(self):
super(TestStockRequest, self).setUp()
# common models
self.stock_request = self.env["stock.request"]
self.request_order = self.env["stock.request.order"]
# refs
self.stock_request_user_group = self.env.ref(
"stock_request.group_stock_request_user"
)
self.stock_request_manager_group = self.env.ref(
"stock_request.group_stock_request_manager"
)
self.main_company = self.env.ref("base.main_company")
self.warehouse = self.env.ref("stock.warehouse0")
self.categ_unit = self.env.ref("uom.product_uom_categ_unit")
self.virtual_loc = self.env.ref("stock.stock_location_customers")
# common data
self.company_2 = self.env["res.company"].create(
{"name": "Comp2", "parent_id": self.main_company.id}
)
self.company_2_address = (
self.env["res.partner"]
.with_context(company_id=self.company_2.id)
.create({"name": "Peñiscola"})
)
self.wh2 = self.env["stock.warehouse"].search(
[("company_id", "=", self.company_2.id)], limit=1
)
self.stock_request_user = self._create_user(
"stock_request_user",
[self.stock_request_user_group.id],
[self.main_company.id, self.company_2.id],
)
self.stock_request_manager = self._create_user(
"stock_request_manager",
[self.stock_request_manager_group.id],
[self.main_company.id, self.company_2.id],
)
self.product = self._create_product("SH", "Shoes", False)
self.product_company_2 = self._create_product(
"SH_2", "Shoes", self.company_2.id
)
self.ressuply_loc = self.env["stock.location"].create(
{
"name": "Ressuply",
"location_id": self.warehouse.view_location_id.id,
"usage": "internal",
"company_id": self.main_company.id,
}
)
self.ressuply_loc_2 = self.env["stock.location"].create(
{
"name": "Ressuply",
"location_id": self.wh2.view_location_id.id,
"usage": "internal",
"company_id": self.company_2.id,
}
)
self.route = self.env["stock.location.route"].create(
{
"name": "Transfer",
"product_categ_selectable": False,
"product_selectable": True,
"company_id": self.main_company.id,
"sequence": 10,
}
)
self.route_2 = self.env["stock.location.route"].create(
{
"name": "Transfer",
"product_categ_selectable": False,
"product_selectable": True,
"company_id": self.company_2.id,
"sequence": 10,
}
)
self.uom_dozen = self.env["uom.uom"].create(
{
"name": "Test-DozenA",
"category_id": self.categ_unit.id,
"factor_inv": 12,
"uom_type": "bigger",
"rounding": 0.001,
}
)
self.env["stock.rule"].create(
{
"name": "Transfer",
"route_id": self.route.id,
"location_src_id": self.ressuply_loc.id,
"location_id": self.warehouse.lot_stock_id.id,
"action": "pull",
"picking_type_id": self.warehouse.int_type_id.id,
"procure_method": "make_to_stock",
"warehouse_id": self.warehouse.id,
"company_id": self.main_company.id,
}
)
self.env["stock.rule"].create(
{
"name": "Transfer",
"route_id": self.route_2.id,
"location_src_id": self.ressuply_loc_2.id,
"location_id": self.wh2.lot_stock_id.id,
"action": "pull",
"picking_type_id": self.wh2.int_type_id.id,
"procure_method": "make_to_stock",
"warehouse_id": self.wh2.id,
"company_id": self.company_2.id,
}
)
def _create_user(self, name, group_ids, company_ids):
return (
self.env["res.users"]
.with_context({"no_reset_password": True})
.create(
{
"name": name,
"password": "demo",
"login": name,
"email": "@".join([name, "test.com"]),
"groups_id": [(6, 0, group_ids)],
"company_ids": [(6, 0, company_ids)],
}
)
)
def _create_product(self, default_code, name, company_id, **vals):
return self.env["product.product"].create(
dict(
name=name,
default_code=default_code,
uom_id=self.env.ref("uom.product_uom_unit").id,
company_id=company_id,
type="product",
**vals
)
)
class TestStockRequestBase(TestStockRequest):
def setUp(self):
super(TestStockRequestBase, self).setUp()
def test_defaults(self):
vals = {
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
}
stock_request = (
self.stock_request.with_user(self.stock_request_user)
.with_context(company_id=self.main_company.id)
.create(vals)
)
self.assertEqual(stock_request.requested_by, self.stock_request_user)
self.assertEqual(stock_request.warehouse_id, self.warehouse)
self.assertEqual(stock_request.location_id, self.warehouse.lot_stock_id)
def test_defaults_order(self):
vals = {}
order = (
self.request_order.with_user(self.stock_request_user)
.with_context(company_id=self.main_company.id)
.create(vals)
)
self.assertEqual(order.requested_by, self.stock_request_user)
self.assertEqual(order.warehouse_id, self.warehouse)
self.assertEqual(order.location_id, self.warehouse.lot_stock_id)
def test_onchanges_order(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
order = self.request_order.with_user(self.stock_request_user).new(vals)
self.stock_request_user.company_id = self.company_2
order.company_id = self.company_2
order.onchange_company_id()
stock_request = order.stock_request_ids
self.assertEqual(order.warehouse_id, self.wh2)
self.assertEqual(order.location_id, self.wh2.lot_stock_id)
self.assertEqual(order.warehouse_id, stock_request.warehouse_id)
procurement_group = self.env["procurement.group"].create({"name": "TEST"})
order.procurement_group_id = procurement_group
order.onchange_procurement_group_id()
self.assertEqual(
order.procurement_group_id, order.stock_request_ids.procurement_group_id
)
order.procurement_group_id = procurement_group
order.onchange_procurement_group_id()
self.assertEqual(
order.procurement_group_id, order.stock_request_ids.procurement_group_id
)
order.picking_policy = "one"
order.onchange_picking_policy()
self.assertEqual(order.picking_policy, order.stock_request_ids.picking_policy)
order.expected_date = datetime.now()
order.onchange_expected_date()
self.assertEqual(order.expected_date, order.stock_request_ids.expected_date)
order.requested_by = self.stock_request_manager
order.onchange_requested_by()
self.assertEqual(order.requested_by, order.stock_request_ids.requested_by)
def test_onchanges(self):
self.product.route_ids = [(6, 0, self.route.ids)]
vals = {
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
}
stock_request = self.stock_request.with_user(self.stock_request_user).new(vals)
stock_request.product_id = self.product
vals = stock_request.default_get(["warehouse_id", "company_id"])
stock_request.update(vals)
stock_request.onchange_product_id()
self.assertIn(self.route.id, stock_request.route_ids.ids)
self.stock_request_user.company_id = self.company_2
stock_request.company_id = self.company_2
stock_request.onchange_company_id()
self.assertEqual(stock_request.warehouse_id, self.wh2)
self.assertEqual(stock_request.location_id, self.wh2.lot_stock_id)
product = self.env["product.product"].create(
{
"name": "Wheat",
"uom_id": self.env.ref("uom.product_uom_kgm").id,
"uom_po_id": self.env.ref("uom.product_uom_kgm").id,
}
)
# Test onchange_product_id
stock_request.product_id = product
res = stock_request.onchange_product_id()
self.assertEqual(
res["domain"]["product_uom_id"],
[("category_id", "=", product.uom_id.category_id.id)],
)
self.assertEqual(
stock_request.product_uom_id, self.env.ref("uom.product_uom_kgm")
)
stock_request.product_id = self.env["product.product"]
res = stock_request.onchange_product_id()
self.assertEqual(res["domain"]["product_uom_id"], [])
# Test onchange_warehouse_id
wh2_2 = (
self.env["stock.warehouse"]
.with_context(company_id=self.company_2.id)
.create(
{
"name": "C2_2",
"code": "C2_2",
"company_id": self.company_2.id,
"partner_id": self.company_2_address.id,
}
)
)
stock_request.warehouse_id = wh2_2
stock_request.onchange_warehouse_id()
self.assertEqual(stock_request.warehouse_id, wh2_2)
self.stock_request_user.company_id = self.main_company
stock_request.warehouse_id = self.warehouse
stock_request.onchange_warehouse_id()
self.assertEqual(stock_request.company_id, self.main_company)
self.assertEqual(stock_request.location_id, self.warehouse.lot_stock_id)
def test_stock_request_order_validations_01(self):
""" Testing the discrepancy in warehouse_id between
stock request and order"""
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.wh2.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_order_validations_02(self):
""" Testing the discrepancy in location_id between
stock request and order"""
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.wh2.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_order_validations_03(self):
""" Testing the discrepancy in requested_by between
stock request and order"""
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"requested_by": self.stock_request_user.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"requested_by": self.stock_request_manager.id,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_order_validations_04(self):
""" Testing the discrepancy in procurement_group_id between
stock request and order"""
procurement_group = self.env["procurement.group"].create(
{"name": "Procurement"}
)
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"procurement_group_id": procurement_group.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_order_validations_05(self):
""" Testing the discrepancy in company between
stock request and order"""
expected_date = fields.Datetime.now()
vals = {
"company_id": self.company_2.id,
"warehouse_id": self.wh2.id,
"location_id": self.wh2.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.company_2.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_order_validations_06(self):
""" Testing the discrepancy in expected dates between
stock request and order"""
expected_date = fields.Datetime.now()
child_expected_date = "2015-01-01"
vals = {
"company_id": self.company_2.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": child_expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.create(vals)
def test_stock_request_order_validations_07(self):
""" Testing the discrepancy in picking policy between
stock request and order"""
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"picking_policy": "one",
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
with self.assertRaises(exceptions.ValidationError):
self.request_order.with_user(self.stock_request_user).create(vals)
def test_stock_request_validations_01(self):
vals = {
"product_id": self.product.id,
"product_uom_id": self.env.ref("uom.product_uom_kgm").id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.stock_request.with_user(self.stock_request_user).create(vals)
def test_stock_request_validations_02(self):
vals = {
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
stock_request = self.stock_request.with_user(self.stock_request_user).create(
vals
)
# With no route found, should raise an error
with self.assertRaises(exceptions.UserError):
stock_request.action_confirm()
def test_create_request_01(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
order = self.request_order.with_user(self.stock_request_user).create(vals)
stock_request = order.stock_request_ids
self.product.route_ids = [(6, 0, self.route.ids)]
order.with_user(self.stock_request_user).action_confirm()
self.assertEqual(order.state, "open")
self.assertEqual(stock_request.state, "open")
self.assertEqual(len(order.picking_ids), 1)
self.assertEqual(len(order.move_ids), 1)
self.assertEqual(len(stock_request.picking_ids), 1)
self.assertEqual(len(stock_request.move_ids), 1)
self.assertEqual(
stock_request.move_ids[0].location_dest_id, stock_request.location_id
)
self.assertEqual(stock_request.qty_in_progress, stock_request.product_uom_qty)
self.env["stock.quant"].create(
{
"product_id": self.product.id,
"location_id": self.ressuply_loc.id,
"quantity": 5.0,
}
)
picking = stock_request.picking_ids[0]
picking.with_user(self.stock_request_manager).action_confirm()
self.assertEqual(stock_request.qty_in_progress, 5.0)
self.assertEqual(stock_request.qty_done, 0.0)
picking.with_user(self.stock_request_manager).action_assign()
self.assertEqual(picking.origin, order.name)
packout1 = picking.move_line_ids[0]
packout1.qty_done = 5
picking.with_user(self.stock_request_manager).action_done()
self.assertEqual(stock_request.qty_in_progress, 0.0)
self.assertEqual(stock_request.qty_done, stock_request.product_uom_qty)
self.assertEqual(order.state, "done")
self.assertEqual(stock_request.state, "done")
def test_create_request_02(self):
"""Use different UoM's"""
vals = {
"product_id": self.product.id,
"product_uom_id": self.uom_dozen.id,
"product_uom_qty": 1.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
stock_request = self.stock_request.with_user(self.stock_request_user).create(
vals
)
self.product.route_ids = [(6, 0, self.route.ids)]
stock_request.with_user(self.stock_request_manager).action_confirm()
self.assertEqual(stock_request.state, "open")
self.assertEqual(len(stock_request.picking_ids), 1)
self.assertEqual(len(stock_request.move_ids), 1)
self.assertEqual(
stock_request.move_ids[0].location_dest_id, stock_request.location_id
)
self.assertEqual(stock_request.qty_in_progress, stock_request.product_uom_qty)
self.env["stock.quant"].create(
{
"product_id": self.product.id,
"location_id": self.ressuply_loc.id,
"quantity": 12.0,
}
)
picking = stock_request.picking_ids[0]
picking.with_user(self.stock_request_manager).action_confirm()
self.assertEqual(stock_request.qty_in_progress, 1.0)
self.assertEqual(stock_request.qty_done, 0.0)
picking.with_user(self.stock_request_manager).action_assign()
packout1 = picking.move_line_ids[0]
packout1.qty_done = 1
picking.with_user(self.stock_request_manager).action_done()
self.assertEqual(stock_request.qty_in_progress, 0.0)
self.assertEqual(stock_request.qty_done, stock_request.product_uom_qty)
self.assertEqual(stock_request.state, "done")
def test_create_request_03(self):
"""Multiple stock requests"""
vals = {
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 4.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
stock_request_1 = (
self.env["stock.request"].with_user(self.stock_request_user).create(vals)
)
stock_request_2 = (
self.env["stock.request"]
.with_user(self.stock_request_manager.id)
.create(vals)
)
stock_request_2.product_uom_qty = 6.0
self.product.route_ids = [(6, 0, self.route.ids)]
stock_request_1.with_user(self.stock_request_manager).action_confirm()
stock_request_2.with_user(self.stock_request_manager).action_confirm()
self.assertEqual(len(stock_request_1.picking_ids), 1)
self.assertEqual(stock_request_1.picking_ids, stock_request_2.picking_ids)
self.assertEqual(stock_request_1.move_ids, stock_request_2.move_ids)
self.env["stock.quant"].create(
{
"product_id": self.product.id,
"location_id": self.ressuply_loc.id,
"quantity": 10.0,
}
)
picking = stock_request_1.picking_ids[0]
picking.with_user(self.stock_request_manager).action_confirm()
picking.with_user(self.stock_request_manager).action_assign()
packout1 = picking.move_line_ids[0]
packout1.qty_done = 10
picking.with_user(self.stock_request_manager).action_done()
def test_cancel_request(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
order = self.request_order.with_user(self.stock_request_user).create(vals)
self.product.route_ids = [(6, 0, self.route.ids)]
order.with_user(self.stock_request_user).action_confirm()
stock_request = order.stock_request_ids
self.assertEqual(len(order.picking_ids), 1)
self.assertEqual(len(order.move_ids), 1)
self.assertEqual(len(stock_request.picking_ids), 1)
self.assertEqual(len(stock_request.move_ids), 1)
self.assertEqual(
stock_request.move_ids[0].location_dest_id, stock_request.location_id
)
self.assertEqual(stock_request.qty_in_progress, stock_request.product_uom_qty)
self.env["stock.quant"].create(
{
"product_id": self.product.id,
"location_id": self.ressuply_loc.id,
"quantity": 5.0,
}
)
picking = stock_request.picking_ids[0]
picking.with_user(self.stock_request_user).action_confirm()
self.assertEqual(stock_request.qty_in_progress, 5.0)
self.assertEqual(stock_request.qty_done, 0.0)
picking.with_user(self.stock_request_manager).action_assign()
order.with_user(self.stock_request_manager).action_cancel()
self.assertEqual(stock_request.qty_in_progress, 0.0)
self.assertEqual(stock_request.qty_done, 0.0)
self.assertEqual(len(stock_request.picking_ids), 0)
# Set the request back to draft
order.with_user(self.stock_request_user).action_draft()
self.assertEqual(order.state, "draft")
self.assertEqual(stock_request.state, "draft")
# Re-confirm. We expect new pickings to be created
order.with_user(self.stock_request_user).action_confirm()
self.assertEqual(len(stock_request.picking_ids), 1)
self.assertEqual(len(stock_request.move_ids), 2)
def test_view_actions(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
"expected_date": expected_date,
},
)
],
}
order = self.request_order.create(vals)
self.product.route_ids = [(6, 0, self.route.ids)]
order.with_user(self.stock_request_manager).action_confirm()
stock_request = order.stock_request_ids
self.assertTrue(stock_request.picking_ids)
self.assertTrue(order.picking_ids)
action = order.action_view_transfer()
self.assertEqual("domain" in action.keys(), True)
self.assertEqual("views" in action.keys(), True)
self.assertEqual(action["res_id"], order.picking_ids[0].id)
action = order.action_view_stock_requests()
self.assertEqual("domain" in action.keys(), True)
self.assertEqual("views" in action.keys(), True)
self.assertEqual(action["res_id"], stock_request[0].id)
action = stock_request.action_view_transfer()
self.assertEqual("domain" in action.keys(), True)
self.assertEqual("views" in action.keys(), True)
self.assertEqual(action["res_id"], stock_request.picking_ids[0].id)
action = stock_request.picking_ids[0].action_view_stock_request()
self.assertEqual(action["type"], "ir.actions.act_window")
self.assertEqual(action["res_id"], stock_request.id)
def test_stock_request_constrains(self):
vals = {
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
stock_request = self.stock_request.with_user(self.stock_request_user).create(
vals
)
# Cannot assign a warehouse that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.warehouse_id = self.wh2
# Cannot assign a product that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.product_id = self.product_company_2
# Cannot assign a location that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.location_id = self.wh2.lot_stock_id
# Cannot assign a route that belongs to another company
with self.assertRaises(exceptions.ValidationError):
stock_request.route_id = self.route_2
def test_stock_request_order_from_products(self):
product_a1 = self._create_product("CODEA1", "Product A1", self.main_company.id)
template_a = product_a1.product_tmpl_id
product_a2 = self._create_product(
"CODEA2", "Product A2", self.main_company.id, product_tmpl_id=template_a.id
)
product_a3 = self._create_product(
"CODEA3", "Product A3", self.main_company.id, product_tmpl_id=template_a.id
)
product_b1 = self._create_product("CODEB1", "Product B1", self.main_company.id)
template_b = product_b1.product_tmpl_id
# One archived variant of B
self._create_product(
"CODEB2",
"Product B2",
self.main_company.id,
product_tmpl_id=template_b.id,
active=False,
)
order = self.request_order
# Selecting some variants and creating an order
preexisting = order.search([])
wanted_products = product_a1 + product_a2 + product_b1
action = order._create_from_product_multiselect(wanted_products)
new_order = order.search([]) - preexisting
self.assertEqual(len(new_order), 1)
self.assertEqual(
action["res_id"],
new_order.id,
msg="Returned action references the wrong record",
)
self.assertEqual(
Counter(wanted_products),
Counter(new_order.stock_request_ids.mapped("product_id")),
msg="Not all wanted products were ordered",
)
# Selecting a template and creating an order
preexisting = order.search([])
action = order._create_from_product_multiselect(template_a)
new_order = order.search([]) - preexisting
self.assertEqual(len(new_order), 1)
self.assertEqual(
action["res_id"],
new_order.id,
msg="Returned action references the wrong record",
)
self.assertEqual(
Counter(product_a1 + product_a2 + product_a3),
Counter(new_order.stock_request_ids.mapped("product_id")),
msg="Not all of the template's variants were ordered",
)
# Selecting a template
preexisting = order.search([])
action = order._create_from_product_multiselect(template_a + template_b)
new_order = order.search([]) - preexisting
self.assertEqual(len(new_order), 1)
self.assertEqual(
action["res_id"],
new_order.id,
msg="Returned action references the wrong record",
)
self.assertEqual(
Counter(product_a1 + product_a2 + product_a3 + product_b1),
Counter(new_order.stock_request_ids.mapped("product_id")),
msg="Inactive variant was ordered though it shouldn't have been",
)
# If a user does not have stock request rights, they can still trigger
# the action from the products, so test that they get a friendlier
# error message.
self.stock_request_user.groups_id -= self.stock_request_user_group
with self.assertRaisesRegexp(
exceptions.UserError,
"Unfortunately it seems you do not have the necessary rights "
"for creating stock requests. Please contact your "
"administrator.",
):
order.with_user(self.stock_request_user)._create_from_product_multiselect(
template_a + template_b
)
# Empty recordsets should just return False
self.assertFalse(
order._create_from_product_multiselect(self.env["product.product"])
)
# Wrong model should just raise ValidationError
with self.assertRaises(exceptions.ValidationError):
order._create_from_product_multiselect(self.stock_request_user)
def test_allow_virtual_location(self):
self.main_company.stock_request_allow_virtual_loc = True
vals = {
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.virtual_loc.id,
}
stock_request = self.stock_request.with_user(self.stock_request_user).create(
vals
)
stock_request.onchange_allow_virtual_location()
self.assertTrue(stock_request.allow_virtual_location)
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.virtual_loc.id,
}
order = self.request_order.with_user(self.stock_request_user).create(vals)
order.onchange_allow_virtual_location()
self.assertTrue(order.allow_virtual_location)
def test_onchange_wh_no_effect_from_order(self):
expected_date = fields.Datetime.now()
vals = {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.virtual_loc.id,
"expected_date": expected_date,
"stock_request_ids": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 5.0,
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.virtual_loc.id,
"expected_date": expected_date,
},
)
],
}
order = self.request_order.with_user(self.stock_request_user).create(vals)
order.stock_request_ids.onchange_warehouse_id()
self.assertEqual(order.stock_request_ids[0].location_id, self.virtual_loc)

View 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>

View File

@@ -0,0 +1,116 @@
<?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 &amp; 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>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>

View File

@@ -0,0 +1,31 @@
<?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>

View File

@@ -0,0 +1,24 @@
<?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>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2017 Eficent
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>

View File

@@ -0,0 +1,46 @@
<?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>

View File

@@ -0,0 +1,137 @@
<?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.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>

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2017 Eficent
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="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"/>
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
<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'}"/>
</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" />
<field name="qty_done" />
</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</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>