diff --git a/README.md b/README.md
index 8b10e6960..6864725ce 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-# odex25-standard-moduless
+# odex25-standard-modules
This Repo contains general standard modules for all projects.
diff --git a/odex25_inventory/ak_material_request/LICENSE b/odex25_inventory/ak_material_request/LICENSE
new file mode 100644
index 000000000..b01eaf1b7
--- /dev/null
+++ b/odex25_inventory/ak_material_request/LICENSE
@@ -0,0 +1,27 @@
+Odoo Proprietary License v1.0
+
+This software and associated files (the "Software") may only be used (executed,
+modified, executed after modifications) if you have purchased a valid license
+from the authors, typically via Odoo Apps, or if you have received a written
+agreement from the authors of the Software (see the COPYRIGHT file).
+
+You may develop Odoo modules that use the Software as a library (typically
+by depending on it, importing it and using its resources), but without copying
+any source code or material from the Software. You may distribute those
+modules under the license of your choice, provided that this license is
+compatible with the terms of the Odoo Proprietary License (For example:
+LGPL, MIT, or proprietary licenses similar to this one).
+
+It is forbidden to publish, distribute, sublicense, or sell copies of the Software
+or modified copies of the Software.
+
+The above copyright notice and this permission notice must be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/odex25_inventory/ak_material_request/__init__.py b/odex25_inventory/ak_material_request/__init__.py
new file mode 100644
index 000000000..6a4be45e1
--- /dev/null
+++ b/odex25_inventory/ak_material_request/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from . import models
+from . import wizard
diff --git a/odex25_inventory/ak_material_request/__manifest__.py b/odex25_inventory/ak_material_request/__manifest__.py
new file mode 100644
index 000000000..409d9b4c7
--- /dev/null
+++ b/odex25_inventory/ak_material_request/__manifest__.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+# Author: Aktiv Software PVT. LTD.
+# mail: odoo@aktivsoftware.com
+# Copyright (C) 2015-Present Aktiv Software PVT. LTD.
+# Contributions:
+# Aktiv Software:
+# - Janvi Raval
+# - Komal Jimudiya
+# - Burhan Vakharia
+# - Tanvi Gajera
+
+{
+ "name": "Internal Material Request / Inter-Warehouse Request",
+ "version": "14.0.1.0.3",
+ "summary": """This module allows Warehouse users to create Internal Material Requests.
+ Internal Warehouse Transfer,
+ Stock Request,
+ Goods Request,
+ 2-Step Delivery Requests with Transit Location,
+ Inter-Warehouse Transfer,
+ Inter Warehouse Transfer,
+ Material Request,
+ Internal Request,
+ Stock Request,
+ Goods Request,
+ Transit Request,
+ Goods Transfer,
+ Stock Transfer,
+ Material Transfer,
+ Warehouse Transfer,
+ Internal Warehouse Transfer,
+ Goods Transfer between Warehouse,
+ Stock Transfer between Transfer,
+ Warehouse to Warehouse Transfer,
+ """,
+ "description": """
+ Title: Internal Material Request / Inter-Warehouse Request \n
+ Author: Aktiv Software PVT. LTD. \n
+ mail: odoo@aktivsoftware.com \n
+ Copyright (C) 2015-Present Aktiv Software PVt. LTD. \n
+ Contributions: Aktiv Software: \n
+ - Janvi Raval
+ - Komal Jimudiya
+ - Burhan Vakharia
+ - Tanvi Gajera
+ This module allows users to create Internal material request request
+ to any internal stock location.
+ It allows users to create Inter-Warehouse Requests.
+ The requests can be 1-Step or 2-Step Requests.
+ 2-Step Requests are routed via a Transit Location.
+ Material Request,
+ Internal Request,
+ Stock Request,
+ Goods Request,
+ Transit Request,
+ Goods Transfer,
+ Stock Transfer,
+ Material Transfer,
+ Warehouse Transfer,
+ Internal Warehouse Transfer,
+ Goods Transfer between Warehouse,
+ Stock Transfer between Transfer,
+ Warehouse to Warehouse Transfer,
+ """,
+ "author": "Aktiv Software",
+ "website": "http://www.aktivsoftware.com",
+ "license": "OPL-1",
+ "price": 18.00,
+ "currency": "EUR",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "depends": ["stock"],
+ "data": [
+ "security/res_groups.xml",
+ "security/ir.model.access.csv",
+ "data/material_request_sequence.xml",
+ "wizard/reject_reason_wizard_views.xml",
+ "wizard/transit_location_wizard.xml",
+ "views/material_request_view.xml",
+ "views/res_config_views.xml",
+ "views/res_company_views.xml",
+ ],
+ "images": ["static/description/banner.jpg"],
+ "installable": True,
+ "application": False,
+ "auto_install": False,
+}
diff --git a/odex25_inventory/ak_material_request/data/material_request_sequence.xml b/odex25_inventory/ak_material_request/data/material_request_sequence.xml
new file mode 100644
index 000000000..224001407
--- /dev/null
+++ b/odex25_inventory/ak_material_request/data/material_request_sequence.xml
@@ -0,0 +1,9 @@
+
+
+
+ Material Request Sequence
+ material.request.seq
+ MR
+ 5
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/i18n/ar_001.po b/odex25_inventory/ak_material_request/i18n/ar_001.po
new file mode 100644
index 000000000..fc6904653
--- /dev/null
+++ b/odex25_inventory/ak_material_request/i18n/ar_001.po
@@ -0,0 +1,613 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * ak_material_request
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2024-02-04 07:46+0000\n"
+"PO-Revision-Date: 2024-02-04 07:46+0000\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: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__two_verify
+msgid "2 Step Delivery(Via Transit Location)"
+msgstr "التسليم بخطوتين (عبر موقع وسيط)"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_res_company__two_step_material_req
+msgid "2 step Picking"
+msgstr "التحويل على خطوتين"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.inherit_res_config_setting_view
+msgid ""
+" "
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_needaction
+msgid "Action Needed"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_ids
+msgid "Activities"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_exception_decoration
+msgid "Activity Exception Decoration"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_state
+msgid "Activity State"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_type_icon
+msgid "Activity Type Icon"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_res_config_settings__two_step_material_req
+msgid "Allow Material Request in 2-Steps"
+msgstr "السماح بطلب التحويل على خطوتين"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+msgid "Approve"
+msgstr "يعتمد"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__state__approve
+msgid "Approved"
+msgstr "موافقة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__approved_user_id
+msgid "Approved By"
+msgstr "تمت الموافقة من طرف"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_attachment_count
+msgid "Attachment Count"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__available_qty
+msgid "Available Quantity"
+msgstr "الكمية المتوفرة"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.reject_reason_form_view
+#: model_terms:ir.ui.view,arch_db:ak_material_request.transit_location_warning_form_view
+msgid "Cancel"
+msgstr "إلغاء"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__product_uom_category_id
+msgid "Category"
+msgstr "فئة"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_res_company
+msgid "Companies"
+msgstr "شركات"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__company_id
+msgid "Company"
+msgstr "شركة"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_res_config_settings
+msgid "Config Settings"
+msgstr "ضبط الاعدادات"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__state__confirm
+msgid "Confirmed"
+msgstr "مؤكد"
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request_line__product_uom_category_id
+msgid ""
+"Conversion between Units of Measure can only occur if they belong to the "
+"same category. The conversion will be made based on the ratios."
+msgstr ""
+"لا يمكن أن يحدث التحويل بين وحدات القياس إلا إذا كانت تنتمي إلى نفس الفئة. "
+"سيتم إجراء التحويل على أساس النسب."
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__create_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__create_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__create_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__create_uid
+msgid "Created by"
+msgstr "انشأ من طرف"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__create_date
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__create_date
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__create_date
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__create_date
+msgid "Created on"
+msgstr "تم الانشاء في"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__dest_location_id
+msgid "Deliver Stock From"
+msgstr "تسليم المخزون من"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__delivery_state
+msgid "Delivery State"
+msgstr "حالة التسليم"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__description
+msgid "Description"
+msgstr "الوصف"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "Destination Location"
+msgstr "الوجهة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_res_company__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_res_config_settings__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_stock_picking__display_name
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__display_name
+msgid "Display Name"
+msgstr "الاسم"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__state__draft
+msgid "Draft"
+msgstr "مسودة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_follower_ids
+msgid "Followers"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_channel_ids
+msgid "Followers (Channels)"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_partner_ids
+msgid "Followers (Partners)"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__activity_type_icon
+msgid "Font awesome icon e.g. fa-tasks"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__good_needed_on
+msgid "Goods Needed On"
+msgstr "استلام المخزون الى"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "Group By"
+msgstr "تجميع بواسطة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__id
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__id
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__id
+#: model:ir.model.fields,field_description:ak_material_request.field_res_company__id
+#: model:ir.model.fields,field_description:ak_material_request.field_res_config_settings__id
+#: model:ir.model.fields,field_description:ak_material_request.field_stock_picking__id
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__id
+msgid "ID"
+msgstr "المُعرف"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_exception_icon
+msgid "Icon"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__activity_exception_icon
+msgid "Icon to indicate an exception activity."
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_needaction
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_unread
+msgid "If checked, new messages require your attention."
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_has_error
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_has_sms_error
+msgid "If checked, some messages have a delivery error."
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.actions.act_window,name:ak_material_request.action_material_request
+#: model:ir.ui.menu,name:ak_material_request.menu_action_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.inherit_res_config_setting_view
+msgid "Internal Material Request"
+msgstr "طلب تحويل داخلي"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_is_follower
+msgid "Is Follower"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_res_company____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_res_config_settings____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_stock_picking____last_update
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning____last_update
+msgid "Last Modified on"
+msgstr "تاريخ آخر تعديل"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__write_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__write_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__write_uid
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__write_uid
+msgid "Last Updated by"
+msgstr "آخر تحديث بواسطة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__write_date
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__write_date
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__write_date
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__write_date
+msgid "Last Updated on"
+msgstr "آخر تحديث بتاريخ"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "Location"
+msgstr "الموقع"
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__dest_location_id
+msgid "Location from where stock will be delivered."
+msgstr "الموقع الذي سيتم تسليم المخزون منه."
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_main_attachment_id
+msgid "Main Attachment"
+msgstr ""
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+msgid "Material Lines"
+msgstr "بنود التحويل"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__material_request_id
+#: model:ir.module.category,description:ak_material_request.material_request_group
+#: model:ir.module.category,name:ak_material_request.material_request_group
+msgid "Material Request"
+msgstr "طلب التحويل"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_material_request_line
+msgid "Material Request Lines"
+msgstr "تفاصيل طلب التحويل"
+
+#. module: ak_material_request
+#: model:res.groups,name:ak_material_request.group_manager_material_request
+msgid "Material Request Manager"
+msgstr "مدير طلبات التحويل"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_reject_reason_wizard
+msgid "Material Request Reject Reason Wizard"
+msgstr "معالج سبب رفض طلب التحويل الداخلي"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "Material Request Search"
+msgstr "بحث طلب تحويل"
+
+#. module: ak_material_request
+#: model:res.groups,name:ak_material_request.group_user_material_request
+msgid "Material Request User"
+msgstr "مستخدم طلبات التحويل"
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_transit_location_warning
+msgid "Material Request: Trasit Location Warning"
+msgstr "طلب التحويل: تحذير حول موقع الانتظار"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_stock_picking__request_id
+msgid "Material Requisition"
+msgstr "طلب التحويل"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_has_error
+msgid "Message Delivery error"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_ids
+msgid "Messages"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__my_activity_date_deadline
+msgid "My Activity Deadline"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_date_deadline
+msgid "Next Activity Deadline"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_summary
+msgid "Next Activity Summary"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_type_id
+msgid "Next Activity Type"
+msgstr "نوع النشاط التالي"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__delivery_state__process
+msgid "No Pending Transfers"
+msgstr "لا يوجد تحويلات معلقة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_needaction_counter
+msgid "Number of Actions"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_has_error_counter
+msgid "Number of errors"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_needaction_counter
+msgid "Number of messages which requires an action"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_has_error_counter
+msgid "Number of messages with delivery error"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__message_unread_counter
+msgid "Number of unread messages"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__picking_count
+msgid "Picking Count"
+msgstr "عدد التحويلات"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__picking_two_verify
+msgid "Picking Two Verify"
+msgstr "تحقق ثنائى للتحويل"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__picking_type_id
+msgid "Picking Type"
+msgstr "نوع العملية"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+msgid "Pickings"
+msgstr "التحويلات"
+
+#. module: ak_material_request
+#: code:addons/ak_material_request/models/material_request.py:0
+#, python-format
+msgid "Please create some requisition lines."
+msgstr "يرجى اضافة بعض بنود للتحويل"
+
+#. module: ak_material_request
+#: code:addons/ak_material_request/models/material_request.py:0
+#, python-format
+msgid "Please select the location from which you want to transfer the Stock."
+msgstr "يرجى تحديد الموقع الذي تريد نقل المخزون منه."
+
+#. module: ak_material_request
+#: code:addons/ak_material_request/models/material_request.py:0
+#, python-format
+msgid "Please update Deliver Stock From Location."
+msgstr "يرجى تحديث تسليم المخزون من الموقع."
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__product_id
+msgid "Product"
+msgstr "منتج"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__qty
+msgid "Quantity"
+msgstr "كمية"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__name
+msgid "Reference"
+msgstr "مرجع"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+#: model_terms:ir.ui.view,arch_db:ak_material_request.reject_reason_form_view
+msgid "Reject"
+msgstr "رفض"
+
+#. module: ak_material_request
+#: model:ir.actions.act_window,name:ak_material_request.action_reject_material_request_wizard
+msgid "Reject Material Request Reason"
+msgstr "سبب رفض طلب التحويل الداخلي"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_reject_reason_wizard__reject_reason
+msgid "Reject Reason"
+msgstr "سبب الرفض"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__state__reject
+msgid "Rejected"
+msgstr "مرفوض"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "Reqisition Date"
+msgstr "تاريخ الطلب"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__request_line_ids
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__request_id
+msgid "Request"
+msgstr "طلب"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__request_date
+msgid "Request Date"
+msgstr "تاريخ الطلب"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__user_id
+msgid "Requested By"
+msgstr "الطلب بواسطة"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+msgid "Reset to draft"
+msgstr "إعادة تعيين للمسودة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__activity_user_id
+msgid "Responsible User"
+msgstr "المستخدم المسؤول"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_has_sms_error
+msgid "SMS Delivery error"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__state
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "State"
+msgstr "الحالة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__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: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__location_id
+msgid "Stock Needed on Location"
+msgstr "المخزون مطلوب في موقع"
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__location_id
+msgid "Stock needed on Location."
+msgstr "المخزون مطلوب في موقع"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_form_view
+msgid "Submit for Approval"
+msgstr "إرسال للموافقة"
+
+#. module: ak_material_request
+#: code:addons/ak_material_request/models/material_request.py:0
+#, python-format
+msgid "This record can be deleted only in Draft or Rejected state."
+msgstr "يمكن حذف هذا السجل فقط في حالة المسودة أو الرفض."
+
+#. module: ak_material_request
+#: model:ir.model,name:ak_material_request.model_stock_picking
+msgid "Transfer"
+msgstr "التحويل"
+
+#. module: ak_material_request
+#: model:ir.model.fields.selection,name:ak_material_request.selection__material_request__delivery_state__pending
+msgid "Transfer Pending"
+msgstr "التحويل معلق"
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__activity_exception_decoration
+msgid "Type of the exception activity on record."
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request_line__uom_id
+msgid "Unit of Measure"
+msgstr "وحدة القياس"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_unread
+msgid "Unread Messages"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__message_unread_counter
+msgid "Unread Messages Counter"
+msgstr ""
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.inherit_res_config_setting_view
+msgid ""
+"Upon enabling this the material request will be navigated via transit "
+"location."
+msgstr "عند تفعيل هذا الخيار، سيتم التحويل عبر موقع وسيط"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.material_request_search_view
+msgid "User"
+msgstr "المستخدم"
+
+#. module: ak_material_request
+#: model_terms:ir.ui.view,arch_db:ak_material_request.transit_location_warning_form_view
+msgid "View Transit Locations"
+msgstr "عرض المواقع الوسيطة"
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_material_request__website_message_ids
+msgid "Website Messages"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,help:ak_material_request.field_material_request__website_message_ids
+msgid "Website communication history"
+msgstr ""
+
+#. module: ak_material_request
+#: model:ir.model.fields,field_description:ak_material_request.field_transit_location_warning__name
+msgid "name"
+msgstr "اسم"
diff --git a/odex25_inventory/ak_material_request/models/__init__.py b/odex25_inventory/ak_material_request/models/__init__.py
new file mode 100644
index 000000000..1df004d17
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/__init__.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from . import stock_picking
+from . import material_request
+from . import material_request_line
+from . import res_config
+from . import res_company
diff --git a/odex25_inventory/ak_material_request/models/material_request.py b/odex25_inventory/ak_material_request/models/material_request.py
new file mode 100644
index 000000000..3e2b4dbf6
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/material_request.py
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import models, fields, api, _
+from datetime import datetime
+from odoo.exceptions import Warning, UserError, ValidationError
+
+
+class MaterialRequest(models.Model):
+ _name = "material.request"
+ _description = "Material Request"
+ _inherit = ["mail.thread", "mail.activity.mixin"]
+ _order = "id desc"
+
+ def compute_delivery_state(self):
+ for material_request in self:
+ picking_recs = self.env["stock.picking"].search(
+ [("request_id", "=", material_request.id)]
+ )
+ if picking_recs and all(
+ picking.state in ["done", "cancel"] for picking in picking_recs
+ ):
+ material_request.delivery_state = "process"
+ else:
+ material_request.delivery_state = "pending"
+
+ name = fields.Char(
+ string="Reference",
+ index=True,
+ readonly=1,
+ )
+ state = fields.Selection(
+ [
+ ("draft", "Draft"),
+ ("confirm", "Confirmed"),
+ ("approve", "Approved"),
+ ("reject", "Rejected"),
+ ],
+ tracking=True,
+ default="draft",
+ )
+ user_id = fields.Many2one(
+ "res.users",
+ string="Requested By",
+ default=lambda self: self.env.user and self.env.user.id or False,
+ required=True,
+ domain=lambda self: [
+ ("groups_id", "in", [self.env.ref("stock.group_stock_user").id])
+ ],
+ )
+ location_id = fields.Many2one(
+ "stock.location",
+ string="Stock Needed on Location",
+ copy=True,
+ help="Stock needed on Location.",
+ )
+ dest_location_id = fields.Many2one(
+ "stock.location",
+ string="Deliver Stock From",
+ copy=False,
+ help="Location from where stock will be delivered.",
+ )
+ request_date = fields.Date(string="Request Date", default=datetime.now().date())
+ request_line_ids = fields.One2many(
+ "material.request.line", "request_id", string="Request", copy=True
+ )
+ picking_type_id = fields.Many2one(
+ "stock.picking.type",
+ string="Picking Type",
+ copy=False,
+ default=lambda self: self.env["stock.picking.type"].search(
+ [("code", "=", "internal")], limit=1
+ ),
+ )
+ two_verify = fields.Boolean(string="2 Step Delivery(Via Transit Location)")
+ delivery_state = fields.Selection(
+ [("pending", "Transfer Pending"), ("process", "No Pending Transfers")],
+ tracking=True,
+ default="pending",
+ compute="compute_delivery_state",
+ )
+ good_needed_on = fields.Datetime(
+ string="Goods Needed On",
+ )
+ picking_count = fields.Integer(string="Picking Count", compute="compute_picking")
+ picking_two_verify = fields.Boolean(string="Picking Two Verify")
+ company_id = fields.Many2one(
+ "res.company", "Company", default=lambda self: self.env.company
+ )
+ approved_user_id = fields.Many2one("res.users", copy=False, string="Approved By")
+
+ @api.constrains("dest_location_id")
+ def check_dest_location(self):
+ for material_req_rec in self:
+ if material_req_rec.dest_location_id.id == material_req_rec.location_id.id:
+ raise ValidationError(_("Please update Deliver Stock From Location."))
+
+ def compute_picking(self):
+ """These method is used for counting the
+ Internal Transfer which is created from the material request."""
+ for material_request_rec in self:
+ material_request_rec.picking_count = self.env["stock.picking"].search_count(
+ [("request_id", "=", material_request_rec.id)]
+ )
+
+ def action_reset_draft(self):
+ for rec in self:
+ rec.write({"state": "draft"})
+
+ @api.onchange("company_id")
+ def _onchange_2_step_delivery(self):
+ for rec in self:
+ if rec.company_id.two_step_material_req:
+ rec.picking_two_verify = True
+
+ def action_confirm(self):
+ for rec in self:
+ rec.write({"state": "confirm"})
+
+ def _prepare_pick_vals(self, req_line=False, picking=False):
+ """This method is used to create stock moves from picking."""
+ pick_vals = {
+ "product_id": req_line.product_id.id,
+ "product_uom_qty": req_line.qty,
+ "product_uom": req_line.uom_id.id,
+ "location_id": picking.location_id.id,
+ "location_dest_id": picking.location_dest_id.id,
+ "picking_type_id": picking.picking_type_id.id,
+ "picking_id": picking.id,
+ "name": req_line.product_id.display_name,
+ }
+ return pick_vals
+
+ def action_approve(self):
+ """This method is used to create Internal Transfer."""
+ pick_list_st = []
+ pick_list_td = []
+ stock_move_obj = self.env["stock.move"]
+ for material_req_rec in self:
+ if not material_req_rec.dest_location_id:
+ raise Warning(
+ _(
+ "Please select the location from which you want to transfer the Stock."
+ )
+ )
+
+ if not material_req_rec.request_line_ids:
+ raise Warning(_("Please create some requisition lines."))
+
+ scheduled_date = False
+ picking_vals = {
+ "picking_type_id": material_req_rec.picking_type_id.id,
+ "request_id": material_req_rec.id,
+ "origin": material_req_rec.name,
+ "location_id": material_req_rec.dest_location_id.id,
+ }
+ if material_req_rec.two_verify:
+ # Picking create for two step.
+ transit_location = self.env["stock.location"].search(
+ [("usage", "=", "transit")], limit=1
+ )
+ if not transit_location:
+ return {
+ "type": "ir.actions.act_window",
+ "name": "No Transit Location Warning",
+ "view_mode": "form",
+ "res_model": "transit.location.warning",
+ "context": {
+ "default_name": "No active Tranisit Location found in the system. Please contact the Inventory manager to Unarchive the Transit Location."
+ },
+ "target": "new",
+ }
+ picking_vals.update(
+ {
+ "location_dest_id": transit_location.id,
+ }
+ )
+ else:
+ picking_vals.update(
+ {
+ "location_dest_id": material_req_rec.location_id.id,
+ }
+ )
+ # Created Main picking.
+ main_picking_rec = self.env["stock.picking"].create(picking_vals)
+
+ if material_req_rec.good_needed_on:
+ scheduled_date = material_req_rec.good_needed_on
+
+ if material_req_rec.two_verify:
+ two_step_picking_rec = self.env["stock.picking"].create(
+ {
+ "location_id": transit_location.id,
+ "location_dest_id": material_req_rec.location_id.id,
+ "picking_type_id": material_req_rec.picking_type_id.id,
+ "request_id": material_req_rec.id,
+ "origin": material_req_rec.name,
+ }
+ )
+
+ for req_line in material_req_rec.request_line_ids:
+ stock_move_vals = material_req_rec._prepare_pick_vals(
+ req_line, main_picking_rec
+ )
+ first_move_rec = stock_move_obj.create(stock_move_vals)
+ main_picking_rec.action_confirm()
+ if material_req_rec.two_verify:
+ two_verify_stock_move_vals = material_req_rec._prepare_pick_vals(
+ req_line, two_step_picking_rec
+ )
+ two_verify_stock_move_vals.update(
+ {"move_orig_ids": [(6, 0, first_move_rec.ids)]}
+ )
+ stock_move_obj.create(two_verify_stock_move_vals)
+ two_step_picking_rec.action_confirm()
+ material_req_rec.write({"state": "approve"})
+
+ def action_reject(self):
+ action = self.env.ref(
+ "ak_material_request.action_reject_material_request_wizard"
+ ).read()[0]
+ action["context"] = {"default_material_request_id": self.id}
+ return action
+
+ @api.model
+ def create(self, vals):
+ """seqeunce is created for material request."""
+ name = self.env["ir.sequence"].next_by_code("material.request.seq")
+ vals.update({"name": name})
+ res = super(MaterialRequest, self).create(vals)
+ return res
+
+ def show_picking(self):
+ """Redirects to the stock picking view."""
+ for rec in self:
+ res = self.env.ref("stock.action_picking_tree_all")
+ res = res.read()[0]
+ res["domain"] = str([("request_id", "=", rec.id)])
+ return res
+
+ def unlink(self):
+ for rec in self:
+ if rec.state not in ["draft", "reject"]:
+ raise UserError(
+ _("This record can be deleted only in Draft or Rejected state.")
+ )
+ return super(MaterialRequest, self).unlink()
diff --git a/odex25_inventory/ak_material_request/models/material_request_line.py b/odex25_inventory/ak_material_request/models/material_request_line.py
new file mode 100644
index 000000000..06fe7942e
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/material_request_line.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import api, fields, models
+
+
+class MaterialRequestLine(models.Model):
+ _name = "material.request.line"
+ _description = "Material Request Lines"
+
+ product_id = fields.Many2one(
+ "product.product",
+ string="Product",
+ required=True,
+ )
+ description = fields.Char(
+ string="Description",
+ required=True,
+ )
+ qty = fields.Float(
+ string="Quantity",
+ default=1,
+ required=True,
+ )
+ available_qty = fields.Float(
+ string="Available Quantity",
+ required=True,
+ )
+ product_uom_category_id = fields.Many2one(
+ related="product_id.uom_id.category_id", readonly=True
+ )
+ uom_id = fields.Many2one(
+ "uom.uom",
+ string="Unit of Measure",
+ required=True,
+ domain="[('category_id', '=', product_uom_category_id)]",
+ )
+ request_id = fields.Many2one("material.request")
+
+ @api.onchange("product_id")
+ def product_id_change(self):
+ if self.product_id:
+ self.description = self.product_id.name
+ self.uom_id = self.product_id.uom_id
+ available_qty = self.env['stock.quant']._get_available_quantity(self.product_id, self.request_id.dest_location_id)
+ self.available_qty = available_qty
diff --git a/odex25_inventory/ak_material_request/models/res_company.py b/odex25_inventory/ak_material_request/models/res_company.py
new file mode 100644
index 000000000..8477d28f0
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/res_company.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import fields, models
+
+
+class ResCompany(models.Model):
+ _inherit = "res.company"
+
+ two_step_material_req = fields.Boolean(string="2 step Picking")
diff --git a/odex25_inventory/ak_material_request/models/res_config.py b/odex25_inventory/ak_material_request/models/res_config.py
new file mode 100644
index 000000000..e3cfe67d8
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/res_config.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = "res.config.settings"
+
+ two_step_material_req = fields.Boolean(
+ related="company_id.two_step_material_req",
+ readonly=False,
+ string="Allow Material Request in 2-Steps",
+ )
diff --git a/odex25_inventory/ak_material_request/models/stock_picking.py b/odex25_inventory/ak_material_request/models/stock_picking.py
new file mode 100644
index 000000000..253f591c3
--- /dev/null
+++ b/odex25_inventory/ak_material_request/models/stock_picking.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import models, fields
+
+
+class StockPicking(models.Model):
+ _inherit = "stock.picking"
+
+ request_id = fields.Many2one(
+ "material.request", string="Material Requisition", readonly=True, copy=False
+ )
+
+ def _create_backorder(self):
+ """
+ Override this method to update material request id in backorder.
+ """
+ backorder_recs = super(StockPicking, self)._create_backorder()
+ for backorder_rec in backorder_recs:
+ if backorder_rec.backorder_id.request_id:
+ backorder_rec.write(
+ {"request_id": backorder_rec.backorder_id.request_id}
+ )
+ return backorder_recs
diff --git a/odex25_inventory/ak_material_request/security/ir.model.access.csv b/odex25_inventory/ak_material_request/security/ir.model.access.csv
new file mode 100644
index 000000000..80a4936e4
--- /dev/null
+++ b/odex25_inventory/ak_material_request/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
+access_material_request,access_material_request,model_material_request,,1,1,1,1
+access_material_request_line,access_material_request_line,model_material_request_line,,1,1,1,1
+access_reject_reason_wizard,access_reject_reason_wizard,model_reject_reason_wizard,,1,1,1,1
+access_transit_location_warning,access_transit_location_warning,model_transit_location_warning,,1,1,1,1
diff --git a/odex25_inventory/ak_material_request/security/res_groups.xml b/odex25_inventory/ak_material_request/security/res_groups.xml
new file mode 100644
index 000000000..7e162c382
--- /dev/null
+++ b/odex25_inventory/ak_material_request/security/res_groups.xml
@@ -0,0 +1,15 @@
+
+
+
+ Material Request
+ Material Request
+
+
+ Material Request User
+
+
+
+ Material Request Manager
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/static/description/aktiv-logo.png b/odex25_inventory/ak_material_request/static/description/aktiv-logo.png
new file mode 100644
index 000000000..b80517cc9
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/aktiv-logo.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/banner.jpg b/odex25_inventory/ak_material_request/static/description/banner.jpg
new file mode 100644
index 000000000..37d12efd0
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/banner.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Consulting.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Consulting.png
new file mode 100644
index 000000000..1f244ce23
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Consulting.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Customization.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Customization.png
new file mode 100644
index 000000000..b2ae9f366
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Customization.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Implementation.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Implementation.png
new file mode 100644
index 000000000..153001076
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Implementation.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Integration.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Integration.png
new file mode 100644
index 000000000..841418705
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Integration.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Migration.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Migration.png
new file mode 100644
index 000000000..c65aa065a
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Migration.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Support.png b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Support.png
new file mode 100644
index 000000000..244d09578
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/Odoo_Support.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/aktiv-logo.png b/odex25_inventory/ak_material_request/static/description/default_icons/aktiv-logo.png
new file mode 100644
index 000000000..b80517cc9
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/aktiv-logo.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/arrow.png b/odex25_inventory/ak_material_request/static/description/default_icons/arrow.png
new file mode 100644
index 000000000..4e2533f15
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/arrow.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/default_icons/check.png b/odex25_inventory/ak_material_request/static/description/default_icons/check.png
new file mode 100644
index 000000000..691a762ae
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/default_icons/check.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/icon.png b/odex25_inventory/ak_material_request/static/description/icon.png
new file mode 100644
index 000000000..8c80e50df
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/icon.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/2_pickings_11.jpg b/odex25_inventory/ak_material_request/static/description/images/2_pickings_11.jpg
new file mode 100644
index 000000000..dac54799e
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/2_pickings_11.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Exchange Rate Management Banner.jpg b/odex25_inventory/ak_material_request/static/description/images/Exchange Rate Management Banner.jpg
new file mode 100644
index 000000000..12532a54c
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Exchange Rate Management Banner.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Inventory Scrap Report Banner.jpg b/odex25_inventory/ak_material_request/static/description/images/Inventory Scrap Report Banner.jpg
new file mode 100644
index 000000000..378c7f263
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Inventory Scrap Report Banner.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Inventory Valuation Reports Banner-01.jpg b/odex25_inventory/ak_material_request/static/description/images/Inventory Valuation Reports Banner-01.jpg
new file mode 100644
index 000000000..e27b3541e
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Inventory Valuation Reports Banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Notify In Stock Banner-01.jpg b/odex25_inventory/ak_material_request/static/description/images/Notify In Stock Banner-01.jpg
new file mode 100644
index 000000000..715f1cc53
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Notify In Stock Banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Product Ageing Report Banner-01.jpg b/odex25_inventory/ak_material_request/static/description/images/Product Ageing Report Banner-01.jpg
new file mode 100644
index 000000000..9748a5546
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Product Ageing Report Banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/Stock Per Each Location Banner-01.jpg b/odex25_inventory/ak_material_request/static/description/images/Stock Per Each Location Banner-01.jpg
new file mode 100644
index 000000000..1a7da3d50
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/Stock Per Each Location Banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/material_request_approval_4.jpg b/odex25_inventory/ak_material_request/static/description/images/material_request_approval_4.jpg
new file mode 100644
index 000000000..58ecade42
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/material_request_approval_4.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.jpg b/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.jpg
new file mode 100644
index 000000000..e2e3fd5ba
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.png b/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.png
new file mode 100644
index 000000000..e9ef056d2
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/material_request_form1_3.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2.jpg b/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2.jpg
new file mode 100644
index 000000000..ad55dfbca
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2_ss.png b/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2_ss.png
new file mode 100644
index 000000000..cfa544c84
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/material_request_tree_2_ss.png differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/menu_1.jpg b/odex25_inventory/ak_material_request/static/description/images/menu_1.jpg
new file mode 100644
index 000000000..1bc88234c
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/menu_1.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/pending_transfers_14.jpg b/odex25_inventory/ak_material_request/static/description/images/pending_transfers_14.jpg
new file mode 100644
index 000000000..9375e6d14
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/pending_transfers_14.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/picking_button_7.jpg b/odex25_inventory/ak_material_request/static/description/images/picking_button_7.jpg
new file mode 100644
index 000000000..4ef2f854f
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/picking_button_7.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/reject_reason_5.jpg b/odex25_inventory/ak_material_request/static/description/images/reject_reason_5.jpg
new file mode 100644
index 000000000..4d03f2fef
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/reject_reason_5.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/reject_reason_chatter_6.jpg b/odex25_inventory/ak_material_request/static/description/images/reject_reason_chatter_6.jpg
new file mode 100644
index 000000000..bb323f790
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/reject_reason_chatter_6.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/res_config_9.jpg b/odex25_inventory/ak_material_request/static/description/images/res_config_9.jpg
new file mode 100644
index 000000000..c8d357107
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/res_config_9.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/source_to_transit_12.jpg b/odex25_inventory/ak_material_request/static/description/images/source_to_transit_12.jpg
new file mode 100644
index 000000000..0b3f4af9b
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/source_to_transit_12.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/transfer_8.jpg b/odex25_inventory/ak_material_request/static/description/images/transfer_8.jpg
new file mode 100644
index 000000000..46434f4ce
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/transfer_8.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/transit_to_destination_13.jpg b/odex25_inventory/ak_material_request/static/description/images/transit_to_destination_13.jpg
new file mode 100644
index 000000000..5cc3db0c1
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/transit_to_destination_13.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/images/two_step_req_10.jpg b/odex25_inventory/ak_material_request/static/description/images/two_step_req_10.jpg
new file mode 100644
index 000000000..adf06ed1d
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/images/two_step_req_10.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/index.html b/odex25_inventory/ak_material_request/static/description/index.html
new file mode 100644
index 000000000..8e1772078
--- /dev/null
+++ b/odex25_inventory/ak_material_request/static/description/index.html
@@ -0,0 +1,577 @@
+
+
+
+
+
+
+
+
+
+
+ Enterprise
+
+
+ Community
+
+
+ Odoo.Sh
+
+
+ Self Hosted
+
+
+
+
+
+
+
+
+
+
+
+
+ Internal Material Request
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Explore this module
+
+
+
+
+
+
+
+
+
+
+ Overview
+
+
+
+ This custom module allows user to generate Internal Material/ Inter-Warehouse Transfer requests.
+ The process could be completed in two ways:
+
1-Step Transfer: Send the stock from source to destination location with manager's approval.
+
2-Step Transfer: Complete the material request transfer from Source to Transit and Transit to
+ Destination location
+
+
+
+
+
+
+
+
+
+ Features
+
+
+
+
+
+
+
+
+
Make Internal or Inter-warehouse
+ material/transfer requests.
+
+
+
+
+
+
It can be performed in two different
+ ways: 1-step transfer or 2-step transfer as per the suitability.
+
+
+
+
+
+
+
+
+
+
Manager requires to approve or
+ disapprove the transfer request.
+
+
+
+
+
+
+
This app supports Single company Setup
+ with Multi Warehouse and Multi Locations.
+
+
+
+
+
+
+
+
+
+
+
+
+ Setup & Configuration
+
+
+
+
+
+ Prerequisites
+
+
+
+ Required Apps
+
+
+ Stock
+
+
+
+
+
+
+
+
+
+
+
+ Screenshots
+
+
+
+
+
+
+
Internal Material Request
+
+
+
+
+
+
+
+
+
+
Material Request Tree View.
+
+
+
+
+
+
+
+
+
Material Request Process:
+
+
+ Upon adding details about the source and destination location along with the
+ list of goods user needs to click send the request for approval to manager which
+ when approved will be marked as "Confirmed".
+
+
+
+
+
+
+
+
+
+
+
+
Material Request Approve/Reject:
+
+
+
+ Warehouse Manager can approve or reject the material or transfer request made by
+ the
+ user. Whereas they also has the rights to update the source location and product
+ lists.
+
+
+
+
+
+
+
+
+
+
+
+
If manager rejects the request, mentioning
+ rejection reason is mandatory.
+
+
+
+
+
+
+
+
+
+
After rejection from manager users material or
+ transfer request will shift to "Rejected" stage with a log note mentioning the
+ rejection reason.
+
+
+
+
+
+
+
+
+
+
Material Request Picking Process 1-Step
+ Transfer:
+
+
+
+ Once the transfer request is approved by manager, an Internal Transfer is
+ created with correct source and destination locations.
+
+
+ The Transfer once created is visible in the Form view wherein one can see its
+ current stage through a smart button as shown in the below screenshot.
+
+
+
+
+
+
+
+
+
+
+
+
If user follows the 1-Step Transfer, single
+ transfer is created with source and destination locations.
+
+
+
+
+
+
+
+
+
+
+
Material Request Picking Process 1-Step Material
+ Request Picking- 2-Step Transfer:
+
+
+
+ Before following the 2-step transfer user needs to configure the following:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
As seen in the 1-step transfer, everything till
+ the manager approval and creation of
+ single transfer will remain the same for the 2-Step transfer.In the 2-step transfer,
+ the first transfer will occur from source location to transit
+ location and second transfer will occur from transit location to destination
+ location.
+
+
+
+
+
+
+
+
+
+
+
Please note, the 2nd Transfer will be in
+ "Waiting for Another Operation" state until the 1st transfer is completed.
+
+
+
+
+
+
+
+
+
+
+
The Transfers are visible in the Form view as
+ well as in the smart button.
+
+
+
+
+
+
+
+
+
+
Pending Transfers Status on the Request Form:
+
+
+
+
+
+
+
+
+
+
+
+
+ Suggested Apps
+
+
+
+
+
+
+
+
+
+
+
+
+ Our Services
+
+
+
+
+
+
+
+
+ Odoo
+ Consulting
+
+
+
+
+
+
+
+ Odoo
+ Customization
+
+
+
+
+
+
+
+ Odoo
+ Implementation
+
+
+
+
+
+
+
+ Odoo
+ Integration
+
+
+
+
+
+
+
+ Odoo
+ Migration
+
+
+
+
+
+
+
+ Odoo
+ Support
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/static/description/material_request.gif b/odex25_inventory/ak_material_request/static/description/material_request.gif
new file mode 100644
index 000000000..caf463bd9
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/material_request.gif differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/ebchange_banner.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/ebchange_banner.jpg
new file mode 100644
index 000000000..12532a54c
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/ebchange_banner.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_scrap_report_banner.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_scrap_report_banner.jpg
new file mode 100644
index 000000000..378c7f263
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_scrap_report_banner.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_valuation_reports_banner-01.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_valuation_reports_banner-01.jpg
new file mode 100644
index 000000000..e27b3541e
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/inventory_valuation_reports_banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/notify_in_stock_banner-01.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/notify_in_stock_banner-01.jpg
new file mode 100644
index 000000000..715f1cc53
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/notify_in_stock_banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/product_ageing_report_banner-01.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/product_ageing_report_banner-01.jpg
new file mode 100644
index 000000000..9748a5546
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/product_ageing_report_banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/static/description/suggested_apps/stock_per_each_location_banner-01.jpg b/odex25_inventory/ak_material_request/static/description/suggested_apps/stock_per_each_location_banner-01.jpg
new file mode 100644
index 000000000..1a7da3d50
Binary files /dev/null and b/odex25_inventory/ak_material_request/static/description/suggested_apps/stock_per_each_location_banner-01.jpg differ
diff --git a/odex25_inventory/ak_material_request/views/material_request_view.xml b/odex25_inventory/ak_material_request/views/material_request_view.xml
new file mode 100644
index 000000000..3f071a2bb
--- /dev/null
+++ b/odex25_inventory/ak_material_request/views/material_request_view.xml
@@ -0,0 +1,142 @@
+
+
+
+ material.request.form.view
+ material.request
+
+
+
+
+
+
+ material.request.view.tree
+ material.request
+
+
+
+
+
+
+
+
+
+
+
+
+
+ material.request.search
+ material.request
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Internal Material Request
+ material.request
+ tree,form
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/views/res_company_views.xml b/odex25_inventory/ak_material_request/views/res_company_views.xml
new file mode 100644
index 000000000..f82cc7832
--- /dev/null
+++ b/odex25_inventory/ak_material_request/views/res_company_views.xml
@@ -0,0 +1,12 @@
+
+
+
+ res.company
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/views/res_config_views.xml b/odex25_inventory/ak_material_request/views/res_config_views.xml
new file mode 100644
index 000000000..b6bb70c19
--- /dev/null
+++ b/odex25_inventory/ak_material_request/views/res_config_views.xml
@@ -0,0 +1,27 @@
+
+
+
+ res.config.settings.view
+ res.config.settings
+
+
+
+ Internal Material Request
+
+
+
+
+
+
+
+
+
+ Upon enabling this the material request will be navigated via transit location.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/wizard/__init__.py b/odex25_inventory/ak_material_request/wizard/__init__.py
new file mode 100644
index 000000000..61e1cc9da
--- /dev/null
+++ b/odex25_inventory/ak_material_request/wizard/__init__.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from . import reject_reason_wizard
+from . import transit_location_warning
diff --git a/odex25_inventory/ak_material_request/wizard/reject_reason_wizard.py b/odex25_inventory/ak_material_request/wizard/reject_reason_wizard.py
new file mode 100644
index 000000000..496f4021d
--- /dev/null
+++ b/odex25_inventory/ak_material_request/wizard/reject_reason_wizard.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import fields, models
+
+
+class RejectReasonWizard(models.TransientModel):
+ _name = "reject.reason.wizard"
+ _description = "Material Request Reject Reason Wizard"
+
+ reject_reason = fields.Text(string="Reject Reason", required=True)
+ material_request_id = fields.Many2one("material.request", string="Material Request")
+
+ def reject_material_req(self):
+ self.material_request_id.write(
+ {
+ "state": "reject",
+ "delivery_state": "",
+ }
+ )
+ display_msg = (
+ " User %s Rejected the material request. "
+ "Rejected Reason: %s
" % (self.env.user.name, self.reject_reason)
+ )
+ self.material_request_id.message_post(body=display_msg)
diff --git a/odex25_inventory/ak_material_request/wizard/reject_reason_wizard_views.xml b/odex25_inventory/ak_material_request/wizard/reject_reason_wizard_views.xml
new file mode 100644
index 000000000..23d68a379
--- /dev/null
+++ b/odex25_inventory/ak_material_request/wizard/reject_reason_wizard_views.xml
@@ -0,0 +1,23 @@
+
+
+
+ reject.reason.wizard
+
+
+
+
+
+
+
+
+
+
+ Reject Material Request Reason
+ reject.reason.wizard
+ form
+ new
+
+
\ No newline at end of file
diff --git a/odex25_inventory/ak_material_request/wizard/transit_location_warning.py b/odex25_inventory/ak_material_request/wizard/transit_location_warning.py
new file mode 100644
index 000000000..0da023325
--- /dev/null
+++ b/odex25_inventory/ak_material_request/wizard/transit_location_warning.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo, Aktiv Software PVT. LTD.
+# See LICENSE file for full copyright & licensing details.
+
+from odoo import fields, models
+
+
+class TransitLocationWarning(models.TransientModel):
+ _name = "transit.location.warning"
+ _description = "Material Request: Trasit Location Warning"
+
+ name = fields.Char(string="name")
+
+ def view_transit_location(self):
+ return {
+ "type": "ir.actions.act_window",
+ "name": "Locations",
+ "view_mode": "tree,form",
+ "res_model": "stock.location",
+ "domain": [("usage", "=", "transit"), ("active", "=", False)],
+ "context": {"default_usage": "transit"},
+ }
diff --git a/odex25_inventory/ak_material_request/wizard/transit_location_wizard.xml b/odex25_inventory/ak_material_request/wizard/transit_location_wizard.xml
new file mode 100644
index 000000000..4229b4967
--- /dev/null
+++ b/odex25_inventory/ak_material_request/wizard/transit_location_wizard.xml
@@ -0,0 +1,15 @@
+
+
+
+ transit.location.warning
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/eg_stock_location_restriction/README.rst b/odex25_inventory/eg_stock_location_restriction/README.rst
new file mode 100644
index 000000000..857959fe2
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/README.rst
@@ -0,0 +1,4 @@
+=================================
+Restriction on Stock Location
+=================================
+This app will restrict stock location for selected users.
\ No newline at end of file
diff --git a/odex25_inventory/eg_stock_location_restriction/__init__.py b/odex25_inventory/eg_stock_location_restriction/__init__.py
new file mode 100644
index 000000000..0650744f6
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/odex25_inventory/eg_stock_location_restriction/__manifest__.py b/odex25_inventory/eg_stock_location_restriction/__manifest__.py
new file mode 100644
index 000000000..81832301e
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/__manifest__.py
@@ -0,0 +1,25 @@
+{
+ "name": "Restriction on Stock Location",
+
+ 'version': "14.0",
+
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "summary": "This app will Restriction on Stock Location.",
+
+ 'author': 'INKERP',
+
+ 'website': "http://www.inkerp.com",
+
+ "depends": ['stock'],
+
+ "data": [
+ "security/security.xml",
+ "views/stock_location_view.xml",
+ ],
+
+ 'images': ['static/description/banner.png'],
+ 'license': "OPL-1",
+ 'installable': True,
+ 'application': True,
+ 'auto_install': False,
+}
diff --git a/odex25_inventory/eg_stock_location_restriction/models/__init__.py b/odex25_inventory/eg_stock_location_restriction/models/__init__.py
new file mode 100644
index 000000000..514de8652
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/models/__init__.py
@@ -0,0 +1 @@
+from. import stock_location
diff --git a/odex25_inventory/eg_stock_location_restriction/models/stock_location.py b/odex25_inventory/eg_stock_location_restriction/models/stock_location.py
new file mode 100644
index 000000000..35989b577
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/models/stock_location.py
@@ -0,0 +1,7 @@
+from odoo import fields, models, api, _
+
+
+class StockLocation(models.Model):
+ _inherit = 'stock.location'
+
+ user_ids = fields.Many2many(comodel_name='res.users', string='Users')
diff --git a/odex25_inventory/eg_stock_location_restriction/security/security.xml b/odex25_inventory/eg_stock_location_restriction/security/security.xml
new file mode 100644
index 000000000..991547d6d
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/security/security.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ Stock Location Restriction
+
+
+
+ Restrict Stock Location
+
+ [('user_ids','!=',user.id)]
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/banner.png b/odex25_inventory/eg_stock_location_restriction/static/description/banner.png
new file mode 100644
index 000000000..b60548500
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/banner.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/company_logo.png b/odex25_inventory/eg_stock_location_restriction/static/description/company_logo.png
new file mode 100644
index 000000000..c3031c391
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/company_logo.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/divider.png b/odex25_inventory/eg_stock_location_restriction/static/description/divider.png
new file mode 100644
index 000000000..607ff93f1
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/divider.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_cancel_quotation.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_cancel_quotation.png
new file mode 100644
index 000000000..c032d97bc
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_cancel_quotation.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_inventory_user_signature_report.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_inventory_user_signature_report.png
new file mode 100644
index 000000000..21b90a389
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_inventory_user_signature_report.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_merge_purchase_order.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_merge_purchase_order.png
new file mode 100644
index 000000000..4e862e842
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_merge_purchase_order.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_po_from_so.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_po_from_so.png
new file mode 100644
index 000000000..24e10e63b
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_po_from_so.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_product_sale_price_history.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_product_sale_price_history.png
new file mode 100644
index 000000000..41354a697
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_product_sale_price_history.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_sendcloud_delivery.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_sendcloud_delivery.png
new file mode 100644
index 000000000..64bd2f174
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_sendcloud_delivery.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_split_quotation_order.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_split_quotation_order.png
new file mode 100644
index 000000000..4af8eafa0
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_split_quotation_order.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_all_in_one_integration.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_all_in_one_integration.png
new file mode 100644
index 000000000..ae97bfe06
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_all_in_one_integration.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_sale_integration.png b/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_sale_integration.png
new file mode 100644
index 000000000..4fb33c073
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/eg_whatsapp_sale_integration.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/icon.png b/odex25_inventory/eg_stock_location_restriction/static/description/icon.png
new file mode 100644
index 000000000..fbae10e82
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/icon.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/images/1.png b/odex25_inventory/eg_stock_location_restriction/static/description/images/1.png
new file mode 100644
index 000000000..91211893f
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/images/1.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/images/2.png b/odex25_inventory/eg_stock_location_restriction/static/description/images/2.png
new file mode 100644
index 000000000..c2b78138d
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/images/2.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/images/3.png b/odex25_inventory/eg_stock_location_restriction/static/description/images/3.png
new file mode 100644
index 000000000..00695b515
Binary files /dev/null and b/odex25_inventory/eg_stock_location_restriction/static/description/images/3.png differ
diff --git a/odex25_inventory/eg_stock_location_restriction/static/description/index.html b/odex25_inventory/eg_stock_location_restriction/static/description/index.html
new file mode 100644
index 000000000..be4be3ad8
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/static/description/index.html
@@ -0,0 +1,392 @@
+
+
+
+ Restriction on Stock Location
+ This app will restrict stock location for selected users.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1) Users view.
+
+
For Applying Restriction user have to assign group of (Stock Location Restriction) to User. so that assigned user can restrict the other user.
+ Navigation: Settings -> Users & Companies -> Users -> Stock Location Restriction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2) Locations view.
+
+
In Stock Location form view restrict manager can see Users field. In that field select that users whom you want restrict from the this particular Stock Location.
+ Navigation: Inventory -> Reporting ->Locations
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
3) Locations view.
+
+
Here you can see that stock location was disappear from records. The location was restricted for Marc Demo.
+ Navigation: Inventory -> Reporting ->Locations
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Featured Applications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/eg_stock_location_restriction/views/stock_location_view.xml b/odex25_inventory/eg_stock_location_restriction/views/stock_location_view.xml
new file mode 100644
index 000000000..caf597053
--- /dev/null
+++ b/odex25_inventory/eg_stock_location_restriction/views/stock_location_view.xml
@@ -0,0 +1,15 @@
+
+
+ stock.location.restriction.form.view
+ stock.location
+
+
+
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/export_stockinfo_xls/README.rst b/odex25_inventory/export_stockinfo_xls/README.rst
new file mode 100644
index 000000000..8bbd69657
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/README.rst
@@ -0,0 +1,42 @@
+Export Product Stock in Excel v16
+=================================
+This module helps you to take current stock report for all products in each warehouse.
+
+Configuration
+=============
+* No additional configurations needed
+
+Company
+-------
+* `Cybrosys Techno Solutions `__
+
+Credits
+-------
+* Developers: Cybrosys Techno Solutions odoo@cybrosys.com
+ Version 15: Midilaj V K @cybrosys
+ Version 16: Sahla Sherin @cybrosys
+
+
+Contacts
+--------
+* Mail Contact : odoo@cybrosys.com
+* Website : https://cybrosys.com
+
+Bug Tracker
+-----------
+Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
+
+Maintainer
+==========
+.. image:: https://cybrosys.com/images/logo.png
+ :target: https://cybrosys.com
+
+This module is maintained by Cybrosys Technologies.
+
+For support and more information, please visit `Our Website `__
+
+Further information
+===================
+HTML Description: ``__
+
+
diff --git a/odex25_inventory/export_stockinfo_xls/__init__.py b/odex25_inventory/export_stockinfo_xls/__init__.py
new file mode 100644
index 000000000..c27fdfec2
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+
+from . import models
+from . import controllers
diff --git a/odex25_inventory/export_stockinfo_xls/__manifest__.py b/odex25_inventory/export_stockinfo_xls/__manifest__.py
new file mode 100644
index 000000000..65b7f11fc
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/__manifest__.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+
+{
+ 'name': 'Export Product Stock in Excel',
+ 'version': '16.0.1.0.0',
+ 'live_test_url': 'https://www.youtube.com/watch?v=9ae4GkApHQM',
+ 'summary': "Current Stock Report for all Products in each Warehouse",
+ 'description': "Current Stock Report for all Products in each Warehouse, Odoo 13,Odoo13",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ 'author': 'Cybrosys Techno Solutions',
+ 'maintainer': 'Cybrosys Techno Solutions',
+ 'company': 'Cybrosys Techno Solutions',
+ 'website': 'https://www.cybrosys.com',
+ 'depends': [
+ 'base',
+ 'sale_management',
+ 'stock',
+ 'purchase',
+ ],
+ 'data': [
+ 'views/wizard_view.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'images': ['static/description/banner.png'],
+ 'assets': {
+ 'web.assets_backend': [
+ 'export_stockinfo_xls/static/src/js/action_manager.js',
+ ],
+ },
+ 'license': 'AGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+ 'auto_install': False,
+}
diff --git a/odex25_inventory/export_stockinfo_xls/controllers/__init__.py b/odex25_inventory/export_stockinfo_xls/controllers/__init__.py
new file mode 100644
index 000000000..b13d77771
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/controllers/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2020-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+from . import main
diff --git a/odex25_inventory/export_stockinfo_xls/controllers/main.py b/odex25_inventory/export_stockinfo_xls/controllers/main.py
new file mode 100644
index 000000000..6961d3481
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/controllers/main.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+
+import json
+from odoo import http
+from odoo.http import content_disposition, request, serialize_exception as _serialize_exception
+from odoo.tools import html_escape
+
+
+class XLSXReportController(http.Controller):
+
+ @http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False)
+ def get_report_xlsx(self, model, options, output_format, report_name, **kw):
+ uid = request.session.uid
+ report_obj = request.env[model].with_user(uid)
+ options = json.loads(options)
+ token = 'dummy-because-api-expects-one'
+ try:
+ if output_format == 'xlsx':
+ response = request.make_response(
+ None,
+ headers=[
+ ('Content-Type', 'application/vnd.ms-excel'),
+ ('Content-Disposition', content_disposition(report_name + '.xlsx'))
+ ]
+ )
+ report_obj.get_xlsx_report(options, response)
+ response.set_cookie('fileToken', token)
+ return response
+ except Exception as e:
+ se = _serialize_exception(e)
+ error = {
+ 'code': 200,
+ 'message': 'Odoo Server Error',
+ 'data': se
+ }
+ return request.make_response(html_escape(json.dumps(error)))
diff --git a/odex25_inventory/export_stockinfo_xls/doc/RELEASE_NOTES.md b/odex25_inventory/export_stockinfo_xls/doc/RELEASE_NOTES.md
new file mode 100644
index 000000000..e99f5f904
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/doc/RELEASE_NOTES.md
@@ -0,0 +1,11 @@
+## Module
+
+#### 04.10.2021
+#### Version 16.0.1.0.0
+#### ADD
+Initial Commit Export Product Stock in Excel
+
+
+
+
+
diff --git a/odex25_inventory/export_stockinfo_xls/models/__init__.py b/odex25_inventory/export_stockinfo_xls/models/__init__.py
new file mode 100644
index 000000000..2a90c7c7d
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/models/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+
+from . import res_partner
+from . import wizard
diff --git a/odex25_inventory/export_stockinfo_xls/models/res_partner.py b/odex25_inventory/export_stockinfo_xls/models/res_partner.py
new file mode 100644
index 000000000..5d20e11d5
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/models/res_partner.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+
+from odoo import models, fields
+
+
+class Partner(models.Model):
+ _inherit = 'res.partner'
+
+ supplier_id = fields.Many2many('wizard.stock.history', 'supp_wiz_rel', 'wiz', 'supp', invisible=True)
+
+
+class Category(models.Model):
+ _inherit = 'product.category'
+
+ obj = fields.Many2many('wizard.stock.history', 'categ_wiz_rel', 'wiz', 'categ', invisible=True)
+
+
+class Warehouse(models.Model):
+ _inherit = 'stock.warehouse'
+
+ obj = fields.Many2many('wizard.stock.history', 'wh_wiz_rel', 'wiz', 'wh', invisible=True)
diff --git a/odex25_inventory/export_stockinfo_xls/models/wizard.py b/odex25_inventory/export_stockinfo_xls/models/wizard.py
new file mode 100644
index 000000000..296e52485
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/models/wizard.py
@@ -0,0 +1,251 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+#
+# Cybrosys Technologies Pvt. Ltd.
+#
+# Copyright (C) 2022-TODAY Cybrosys Technologies()
+# Author: Midilaj ()
+#
+# You can modify it under the terms of the GNU LESSER
+# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
+#
+# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
+# (LGPL v3) along with this program.
+# If not, see .
+#
+#############################################################################
+import time
+from datetime import date, datetime
+import pytz
+import json
+import datetime
+import io
+from odoo import api, fields, models, _
+from odoo.tools import date_utils
+
+try:
+ from odoo.tools.misc import xlsxwriter
+except ImportError:
+ import xlsxwriter
+
+
+class StockReport(models.TransientModel):
+ _name = "wizard.stock.history"
+ _description = "Current Stock History"
+
+ warehouse_ids = fields.Many2many('stock.warehouse', 'wh_wiz_rel', 'wh', 'wiz', string='Warehouse', required=True)
+ category_ids = fields.Many2many('product.category', 'categ_wiz_rel', 'categ', 'wiz', string='Category')
+
+ def export_xls(self):
+ data = {
+ 'ids': self.ids,
+ 'model': self._name,
+ 'warehouse': self.warehouse_ids.ids,
+ 'category': self.category_ids.ids,
+
+ }
+ return {
+ 'type': 'ir.actions.report',
+ 'data': {'model': 'wizard.stock.history',
+ 'options': json.dumps(data, default=date_utils.json_default),
+ 'output_format': 'xlsx',
+ 'report_name': 'Current Stock History',
+ },
+ 'report_type': 'stock_xlsx'
+ }
+
+ def get_warehouse(self, data):
+ wh = data.warehouse_ids.mapped('id')
+ obj = self.env['stock.warehouse'].search([('id', 'in', wh)])
+ l1 = []
+ l2 = []
+ for j in obj:
+ l1.append(j.name)
+ l2.append(j.id)
+ return l1, l2
+
+ def get_lines(self, data, warehouse_ids):
+ lines = []
+ categ_id = data.mapped('id')
+ if categ_id:
+ categ_products = self.env['product.product'].search([('categ_id', 'in', categ_id)])
+
+ else:
+ categ_products = self.env['product.product'].search([])
+ product_ids = tuple([pro_id.id for pro_id in categ_products])
+ sale_query = """
+ SELECT sum(s_o_l.product_uom_qty) AS product_uom_qty, s_o_l.product_id FROM sale_order_line AS s_o_l
+ JOIN sale_order AS s_o ON s_o_l.order_id = s_o.id
+ WHERE s_o.state IN ('sale','done')
+ AND s_o.warehouse_id = %s
+ AND s_o_l.product_id in %s group by s_o_l.product_id"""
+ purchase_query = """
+ SELECT sum(p_o_l.product_qty) AS product_qty, p_o_l.product_id FROM purchase_order_line AS p_o_l
+ JOIN purchase_order AS p_o ON p_o_l.order_id = p_o.id
+ INNER JOIN stock_picking_type AS s_p_t ON p_o.picking_type_id = s_p_t.id
+ WHERE p_o.state IN ('purchase','done')
+ AND s_p_t.warehouse_id = %s AND p_o_l.product_id in %s group by p_o_l.product_id"""
+ params = warehouse_ids, product_ids if product_ids else (0, 0)
+ self._cr.execute(sale_query, params)
+ sol_query_obj = self._cr.dictfetchall()
+ self._cr.execute(purchase_query, params)
+ pol_query_obj = self._cr.dictfetchall()
+ for obj in categ_products:
+ sale_value = 0
+ purchase_value = 0
+ for sol_product in sol_query_obj:
+ if sol_product['product_id'] == obj.id:
+ sale_value = sol_product['product_uom_qty']
+ for pol_product in pol_query_obj:
+ if pol_product['product_id'] == obj.id:
+ purchase_value = pol_product['product_qty']
+ virtual_available = obj.with_context({'warehouse': warehouse_ids}).virtual_available
+ outgoing_qty = obj.with_context({'warehouse': warehouse_ids}).outgoing_qty
+ incoming_qty = obj.with_context({'warehouse': warehouse_ids}).incoming_qty
+ available_qty = virtual_available + outgoing_qty - incoming_qty
+ value = available_qty * obj.standard_price
+ vals = {
+ 'sku': obj.default_code,
+ 'name': obj.name,
+ 'category': obj.categ_id.name,
+ 'cost_price': obj.standard_price,
+ 'available': available_qty,
+ 'virtual': virtual_available,
+ 'incoming': incoming_qty,
+ 'outgoing': outgoing_qty,
+ 'net_on_hand': obj.with_context({'warehouse': warehouse_ids}).qty_available,
+ 'total_value': value,
+ 'sale_value': sale_value,
+ 'purchase_value': purchase_value,
+ }
+ lines.append(vals)
+ return lines
+
+ def get_xlsx_report(self, data, response):
+ output = io.BytesIO()
+ workbook = xlsxwriter.Workbook(output, {'in_memory': True})
+ lines = self.browse(data['ids'])
+ d = lines.category_ids
+ get_warehouse = self.get_warehouse(lines)
+ count = len(get_warehouse[0]) * 11 + 6
+ comp = self.env.user.company_id.name
+ sheet = workbook.add_worksheet('Stock Info')
+ format0 = workbook.add_format({'font_size': 20, 'align': 'center', 'bold': True})
+ format1 = workbook.add_format({'font_size': 14, 'align': 'vcenter', 'bold': True})
+ format11 = workbook.add_format({'font_size': 12, 'align': 'center', 'bold': True})
+ format21 = workbook.add_format({'font_size': 10, 'align': 'center', 'bold': True})
+ format3 = workbook.add_format({'bottom': True, 'top': True, 'font_size': 12})
+ format4 = workbook.add_format({'font_size': 12, 'align': 'left', 'bold': True})
+ font_size_8 = workbook.add_format({'font_size': 8, 'align': 'center'})
+ font_size_8_l = workbook.add_format({'font_size': 8, 'align': 'left'})
+ font_size_8_r = workbook.add_format({'font_size': 8, 'align': 'right'})
+ red_mark = workbook.add_format({'font_size': 8, 'bg_color': 'red'})
+ justify = workbook.add_format({'font_size': 12})
+ format3.set_align('center')
+ justify.set_align('justify')
+ format1.set_align('center')
+ red_mark.set_align('center')
+ sheet.merge_range(1, 7, 2, 10, 'Product Stock Info', format0)
+ sheet.merge_range(3, 7, 3, 10, comp, format11)
+ w_house = ', '
+ cat = ', '
+ c = []
+ d1 = d.mapped('id')
+ if d1:
+ for i in d1:
+ c.append(self.env['product.category'].browse(i).name)
+ cat = cat.join(c)
+ sheet.merge_range(4, 0, 4, 1, 'Category(s) : ', format4)
+ sheet.merge_range(4, 2, 4, 3 + len(d1), cat, format4)
+ sheet.merge_range(5, 0, 5, 1, 'Warehouse(s) : ', format4)
+ w_house = w_house.join(get_warehouse[0])
+ sheet.merge_range(5, 2, 5, 3 + len(get_warehouse[0]), w_house, format4)
+ user = self.env['res.users'].browse(self.env.uid)
+ tz = pytz.timezone(user.tz if user.tz else 'UTC')
+ times = pytz.utc.localize(datetime.datetime.now()).astimezone(tz)
+ sheet.merge_range('A8:G8', 'Report Date: ' + str(times.strftime("%Y-%m-%d %H:%M %p")), format1)
+ sheet.merge_range(7, 7, 7, count, 'Warehouses', format1)
+ sheet.merge_range('A9:G9', 'Product Information', format11)
+ w_col_no = 6
+ w_col_no1 = 7
+ for i in get_warehouse[0]:
+ w_col_no = w_col_no + 11
+ sheet.merge_range(8, w_col_no1, 8, w_col_no, i, format11)
+ w_col_no1 = w_col_no1 + 11
+ sheet.write(9, 0, 'SKU', format21)
+ sheet.merge_range(9, 1, 9, 3, 'Name', format21)
+ sheet.merge_range(9, 4, 9, 5, 'Category', format21)
+ sheet.write(9, 6, 'Cost Price', format21)
+ p_col_no1 = 7
+ for i in get_warehouse[0]:
+ sheet.write(9, p_col_no1, 'Available', format21)
+ sheet.write(9, p_col_no1 + 1, 'Virtual', format21)
+ sheet.write(9, p_col_no1 + 2, 'Incoming', format21)
+ sheet.write(9, p_col_no1 + 3, 'Outgoing', format21)
+ sheet.merge_range(9, p_col_no1 + 4, 9, p_col_no1 + 5, 'Net On Hand', format21)
+ sheet.merge_range(9, p_col_no1 + 6, 9, p_col_no1 + 7, 'Total Sold', format21)
+ sheet.merge_range(9, p_col_no1 + 8, 9, p_col_no1 + 9, 'Total Purchased', format21)
+ sheet.write(9, p_col_no1 + 10, 'Valuation', format21)
+ p_col_no1 = p_col_no1 + 11
+ prod_row = 10
+ prod_col = 0
+ for i in get_warehouse[1]:
+ get_line = self.get_lines(d, i)
+ for each in get_line:
+ sheet.write(prod_row, prod_col, each['sku'], font_size_8)
+ sheet.merge_range(prod_row, prod_col + 1, prod_row, prod_col + 3, each['name'], font_size_8_l)
+ sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['category'], font_size_8_l)
+ sheet.write(prod_row, prod_col + 6, each['cost_price'], font_size_8_r)
+ prod_row = prod_row + 1
+ break
+ prod_row = 10
+ prod_col = 7
+ for i in get_warehouse[1]:
+ get_line = self.get_lines(d, i)
+ for each in get_line:
+ if each['available'] < 0:
+ sheet.write(prod_row, prod_col, each['available'], red_mark)
+ else:
+ sheet.write(prod_row, prod_col, each['available'], font_size_8)
+ if each['virtual'] < 0:
+ sheet.write(prod_row, prod_col + 1, each['virtual'], red_mark)
+ else:
+ sheet.write(prod_row, prod_col + 1, each['virtual'], font_size_8)
+ if each['incoming'] < 0:
+ sheet.write(prod_row, prod_col + 2, each['incoming'], red_mark)
+ else:
+ sheet.write(prod_row, prod_col + 2, each['incoming'], font_size_8)
+ if each['outgoing'] < 0:
+ sheet.write(prod_row, prod_col + 3, each['outgoing'], red_mark)
+ else:
+ sheet.write(prod_row, prod_col + 3, each['outgoing'], font_size_8)
+ if each['net_on_hand'] < 0:
+ sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['net_on_hand'], red_mark)
+ else:
+ sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['net_on_hand'], font_size_8)
+ if each['sale_value'] < 0:
+ sheet.merge_range(prod_row, prod_col + 6, prod_row, prod_col + 7, each['sale_value'], red_mark)
+ else:
+ sheet.merge_range(prod_row, prod_col + 6, prod_row, prod_col + 7, each['sale_value'],
+ font_size_8)
+ if each['purchase_value'] < 0:
+ sheet.merge_range(prod_row, prod_col + 8, prod_row, prod_col + 9, each['purchase_value'], red_mark)
+ else:
+ sheet.merge_range(prod_row, prod_col + 8, prod_row, prod_col + 9, each['purchase_value'],
+ font_size_8)
+ if each['total_value'] < 0:
+ sheet.write(prod_row, prod_col + 10, each['total_value'], red_mark)
+ else:
+ sheet.write(prod_row, prod_col + 10, each['total_value'], font_size_8_r)
+ prod_row = prod_row + 1
+ prod_row = 10
+ prod_col = prod_col + 11
+ workbook.close()
+ output.seek(0)
+ response.stream.write(output.read())
+ output.close()
diff --git a/odex25_inventory/export_stockinfo_xls/security/ir.model.access.csv b/odex25_inventory/export_stockinfo_xls/security/ir.model.access.csv
new file mode 100644
index 000000000..90b0f59ea
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_wizard_stock_history_user,wizard.stock.history,model_wizard_stock_history,base.group_user,1,1,1,1
+
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/check.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/check.png
new file mode 100644
index 000000000..c8e85f51d
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/check.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/chevron.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/chevron.png
new file mode 100644
index 000000000..2089293d6
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/chevron.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/cogs.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/cogs.png
new file mode 100644
index 000000000..95d0bad62
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/cogs.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/consultation.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/consultation.png
new file mode 100644
index 000000000..8319d4baa
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/consultation.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/ecom-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/ecom-black.png
new file mode 100644
index 000000000..a9385ff13
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/ecom-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/education-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/education-black.png
new file mode 100644
index 000000000..3eb09b27b
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/education-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/hotel-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/hotel-black.png
new file mode 100644
index 000000000..130f613be
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/hotel-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/license.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/license.png
new file mode 100644
index 000000000..a5869797e
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/license.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png
new file mode 100644
index 000000000..658d56ccc
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png
new file mode 100644
index 000000000..697eb0e9f
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/pos-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/pos-black.png
new file mode 100644
index 000000000..97c0f90c1
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/pos-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/puzzle.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/puzzle.png
new file mode 100644
index 000000000..65cf854e7
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/puzzle.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png
new file mode 100644
index 000000000..4a35eb939
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/service-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/service-black.png
new file mode 100644
index 000000000..301ab51cb
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/service-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/trading-black.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/trading-black.png
new file mode 100644
index 000000000..9398ba2f1
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/trading-black.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/training.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/training.png
new file mode 100644
index 000000000..884ca024d
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/training.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/update.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/update.png
new file mode 100644
index 000000000..ecbc5a01a
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/update.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/user.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/user.png
new file mode 100644
index 000000000..6ffb23d9f
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/user.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/wrench.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/wrench.png
new file mode 100644
index 000000000..6c04dea0f
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/icons/wrench.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/categories.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/categories.png
new file mode 100644
index 000000000..bedf1e0b1
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/categories.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/check-box.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/check-box.png
new file mode 100644
index 000000000..42caf24b9
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/check-box.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/compass.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/compass.png
new file mode 100644
index 000000000..d5fed8faa
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/compass.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/corporate.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/corporate.png
new file mode 100644
index 000000000..2eb13edbf
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/corporate.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/customer-support.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/customer-support.png
new file mode 100644
index 000000000..79efc72ed
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/customer-support.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/cybrosys-logo.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/cybrosys-logo.png
new file mode 100644
index 000000000..cc3cc0ccf
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/cybrosys-logo.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/features.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/features.png
new file mode 100644
index 000000000..b41769f77
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/features.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/logo.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/logo.png
new file mode 100644
index 000000000..478462d3e
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/logo.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pictures.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pictures.png
new file mode 100644
index 000000000..56d255fe9
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pictures.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pie-chart.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pie-chart.png
new file mode 100644
index 000000000..426e05244
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/pie-chart.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/right-arrow.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/right-arrow.png
new file mode 100644
index 000000000..730984a06
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/right-arrow.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/star.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/star.png
new file mode 100644
index 000000000..2eb9ab29f
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/star.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/support.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/support.png
new file mode 100644
index 000000000..4f18b8b82
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/support.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/whatsapp.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/whatsapp.png
new file mode 100644
index 000000000..d513a5356
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/misc/whatsapp.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/1.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/1.png
new file mode 100644
index 000000000..489f44e86
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/1.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/2.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/2.png
new file mode 100644
index 000000000..273effef7
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/2.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/3.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/3.png
new file mode 100644
index 000000000..55fb7ba18
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/3.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/4.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/4.png
new file mode 100644
index 000000000..c1f30354a
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/4.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/5.gif b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/5.gif
new file mode 100644
index 000000000..8f40aab85
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/5.gif differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/6.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/6.png
new file mode 100644
index 000000000..31ed46762
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/modules/6.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_01.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_01.png
new file mode 100644
index 000000000..2d2701b61
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_01.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_02.png b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_02.png
new file mode 100644
index 000000000..1164f892e
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/export_stockinfo_xls_02.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/hero.gif b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/hero.gif
new file mode 100644
index 000000000..304614689
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/assets/screenshots/hero.gif differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/banner.png b/odex25_inventory/export_stockinfo_xls/static/description/banner.png
new file mode 100644
index 000000000..359d3e4d6
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/banner.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/icon.png b/odex25_inventory/export_stockinfo_xls/static/description/icon.png
new file mode 100644
index 000000000..3ff4cc133
Binary files /dev/null and b/odex25_inventory/export_stockinfo_xls/static/description/icon.png differ
diff --git a/odex25_inventory/export_stockinfo_xls/static/description/index.html b/odex25_inventory/export_stockinfo_xls/static/description/index.html
new file mode 100644
index 000000000..813011c1d
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/static/description/index.html
@@ -0,0 +1,573 @@
+
+
+
+
+
+
+ Community
+
+
+ Enterprise
+
+
+ Odoo.sh
+
+
+
+
+
+
+
+
+
+ Current
+ Stock
+ XLS
+
Current Stock Report for all
+ Products in each Warehouse.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Explore This
+ Module
+
+
+
+
+
+
+
+
+
+
Overview
+
+
+
+
+ This module helps to print Current Stock Report for all Products in each Warehouse with XLS
+
+
+
+
+
+
+
+
+
+
Features
+
+
+
+
+
+
+
Community &
+ Enterprise Support.
+
+
+
+
Current Stock
+ XLS.
+
+
+
+
Select category for
+ products.
+
+
+
+
Negative stock will be
+ highlighted in "red" cells.
+
+
+
+
+
+
+
Available in Odoo 16.0
+ Community and Enterprise.
+
+
+
+
+
Current Stock Report
+ for
+ all Products in each Warehouse.
+
+
+
+
+
Get your stock
+ valuation
+ details.
+
+
+
+
+
+
+
+
+
+
+
+
Screenshots
+
+
+
+
+
+
+
Export Stock Info Wizard
+
+
Go to Inventory -> Reports
+ ->
+ Current stock in Excel. Now a wizard will appear on your screen.
+ Please enter the warehouses which you want to take the report.
+ You can also select category for products(It is Optional). Then Click "Export Product with Stock
+ Info" button. Then You will get the corresponding report in XLS.
+
+
+
+
+
Stock Info Excel Report
+
+
Per warehouse you can get
+ Available Qty, Virtual Qty, Incoming Qty, Outgoing Qty, Net On Hand Qty,
+ Total Sold & Total Purchased Qty.
+ You can get your stock valuation details too.
+ Negative stock will be highlighted in "red" cells.
+
+
+
+
+
+
+
+
+
+
+
+
+
Related
+ Products
+
+
+
+
+
+
+
+
+
+
+
+
Our Services
+
+
+
+
+
+
+
+
+
+
+ Odoo
+ Customization
+
+
+
+
+
+
+
+ Odoo
+ Implementation
+
+
+
+
+
+
+
+ Odoo
+ Support
+
+
+
+
+
+
+
+
+ Hire
+ Odoo
+ Developer
+
+
+
+
+
+
+
+ Odoo
+ Integration
+
+
+
+
+
+
+
+ Odoo
+ Migration
+
+
+
+
+
+
+
+
+ Odoo
+ Consultancy
+
+
+
+
+
+
+
+ Odoo
+ Implementation
+
+
+
+
+
+
+
+ Odoo
+ Licensing Consultancy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Our
+ Industries
+
+
+
+
+
+
+
+
+
+ Trading
+
+
+ Easily procure
+ and
+ sell your products
+
+
+
+
+
+
+
+ POS
+
+
+ Easy
+ configuration
+ and convivial experience
+
+
+
+
+
+
+
+ Education
+
+
+ A platform for
+ educational management
+
+
+
+
+
+
+
+ Manufacturing
+
+
+ Plan, track and
+ schedule your operations
+
+
+
+
+
+
+
+ E-commerce & Website
+
+
+ Mobile
+ friendly,
+ awe-inspiring product pages
+
+
+
+
+
+
+
+ Service Management
+
+
+ Keep track of
+ services and invoice
+
+
+
+
+
+
+
+ Restaurant
+
+
+ Run your bar or
+ restaurant methodically
+
+
+
+
+
+
+
+ Hotel Management
+
+
+ An
+ all-inclusive
+ hotel management application
+
+
+
+
+
+
+
+
+
+
+
+
+
Support
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/odex25_inventory/export_stockinfo_xls/static/src/js/action_manager.js b/odex25_inventory/export_stockinfo_xls/static/src/js/action_manager.js
new file mode 100644
index 000000000..e9e276eb8
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/static/src/js/action_manager.js
@@ -0,0 +1,21 @@
+/** @odoo-module */
+
+import { registry } from "@web/core/registry";
+import { download } from "@web/core/network/download";
+import framework from 'web.framework';
+import session from 'web.session';
+
+registry.category("ir.actions.report handlers").add("stock_xlsx", async (action) => {
+ if (action.report_type === 'stock_xlsx') {
+ framework.blockUI();
+ var def = $.Deferred();
+ session.get_file({
+ url: '/xlsx_reports',
+ data: action.data,
+ success: def.resolve.bind(def),
+ error: (error) => this.call('crash_manager', 'rpc_error', error),
+ complete: framework.unblockUI,
+ });
+ return def;
+ }
+});
diff --git a/odex25_inventory/export_stockinfo_xls/views/wizard_view.xml b/odex25_inventory/export_stockinfo_xls/views/wizard_view.xml
new file mode 100644
index 000000000..64c07b58f
--- /dev/null
+++ b/odex25_inventory/export_stockinfo_xls/views/wizard_view.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+ wizard.stock.history.form
+ wizard.stock.history
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Export product stock in Excel
+ wizard.stock.history
+ ir.actions.act_window
+ form
+
+ new
+
+
+
+
+
+
+
diff --git a/odex25_inventory/hyd_stock_available/__init__.py b/odex25_inventory/hyd_stock_available/__init__.py
new file mode 100644
index 000000000..0650744f6
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/odex25_inventory/hyd_stock_available/__manifest__.py b/odex25_inventory/hyd_stock_available/__manifest__.py
new file mode 100644
index 000000000..b56f014ca
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/__manifest__.py
@@ -0,0 +1,22 @@
+{
+ "name": "Available stock during move",
+ "summary": "This module add not reserved quantity in stock move",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "version": "14.0.0.1.0",
+ "author": "HyD Freelance",
+ "support": "mail.hyd.freelance@gmail.com",
+ "website": "",
+ "license": "AGPL-3",
+ "depends": ["stock"],
+ "data": [
+ # views
+ "views/stock_picking_views.xml",
+ "views/stock_move_line_views.xml",
+ ],
+ "demo": [],
+ "test": [],
+ "installable": True,
+ "price": 0,
+ "currency": "USD",
+ "images": ["static/images/main_screenshot.png"],
+}
diff --git a/odex25_inventory/hyd_stock_available/models/__init__.py b/odex25_inventory/hyd_stock_available/models/__init__.py
new file mode 100644
index 000000000..f800274cd
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/models/__init__.py
@@ -0,0 +1,2 @@
+from . import stock_move
+from . import stock_move_line
diff --git a/odex25_inventory/hyd_stock_available/models/stock_move.py b/odex25_inventory/hyd_stock_available/models/stock_move.py
new file mode 100644
index 000000000..f77d5f6ab
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/models/stock_move.py
@@ -0,0 +1,26 @@
+from odoo import api, fields, models
+
+
+class StockMove(models.Model):
+
+ _inherit = "stock.move"
+ _name = "stock.move"
+
+ not_reserved = fields.Float(
+ string="Not reserved",
+ compute="_compute_available_qty",
+ store=True,
+ readonly=True,
+ )
+
+ @api.depends("product_id", "product_uom_qty")
+ def _compute_available_qty(self):
+ for record in self:
+ if record.product_id and record.state != "done":
+ actual_qty = record.product_id.with_context(
+ {"location": record.location_id.id}
+ ).qty_available
+ outgoing_qty = record.product_id.with_context(
+ {"location": record.location_id.id}
+ ).outgoing_qty
+ record.not_reserved = actual_qty - outgoing_qty
diff --git a/odex25_inventory/hyd_stock_available/models/stock_move_line.py b/odex25_inventory/hyd_stock_available/models/stock_move_line.py
new file mode 100644
index 000000000..a6aaafcf7
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/models/stock_move_line.py
@@ -0,0 +1,27 @@
+from odoo import api, fields, models
+
+
+class StockMoveLine(models.Model):
+
+ _inherit = "stock.move.line"
+ _name = "stock.move.line"
+
+ not_reserved = fields.Float(
+ string="Not reserved",
+ compute="_compute_available_qty",
+ store=True,
+ readonly=True,
+ )
+
+ @api.depends("product_id", "product_uom_qty", "lot_id")
+ def _compute_available_qty(self):
+ for record in self:
+ if record.product_id and record.move_id.state != "done":
+ id_lot = record.lot_id.id if record.lot_id else None
+ actual_qty = record.product_id.with_context(
+ {"location": record.location_id.id, "lot_id": id_lot}
+ ).qty_available
+ outgoing_qty = record.product_id.with_context(
+ {"location": record.location_id.id, "lot_id": id_lot}
+ ).outgoing_qty
+ record.not_reserved = actual_qty - outgoing_qty
diff --git a/odex25_inventory/hyd_stock_available/static/description/001.png b/odex25_inventory/hyd_stock_available/static/description/001.png
new file mode 100644
index 000000000..b0726e77b
Binary files /dev/null and b/odex25_inventory/hyd_stock_available/static/description/001.png differ
diff --git a/odex25_inventory/hyd_stock_available/static/description/icon.png b/odex25_inventory/hyd_stock_available/static/description/icon.png
new file mode 100644
index 000000000..45d830a48
Binary files /dev/null and b/odex25_inventory/hyd_stock_available/static/description/icon.png differ
diff --git a/odex25_inventory/hyd_stock_available/static/description/index.html b/odex25_inventory/hyd_stock_available/static/description/index.html
new file mode 100644
index 000000000..f17282840
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/static/description/index.html
@@ -0,0 +1,30 @@
+
+
+
Non reserved Quantity in stock picking
+
+
This module give you the no reserved quantity for the product selected in stock picking lines. You can now be sure of the quantity available during stock move.
+
+
+ A screenshot.
+
+
+
+
+
+
+
+
+
+
+
+
+
Contact / Support
+ Need help or want extra features?
+
+
+
+
diff --git a/odex25_inventory/hyd_stock_available/static/images/main_screenshot.png b/odex25_inventory/hyd_stock_available/static/images/main_screenshot.png
new file mode 100644
index 000000000..e95569cfe
Binary files /dev/null and b/odex25_inventory/hyd_stock_available/static/images/main_screenshot.png differ
diff --git a/odex25_inventory/hyd_stock_available/views/stock_move_line_views.xml b/odex25_inventory/hyd_stock_available/views/stock_move_line_views.xml
new file mode 100644
index 000000000..1c839979c
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/views/stock_move_line_views.xml
@@ -0,0 +1,15 @@
+
+
+
+ stock.move.line
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/hyd_stock_available/views/stock_picking_views.xml b/odex25_inventory/hyd_stock_available/views/stock_picking_views.xml
new file mode 100644
index 000000000..d08361d5e
--- /dev/null
+++ b/odex25_inventory/hyd_stock_available/views/stock_picking_views.xml
@@ -0,0 +1,15 @@
+
+
+
+ stock.picking
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/stock_card_report/README.rst b/odex25_inventory/stock_card_report/README.rst
new file mode 100644
index 000000000..ebdff3227
--- /dev/null
+++ b/odex25_inventory/stock_card_report/README.rst
@@ -0,0 +1,86 @@
+=================
+Stock Card Report
+=================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:b59e9d3648bd6b1fbe7c6adc993c1eee4d3612c663656f3898cc6b6407e413df
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--reporting-lightgray.png?logo=github
+ :target: https://github.com/OCA/stock-logistics-reporting/tree/14.0/stock_card_report
+ :alt: OCA/stock-logistics-reporting
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/stock-logistics-reporting-14-0/stock-logistics-reporting-14-0-stock_card_report
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-reporting&target_branch=14.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+Stock Card Report is the report that display movement (in/out) of a product in a specified location and date range.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+To use this module, you need to:
+
+#. Go to Inventory > Reporting > Stock Card.
+#. Select Start date, End date, Products, Location.
+#. Choose View or Export PDF or Export XLSX or Cancel.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Ecosoft
+
+Contributors
+~~~~~~~~~~~~
+
+* Pimolnat Suntian
+* Prapassorn Sornkaew
+
+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-reporting `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/odex25_inventory/stock_card_report/__init__.py b/odex25_inventory/stock_card_report/__init__.py
new file mode 100644
index 000000000..2873ef602
--- /dev/null
+++ b/odex25_inventory/stock_card_report/__init__.py
@@ -0,0 +1,5 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import wizard
+from . import reports
diff --git a/odex25_inventory/stock_card_report/__manifest__.py b/odex25_inventory/stock_card_report/__manifest__.py
new file mode 100644
index 000000000..44e10f01e
--- /dev/null
+++ b/odex25_inventory/stock_card_report/__manifest__.py
@@ -0,0 +1,21 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ "name": "Stock Card Report",
+ "summary": "Add stock card report on Inventory Reporting.",
+ "version": "14.0.1.0.2",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "website": "https://github.com/OCA/stock-logistics-reporting",
+ "author": "Ecosoft, Odoo Community Association (OCA)",
+ "license": "AGPL-3",
+ "depends": ["stock", "date_range", "report_xlsx_helper"],
+ "data": [
+ "security/ir.model.access.csv",
+ "data/paper_format.xml",
+ "data/report_data.xml",
+ "reports/stock_card_report.xml",
+ "wizard/stock_card_report_wizard_view.xml",
+ ],
+ "installable": True,
+}
diff --git a/odex25_inventory/stock_card_report/data/paper_format.xml b/odex25_inventory/stock_card_report/data/paper_format.xml
new file mode 100644
index 000000000..ca082e930
--- /dev/null
+++ b/odex25_inventory/stock_card_report/data/paper_format.xml
@@ -0,0 +1,19 @@
+
+
+
+ Stock Card A4
+
+ A4
+ 0
+ 0
+ Portrait
+ 28
+ 28
+ 7
+ 7
+
+ 24
+ 90
+
+
+
diff --git a/odex25_inventory/stock_card_report/data/report_data.xml b/odex25_inventory/stock_card_report/data/report_data.xml
new file mode 100644
index 000000000..0c0535e38
--- /dev/null
+++ b/odex25_inventory/stock_card_report/data/report_data.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ Stock Card Report
+ stock_card_report_backend
+
+
+
+
+ Stock Card PDF
+ report.stock.card.report
+ qweb-pdf
+ stock_card_report.report_stock_card_report_pdf
+ stock_card_report.report_stock_card_report_pdf
+ 'Stock Card Report - [%s]' % (object.location_id.complete_name)
+
+
+
+
+ Stock Card XLSX
+ report.stock.card.report
+ xlsx
+ stock_card_report.report_stock_card_report_xlsx
+ Stock Card Report
+
+
+
diff --git a/odex25_inventory/stock_card_report/i18n/es.po b/odex25_inventory/stock_card_report/i18n/es.po
new file mode 100644
index 000000000..58b14439d
--- /dev/null
+++ b/odex25_inventory/stock_card_report/i18n/es.po
@@ -0,0 +1,277 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_card_report
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 13.0\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#. module: stock_card_report
+#: model:ir.actions.report,print_report_name:stock_card_report.action_stock_card_report_pdf
+msgid "'Stock Card Report - [%s]' % (object.location_id.complete_name)"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_buttons
+msgid " Export"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_buttons
+msgid " Print"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Balance"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Cancel"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__create_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__create_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__create_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__create_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__date
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Date"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__date_from
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Date From"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__date_to
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Date To"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_to
+msgid "End Date"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Export PDF"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Export XLSX"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__id
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx__id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "In"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_base
+msgid "Initial"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__is_initial
+msgid "Is Initial"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__write_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__write_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__write_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__write_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__location_id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__location_id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__location_id
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Location"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__location_dest_id
+msgid "Location Dest"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Out"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_range_id
+msgid "Period"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__picking_id
+msgid "Picking"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__product_ids
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_id
+msgid "Product"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_in
+msgid "Product In"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_out
+msgid "Product Out"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_qty
+msgid "Product Qty"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_uom
+msgid "Product Uom"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_uom_qty
+msgid "Product Uom Qty"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__product_ids
+msgid "Products"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__reference
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Reference"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__results
+msgid "Results"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_from
+msgid "Start Date"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.actions.act_window,name:stock_card_report.stock_card_report_action
+#: model:ir.ui.menu,name:stock_card_report.stock_card_report_menu
+msgid "Stock Card"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_base
+msgid "Stock Card -"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.actions.report,name:stock_card_report.action_stock_card_report_pdf
+msgid "Stock Card PDF"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.actions.client,name:stock_card_report.action_report_stock_card_report_html
+#: model:ir.model,name:stock_card_report.model_report_stock_card_report
+msgid "Stock Card Report"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_stock_card_report_wizard
+msgid "Stock Card Report Wizard"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_report_stock_card_report_report_stock_card_report_xlsx
+msgid "Stock Card Report XLSX"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_stock_card_view
+msgid "Stock Card View"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.actions.report,name:stock_card_report.action_stock_card_report_xlsx
+msgid "Stock Card XLSX"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,help:stock_card_report.field_report_stock_card_report__results
+msgid "Use compute fields, so there is nothing store in database"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "View"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "or"
+msgstr ""
diff --git a/odex25_inventory/stock_card_report/i18n/es_EC.po b/odex25_inventory/stock_card_report/i18n/es_EC.po
new file mode 100644
index 000000000..08f14e7b7
--- /dev/null
+++ b/odex25_inventory/stock_card_report/i18n/es_EC.po
@@ -0,0 +1,288 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_card_report
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 13.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2020-09-29 16:03+0000\n"
+"Last-Translator: johnnypiguave \n"
+"Language-Team: none\n"
+"Language: es_EC\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 3.10\n"
+
+#. module: stock_card_report
+#: model:ir.actions.report,print_report_name:stock_card_report.action_stock_card_report_pdf
+msgid "'Stock Card Report - [%s]' % (object.location_id.complete_name)"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_buttons
+msgid " Export"
+msgstr "Exportar"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_buttons
+msgid " Print"
+msgstr "Imprimir"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Balance"
+msgstr "Saldo"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Cancel"
+msgstr "Cancelar"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__create_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__create_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__create_uid
+msgid "Created by"
+msgstr "Creado por"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__create_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__create_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__create_date
+msgid "Created on"
+msgstr "Creado el"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__date
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Date"
+msgstr "Fecha"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__date_from
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Date From"
+msgstr "Fecha desde"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__date_to
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Date To"
+msgstr "Fecha hasta"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__display_name
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__display_name
+msgid "Display Name"
+msgstr "Nombre a mostrar"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_to
+msgid "End Date"
+msgstr "Fecha final"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Export PDF"
+msgstr "Exportar a PDF"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "Export XLSX"
+msgstr "Exportar XLSX"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__id
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx__id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__id
+msgid "ID"
+msgstr "ID"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "In"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_base
+msgid "Initial"
+msgstr "Inicial"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__is_initial
+msgid "Is Initial"
+msgstr "Es inicial"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report_report_stock_card_report_xlsx____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard____last_update
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view____last_update
+msgid "Last Modified on"
+msgstr "Última modificación el"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__write_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__write_uid
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__write_uid
+msgid "Last Updated by"
+msgstr "Última actualización por"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__write_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__write_date
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__write_date
+msgid "Last Updated on"
+msgstr "Ultima actualización en"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__location_id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__location_id
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__location_id
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_filters
+msgid "Location"
+msgstr "Ubicación"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__location_dest_id
+msgid "Location Dest"
+msgstr "Ubicación destino"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Out"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_range_id
+msgid "Period"
+msgstr "Periodo"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__picking_id
+msgid "Picking"
+msgstr ""
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__product_ids
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_id
+msgid "Product"
+msgstr "Producto"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_in
+msgid "Product In"
+msgstr "Entrada"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_out
+msgid "Product Out"
+msgstr "Salida"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_qty
+msgid "Product Qty"
+msgstr "Cantidad"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_uom
+msgid "Product Uom"
+msgstr "Unidad de medida"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__product_uom_qty
+msgid "Product Uom Qty"
+msgstr "Cantidad uom"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__product_ids
+msgid "Products"
+msgstr "Productos"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_view__reference
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_lines_header
+msgid "Reference"
+msgstr "Referencia"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_report_stock_card_report__results
+msgid "Results"
+msgstr "Resultado"
+
+#. module: stock_card_report
+#: model:ir.model.fields,field_description:stock_card_report.field_stock_card_report_wizard__date_from
+msgid "Start Date"
+msgstr "Fecha inicio"
+
+#. module: stock_card_report
+#: model:ir.actions.act_window,name:stock_card_report.stock_card_report_action
+#: model:ir.ui.menu,name:stock_card_report.stock_card_report_menu
+msgid "Stock Card"
+msgstr "Kardex"
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.report_stock_card_report_base
+msgid "Stock Card -"
+msgstr "Kardex -"
+
+#. module: stock_card_report
+#: model:ir.actions.report,name:stock_card_report.action_stock_card_report_pdf
+msgid "Stock Card PDF"
+msgstr "Kardex PDF"
+
+#. module: stock_card_report
+#: model:ir.actions.client,name:stock_card_report.action_report_stock_card_report_html
+#: model:ir.model,name:stock_card_report.model_report_stock_card_report
+msgid "Stock Card Report"
+msgstr "Reporte Kardex"
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_stock_card_report_wizard
+msgid "Stock Card Report Wizard"
+msgstr "Kardex Wizard"
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_report_stock_card_report_report_stock_card_report_xlsx
+msgid "Stock Card Report XLSX"
+msgstr "Reporte Kardex XLSX"
+
+#. module: stock_card_report
+#: model:ir.model,name:stock_card_report.model_stock_card_view
+msgid "Stock Card View"
+msgstr "Vista Kardex"
+
+#. module: stock_card_report
+#: model:ir.actions.report,name:stock_card_report.action_stock_card_report_xlsx
+msgid "Stock Card XLSX"
+msgstr "Kardex XLSX"
+
+#. module: stock_card_report
+#: model:ir.model.fields,help:stock_card_report.field_report_stock_card_report__results
+msgid "Use compute fields, so there is nothing store in database"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "View"
+msgstr ""
+
+#. module: stock_card_report
+#: model_terms:ir.ui.view,arch_db:stock_card_report.stock_card_report_wizard_form
+msgid "or"
+msgstr ""
+
+#~ msgid "'Stock Card Report - [%s]' % object.location_id.display_name"
+#~ msgstr "Reporte kardex"
+
+#~ msgid "Input"
+#~ msgstr "Ingreso"
+
+#~ msgid "Output"
+#~ msgstr "Salida"
diff --git a/odex25_inventory/stock_card_report/readme/CONTRIBUTORS.rst b/odex25_inventory/stock_card_report/readme/CONTRIBUTORS.rst
new file mode 100644
index 000000000..1def9e862
--- /dev/null
+++ b/odex25_inventory/stock_card_report/readme/CONTRIBUTORS.rst
@@ -0,0 +1,2 @@
+* Pimolnat Suntian
+* Prapassorn Sornkaew
diff --git a/odex25_inventory/stock_card_report/readme/DESCRIPTION.rst b/odex25_inventory/stock_card_report/readme/DESCRIPTION.rst
new file mode 100644
index 000000000..1d5fcb259
--- /dev/null
+++ b/odex25_inventory/stock_card_report/readme/DESCRIPTION.rst
@@ -0,0 +1 @@
+Stock Card Report is the report that display movement (in/out) of a product in a specified location and date range.
diff --git a/odex25_inventory/stock_card_report/readme/USAGE.rst b/odex25_inventory/stock_card_report/readme/USAGE.rst
new file mode 100644
index 000000000..cce04a48f
--- /dev/null
+++ b/odex25_inventory/stock_card_report/readme/USAGE.rst
@@ -0,0 +1,5 @@
+To use this module, you need to:
+
+#. Go to Inventory > Reporting > Stock Card.
+#. Select Start date, End date, Products, Location.
+#. Choose View or Export PDF or Export XLSX or Cancel.
diff --git a/odex25_inventory/stock_card_report/reports/__init__.py b/odex25_inventory/stock_card_report/reports/__init__.py
new file mode 100644
index 000000000..0bf3b7a7f
--- /dev/null
+++ b/odex25_inventory/stock_card_report/reports/__init__.py
@@ -0,0 +1,5 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import stock_card_report
+from . import stock_card_report_xlsx
diff --git a/odex25_inventory/stock_card_report/reports/stock_card_report.py b/odex25_inventory/stock_card_report/reports/stock_card_report.py
new file mode 100644
index 000000000..e7398ec05
--- /dev/null
+++ b/odex25_inventory/stock_card_report/reports/stock_card_report.py
@@ -0,0 +1,117 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+
+
+class StockCardView(models.TransientModel):
+ _name = "stock.card.view"
+ _description = "Stock Card View"
+ _order = "date"
+
+ date = fields.Datetime()
+ product_id = fields.Many2one(comodel_name="product.product")
+ product_qty = fields.Float()
+ product_uom_qty = fields.Float()
+ product_uom = fields.Many2one(comodel_name="uom.uom")
+ reference = fields.Char()
+ location_id = fields.Many2one(comodel_name="stock.location")
+ location_dest_id = fields.Many2one(comodel_name="stock.location")
+ is_initial = fields.Boolean()
+ product_in = fields.Float()
+ product_out = fields.Float()
+ picking_id = fields.Many2one(comodel_name="stock.picking")
+
+ def name_get(self):
+ result = []
+ for rec in self:
+ name = rec.reference
+ if rec.picking_id.origin:
+ name = "{} ({})".format(name, rec.picking_id.origin)
+ result.append((rec.id, name))
+ return result
+
+
+class StockCardReport(models.TransientModel):
+ _name = "report.stock.card.report"
+ _description = "Stock Card Report"
+
+ # Filters fields, used for data computation
+ date_from = fields.Date()
+ date_to = fields.Date()
+ product_ids = fields.Many2many(comodel_name="product.product")
+ location_id = fields.Many2one(comodel_name="stock.location")
+
+ # Data fields, used to browse report data
+ results = fields.Many2many(
+ comodel_name="stock.card.view",
+ compute="_compute_results",
+ help="Use compute fields, so there is nothing store in database",
+ )
+
+ def _compute_results(self):
+ self.ensure_one()
+ date_from = self.date_from or "0001-01-01"
+ self.date_to = self.date_to or fields.Date.context_today(self)
+ locations = self.env["stock.location"].search(
+ [("id", "child_of", [self.location_id.id])]
+ )
+ self._cr.execute(
+ """
+ SELECT move.date, move.product_id, move.product_qty,
+ move.product_uom_qty, move.product_uom, move.reference,
+ move.location_id, move.location_dest_id,
+ case when move.location_dest_id in %s
+ then move.product_qty end as product_in,
+ case when move.location_id in %s
+ then move.product_qty end as product_out,
+ case when move.date < %s then True else False end as is_initial,
+ move.picking_id
+ FROM stock_move move
+ WHERE (move.location_id in %s or move.location_dest_id in %s)
+ and move.state = 'done' and move.product_id in %s
+ and CAST(move.date AS date) <= %s
+ ORDER BY move.date, move.reference
+ """,
+ (
+ tuple(locations.ids),
+ tuple(locations.ids),
+ date_from,
+ tuple(locations.ids),
+ tuple(locations.ids),
+ tuple(self.product_ids.ids),
+ self.date_to,
+ ),
+ )
+ stock_card_results = self._cr.dictfetchall()
+ ReportLine = self.env["stock.card.view"]
+ self.results = [ReportLine.new(line).id for line in stock_card_results]
+
+ def _get_initial(self, product_line):
+ product_input_qty = sum(product_line.mapped("product_in"))
+ product_output_qty = sum(product_line.mapped("product_out"))
+ return product_input_qty - product_output_qty
+
+ def print_report(self, report_type="qweb"):
+ self.ensure_one()
+ action = (
+ report_type == "xlsx"
+ and self.env.ref("stock_card_report.action_stock_card_report_xlsx")
+ or self.env.ref("stock_card_report.action_stock_card_report_pdf")
+ )
+ return action.report_action(self, config=False)
+
+ def _get_html(self):
+ result = {}
+ rcontext = {}
+ report = self.browse(self._context.get("active_id"))
+ if report:
+ rcontext["o"] = report
+ result["html"] = self.env.ref(
+ "stock_card_report.report_stock_card_report_html"
+ )._render(rcontext)
+ return result
+
+ @api.model
+ def get_html(self, given_context=None):
+ return self.with_context(given_context)._get_html()
diff --git a/odex25_inventory/stock_card_report/reports/stock_card_report.xml b/odex25_inventory/stock_card_report/reports/stock_card_report.xml
new file mode 100644
index 000000000..70e90b2db
--- /dev/null
+++ b/odex25_inventory/stock_card_report/reports/stock_card_report.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Print
+ Export
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Stock Card -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Initial
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Date From
+
Date To
+
Location
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/stock_card_report/reports/stock_card_report_xlsx.py b/odex25_inventory/stock_card_report/reports/stock_card_report_xlsx.py
new file mode 100644
index 000000000..2b577b4f9
--- /dev/null
+++ b/odex25_inventory/stock_card_report/reports/stock_card_report_xlsx.py
@@ -0,0 +1,186 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+import logging
+
+from odoo import models
+
+from odoo.addons.report_xlsx_helper.report.report_xlsx_format import (
+ FORMATS,
+ XLS_HEADERS,
+)
+
+_logger = logging.getLogger(__name__)
+
+
+class ReportStockCardReportXlsx(models.AbstractModel):
+ _name = "report.stock_card_report.report_stock_card_report_xlsx"
+ _description = "Stock Card Report XLSX"
+ _inherit = "report.report_xlsx.abstract"
+
+ def generate_xlsx_report(self, workbook, data, objects):
+ self._define_formats(workbook)
+ for product in objects.product_ids:
+ for ws_params in self._get_ws_params(workbook, data, product):
+ ws_name = ws_params.get("ws_name")
+ ws_name = self._check_ws_name(ws_name)
+ ws = workbook.add_worksheet(ws_name)
+ generate_ws_method = getattr(self, ws_params["generate_ws_method"])
+ generate_ws_method(workbook, ws, ws_params, data, objects, product)
+
+ def _get_ws_params(self, wb, data, product):
+ filter_template = {
+ "1_date_from": {
+ "header": {"value": "Date from"},
+ "data": {
+ "value": self._render("date_from"),
+ "format": FORMATS["format_tcell_date_center"],
+ },
+ },
+ "2_date_to": {
+ "header": {"value": "Date to"},
+ "data": {
+ "value": self._render("date_to"),
+ "format": FORMATS["format_tcell_date_center"],
+ },
+ },
+ "3_location": {
+ "header": {"value": "Location"},
+ "data": {
+ "value": self._render("location"),
+ "format": FORMATS["format_tcell_center"],
+ },
+ },
+ }
+ initial_template = {
+ "1_ref": {
+ "data": {"value": "Initial", "format": FORMATS["format_tcell_center"]},
+ "colspan": 4,
+ },
+ "2_balance": {
+ "data": {
+ "value": self._render("balance"),
+ "format": FORMATS["format_tcell_amount_right"],
+ }
+ },
+ }
+ stock_card_template = {
+ "1_date": {
+ "header": {"value": "Date"},
+ "data": {
+ "value": self._render("date"),
+ "format": FORMATS["format_tcell_date_left"],
+ },
+ "width": 25,
+ },
+ "2_reference": {
+ "header": {"value": "Reference"},
+ "data": {
+ "value": self._render("reference"),
+ "format": FORMATS["format_tcell_left"],
+ },
+ "width": 25,
+ },
+ "3_input": {
+ "header": {"value": "In"},
+ "data": {"value": self._render("input")},
+ "width": 25,
+ },
+ "4_output": {
+ "header": {"value": "Out"},
+ "data": {"value": self._render("output")},
+ "width": 25,
+ },
+ "5_balance": {
+ "header": {"value": "Balance"},
+ "data": {"value": self._render("balance")},
+ "width": 25,
+ },
+ }
+
+ ws_params = {
+ "ws_name": product.name,
+ "generate_ws_method": "_stock_card_report",
+ "title": "Stock Card - {}".format(product.name),
+ "wanted_list_filter": [k for k in sorted(filter_template.keys())],
+ "col_specs_filter": filter_template,
+ "wanted_list_initial": [k for k in sorted(initial_template.keys())],
+ "col_specs_initial": initial_template,
+ "wanted_list": [k for k in sorted(stock_card_template.keys())],
+ "col_specs": stock_card_template,
+ }
+ return [ws_params]
+
+ def _stock_card_report(self, wb, ws, ws_params, data, objects, product):
+ ws.set_portrait()
+ ws.fit_to_pages(1, 0)
+ ws.set_header(XLS_HEADERS["xls_headers"]["standard"])
+ ws.set_footer(XLS_HEADERS["xls_footers"]["standard"])
+ self._set_column_width(ws, ws_params)
+ # Title
+ row_pos = 0
+ row_pos = self._write_ws_title(ws, row_pos, ws_params, True)
+ # Filter Table
+ row_pos = self._write_line(
+ ws,
+ row_pos,
+ ws_params,
+ col_specs_section="header",
+ default_format=FORMATS["format_theader_blue_center"],
+ col_specs="col_specs_filter",
+ wanted_list="wanted_list_filter",
+ )
+ row_pos = self._write_line(
+ ws,
+ row_pos,
+ ws_params,
+ col_specs_section="data",
+ render_space={
+ "date_from": objects.date_from or "",
+ "date_to": objects.date_to or "",
+ "location": objects.location_id.display_name or "",
+ },
+ col_specs="col_specs_filter",
+ wanted_list="wanted_list_filter",
+ )
+ row_pos += 1
+ # Stock Card Table
+ row_pos = self._write_line(
+ ws,
+ row_pos,
+ ws_params,
+ col_specs_section="header",
+ default_format=FORMATS["format_theader_blue_center"],
+ )
+ ws.freeze_panes(row_pos, 0)
+ balance = objects._get_initial(
+ objects.results.filtered(lambda l: l.product_id == product and l.is_initial)
+ )
+ row_pos = self._write_line(
+ ws,
+ row_pos,
+ ws_params,
+ col_specs_section="data",
+ render_space={"balance": balance},
+ col_specs="col_specs_initial",
+ wanted_list="wanted_list_initial",
+ )
+ product_lines = objects.results.filtered(
+ lambda l: l.product_id == product and not l.is_initial
+ )
+ for line in product_lines:
+ balance += line.product_in - line.product_out
+ row_pos = self._write_line(
+ ws,
+ row_pos,
+ ws_params,
+ col_specs_section="data",
+ render_space={
+ "date": line.date or "",
+ "reference": line.display_name or "",
+ "input": line.product_in or 0,
+ "output": line.product_out or 0,
+ "balance": balance,
+ },
+ default_format=FORMATS["format_tcell_amount_right"],
+ )
diff --git a/odex25_inventory/stock_card_report/security/ir.model.access.csv b/odex25_inventory/stock_card_report/security/ir.model.access.csv
new file mode 100644
index 000000000..851a6cf51
--- /dev/null
+++ b/odex25_inventory/stock_card_report/security/ir.model.access.csv
@@ -0,0 +1,4 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_stock_card_report_wizard,access.stock.card.report.wizard,model_stock_card_report_wizard,base.group_user,1,1,1,0
+access_report_stock_card_report,access.report.stock.card.report,model_report_stock_card_report,base.group_user,1,1,1,0
+access_stock_card_view,access.stock.card.view,model_stock_card_view,base.group_user,1,1,1,0
diff --git a/odex25_inventory/stock_card_report/static/description/icon.png b/odex25_inventory/stock_card_report/static/description/icon.png
new file mode 100644
index 000000000..3a0328b51
Binary files /dev/null and b/odex25_inventory/stock_card_report/static/description/icon.png differ
diff --git a/odex25_inventory/stock_card_report/static/description/index.html b/odex25_inventory/stock_card_report/static/description/index.html
new file mode 100644
index 000000000..f0ddc8cc7
--- /dev/null
+++ b/odex25_inventory/stock_card_report/static/description/index.html
@@ -0,0 +1,432 @@
+
+
+
+
+
+
+Stock Card Report
+
+
+
+
+
Stock Card Report
+
+
+
+
Stock Card Report is the report that display movement (in/out) of a product in a specified location and date range.
+
Table of contents
+
+
+
+
To use this module, you need to:
+
+Go to Inventory > Reporting > Stock Card.
+Select Start date, End date, Products, Location.
+Choose View or Export PDF or Export XLSX or Cancel.
+
+
+
+
+
Bugs are tracked on GitHub Issues .
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+feedback .
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
This module is maintained by the OCA.
+
+
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-reporting project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute .
+
+
+
+
+
diff --git a/odex25_inventory/stock_card_report/static/src/css/report.css b/odex25_inventory/stock_card_report/static/src/css/report.css
new file mode 100644
index 000000000..043291d22
--- /dev/null
+++ b/odex25_inventory/stock_card_report/static/src/css/report.css
@@ -0,0 +1,59 @@
+.act_as_table {
+ display: table !important;
+ background-color: white;
+}
+.act_as_row {
+ display: table-row !important;
+ page-break-inside: avoid;
+}
+.act_as_cell {
+ display: table-cell !important;
+ page-break-inside: avoid;
+}
+.act_as_thead {
+ display: table-header-group !important;
+}
+.act_as_row.labels {
+ background-color: #f0f0f0 !important;
+}
+.data_table {
+ width: 100% !important;
+ border-left: 0px;
+ border-right: 0px;
+ text-align: center;
+ font-size: 10px;
+ padding-right: 3px;
+ padding-left: 3px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ border-collapse: collapse;
+}
+.data_table .act_as_cell {
+ border: 1px solid lightGrey;
+ text-align: center;
+}
+.data_table .act_as_cell {
+ word-wrap: break-word;
+}
+.data_table .act_as_row.labels {
+ font-weight: bold;
+}
+.act_as_cell.left {
+ text-align: left;
+}
+.act_as_cell.right {
+ text-align: right;
+}
+.custom_footer {
+ font-size: 7px !important;
+}
+.button_row {
+ padding-bottom: 10px;
+}
+.o_stock_card_reports_page {
+ padding-top: 10px;
+ width: 90%;
+ margin-right: auto;
+ margin-left: auto;
+ font-family: Helvetica, Arial;
+}
diff --git a/odex25_inventory/stock_card_report/static/src/js/stock_card_report_backend.js b/odex25_inventory/stock_card_report/static/src/js/stock_card_report_backend.js
new file mode 100644
index 000000000..fa420a5d1
--- /dev/null
+++ b/odex25_inventory/stock_card_report/static/src/js/stock_card_report_backend.js
@@ -0,0 +1,107 @@
+odoo.define("stock_card_report.stock_card_report_backend", function (require) {
+ "use strict";
+
+ var AbstractAction = require("web.AbstractAction");
+ var core = require("web.core");
+ var ReportWidget = require("web.Widget");
+
+ var report_backend = AbstractAction.extend({
+ hasControlPanel: true,
+ // Stores all the parameters of the action.
+ events: {
+ "click .o_stock_card_reports_print": "print",
+ "click .o_stock_card_reports_export": "export",
+ },
+ init: function (parent, action) {
+ this._super.apply(this, arguments);
+ this.actionManager = parent;
+ this.given_context = {};
+ this.odoo_context = action.context;
+ this.controller_url = action.context.url;
+ if (action.context.context) {
+ this.given_context = action.context.context;
+ }
+ this.given_context.active_id =
+ action.context.active_id || action.params.active_id;
+ this.given_context.model = action.context.active_model || false;
+ this.given_context.ttype = action.context.ttype || false;
+ },
+ willStart: function () {
+ return Promise.all([this._super.apply(this, arguments), this.get_html()]);
+ },
+ set_html: function () {
+ var self = this;
+ var def = Promise.resolve();
+ if (!this.report_widget) {
+ this.report_widget = new ReportWidget(this, this.given_context);
+ def = this.report_widget.appendTo(this.$(".o_content"));
+ }
+ def.then(function () {
+ self.report_widget.$el.html(self.html);
+ });
+ },
+ start: function () {
+ this.set_html();
+ return this._super();
+ },
+ // Fetches the html and is previous report.context if any,
+ // else create it
+ get_html: function () {
+ var self = this;
+ var defs = [];
+ return this._rpc({
+ model: this.given_context.model,
+ method: "get_html",
+ args: [self.given_context],
+ context: self.odoo_context,
+ }).then(function (result) {
+ self.html = result.html;
+ defs.push(self.update_cp());
+ return $.when.apply($, defs);
+ });
+ },
+ // Updates the control panel and render the elements that have yet
+ // to be rendered
+ update_cp: function () {
+ if (this.$buttons) {
+ var status = {
+ breadcrumbs: this.actionManager.get_breadcrumbs(),
+ cp_content: {$buttons: this.$buttons},
+ };
+ return this.update_control_panel(status);
+ }
+ },
+ do_show: function () {
+ this._super();
+ this.update_cp();
+ },
+ print: function () {
+ var self = this;
+ this._rpc({
+ model: this.given_context.model,
+ method: "print_report",
+ args: [this.given_context.active_id, "qweb-pdf"],
+ context: self.odoo_context,
+ }).then(function (result) {
+ self.do_action(result);
+ });
+ },
+ export: function () {
+ var self = this;
+ this._rpc({
+ model: this.given_context.model,
+ method: "print_report",
+ args: [this.given_context.active_id, "xlsx"],
+ context: self.odoo_context,
+ }).then(function (result) {
+ self.do_action(result);
+ });
+ },
+ canBeRemoved: function () {
+ return Promise.resolve();
+ },
+ });
+
+ core.action_registry.add("stock_card_report_backend", report_backend);
+ return report_backend;
+});
diff --git a/odex25_inventory/stock_card_report/tests/__init__.py b/odex25_inventory/stock_card_report/tests/__init__.py
new file mode 100644
index 000000000..6a89db00e
--- /dev/null
+++ b/odex25_inventory/stock_card_report/tests/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import test_stock_card_report
diff --git a/odex25_inventory/stock_card_report/tests/test_stock_card_report.py b/odex25_inventory/stock_card_report/tests/test_stock_card_report.py
new file mode 100644
index 000000000..02fa0c6d4
--- /dev/null
+++ b/odex25_inventory/stock_card_report/tests/test_stock_card_report.py
@@ -0,0 +1,257 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import logging
+import time
+from datetime import date
+
+from odoo.tests import common
+from odoo.tools import test_reports
+
+_logger = logging.getLogger(__name__)
+
+
+class TestStockCard(common.TransactionCase):
+ def setUp(self):
+ super().setUp()
+
+ # Create uom:
+ uom_id = self.ref("uom.product_uom_unit")
+
+ # Create products:
+ self.product_A = self.env["product.product"].create(
+ {
+ "name": "Product A",
+ "type": "product",
+ "uom_id": uom_id,
+ "uom_po_id": uom_id,
+ }
+ )
+
+ # Create location:
+ self.location_1 = self.env.ref("stock.stock_location_stock")
+ self.location_2 = self.env.ref("stock.stock_location_customers")
+
+ # Create operation type:
+ operation_type = self.env.ref("stock.picking_type_in")
+
+ # Create stock picking:
+ picking = self.env["stock.picking"].create(
+ {
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ "picking_type_id": operation_type.id,
+ }
+ )
+ self.env["stock.move"].create(
+ {
+ "name": self.product_A.name,
+ "product_id": self.product_A.id,
+ "product_uom_qty": 50.000,
+ "product_uom": self.product_A.uom_id.id,
+ "picking_id": picking.id,
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ }
+ )
+ picking.action_confirm()
+ picking.move_ids_without_package.quantity_done = 50.000
+ picking.button_validate()
+
+ self.model = self._getReportModel()
+
+ self.qweb_report_name = self._getQwebReportName()
+ self.xlsx_report_name = self._getXlsxReportName()
+ self.xlsx_action_name = self._getXlsxReportActionName()
+
+ self.report_title = self._getReportTitle()
+
+ self.base_filters = self._getBaseFilters()
+
+ self.report = self.model.create(self.base_filters)
+ self.report._compute_results()
+
+ def test_html(self):
+ test_reports.try_report(
+ self.env.cr,
+ self.env.uid,
+ self.qweb_report_name,
+ [self.report.id],
+ report_type="qweb-html",
+ )
+
+ def test_qweb(self):
+ test_reports.try_report(
+ self.env.cr,
+ self.env.uid,
+ self.qweb_report_name,
+ [self.report.id],
+ report_type="qweb-pdf",
+ )
+
+ def test_xlsx(self):
+ test_reports.try_report(
+ self.env.cr,
+ self.env.uid,
+ self.xlsx_report_name,
+ [self.report.id],
+ report_type="xlsx",
+ )
+
+ def test_print(self):
+ self.report.print_report("qweb")
+ self.report.print_report("xlsx")
+
+ def _getReportModel(self):
+ return self.env["report.stock.card.report"]
+
+ def _getQwebReportName(self):
+ return "stock_card_report.report_stock_card_report_pdf"
+
+ def _getXlsxReportName(self):
+ return "stock_card_report.report_stock_card_report_xlsx"
+
+ def _getXlsxReportActionName(self):
+ return "stock_card_report.action_report_stock_card_report_xlsx"
+
+ def _getReportTitle(self):
+ return "Stock Card Report"
+
+ def _getBaseFilters(self):
+ return {
+ "product_ids": [(6, 0, [self.product_A.id])],
+ "location_id": self.location_1.id,
+ }
+
+
+class TestStockCardReport(common.TransactionCase):
+ def setUp(self):
+ super().setUp()
+
+ # Create uom:
+ uom_id = self.ref("uom.product_uom_unit")
+
+ # Create products:
+ self.product_A = self.env["product.product"].create(
+ {
+ "name": "Product A",
+ "type": "product",
+ "uom_id": uom_id,
+ "uom_po_id": uom_id,
+ }
+ )
+ self.product_B = self.env["product.product"].create(
+ {
+ "name": "Product B",
+ "type": "product",
+ "uom_id": uom_id,
+ "uom_po_id": uom_id,
+ }
+ )
+
+ # Create location:
+ self.location_1 = self.env.ref("stock.stock_location_stock")
+ self.location_2 = self.env.ref("stock.stock_location_customers")
+
+ # Create operation type:
+ operation_type = self.env.ref("stock.picking_type_in")
+
+ # Create stock picking:
+ picking_1 = self.env["stock.picking"].create(
+ {
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ "picking_type_id": operation_type.id,
+ }
+ )
+ self.env["stock.move"].create(
+ {
+ "name": self.product_A.name,
+ "product_id": self.product_A.id,
+ "product_uom_qty": 50.000,
+ "product_uom": self.product_A.uom_id.id,
+ "picking_id": picking_1.id,
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ }
+ )
+ picking_1.action_confirm()
+ picking_1.move_ids_without_package.quantity_done = 50.000
+ picking_1.button_validate()
+
+ picking_2 = self.env["stock.picking"].create(
+ {
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ "picking_type_id": operation_type.id,
+ }
+ )
+ self.env["stock.move"].create(
+ {
+ "name": self.product_B.name,
+ "product_id": self.product_B.id,
+ "product_uom_qty": 100.000,
+ "product_uom": self.product_B.uom_id.id,
+ "picking_id": picking_2.id,
+ "location_id": self.location_2.id,
+ "location_dest_id": self.location_1.id,
+ }
+ )
+ picking_2.action_confirm()
+ picking_2.move_ids_without_package.quantity_done = 100.000
+ picking_2.button_validate()
+
+ def test_reports(self):
+ report = self.env["report.stock.card.report"].create(
+ {
+ "product_ids": [(6, 0, [self.product_A.id, self.product_B.id])],
+ "location_id": self.location_1.id,
+ }
+ )
+ report._compute_results()
+ report.print_report("qweb")
+ report.print_report("xlsx")
+
+ def test_get_report_html(self):
+ report = self.env["report.stock.card.report"].create(
+ {
+ "product_ids": [(6, 0, [self.product_A.id, self.product_B.id])],
+ "location_id": self.location_1.id,
+ }
+ )
+ report._compute_results()
+ report.get_html(given_context={"active_id": report.id})
+
+ def test_wizard_date_range(self):
+ date_range = self.env["date.range"]
+ self.type = self.env["date.range.type"].create(
+ {"name": "Month", "company_id": False, "allow_overlap": False}
+ )
+ dt = date_range.create(
+ {
+ "name": "FiscalYear",
+ "date_start": time.strftime("%Y-%m-01"),
+ "date_end": time.strftime("%Y-%m-28"),
+ "type_id": self.type.id,
+ }
+ )
+ wizard = self.env["stock.card.report.wizard"].create(
+ {
+ "date_range_id": dt.id,
+ "date_from": time.strftime("%Y-%m-28"),
+ "date_to": time.strftime("%Y-%m-01"),
+ "product_ids": [(6, 0, [self.product_A.id, self.product_B.id])],
+ "location_id": self.location_1.id,
+ }
+ )
+ wizard._onchange_date_range_id()
+ self.assertEqual(
+ wizard.date_from, date(date.today().year, date.today().month, 1)
+ )
+ self.assertEqual(
+ wizard.date_to, date(date.today().year, date.today().month, 28)
+ )
+ wizard._export("qweb-pdf")
+ wizard.button_export_html()
+ wizard.button_export_pdf()
+ wizard.button_export_xlsx()
diff --git a/odex25_inventory/stock_card_report/wizard/__init__.py b/odex25_inventory/stock_card_report/wizard/__init__.py
new file mode 100644
index 000000000..42562f3a3
--- /dev/null
+++ b/odex25_inventory/stock_card_report/wizard/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import stock_card_report_wizard
diff --git a/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard.py b/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard.py
new file mode 100644
index 000000000..b0639feca
--- /dev/null
+++ b/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard.py
@@ -0,0 +1,63 @@
+# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import api, fields, models
+from odoo.tools.safe_eval import safe_eval
+
+
+class StockCardReportWizard(models.TransientModel):
+ _name = "stock.card.report.wizard"
+ _description = "Stock Card Report Wizard"
+
+ date_range_id = fields.Many2one(comodel_name="date.range", string="Period")
+ date_from = fields.Date(string="Start Date")
+ date_to = fields.Date(string="End Date")
+ location_id = fields.Many2one(
+ comodel_name="stock.location", string="Location", required=True
+ )
+ product_ids = fields.Many2many(
+ comodel_name="product.product", string="Products", required=True
+ )
+
+ @api.onchange("date_range_id")
+ def _onchange_date_range_id(self):
+ self.date_from = self.date_range_id.date_start
+ self.date_to = self.date_range_id.date_end
+
+ def button_export_html(self):
+ self.ensure_one()
+ action = self.env.ref("stock_card_report.action_report_stock_card_report_html")
+ vals = action.sudo().read()[0]
+ context = vals.get("context", {})
+ if context:
+ context = safe_eval(context)
+ model = self.env["report.stock.card.report"]
+ report = model.create(self._prepare_stock_card_report())
+ context["active_id"] = report.id
+ context["active_ids"] = report.ids
+ vals["context"] = context
+ return vals
+
+ def button_export_pdf(self):
+ self.ensure_one()
+ report_type = "qweb-pdf"
+ return self._export(report_type)
+
+ def button_export_xlsx(self):
+ self.ensure_one()
+ report_type = "xlsx"
+ return self._export(report_type)
+
+ def _prepare_stock_card_report(self):
+ self.ensure_one()
+ return {
+ "date_from": self.date_from,
+ "date_to": self.date_to or fields.Date.context_today(self),
+ "product_ids": [(6, 0, self.product_ids.ids)],
+ "location_id": self.location_id.id,
+ }
+
+ def _export(self, report_type):
+ model = self.env["report.stock.card.report"]
+ report = model.create(self._prepare_stock_card_report())
+ return report.print_report(report_type)
diff --git a/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard_view.xml b/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard_view.xml
new file mode 100644
index 000000000..031ff3690
--- /dev/null
+++ b/odex25_inventory/stock_card_report/wizard/stock_card_report_wizard_view.xml
@@ -0,0 +1,60 @@
+
+
+
+ stock.card.report.wizard.form
+ stock.card.report.wizard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Stock Card
+ stock.card.report.wizard
+ form
+ new
+
+
+
+
+
diff --git a/odex25_inventory/stock_no_negative/README.rst b/odex25_inventory/stock_no_negative/README.rst
new file mode 100644
index 000000000..15ba8dacf
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/README.rst
@@ -0,0 +1,149 @@
+=======================
+Stock Disallow Negative
+=======================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:edaa5834a305284dada7a914038aacf5c37b9bf612cdc1fa875858368adb6965
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--workflow-lightgray.png?logo=github
+ :target: https://github.com/OCA/stock-logistics-workflow/tree/14.0/stock_no_negative
+ :alt: OCA/stock-logistics-workflow
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/stock-logistics-workflow-14-0/stock-logistics-workflow-14-0-stock_no_negative
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-workflow&target_branch=14.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+By default, Odoo allows negative stock. The advantage of negative stock
+is that, if some stock levels are wrong in the ERP, you will not be blocked
+when validating the picking for a customer... so you will still be able to
+ship the products on time (it's an example !). The problem is that, after you
+forced the stock level to negative, you are supposed to fix the stock level
+later via an inventory ; but this action is often forgotten by users,
+so you end up with negative stock levels in your ERP and it can stay like
+this forever (or at least until the next full inventory).
+
+If you disallow negative stock in Odoo with this module, you will be blocked
+when trying to validate a stock operation that will set the stock level of
+a product and/or location as negative. So you will have to fix the
+wrong stock level of that product without delay, in order to validate the
+stock operation in Odoo...you can't forget it anymore !
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Configuration
+=============
+
+By default, the stockable products will not be allowed to have a negative
+stock. If you want to make some exceptions for some products, product
+categories or locations, you can activate the option *Allow Negative Stock*:
+
+For products:
+
+#. Go to *Inventory / Master Data / Products* and in the
+ tab *General Information* activate this option.
+
+For product categories:
+
+#. Go to *Inventory / Configuration / Products / Product Categories*
+ and activate this option.
+
+For individual locations:
+
+#. Go to *Inventory / Configuration / Settings* and activate
+ the option *Storage Locations*.
+#. Go to *Inventory / Configuration / Warehouse Management / Locations* and
+ activate the option the option *Allow Negative Stock* for the locations you
+ choose.
+
+Usage
+=====
+
+When you validate a stock operation (a stock move, a picking,
+a manufacturing order, etc.) that will set the stock level of a
+stockable product as negative, you will be blocked by an error message.
+The consumable products can still have a negative stock level.
+
+Changelog
+=========
+
+14.0.1.0.0 (2020-12-14)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* [14.0][MIG] stock_no_negative
+
+13.0.1.0.0 (2020-01-03)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* [13.0][MIG] stock_no_negative
+ Remove all decorators @api.multi
+
+11.0.1.1.0 (2018-12-13)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* Add the ability to allow negative stock for individual stock locations.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Akretion
+
+Contributors
+~~~~~~~~~~~~
+
+* Alexis de Lattre
+* ForgeFlow S.L.
+ * Jordi Ballester
+* Serpent Consulting Services Pvt. Ltd.
+* Tecnativa
+ * Pedro M. Baeza
+* Spacefoot
+ * Quentin Delcourte
+* Vishnu Vanneri
+
+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-workflow `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/odex25_inventory/stock_no_negative/__init__.py b/odex25_inventory/stock_no_negative/__init__.py
new file mode 100644
index 000000000..31c468823
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/__init__.py
@@ -0,0 +1,5 @@
+# ?? 2015-2016 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import models
diff --git a/odex25_inventory/stock_no_negative/__manifest__.py b/odex25_inventory/stock_no_negative/__manifest__.py
new file mode 100644
index 000000000..492b12fa5
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/__manifest__.py
@@ -0,0 +1,17 @@
+# ?? 2015-2016 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+{
+ "name": "Stock Disallow Negative",
+ "version": "14.0.1.0.1",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "license": "AGPL-3",
+ "summary": "Disallow negative stock levels by default",
+ "author": "Akretion,Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/stock-logistics-workflow",
+ "depends": ["stock"],
+ "data": ["views/product_product_views.xml", "views/stock_location_views.xml"],
+ "installable": True,
+}
diff --git a/odex25_inventory/stock_no_negative/i18n/ar.po b/odex25_inventory/stock_no_negative/i18n/ar.po
new file mode 100644
index 000000000..af3355147
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/i18n/ar.po
@@ -0,0 +1,121 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_no_negative
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-02-26 09:53+0000\n"
+"PO-Revision-Date: 2019-02-26 09:53+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid " lot '%s'"
+msgstr " lote '%s'"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock
+msgid "Allow Negative Stock"
+msgstr "Permitir Stock Negativo"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"category. The options doesn't apply to products attached to sub-categories "
+"of this category."
+msgstr ""
+"Permitir niveles de stock negativos para los productos en stock adjuntos a "
+"esta categoría. Las opciones no se aplican a los productos adjuntos a "
+"subcategorías de esta categoría."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"location."
+msgstr ""
+"Permitir niveles de stock negativos para los productos en stock ubicados en "
+"esta ubicación."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock
+msgid ""
+"If this option is not active on this product nor on its product category and "
+"that this product is a stockable product, then the validation of the related "
+"stock moves will be blocked if the stock level becomes negative with the "
+"stock move."
+msgstr ""
+"Si esta opción no está activa en este producto ni en su categoría de "
+"producto y este producto es un producto almacenable, entonces la validación "
+"de los movimientos de stock relacionados se bloqueará si el nivel de stock "
+"se vuelve negativo con el movimiento de stock."
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_location
+msgid "Inventory Locations"
+msgstr "Ubicaciones de inventario"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_category
+msgid "Product Category"
+msgstr "Categoría de producto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_template
+msgid "Product Template"
+msgstr "Plantilla de producto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_quant
+msgid "Quants"
+msgstr "Quants"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid ""
+"You cannot validate this stock operation because the stock level of the "
+"product '%s'%s would become negative (%s) on the stock location '%s' and "
+"negative stock is not allowed for this product and/or location."
+msgstr ""
+"No se puede validar esta operación de stock porque el nivel de stock del "
+"producto '%s'%s se volvería negativo (%s) en la ubicación de stock '%s' y no "
+"se permite stock negativo para este producto y/o ubicación."
diff --git a/odex25_inventory/stock_no_negative/i18n/es.po b/odex25_inventory/stock_no_negative/i18n/es.po
new file mode 100644
index 000000000..af3355147
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/i18n/es.po
@@ -0,0 +1,121 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_no_negative
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-02-26 09:53+0000\n"
+"PO-Revision-Date: 2019-02-26 09:53+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid " lot '%s'"
+msgstr " lote '%s'"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock
+msgid "Allow Negative Stock"
+msgstr "Permitir Stock Negativo"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"category. The options doesn't apply to products attached to sub-categories "
+"of this category."
+msgstr ""
+"Permitir niveles de stock negativos para los productos en stock adjuntos a "
+"esta categoría. Las opciones no se aplican a los productos adjuntos a "
+"subcategorías de esta categoría."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"location."
+msgstr ""
+"Permitir niveles de stock negativos para los productos en stock ubicados en "
+"esta ubicación."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock
+msgid ""
+"If this option is not active on this product nor on its product category and "
+"that this product is a stockable product, then the validation of the related "
+"stock moves will be blocked if the stock level becomes negative with the "
+"stock move."
+msgstr ""
+"Si esta opción no está activa en este producto ni en su categoría de "
+"producto y este producto es un producto almacenable, entonces la validación "
+"de los movimientos de stock relacionados se bloqueará si el nivel de stock "
+"se vuelve negativo con el movimiento de stock."
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_location
+msgid "Inventory Locations"
+msgstr "Ubicaciones de inventario"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_category
+msgid "Product Category"
+msgstr "Categoría de producto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_template
+msgid "Product Template"
+msgstr "Plantilla de producto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_quant
+msgid "Quants"
+msgstr "Quants"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid ""
+"You cannot validate this stock operation because the stock level of the "
+"product '%s'%s would become negative (%s) on the stock location '%s' and "
+"negative stock is not allowed for this product and/or location."
+msgstr ""
+"No se puede validar esta operación de stock porque el nivel de stock del "
+"producto '%s'%s se volvería negativo (%s) en la ubicación de stock '%s' y no "
+"se permite stock negativo para este producto y/o ubicación."
diff --git a/odex25_inventory/stock_no_negative/i18n/fr.po b/odex25_inventory/stock_no_negative/i18n/fr.po
new file mode 100644
index 000000000..e07ee996c
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/i18n/fr.po
@@ -0,0 +1,125 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_no_negative
+#
+# Translators:
+# OCA Transbot , 2017
+# guillaume bauer , 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 11.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-02-28 08:51+0000\n"
+"PO-Revision-Date: 2023-10-28 21:32+0000\n"
+"Last-Translator: Alexis de Lattre \n"
+"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid " lot '%s'"
+msgstr " lot '%s'"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock
+msgid "Allow Negative Stock"
+msgstr "Autoriser le stock négatif"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"category. The options doesn't apply to products attached to sub-categories "
+"of this category."
+msgstr ""
+"Autorise les niveaux de stock négatifs pour les articles stockables de cette "
+"catégorie. Cette option ne s'applique pas aux articles appartenant à des "
+"sous-catégories de cette catégorie."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"location."
+msgstr ""
+"Autorise les niveaux de stock négatifs pour les articles stockables sur cet "
+"emplacement."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__display_name
+msgid "Display Name"
+msgstr "Nom affiché"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock
+msgid ""
+"If this option is not active on this product nor on its product category and "
+"that this product is a stockable product, then the validation of the related "
+"stock moves will be blocked if the stock level becomes negative with the "
+"stock move."
+msgstr ""
+"Si cette option n'est pas cochée sur cet article ni sur la catégorie à "
+"laquelle il est rattaché et que cet article est un produit stockable, alors "
+"la validation des mouvements de stock sera bloquée si le niveau de stock "
+"devient négatif suite à ce mouvement de stock."
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_location
+msgid "Inventory Locations"
+msgstr "Emplacements de stock"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant____last_update
+msgid "Last Modified on"
+msgstr "Dernière modification le"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_category
+msgid "Product Category"
+msgstr "Catégorie d'article"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_template
+msgid "Product Template"
+msgstr "Modèle d'article"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_quant
+msgid "Quants"
+msgstr "Quants"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid ""
+"You cannot validate this stock operation because the stock level of the "
+"product '%s'%s would become negative (%s) on the stock location '%s' and "
+"negative stock is not allowed for this product and/or location."
+msgstr ""
+"Impossible de valider ce mouvement de stock car le niveau de stock de "
+"l'article %s'%s deviendrait négatif (%s) sur l'emplacement du stock '%s'. Or "
+"les stocks négatifs sont interdits pour cet article sur cet emplacement."
diff --git a/odex25_inventory/stock_no_negative/i18n/it.po b/odex25_inventory/stock_no_negative/i18n/it.po
new file mode 100644
index 000000000..33442b045
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/i18n/it.po
@@ -0,0 +1,120 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_no_negative
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2023-12-06 09:36+0000\n"
+"Last-Translator: mymage \n"
+"Language-Team: none\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid " lot '%s'"
+msgstr " lotto '%s'"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock
+msgid "Allow Negative Stock"
+msgstr "Consenti giacenze negative"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"category. The options doesn't apply to products attached to sub-categories "
+"of this category."
+msgstr ""
+"Consente giacenze negative per prodotti stoccabili associati a questa "
+"categoria. Questa opzione non si applica ai prodotti associati alle sotto "
+"categorie della categoria."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"location."
+msgstr ""
+"Consente giacenze negative per prodotti stoccabili associati a questa "
+"ubicazione."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__display_name
+msgid "Display Name"
+msgstr "Nome visualizzato"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__id
+msgid "ID"
+msgstr "ID"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock
+msgid ""
+"If this option is not active on this product nor on its product category and "
+"that this product is a stockable product, then the validation of the related "
+"stock moves will be blocked if the stock level becomes negative with the "
+"stock move."
+msgstr ""
+"Se questa opzione non è attiva nè in questo prodotto nè nella sua categoria "
+"e il prodotto è stoccabile, se la giacenza diventa negativa con il movimento "
+"di magazzino allora la validazione del relativo movimento verrà bloccata ."
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_location
+msgid "Inventory Locations"
+msgstr "Ubicazioni di inventario"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant____last_update
+msgid "Last Modified on"
+msgstr "Ultima modifica il"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_category
+msgid "Product Category"
+msgstr "Categoria prodotto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_template
+msgid "Product Template"
+msgstr "Modello prodotto"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_quant
+msgid "Quants"
+msgstr "Quanti"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid ""
+"You cannot validate this stock operation because the stock level of the "
+"product '%s'%s would become negative (%s) on the stock location '%s' and "
+"negative stock is not allowed for this product and/or location."
+msgstr ""
+"Questa operazione di magazzino non può essere validata perché la giacenza "
+"del prodotto '%s'%s diventerebbe negativa (%s) nell'ubicazione '%s' e per "
+"questo prodotto e/o ubicazione non è consentata la giacenza negativa."
diff --git a/odex25_inventory/stock_no_negative/i18n/pt.po b/odex25_inventory/stock_no_negative/i18n/pt.po
new file mode 100644
index 000000000..1ee59a681
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/i18n/pt.po
@@ -0,0 +1,121 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_no_negative
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2021-07-08 20:48+0000\n"
+"Last-Translator: Daniel Reis \n"
+"Language-Team: none\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.3.2\n"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid " lot '%s'"
+msgstr " lote '%s'"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__allow_negative_stock
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__allow_negative_stock
+msgid "Allow Negative Stock"
+msgstr "Permitir Stock Negativo"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_category__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"category. The options doesn't apply to products attached to sub-categories "
+"of this category."
+msgstr ""
+"Permitir níveis de stock negativos para os produtos armazenáveis ligados a "
+"esta categoria. As opções não se aplicam aos produtos vinculados a sub-"
+"categorias desta categoria."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_stock_location__allow_negative_stock
+msgid ""
+"Allow negative stock levels for the stockable products attached to this "
+"location."
+msgstr ""
+"Permitir níveis de stock negativos para os produtos armazenáveis anexados a "
+"este local."
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__display_name
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__display_name
+msgid "Display Name"
+msgstr "Nome a Apresentar"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location__id
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant__id
+msgid "ID"
+msgstr "ID"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,help:stock_no_negative.field_product_product__allow_negative_stock
+#: model:ir.model.fields,help:stock_no_negative.field_product_template__allow_negative_stock
+msgid ""
+"If this option is not active on this product nor on its product category and "
+"that this product is a stockable product, then the validation of the related "
+"stock moves will be blocked if the stock level becomes negative with the "
+"stock move."
+msgstr ""
+"Se esta opção não estiver ativa neste produto nem na sua categoria e se este "
+"for um produto armazenável, então a validação dos movimentos de stock "
+"relacionados será bloqueada se o nível de stock se tornar negativo após o "
+"movimento de stock."
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_location
+msgid "Inventory Locations"
+msgstr "Localizações de Inventário"
+
+#. module: stock_no_negative
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_category____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_product_template____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_location____last_update
+#: model:ir.model.fields,field_description:stock_no_negative.field_stock_quant____last_update
+msgid "Last Modified on"
+msgstr "Última Modificação em"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_category
+msgid "Product Category"
+msgstr "Categoria do Artigo"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_product_template
+msgid "Product Template"
+msgstr "Modelo de Artigo"
+
+#. module: stock_no_negative
+#: model:ir.model,name:stock_no_negative.model_stock_quant
+msgid "Quants"
+msgstr "Quants"
+
+#. module: stock_no_negative
+#: code:addons/stock_no_negative/models/stock_quant.py:0
+#, python-format
+msgid ""
+"You cannot validate this stock operation because the stock level of the "
+"product '%s'%s would become negative (%s) on the stock location '%s' and "
+"negative stock is not allowed for this product and/or location."
+msgstr ""
+"Não pode validar esta operação de stock porque o nível de stock do produto "
+"'%s'%s tornar-se-ia negativo (%s) na localização '%s' e um stock negativo "
+"não é permitido para este produto e/ou local."
diff --git a/odex25_inventory/stock_no_negative/models/__init__.py b/odex25_inventory/stock_no_negative/models/__init__.py
new file mode 100644
index 000000000..8d84cb148
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/models/__init__.py
@@ -0,0 +1,7 @@
+# ?? 2015-2016 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import product
+from . import stock_quant
+from . import stock_location
diff --git a/odex25_inventory/stock_no_negative/models/product.py b/odex25_inventory/stock_no_negative/models/product.py
new file mode 100644
index 000000000..f95d7f4f1
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/models/product.py
@@ -0,0 +1,28 @@
+# ?? 2015-2016 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class ProductCategory(models.Model):
+ _inherit = "product.category"
+
+ allow_negative_stock = fields.Boolean(
+ string="Allow Negative Stock",
+ help="Allow negative stock levels for the stockable products "
+ "attached to this category. The options doesn't apply to products "
+ "attached to sub-categories of this category.",
+ )
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ allow_negative_stock = fields.Boolean(
+ string="Allow Negative Stock",
+ help="If this option is not active on this product nor on its "
+ "product category and that this product is a stockable product, "
+ "then the validation of the related stock moves will be blocked if "
+ "the stock level becomes negative with the stock move.",
+ )
diff --git a/odex25_inventory/stock_no_negative/models/stock_location.py b/odex25_inventory/stock_no_negative/models/stock_location.py
new file mode 100644
index 000000000..648fe6985
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/models/stock_location.py
@@ -0,0 +1,15 @@
+# ?? 2018 ForgeFlow (https://www.forgeflow.com)
+# @author Jordi Ballester
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class StockLocation(models.Model):
+ _inherit = "stock.location"
+
+ allow_negative_stock = fields.Boolean(
+ string="Allow Negative Stock",
+ help="Allow negative stock levels for the stockable products "
+ "attached to this location.",
+ )
diff --git a/odex25_inventory/stock_no_negative/models/stock_quant.py b/odex25_inventory/stock_no_negative/models/stock_quant.py
new file mode 100644
index 000000000..817c56991
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/models/stock_quant.py
@@ -0,0 +1,51 @@
+# ?? 2015-2017 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import _, api, models
+from odoo.exceptions import ValidationError
+from odoo.tools import config, float_compare
+
+
+class StockQuant(models.Model):
+ _inherit = "stock.quant"
+
+ @api.constrains("product_id", "quantity")
+ def check_negative_qty(self):
+ p = self.env["decimal.precision"].precision_get("Product Unit of Measure")
+ check_negative_qty = (
+ config["test_enable"] and self.env.context.get("test_stock_no_negative")
+ ) or not config["test_enable"]
+ if not check_negative_qty:
+ return
+
+ for quant in self:
+ disallowed_by_product = (
+ not quant.product_id.allow_negative_stock
+ and not quant.product_id.categ_id.allow_negative_stock
+ )
+ disallowed_by_location = not quant.location_id.allow_negative_stock
+ if (
+ float_compare(quant.quantity, 0, precision_digits=p) == -1
+ and quant.product_id.type == "product"
+ and quant.location_id.usage in ["internal", "transit"]
+ and disallowed_by_product
+ and disallowed_by_location
+ ):
+ msg_add = ""
+ if quant.lot_id:
+ msg_add = _(" lot '%s'") % quant.lot_id.name_get()[0][1]
+ raise ValidationError(
+ _(
+ "You cannot validate this stock operation because the "
+ "stock level of the product '%s'%s would become negative "
+ "(%s) on the stock location '%s' and negative stock is "
+ "not allowed for this product and/or location."
+ )
+ % (
+ quant.product_id.display_name,
+ msg_add,
+ quant.quantity,
+ quant.location_id.complete_name,
+ )
+ )
diff --git a/odex25_inventory/stock_no_negative/readme/CONFIGURE.rst b/odex25_inventory/stock_no_negative/readme/CONFIGURE.rst
new file mode 100644
index 000000000..1ecf43331
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/readme/CONFIGURE.rst
@@ -0,0 +1,21 @@
+By default, the stockable products will not be allowed to have a negative
+stock. If you want to make some exceptions for some products, product
+categories or locations, you can activate the option *Allow Negative Stock*:
+
+For products:
+
+#. Go to *Inventory / Master Data / Products* and in the
+ tab *General Information* activate this option.
+
+For product categories:
+
+#. Go to *Inventory / Configuration / Products / Product Categories*
+ and activate this option.
+
+For individual locations:
+
+#. Go to *Inventory / Configuration / Settings* and activate
+ the option *Storage Locations*.
+#. Go to *Inventory / Configuration / Warehouse Management / Locations* and
+ activate the option the option *Allow Negative Stock* for the locations you
+ choose.
diff --git a/odex25_inventory/stock_no_negative/readme/CONTRIBUTORS.rst b/odex25_inventory/stock_no_negative/readme/CONTRIBUTORS.rst
new file mode 100644
index 000000000..ea21869eb
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/readme/CONTRIBUTORS.rst
@@ -0,0 +1,9 @@
+* Alexis de Lattre
+* ForgeFlow S.L.
+ * Jordi Ballester
+* Serpent Consulting Services Pvt. Ltd.
+* Tecnativa
+ * Pedro M. Baeza
+* Spacefoot
+ * Quentin Delcourte
+* Vishnu Vanneri
diff --git a/odex25_inventory/stock_no_negative/readme/DESCRIPTION.rst b/odex25_inventory/stock_no_negative/readme/DESCRIPTION.rst
new file mode 100644
index 000000000..9144dac41
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/readme/DESCRIPTION.rst
@@ -0,0 +1,14 @@
+By default, Odoo allows negative stock. The advantage of negative stock
+is that, if some stock levels are wrong in the ERP, you will not be blocked
+when validating the picking for a customer... so you will still be able to
+ship the products on time (it's an example !). The problem is that, after you
+forced the stock level to negative, you are supposed to fix the stock level
+later via an inventory ; but this action is often forgotten by users,
+so you end up with negative stock levels in your ERP and it can stay like
+this forever (or at least until the next full inventory).
+
+If you disallow negative stock in Odoo with this module, you will be blocked
+when trying to validate a stock operation that will set the stock level of
+a product and/or location as negative. So you will have to fix the
+wrong stock level of that product without delay, in order to validate the
+stock operation in Odoo...you can't forget it anymore !
diff --git a/odex25_inventory/stock_no_negative/readme/HISTORY.rst b/odex25_inventory/stock_no_negative/readme/HISTORY.rst
new file mode 100644
index 000000000..e937e90e5
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/readme/HISTORY.rst
@@ -0,0 +1,15 @@
+14.0.1.0.0 (2020-12-14)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* [14.0][MIG] stock_no_negative
+
+13.0.1.0.0 (2020-01-03)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* [13.0][MIG] stock_no_negative
+ Remove all decorators @api.multi
+
+11.0.1.1.0 (2018-12-13)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+* Add the ability to allow negative stock for individual stock locations.
diff --git a/odex25_inventory/stock_no_negative/readme/USAGE.rst b/odex25_inventory/stock_no_negative/readme/USAGE.rst
new file mode 100644
index 000000000..004f5169f
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/readme/USAGE.rst
@@ -0,0 +1,4 @@
+When you validate a stock operation (a stock move, a picking,
+a manufacturing order, etc.) that will set the stock level of a
+stockable product as negative, you will be blocked by an error message.
+The consumable products can still have a negative stock level.
diff --git a/odex25_inventory/stock_no_negative/static/description/icon.png b/odex25_inventory/stock_no_negative/static/description/icon.png
new file mode 100644
index 000000000..3a0328b51
Binary files /dev/null and b/odex25_inventory/stock_no_negative/static/description/icon.png differ
diff --git a/odex25_inventory/stock_no_negative/static/description/index.html b/odex25_inventory/stock_no_negative/static/description/index.html
new file mode 100644
index 000000000..f2294591d
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/static/description/index.html
@@ -0,0 +1,502 @@
+
+
+
+
+
+
+Stock Disallow Negative
+
+
+
+
+
Stock Disallow Negative
+
+
+
+
By default, Odoo allows negative stock. The advantage of negative stock
+is that, if some stock levels are wrong in the ERP, you will not be blocked
+when validating the picking for a customer… so you will still be able to
+ship the products on time (it’s an example !). The problem is that, after you
+forced the stock level to negative, you are supposed to fix the stock level
+later via an inventory ; but this action is often forgotten by users,
+so you end up with negative stock levels in your ERP and it can stay like
+this forever (or at least until the next full inventory).
+
If you disallow negative stock in Odoo with this module, you will be blocked
+when trying to validate a stock operation that will set the stock level of
+a product and/or location as negative. So you will have to fix the
+wrong stock level of that product without delay, in order to validate the
+stock operation in Odoo…you can’t forget it anymore !
+
Table of contents
+
+
+
+
By default, the stockable products will not be allowed to have a negative
+stock. If you want to make some exceptions for some products, product
+categories or locations, you can activate the option Allow Negative Stock :
+
For products:
+
+Go to Inventory / Master Data / Products and in the
+tab General Information activate this option.
+
+
For product categories:
+
+Go to Inventory / Configuration / Products / Product Categories
+and activate this option.
+
+
For individual locations:
+
+Go to Inventory / Configuration / Settings and activate
+the option Storage Locations .
+Go to Inventory / Configuration / Warehouse Management / Locations and
+activate the option the option Allow Negative Stock for the locations you
+choose.
+
+
+
+
+
When you validate a stock operation (a stock move, a picking,
+a manufacturing order, etc.) that will set the stock level of a
+stockable product as negative, you will be blocked by an error message.
+The consumable products can still have a negative stock level.
+
+
+
+
+
+
+[14.0][MIG] stock_no_negative
+
+
+
+
+
+[13.0][MIG] stock_no_negative
+Remove all decorators @api.multi
+
+
+
+
+
+Add the ability to allow negative stock for individual stock locations.
+
+
+
+
+
+
Bugs are tracked on GitHub Issues .
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+feedback .
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
This module is maintained by the OCA.
+
+
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-workflow project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute .
+
+
+
+
+
diff --git a/odex25_inventory/stock_no_negative/tests/__init__.py b/odex25_inventory/stock_no_negative/tests/__init__.py
new file mode 100644
index 000000000..2195ba144
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/tests/__init__.py
@@ -0,0 +1,8 @@
+# ?? 2015-2016 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre
+# ?? 2016 ForgeFlow S.L.
+# (http://www.forgeflow.com)
+# ?? 2016 Serpent Consulting Services Pvt. Ltd. ()
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from . import test_stock_no_negative
diff --git a/odex25_inventory/stock_no_negative/tests/test_stock_no_negative.py b/odex25_inventory/stock_no_negative/tests/test_stock_no_negative.py
new file mode 100644
index 000000000..e7175c141
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/tests/test_stock_no_negative.py
@@ -0,0 +1,109 @@
+# Copyright 2015-2016 Akretion (http://www.akretion.com) - Alexis de Lattre
+# Copyright 2016 ForgeFlow (http://www.forgeflow.com)
+# Copyright 2016 Serpent Consulting Services ()
+# Copyright 2018 Tecnativa - Pedro M. Baeza
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+
+from odoo.exceptions import ValidationError
+from odoo.tests.common import TransactionCase
+
+
+class TestStockNoNegative(TransactionCase):
+ at_install = False
+ post_install = True
+
+ def setUp(self):
+ super(TestStockNoNegative, self).setUp()
+ self.product_model = self.env["product.product"]
+ self.product_ctg_model = self.env["product.category"]
+ self.picking_type_id = self.env.ref("stock.picking_type_out")
+ self.location_id = self.env.ref("stock.stock_location_stock")
+ self.location_dest_id = self.env.ref("stock.stock_location_customers")
+ # Create product category
+ self.product_ctg = self._create_product_category()
+ # Create a Product
+ self.product = self._create_product("test_product1")
+ self._create_picking()
+
+ def _create_product_category(self):
+ product_ctg = self.product_ctg_model.create(
+ {"name": "test_product_ctg", "allow_negative_stock": False}
+ )
+ return product_ctg
+
+ def _create_product(self, name):
+ product = self.product_model.create(
+ {
+ "name": name,
+ "categ_id": self.product_ctg.id,
+ "type": "product",
+ "allow_negative_stock": False,
+ }
+ )
+ return product
+
+ def _create_picking(self):
+ self.stock_picking = (
+ self.env["stock.picking"]
+ .with_context(test_stock_no_negative=True)
+ .create(
+ {
+ "picking_type_id": self.picking_type_id.id,
+ "move_type": "direct",
+ "location_id": self.location_id.id,
+ "location_dest_id": self.location_dest_id.id,
+ }
+ )
+ )
+
+ self.stock_move = self.env["stock.move"].create(
+ {
+ "name": "Test Move",
+ "product_id": self.product.id,
+ "product_uom_qty": 100.0,
+ "product_uom": self.product.uom_id.id,
+ "picking_id": self.stock_picking.id,
+ "state": "draft",
+ "location_id": self.location_id.id,
+ "location_dest_id": self.location_dest_id.id,
+ "quantity_done": 100.0,
+ }
+ )
+
+ def test_check_constrains(self):
+ """Assert that constraint is raised when user
+ tries to validate the stock operation which would
+ make the stock level of the product negative"""
+ self.stock_picking.action_confirm()
+ with self.assertRaises(ValidationError):
+ self.stock_picking.button_validate()
+
+ def test_true_allow_negative_stock_product(self):
+ """Assert that negative stock levels are allowed when
+ the allow_negative_stock is set active in the product"""
+ self.product.allow_negative_stock = True
+ self.stock_picking.action_confirm()
+ self.stock_picking.button_validate()
+ quant = self.env["stock.quant"].search(
+ [
+ ("product_id", "=", self.product.id),
+ ("location_id", "=", self.location_id.id),
+ ]
+ )
+ self.assertEqual(quant.quantity, -100)
+
+ def test_true_allow_negative_stock_location(self):
+ """Assert that negative stock levels are allowed when
+ the allow_negative_stock is set active in the product"""
+ self.product.allow_negative_stock = False
+ self.location_id.allow_negative_stock = True
+ self.stock_picking.action_confirm()
+ self.stock_picking.button_validate()
+ quant = self.env["stock.quant"].search(
+ [
+ ("product_id", "=", self.product.id),
+ ("location_id", "=", self.location_id.id),
+ ]
+ )
+ self.assertEqual(quant.quantity, -100)
diff --git a/odex25_inventory/stock_no_negative/views/product_product_views.xml b/odex25_inventory/stock_no_negative/views/product_product_views.xml
new file mode 100644
index 000000000..3e1bfafd3
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/views/product_product_views.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ stock_no_negative.product.template.form
+ product.template
+
+
+
+
+
+
+
+
+ stock_no_negative.product.category.form
+ product.category
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/stock_no_negative/views/stock_location_views.xml b/odex25_inventory/stock_no_negative/views/stock_location_views.xml
new file mode 100644
index 000000000..a112be2f1
--- /dev/null
+++ b/odex25_inventory/stock_no_negative/views/stock_location_views.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ stock.location.form.allow_negative_stock
+ stock.location
+
+
+
+
+
+
+
+
diff --git a/odex25_inventory/stock_quantity_history_location/README.rst b/odex25_inventory/stock_quantity_history_location/README.rst
new file mode 100644
index 000000000..a9998471c
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/README.rst
@@ -0,0 +1,82 @@
+===============================
+Stock Quantity History Location
+===============================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:7f482ff9521e3dbc50c6baff32d9b81ef29ae306265e2f1f68cb22718e0fe70c
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |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-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--reporting-lightgray.png?logo=github
+ :target: https://github.com/OCA/stock-logistics-reporting/tree/14.0/stock_quantity_history_location
+ :alt: OCA/stock-logistics-reporting
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/stock-logistics-reporting-14-0/stock-logistics-reporting-14-0-stock_quantity_history_location
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-reporting&target_branch=14.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module allows to run an Inventory report or Inventory Valuation
+report by location, for a past date or for current date.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+* Go to: *Inventory / Reporting / Inventory or Inventory Valuation*
+* Filter by location
+* **Optionally: Mark if you want to include child location**
+* Choose a moment in time:
+ * Current Inventory
+ * At a Specific Date
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* ForgeFlow
+
+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-reporting `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/odex25_inventory/stock_quantity_history_location/__init__.py b/odex25_inventory/stock_quantity_history_location/__init__.py
new file mode 100644
index 000000000..5cb1c4914
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/__init__.py
@@ -0,0 +1 @@
+from . import wizards
diff --git a/odex25_inventory/stock_quantity_history_location/__manifest__.py b/odex25_inventory/stock_quantity_history_location/__manifest__.py
new file mode 100644
index 000000000..c2975584a
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/__manifest__.py
@@ -0,0 +1,15 @@
+# Copyright 2019 ForgeFlow S.L.
+# Copyright 2019 Aleph Objects, Inc.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+{
+ "name": "Stock Quantity History Location",
+ "summary": "Provides stock quantity by location on past date",
+ 'category': 'Odex25-Inventory/Odex25-Inventory',
+ "version": "14.0.1.0.0",
+ "license": "AGPL-3",
+ "author": "ForgeFlow," "Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/stock-logistics-reporting",
+ "depends": ["stock"],
+ "data": ["wizards/stock_quantity_history.xml"],
+}
diff --git a/odex25_inventory/stock_quantity_history_location/i18n/it.po b/odex25_inventory/stock_quantity_history_location/i18n/it.po
new file mode 100644
index 000000000..5d146d523
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/i18n/it.po
@@ -0,0 +1,50 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_quantity_history_location
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 13.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2023-11-24 17:35+0000\n"
+"Last-Translator: mymage \n"
+"Language-Team: none\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.17\n"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__display_name
+msgid "Display Name"
+msgstr "Nome visualizzato"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__id
+msgid "ID"
+msgstr "ID"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__include_child_locations
+msgid "Include child locations"
+msgstr "Includere ubicazioni figlie"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history____last_update
+msgid "Last Modified on"
+msgstr "Ultima modifica il"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__location_id
+msgid "Location"
+msgstr "Ubicazione"
+
+#. module: stock_quantity_history_location
+#: model:ir.model,name:stock_quantity_history_location.model_stock_quantity_history
+msgid "Stock Quantity History"
+msgstr "Storico quantità giacenza"
+
+#~ msgid "Inventory at Date & Location"
+#~ msgstr "Inventario alla Data e Locazione"
diff --git a/odex25_inventory/stock_quantity_history_location/i18n/ja.po b/odex25_inventory/stock_quantity_history_location/i18n/ja.po
new file mode 100644
index 000000000..f73cf89c4
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/i18n/ja.po
@@ -0,0 +1,47 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_quantity_history_location
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2022-01-21 10:40+0000\n"
+"Last-Translator: kakurai8 \n"
+"Language-Team: none\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 4.3.2\n"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__display_name
+msgid "Display Name"
+msgstr "表示名"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__id
+msgid "ID"
+msgstr "ID"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__include_child_locations
+msgid "Include child locations"
+msgstr "子ロケーションを含める"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history____last_update
+msgid "Last Modified on"
+msgstr "最終更新日"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__location_id
+msgid "Location"
+msgstr "ロケーション"
+
+#. module: stock_quantity_history_location
+#: model:ir.model,name:stock_quantity_history_location.model_stock_quantity_history
+msgid "Stock Quantity History"
+msgstr "在庫数量履歴"
diff --git a/odex25_inventory/stock_quantity_history_location/i18n/ro.po b/odex25_inventory/stock_quantity_history_location/i18n/ro.po
new file mode 100644
index 000000000..0f45d5697
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/i18n/ro.po
@@ -0,0 +1,52 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_quantity_history_location
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 13.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2020-09-29 11:00+0000\n"
+"Last-Translator: erik-bzcl \n"
+"Language-Team: none\n"
+"Language: ro\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
+"20)) ? 1 : 2;\n"
+"X-Generator: Weblate 3.10\n"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__include_child_locations
+msgid "Include child locations"
+msgstr "Inclusiv sublocaţii"
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: stock_quantity_history_location
+#: model:ir.model.fields,field_description:stock_quantity_history_location.field_stock_quantity_history__location_id
+msgid "Location"
+msgstr "Locaţia"
+
+#. module: stock_quantity_history_location
+#: model:ir.model,name:stock_quantity_history_location.model_stock_quantity_history
+msgid "Stock Quantity History"
+msgstr "Stoc cantitativ istoric"
+
+#, python-format
+#~ msgid "Inventory at Date & Location"
+#~ msgstr "Stoc în data & locaţia"
diff --git a/odex25_inventory/stock_quantity_history_location/readme/CONSTRIBUTORS.rst b/odex25_inventory/stock_quantity_history_location/readme/CONSTRIBUTORS.rst
new file mode 100644
index 000000000..6860affe2
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/readme/CONSTRIBUTORS.rst
@@ -0,0 +1 @@
+* Jordi Ballester Alomar
diff --git a/odex25_inventory/stock_quantity_history_location/readme/DESCRIPTION.rst b/odex25_inventory/stock_quantity_history_location/readme/DESCRIPTION.rst
new file mode 100644
index 000000000..9b1d7c478
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/readme/DESCRIPTION.rst
@@ -0,0 +1,2 @@
+This module allows to run an Inventory report or Inventory Valuation
+report by location, for a past date or for current date.
diff --git a/odex25_inventory/stock_quantity_history_location/readme/USAGE.rst b/odex25_inventory/stock_quantity_history_location/readme/USAGE.rst
new file mode 100644
index 000000000..9037ce543
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/readme/USAGE.rst
@@ -0,0 +1,6 @@
+* Go to: *Inventory / Reporting / Inventory or Inventory Valuation*
+* Filter by location
+* **Optionally: Mark if you want to include child location**
+* Choose a moment in time:
+ * Current Inventory
+ * At a Specific Date
diff --git a/odex25_inventory/stock_quantity_history_location/static/description/icon.png b/odex25_inventory/stock_quantity_history_location/static/description/icon.png
new file mode 100644
index 000000000..3a0328b51
Binary files /dev/null and b/odex25_inventory/stock_quantity_history_location/static/description/icon.png differ
diff --git a/odex25_inventory/stock_quantity_history_location/static/description/index.html b/odex25_inventory/stock_quantity_history_location/static/description/index.html
new file mode 100644
index 000000000..35e7b8968
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/static/description/index.html
@@ -0,0 +1,433 @@
+
+
+
+
+
+
+Stock Quantity History Location
+
+
+
+
+
Stock Quantity History Location
+
+
+
+
This module allows to run an Inventory report or Inventory Valuation
+report by location, for a past date or for current date.
+
Table of contents
+
+
+
+
+Go to: Inventory / Reporting / Inventory or Inventory Valuation
+Filter by location
+Optionally: Mark if you want to include child location
+
+Choose a moment in time:
+
+Current Inventory
+At a Specific Date
+
+
+
+
+
+
+
+
+
Bugs are tracked on GitHub Issues .
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+feedback .
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
This module is maintained by the OCA.
+
+
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-reporting project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute .
+
+
+
+
+
diff --git a/odex25_inventory/stock_quantity_history_location/static/src/js/inventory_report.js b/odex25_inventory/stock_quantity_history_location/static/src/js/inventory_report.js
new file mode 100644
index 000000000..434ae0875
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/static/src/js/inventory_report.js
@@ -0,0 +1,23 @@
+/* eslint-disable no-unused-vars */
+odoo.define("stock.InventoryReportLocationListController", function (require) {
+ "use strict";
+
+ var core = require("web.core");
+ var InventoryReportListController = require("stock.InventoryReportListController");
+
+ var qweb = core.qweb;
+ var _t = core._t;
+
+ var InventoryReportLocationListController = InventoryReportListController.include({
+ renderButtons: function ($node) {
+ this._super.apply(this, arguments);
+ if (this.context.no_at_date) {
+ return;
+ }
+ if (this.modelName === "stock.quant" && this.$buttons.length) {
+ this.$buttons[0].firstElementChild.innerHTML =
+ "Inventory at Date & Location";
+ }
+ },
+ });
+});
diff --git a/odex25_inventory/stock_quantity_history_location/tests/__init__.py b/odex25_inventory/stock_quantity_history_location/tests/__init__.py
new file mode 100644
index 000000000..6be307f3d
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_stock_quantity_history_location
diff --git a/odex25_inventory/stock_quantity_history_location/tests/common.py b/odex25_inventory/stock_quantity_history_location/tests/common.py
new file mode 100644
index 000000000..b02962126
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/tests/common.py
@@ -0,0 +1,28 @@
+# Copyright 2021 Tecnativa - Víctor Martínez
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.tests.common import SavepointCase
+
+
+class TestCommon(SavepointCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ def _create_stock_move(self, location_dest_id, qty):
+ move = self.env["stock.move"].create(
+ {
+ "name": "Stock move in",
+ "location_id": self.supplier_location.id,
+ "location_dest_id": location_dest_id.id,
+ "product_id": self.product.id,
+ "product_uom": self.product.uom_id.id,
+ "product_uom_qty": qty,
+ }
+ )
+ move._action_confirm()
+ move._action_assign()
+ move_line = move.move_line_ids[0]
+ move_line.qty_done = qty
+ move._action_done()
+ return move
diff --git a/odex25_inventory/stock_quantity_history_location/tests/test_stock_quantity_history_location.py b/odex25_inventory/stock_quantity_history_location/tests/test_stock_quantity_history_location.py
new file mode 100644
index 000000000..f6038a0ec
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/tests/test_stock_quantity_history_location.py
@@ -0,0 +1,65 @@
+# Copyright 2019 ForgeFlow S.L.
+# Copyright 2021 Tecnativa - Víctor Martínez
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import fields
+
+from .common import TestCommon
+
+
+class TestStockQuantityHistoryLocation(TestCommon):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
+ cls.main_company = cls.env.ref("base.main_company")
+ cls.product = cls.env.ref("product.product_product_3")
+ cls.test_stock_loc = cls.env["stock.location"].create(
+ {
+ "usage": "internal",
+ "name": "Test Stock Location",
+ "company_id": cls.main_company.id,
+ }
+ )
+ cls.child_test_stock_loc = cls.env["stock.location"].create(
+ {
+ "usage": "internal",
+ "name": "Child Test Stock Location",
+ "location_id": cls.test_stock_loc.id,
+ "company_id": cls.main_company.id,
+ }
+ )
+ cls._create_stock_move(cls, location_dest_id=cls.child_test_stock_loc, qty=100)
+
+ def test_wizard_past_date(self):
+ wizard = self.env["stock.quantity.history"].create(
+ {
+ "location_id": self.test_stock_loc.id,
+ "include_child_locations": True,
+ "inventory_datetime": fields.Datetime.now(),
+ }
+ )
+ action = wizard.with_context(company_owned=True).open_at_date()
+ self.assertEquals(
+ self.product.with_context(action["context"]).qty_available, 100.0
+ )
+ self.assertEquals(
+ self.product.with_context(
+ location=self.child_test_stock_loc.id, to_date="2019-08-10"
+ ).qty_available,
+ 0.0,
+ )
+
+ def test_wizard_current(self):
+ wizard = self.env["stock.quantity.history"].create(
+ {"location_id": self.test_stock_loc.id, "include_child_locations": False}
+ )
+ action = wizard.with_context().open_at_date()
+ self.assertEquals(action["context"]["compute_child"], False)
+ self.assertEquals(action["context"]["location"], self.test_stock_loc.id)
+ wizard = self.env["stock.quantity.history"].create(
+ {"location_id": self.test_stock_loc.id, "include_child_locations": True}
+ )
+ action = wizard.with_context().open_at_date()
+ self.assertEquals(action["context"]["compute_child"], True)
+ self.assertEquals(action["context"]["location"], self.test_stock_loc.id)
diff --git a/odex25_inventory/stock_quantity_history_location/wizards/__init__.py b/odex25_inventory/stock_quantity_history_location/wizards/__init__.py
new file mode 100644
index 000000000..51f2abb55
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/wizards/__init__.py
@@ -0,0 +1 @@
+from . import stock_quantity_history
diff --git a/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.py b/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.py
new file mode 100644
index 000000000..b08741d86
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.py
@@ -0,0 +1,32 @@
+# Copyright 2019 ForgeFlow S.L.
+# Copyright 2019 Aleph Objects, Inc.
+# Copyright 2021 Tecnativa - Víctor Martínez
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import ast
+
+from odoo import fields, models
+
+
+class StockQuantityHistory(models.TransientModel):
+ _inherit = "stock.quantity.history"
+
+ location_id = fields.Many2one(
+ "stock.location", domain=[("usage", "in", ["internal", "transit"])]
+ )
+ include_child_locations = fields.Boolean("Include child locations", default=True)
+
+ def open_at_date(self):
+ action = super(StockQuantityHistory, self).open_at_date()
+ ctx = action["context"]
+ if isinstance(ctx, str):
+ ctx = ast.literal_eval(ctx)
+ if self.location_id:
+ ctx["location"] = self.location_id.id
+ ctx["compute_child"] = self.include_child_locations
+ if ctx.get("company_owned", False):
+ ctx.pop("company_owned")
+ action["name"] = "{} ({})".format(
+ action["name"], self.location_id.complete_name
+ )
+ action["context"] = ctx
+ return action
diff --git a/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.xml b/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.xml
new file mode 100644
index 000000000..d81ccff78
--- /dev/null
+++ b/odex25_inventory/stock_quantity_history_location/wizards/stock_quantity_history.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+ Inventory Report
+ stock.quantity.history
+
+
+
+
+
+
+
+
+