diff --git a/.gitignore b/.gitignore index 72364f99f..0f709c602 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,15 @@ coverage.xml *.mo *.pot +# Pycharm +.idea + +# Eclipse +.settings + +# Visual Studio cache/options directory +.vs + # Django stuff: *.log local_settings.py diff --git a/.idea/ddmrp.iml b/.idea/ddmrp.iml deleted file mode 100644 index d0876a78d..000000000 --- a/.idea/ddmrp.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 6599d810b..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 3fa694a73..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index a6757736c..000000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - 1476887114050 - - - - - - - - - - \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..ef413c898 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,43 @@ +language: python +sudo: false +cache: + apt: true + directories: + - $HOME/.cache/pip + +python: + - "2.7" + +addons: + apt: + packages: + - expect-dev # provides unbuffer utility + - python-lxml # because pip installation is slow + - python-simplejson + - python-serial + - python-yaml + +env: + global: + - VERSION="9.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" + - TRANSIFEX_USER='transbot@odoo-community.org' + - secure: HgKcIRHgUR14+T9TRJvGAd2jn9cSChN87zrYPSZ2p63i/6+MU+WaK3lFf6T0NIjHQUvMMpZ1Cu+OFymvjbIEtvPeglaBVUA9ZED5vrW6ED2p24c09ADdQa3LeBoqLxholaYtbbaQrLNWEnZF0pY4Qk9ZQsHkj2LTpV+M+tOYwpM= + matrix: + - LINT_CHECK="1" + - TRANSIFEX="1" + - TESTS="0" ODOO_REPO="odoo/odoo" + - TESTS="0" ODOO_REPO="OCA/OCB" + +virtualenv: + system_site_packages: true + +install: + - git clone https://github.com/OCA/maintainer-quality-tools.git $HOME/maintainer-quality-tools + - export PATH=$HOME/maintainer-quality-tools/travis:$PATH + - travis_install_nightly + +script: + - travis_run_tests + +after_success: + - travis_after_tests_success \ No newline at end of file diff --git a/ddmrp/README.rst b/ddmrp/README.rst index 9329ab13f..42b19b8c6 100644 --- a/ddmrp/README.rst +++ b/ddmrp/README.rst @@ -6,10 +6,235 @@ DDMRP ===== -Pending... +Demand Driven Material Requirements Planning is a formal multi-echelon +planning and execution method developed by Ms. Carol Ptak and Mr. Chad Smith. +DDMRP combines blended aspects of Material Requirements Planning (MRP), +Distribution Requirements Planning (DRP) with the pull and visibility +emphases found in Lean and the Theory of Constraints and the variability +reduction emphasis of Six Sigma. + +This method has five sequential components: + +#. *Strategic Inventory Positioning*. Answers the question "Given our system + and environment, where should we place inventory to have the best + protection?" and determines where should decoupling points of inventory be + placed. + +#. *Buffer Profiles and Levels*. Determine the amount of protection at those + decoupling points. + +#. *Dynamic Adjustments*. Allow the company to adapt buffers to group and + individual part trait changes over time through the use of several types + of adjustments. + +#. *Demand Driven Planning*. Allow to lauch purchase orders (POs), + manufacturing ordrers (MOs) and Transfer Orders (TOs) based on the priority + dictated by the buffers. + +#. *Visible and Collaborative Execution*. These POs, MOs and TOs have to be + effectively managed to synchronize with the changes that often occur within + the "execution horizon." + +These five components work together to greatly dampen, if not eliminate, +the nervousness of traditional MRP systems and the bullwhip effect in +complex and challenging environments. + +This approach provides real information about those parts that are +truly at risk of negatively impacting the planned availability of inventory. + +DDMRP sorts the significant few items that require attention from +the many parts that are being managed. Under the DDMRP approach, +fewer planners can make better decisions more quickly. That means companies +will be better able to leverage their working and human capital. + +Demand Driven Material Requirements Planning is quickly being adopted +by a wide variety of leading companies across the world. + +Some of the benefits reported by the DDMRP method include: + +* High fill rate performance +* Lead time reductions. +* Inventory reductions, while improving customer service +* Eliminate costs related to expedite +* Planners see priorities instead of constantly fighting the conflicting + messages of MRP + +It is highly recommended to read the book 'Demand Driven Material +Requirements Planning (DDMRP)' by Carol Ptak and Chad Smith. + +Installation +============ + +To install this module you need to first download modules from other OCA +repositories: + +Modules from http://github.com/OCA/web : + +* 'web_tree_dynamic_colored_field' +* 'web_widget_bokeh_chart' +* 'web_tree_many2one_clickable' + +Modules from http://github.com/OCA/stock-logistics-warehouse : + +* 'stock_warehouse_orderpoint_stock_info' +* 'stock_warehouse_orderpoint_stock_info_unreserved' +* 'stock_available_unreserved' +* 'stock_orderpoint_uom' +* 'stock_orderpoint_manual_procurement' +* 'stock_demand_estimate' + +Modules from http://github.com/OCA/server-tools + +* 'base_cron_exclusion' + +We strongly recommend to **uninstall** ``procurement_jit`` (so deliveries +related to Sales Orders aren't automatically reserved) and to avoid to +reserve stock for specific moves, buffers are in fact a reservation of stock. +However, while **reservation is discouraged**, it is still available to be +used, in case of reserved stock be aware that the buffer will be blind to this +transfers and stock and you are bypassing the DDMRP reordering flow. + +Configuration +============= + +Scheduled actions +----------------- + +* Go to *Settings > Technical*. +* 'DDMRP Buffer ADU calculation'. Computes the Average Daily Usage for all + Buffers. +* 'Reordering Rule DDMRP calculation'. Computes the Qualified Demand, Net + Flow Position, Planning and Execution priorities for all Buffers. + +Decoupled Lead Time computation +------------------------------- + +The DLT is automatically computed by the system. + +A) For manufactured products' buffers just remember to provide and + set properly the following information: + +* The *Manufacturing Lead Time* for the manufactured product. It can be found + at the product form view under the tab *Sales*. +* The *Delivery Lead Time* for the preferred vendor of a product. This is + important for the products which are purchased and are components in any + Bill of Materials. + +B) For purchased/distributed products' buffers the logic is simpler. + +* In the first place the system will look if there are Vendors for the product, + if so it will use the *Delivery Lead Time* of the preferred one. +* In case of absence of vendors, the *Lead Time* at the bottom of the Buffer + form view will be used. + +Usage +===== + +To easily identify were are you maintaining buffers in your Bill of +Materials, you will need to first provide location information on the Bills +of Materials. + +* Go to *Manufacturing / Products / Bill of Materials* and update the + 'Location' in all the Bill of Materials and associated lines, + indicating where will the parts be placed/used during the manufacturing + process. + +* Print the report 'BOM Structure' to display where in your BOM are you + maintaining buffers, and to identify the Lead Time (LT) of each product, and + Decouple Lead Time (DLT). + + +Buffers +------- + +To list the list of inventory buffers, go to one of the following: +* *Inventory / Inventory Control / Stock Buffer Planning* +* *Inventory / Inventory Control / Reordering Rules* + + +Buffer Profiles +--------------- +Buffer profiles make maintenance of buffers easier by grouping them in +profiles. Changes applied to the profiles will be applicable in the +associated buffer calculations. + +* Go to *Inventory / Configuration / Buffer Profiles*. + +The Buffer Profile Lead Time Factor influences the size of the Buffer Green +zone. Items with longer lead times will usually have smaller green zones, which +will translate in more frequent supply order generation. + +* Go to *Inventory / Configuration / Buffer Profile Lead Time Factor* to + chan + +The Buffer Profile Variability Factor influences the size of the Buffer Red +Safety zone. Items with longer lead times will usually have smaller green +zones, which will translate in more frequent supply order generation. + +* Go to *Inventory / Configuration / Buffer Profile Lead Time Factor*. + +Usual factors should range from 0.2 (long lead time) to 0.7 (short lead time). + + +Product attributes +------------------ + +* For manufactured products, go to *Manufacturing / Products* and + update the 'Manufacturing Lead Time' field, available in the tab *Sales*. +* For purchased products, go to go to *Purchasing / Products* and update the + *Delivery Lead Time* for each vendor, available in tab *Invenory* section + *Vendors*. + + +ADU Calculation Methods +----------------------- + +The Average Daily Usage (ADU) defines the frequency of demand of a product in a +certain location. + +#. Go to *Inventory / Configuration / ADU calculation methods*. +#. To create new, indicate a name, calculation method (fixed, past-looking, + future-looking), and the lengh of period consideration (in days). + +If you do not have prior history of stock moves in your system, it is advised +to usefixed method. If you have past-history of stock moves, best use +past-looking method. + + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/95/8.0 + +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 smashing it by providing a detailed and welcomed feedback +`here `_. + + +Credits +======= Contributors ------------ * Jordi Ballester Alomar +* Lois Rilo Antelo + + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/ddmrp/__init__.py b/ddmrp/__init__.py index a48767579..3616de049 100644 --- a/ddmrp/__init__.py +++ b/ddmrp/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models +from . import wizards +from . import report diff --git a/ddmrp/__manifest__.py b/ddmrp/__manifest__.py new file mode 100644 index 000000000..5fb61678f --- /dev/null +++ b/ddmrp/__manifest__.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "DDMRP", + "summary": "Demand Driven Material Requirements Planning", + "version": "10.0.1.0.0", + "author": "Eficent, " + "Aleph Objects, Inc., " + "Odoo Community Association (OCA)", + "website": "http://www.eficent.com", + "category": "Warehouse Management", + "depends": [ + "purchase", + "mrp_bom_location", + "web_tree_dynamic_colored_field", + "stock_warehouse_orderpoint_stock_info", + "stock_warehouse_orderpoint_stock_info_unreserved", + "stock_available_unreserved", + "stock_orderpoint_uom", + "stock_orderpoint_manual_procurement", + "stock_demand_estimate", + "web_widget_bokeh_chart", + "mrp_mto_with_stock", + "base_cron_exclusion", + "web_tree_many2one_clickable", + ], + "data": [ + "data/product_adu_calculation_method_data.xml", + "data/stock_buffer_profile_variability_data.xml", + "data/stock_buffer_profile_lead_time_data.xml", + "data/stock_buffer_profile_data.xml", + "security/ir.model.access.csv", + "security/stock_security.xml", + "views/stock_buffer_profile_view.xml", + "views/stock_buffer_profile_variability_view.xml", + "views/stock_buffer_profile_lead_time_view.xml", + "views/product_adu_calculation_method_view.xml", + "views/stock_warehouse_orderpoint_view.xml", + "views/procurement_order_view.xml", + "views/mrp_production_view.xml", + "views/purchase_order_line_view.xml", + "views/mrp_bom_view.xml", + "views/report_mrpbomstructure.xml", + "data/ir_cron.xml", + ], + "demo": [ + "demo/res_partner_demo.xml", + "demo/product_category_demo.xml", + "demo/product_product_demo.xml", + "demo/product_supplierinfo_demo.xml", + "demo/mrp_bom_demo.xml", + "demo/stock_warehouse_orderpoint_demo.xml", + "demo/initial_on_hand_demo.xml", + "demo/supply_demand_demo.yml", + ], + "license": "AGPL-3", + 'installable': True, + 'application': True, +} diff --git a/ddmrp/__openerp__.py b/ddmrp/__openerp__.py deleted file mode 100644 index 0fffe1f78..000000000 --- a/ddmrp/__openerp__.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -{ - "name": "DDMRP", - "summary": "Demand-driven MRP", - "version": "9.0.2.1.0", - "author": "Eficent," - "Aleph Objects, Inc.," - "Odoo Community Association (OCA)", - "website": "http://www.eficent.com", - "category": "Warehouse Management", - "depends": ["purchase", - "mrp", - "web_tree_dynamic_colored_field", - "stock_warehouse_orderpoint_stock_info", - "stock_warehouse_orderpoint_stock_info_unreserved", - "stock_available_unreserved", - "stock_orderpoint_uom", - "stock_orderpoint_manual_procurement", - "stock_demand_estimate", - "web_widget_bokeh_chart"], - "data": ["data/product_adu_calculation_method_data.xml", - "data/stock_buffer_profile_variability_data.xml", - "data/stock_buffer_profile_lead_time_data.xml", - "data/stock_buffer_profile_data.xml", - "security/ir.model.access.csv", - "security/stock_security.xml", - "views/stock_buffer_profile_variability_view.xml", - "views/stock_buffer_profile_lead_time_view.xml", - "views/stock_buffer_profile_view.xml", - "views/product_adu_calculation_method_view.xml", - "views/stock_warehouse_orderpoint_view.xml", - "views/procurement_order_view.xml", - "views/mrp_production_view.xml", - "data/ir_cron.xml" - ], - "license": "AGPL-3", - 'installable': True, - 'application': True, -} diff --git a/ddmrp/data/ir_cron.xml b/ddmrp/data/ir_cron.xml index 771e68b34..aa143497b 100644 --- a/ddmrp/data/ir_cron.xml +++ b/ddmrp/data/ir_cron.xml @@ -1,17 +1,34 @@ - - - - Reordering Rule DDMRP calculation - - 1 - days - -1 - - - - - - - + + + + Reordering Rule DDMRP calculation + + 8 + hours + -1 + 6 + + + + + + + + DDMRP Buffer ADU calculation + + 1 + days + -1 + 3 + + + + + + + + diff --git a/ddmrp/data/product_adu_calculation_method_data.xml b/ddmrp/data/product_adu_calculation_method_data.xml index ee540381a..964afd98a 100644 --- a/ddmrp/data/product_adu_calculation_method_data.xml +++ b/ddmrp/data/product_adu_calculation_method_data.xml @@ -1,26 +1,24 @@ - - - + + - - Fixed - fixed - + + Fixed + fixed + - - Past (120 days) - past - 120 - - - Future (120 days) - future - True - 120 - + + Past (120 days) + past + 120 + + + Future (120 days) + future + True + 120 + - - + diff --git a/ddmrp/data/stock_buffer_profile_data.xml b/ddmrp/data/stock_buffer_profile_data.xml index 668b5c2ee..a25c992eb 100644 --- a/ddmrp/data/stock_buffer_profile_data.xml +++ b/ddmrp/data/stock_buffer_profile_data.xml @@ -1,26 +1,275 @@ - - - - - - replenish - purchased - - - - - - replenish - manufactured - - - - - - + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + + replenish + purchased + + + + + + replenish + manufactured + + + + + + replenish + distributed + + + + + diff --git a/ddmrp/data/stock_buffer_profile_lead_time_data.xml b/ddmrp/data/stock_buffer_profile_lead_time_data.xml index f97041bdd..4bbab8511 100644 --- a/ddmrp/data/stock_buffer_profile_lead_time_data.xml +++ b/ddmrp/data/stock_buffer_profile_lead_time_data.xml @@ -1,12 +1,22 @@ - - - - - - Medium - 0.5 - - - - + + + + + Short + 0.75 + + + + Medium + 0.5 + + + + Long + 0.25 + + + diff --git a/ddmrp/data/stock_buffer_profile_variability_data.xml b/ddmrp/data/stock_buffer_profile_variability_data.xml index a67ad482b..fea461ef2 100644 --- a/ddmrp/data/stock_buffer_profile_variability_data.xml +++ b/ddmrp/data/stock_buffer_profile_variability_data.xml @@ -1,12 +1,22 @@ - - - - - - Medium - 0.5 - - - - + + + + + Low + 0.25 + + + + Medium + 0.5 + + + + High + 0.75 + + + diff --git a/ddmrp/demo/initial_on_hand_demo.xml b/ddmrp/demo/initial_on_hand_demo.xml new file mode 100644 index 000000000..1ec362b1b --- /dev/null +++ b/ddmrp/demo/initial_on_hand_demo.xml @@ -0,0 +1,24 @@ + + + + + + + Simulating DDMRP Supply Order Generation + + + + + + + + 65 + + + + + + + + + \ No newline at end of file diff --git a/ddmrp/demo/mrp_bom_demo.xml b/ddmrp/demo/mrp_bom_demo.xml new file mode 100644 index 000000000..aae2c0ba5 --- /dev/null +++ b/ddmrp/demo/mrp_bom_demo.xml @@ -0,0 +1,305 @@ + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + 1 + + 10 + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + 1 + + 10 + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + 1 + + 10 + + + + + + + + + + 5 + + + + + 1 + + + 5 + + + + + + + + + 5 + + + + + 1 + + + 5 + + + + + + 1 + + + 10 + + + + + + + + + + 5 + + + + + 1 + + + 5 + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + 1 + + 10 + + + + + + + + + + + 5 + + + + + 1 + + 5 + + + + + + + + + 5 + + + + + + 1 + + 5 + + + + + + + 1 + + 5 + + + + + + + + + 5 + + + + + + 1 + + 5 + + + + + + + 1 + + 5 + + + + + + + + + 5 + + + + + + 1 + + 5 + + + + + + diff --git a/ddmrp/demo/product_category_demo.xml b/ddmrp/demo/product_category_demo.xml new file mode 100644 index 000000000..c27a50ecf --- /dev/null +++ b/ddmrp/demo/product_category_demo.xml @@ -0,0 +1,14 @@ + + + + + + DDMRP + + + + Simulating DDMRP Supply Order Generation + + + + diff --git a/ddmrp/demo/product_product_demo.xml b/ddmrp/demo/product_product_demo.xml new file mode 100644 index 000000000..a082485e5 --- /dev/null +++ b/ddmrp/demo/product_product_demo.xml @@ -0,0 +1,260 @@ + + + + + + FPA + + + product + + + 1 + + + + + FPB + + + product + + + 1 + + + + + FPC + + + product + + + 1 + + + + + 101 + + + product + + + 3 + + + + + + 102 + + + product + + + 3 + + + + + + 201 + + + product + + + 5 + + + + + + 202 + + + product + + + 2 + + + + + + 203 + + + product + + + 2 + + + + + + 207 + + + product + + + 3 + + + + + + 301 + + + product + + + 4 + + + + + + 302P + + + product + + + + + + + 303 + + + product + + + 4 + + + + + + 305P + + + product + + + + + + + 306 + + + product + + + 2 + + + + + + + 310 + + + product + + + 2 + + + + + + 401P + + + product + + + + + + + + + 402P + + + product + + + + + + + 403P + + + product + + + + + + + 404P + + + product + + + + + + + 410P + + + product + + + + + + + 411P + + + product + + + + + + + 901P + + product + + + + + + + PCB + + product + + + + + + + diff --git a/ddmrp/demo/product_supplierinfo_demo.xml b/ddmrp/demo/product_supplierinfo_demo.xml new file mode 100644 index 000000000..9e648e159 --- /dev/null +++ b/ddmrp/demo/product_supplierinfo_demo.xml @@ -0,0 +1,87 @@ + + + + + + + + 12 + 0 + 100 + + + + + + 30 + 0 + 100 + + + + + + 10 + 0 + 100 + + + + + + 30 + 0 + 100 + + + + + + 30 + 0 + 100 + + + + + + 90 + 0 + 100 + + + + + + 30 + 0 + 100 + + + + + + 30 + 0 + 100 + + + + + + 10 + 0 + 100 + + + + + + 7 + 20 + 100 + + + + + diff --git a/ddmrp/demo/res_partner_demo.xml b/ddmrp/demo/res_partner_demo.xml new file mode 100644 index 000000000..43acf1ef7 --- /dev/null +++ b/ddmrp/demo/res_partner_demo.xml @@ -0,0 +1,22 @@ + + + + + + Eficent + 1 + + + + eficent@yourcompany.example.com + Rambla de Catalunya + Barcelona + 08008 + http://www.eficent.com + + + + + + diff --git a/ddmrp/demo/stock_warehouse_orderpoint_demo.xml b/ddmrp/demo/stock_warehouse_orderpoint_demo.xml new file mode 100644 index 000000000..7192af8b2 --- /dev/null +++ b/ddmrp/demo/stock_warehouse_orderpoint_demo.xml @@ -0,0 +1,173 @@ + + + + + + + + + 250 + 10 + 0 + 0 + + + 20 + 3 + 250 + + + + + + + 100 + 10 + 0 + 0 + + + 23 + 3 + 250 + + + + + + + 300 + 10 + 0 + 0 + + + 23 + 3 + 250 + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 10 + 0 + 0 + + + + + + + + + 10 + 8 + 0 + 0 + + + 20 + + + + diff --git a/ddmrp/demo/supply_demand_demo.yml b/ddmrp/demo/supply_demand_demo.yml new file mode 100644 index 000000000..dc47cfcfc --- /dev/null +++ b/ddmrp/demo/supply_demand_demo.yml @@ -0,0 +1,517 @@ +- + !record {model: stock.picking, id: incomming_shipment_example_2}: + picking_type_id: stock.picking_type_in + origin: 'incoming_shipment' + partner_id: res_partner_eficent + date: !eval "(datetime.now()-timedelta(days=6)).strftime('%Y-%m-%d 08:00:00')" + min_date: !eval "(datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d 08:00:00')" + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock + state: 'assigned' + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 35.0 + picking_type_id: stock.picking_type_in + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock + +- + !record {model: stock.picking, id: incomming_shipment_example_6}: + picking_type_id: stock.picking_type_in + origin: 'incoming_shipment' + partner_id: res_partner_eficent + date: !eval "(datetime.now()-timedelta(days=2)).strftime('%Y-%m-%d 08:00:00')" + min_date: !eval "(datetime.now()+timedelta(days=5)).strftime('%Y-%m-%d 08:00:00')" + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock + state: 'assigned' + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 37.0 + picking_type_id: stock.picking_type_in + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_1}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "datetime.now().strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 10.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "datetime.now().strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_2}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 18.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_3}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=2)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 17.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=2)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_4}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=3)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 6.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=3)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_5}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=4)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 5.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=4)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_6}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=5)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 9.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=5)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_7}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=6)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 10.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=6)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_8}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=7)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 30.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=7)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_9}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=8)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 5.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=8)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_10}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=9)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 6.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=9)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_11}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=10)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 9.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=10)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_12}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=11)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 10.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=11)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_13}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=12)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 20.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=12)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_14}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=13)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 6.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=13)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_15}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=14)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 11.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=14)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_16}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=15)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 10.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=15)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_17}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=16)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 18.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=16)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_18}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=17)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 12.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=17)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_19}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=18)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 8.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=18)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_20}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=19)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 6.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=19)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_21}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=20)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 5.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=20)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_22}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=21)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 8.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=21)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_23}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=22)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 2.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=22)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_24}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=23)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 12.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=23)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_25}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=24)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 20.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=24)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_26}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=25)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 9.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=25)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_27}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=26)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 8.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=26)).strftime('%Y-%m-%d 20:00:00')" + +- + !record {model: stock.picking, id: outgoing_shipment_example_day_28}: + picking_type_id: stock.picking_type_out + origin: 'outgoing shipment main_warehouse' + partner_id: base.res_partner_1 + min_date: !eval "(datetime.now()+timedelta(days=27)).strftime('%Y-%m-%d 20:00:00')" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + move_lines: + - product_id: product_product_example + product_uom: product.product_uom_unit + product_uom_qty: 5.0 + picking_type_id: stock.picking_type_out + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers + date: !eval "(datetime.now()+timedelta(days=27)).strftime('%Y-%m-%d 20:00:00')" + +- + !python {model: stock.picking}: | + self.browse([ref('incomming_shipment_example_2'),ref('incomming_shipment_example_6'),ref('outgoing_shipment_example_day_1'), ref('outgoing_shipment_example_day_2'), ref('outgoing_shipment_example_day_3'), ref('outgoing_shipment_example_day_4'), ref('outgoing_shipment_example_day_5'), ref('outgoing_shipment_example_day_6'), ref('outgoing_shipment_example_day_7'), ref('outgoing_shipment_example_day_8'), ref('outgoing_shipment_example_day_9'), ref('outgoing_shipment_example_day_10'), ref('outgoing_shipment_example_day_11'), ref('outgoing_shipment_example_day_12'), ref('outgoing_shipment_example_day_13'), ref('outgoing_shipment_example_day_14'), ref('outgoing_shipment_example_day_15'), ref('outgoing_shipment_example_day_16'), ref('outgoing_shipment_example_day_17'), ref('outgoing_shipment_example_day_18'), ref('outgoing_shipment_example_day_19'), ref('outgoing_shipment_example_day_20'), ref('outgoing_shipment_example_day_21'), ref('outgoing_shipment_example_day_22'), ref('outgoing_shipment_example_day_23'), ref('outgoing_shipment_example_day_24'), ref('outgoing_shipment_example_day_25'), ref('outgoing_shipment_example_day_26'), ref('outgoing_shipment_example_day_27'), ref('outgoing_shipment_example_day_28')]).action_confirm() + + diff --git a/ddmrp/migrations/8.0.2.0.0/post-migration.py b/ddmrp/migrations/8.0.2.0.0/post-migration.py index 130741fbe..ee5bb2b44 100644 --- a/ddmrp/migrations/8.0.2.0.0/post-migration.py +++ b/ddmrp/migrations/8.0.2.0.0/post-migration.py @@ -4,8 +4,8 @@ # © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import pooler -from openerp import SUPERUSER_ID +from odoo import pooler +from odoo import SUPERUSER_ID import logging @@ -68,4 +68,3 @@ def migrate(cr, version): return migrate_variability(cr) migrate_lead_time(cr) - diff --git a/ddmrp/models/__init__.py b/ddmrp/models/__init__.py index e227990ae..4aca9ceea 100644 --- a/ddmrp/models/__init__.py +++ b/ddmrp/models/__init__.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + from . import stock_buffer_profile_variability from . import stock_buffer_profile_lead_time from . import stock_buffer_profile @@ -10,3 +8,5 @@ from . import product_adu_calculation_method from . import stock_warehouse_orderpoint from . import mrp_production +from . import purchase_order +from . import mrp_bom diff --git a/ddmrp/models/mrp_bom.py b/ddmrp/models/mrp_bom.py new file mode 100644 index 000000000..6c971c116 --- /dev/null +++ b/ddmrp/models/mrp_bom.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from openerp import api, fields, models +_logger = logging.getLogger(__name__) + + +class MrpBom(models.Model): + _inherit = "mrp.bom" + + is_buffered = fields.Boolean( + string="Buffered?", compute="_compute_is_buffered", + help="True when the product has an DDMRP buffer associated.", + ) + orderpoint_id = fields.Many2one( + comodel_name='stock.warehouse.orderpoint', string='Orderpoint', + compute="_compute_is_buffered", + ) + dlt = fields.Float( + string="Decoupled Lead Time (days)", + compute="_compute_dlt", + ) + has_mto_rule = fields.Boolean( + string="MTO", + help="Follows an MTO Pull Rule", + compute="_compute_mto_rule", + ) + + def _get_search_buffer_domain(self): + product = self.product_id or \ + self.product_tmpl_id.product_variant_ids[0] + domain = [('product_id', '=', product.id), + ('buffer_profile_id', '!=', False)] + if self.location_id: + domain.append(('location_id', '=', self.location_id.id)) + return domain + + def _compute_is_buffered(self): + for bom in self: + domain = bom._get_search_buffer_domain() + orderpoint = self.env['stock.warehouse.orderpoint'].search( + domain, limit=1) + bom.orderpoint_id = orderpoint + bom.is_buffered = True if orderpoint else False + + def _compute_mto_rule(self): + for rec in self: + template = rec.product_id.product_tmpl_id or rec.product_tmpl_id + rec.has_mto_rule = True if ( + rec.location_id in + template.mrp_mts_mto_location_ids) else False + + @api.multi + def _get_longest_path(self): + if not self.bom_line_ids: + return 0.0 + paths = [0] * len(self.bom_line_ids) + i = 0 + for line in self.bom_line_ids: + if line.is_buffered: + i += 1 + elif line.product_id.bom_ids: + # If the a component is manufactured we continue exploding. + location = line.location_id + line_boms = line.product_id.bom_ids + bom = line_boms.filtered( + lambda bom: bom.location_id == location) or \ + line_boms.filtered(lambda bom: not bom.location_id) + if bom: + produce_delay = bom[0].product_id.produce_delay or \ + bom[0].product_tmpl_id.produce_delay + paths[i] += produce_delay + paths[i] += bom[0]._get_longest_path() + else: + _logger.info( + "ddmrp (dlt): Product %s has no BOM for location " + "%s." % (line.product_id.name, location.name)) + i += 1 + else: + # assuming they are purchased, + if line.product_id.seller_ids: + paths[i] = line.product_id.seller_ids[0].delay + else: + _logger.info( + "ddmrp (dlt): Product %s has no seller set." % + line.product_id.name) + i += 1 + return max(paths) + + @api.multi + def _get_manufactured_dlt(self): + """Computes the Decoupled Lead Time exploding all the branches of the + BOM until a buffered position and then selecting the greatest.""" + self.ensure_one() + dlt = self.product_id.produce_delay or \ + self.product_tmpl_id.produce_delay + dlt += self._get_longest_path() + return dlt + + def _compute_dlt(self): + for rec in self: + rec.dlt = rec._get_manufactured_dlt() + + +class MrpBomLine(models.Model): + _inherit = "mrp.bom.line" + + is_buffered = fields.Boolean( + string="Buffered?", compute="_compute_is_buffered", + help="True when the product has an DDMRP buffer associated.", + ) + orderpoint_id = fields.Many2one( + comodel_name='stock.warehouse.orderpoint', string='Orderpoint', + compute="_compute_is_buffered", + ) + dlt = fields.Float( + string="Decoupled Lead Time (days)", + compute="_compute_dlt", + ) + has_mto_rule = fields.Boolean( + string="MTO", + help="Follows an MTO Pull Rule", + compute="_compute_mto_rule", + ) + + def _get_search_buffer_domain(self): + product = self.product_id or \ + self.product_tmpl_id.product_variant_ids[0] + domain = [('product_id', '=', product.id), + ('buffer_profile_id', '!=', False)] + if self.location_id: + domain.append(('location_id', '=', self.location_id.id)) + return domain + + def _compute_is_buffered(self): + for line in self: + domain = line._get_search_buffer_domain() + orderpoint = self.env['stock.warehouse.orderpoint'].search( + domain, limit=1) + line.orderpoint_id = orderpoint + line.is_buffered = True if orderpoint else False + + def _compute_dlt(self): + for rec in self: + if rec.product_id.bom_ids: + rec.dlt = rec.product_id.bom_ids[0].dlt + else: + rec.dlt = rec.product_id.seller_ids and \ + rec.product_id.seller_ids[0].delay or 0.0 + + def _compute_mto_rule(self): + for rec in self: + rec.has_mto_rule = True if ( + rec.location_id in + rec.product_id.mrp_mts_mto_location_ids) else False diff --git a/ddmrp/models/mrp_production.py b/ddmrp/models/mrp_production.py index ee749269c..53cc94cfb 100644 --- a/ddmrp/models/mrp_production.py +++ b/ddmrp/models/mrp_production.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models +from odoo import api, fields, models from .stock_warehouse_orderpoint import _PRIORITY_LEVEL @@ -15,10 +15,6 @@ class MrpProduction(models.Model): def _search_procurements(self): return [('production_id', '=', self.id)] - @api.model - def _search_orderpoints(self): - return [('name', '=', self.move_prod_id.origin)] - @api.model def _find_orderpoint_from_procurement(self, procurement): orderpoint = procurement.move_dest_id.procurement_id.orderpoint_id @@ -26,7 +22,7 @@ def _find_orderpoint_from_procurement(self, procurement): return procurement, orderpoint @api.multi - @api.depends('move_prod_id', 'procurement_ids') + @api.depends('move_finished_ids', 'procurement_ids') def _compute_orderpoint_id(self): for rec in self: domain = rec._search_procurements() @@ -49,16 +45,19 @@ def _compute_orderpoint_id(self): break @api.multi - @api.depends('orderpoint_id', 'procurement_ids', 'move_prod_id') - def _compute_execution_priority(self): - for rec in self.filtered(lambda r: r.orderpoint_id): - if rec.state in ['done', 'cancel']: - rec.execution_priority_level = None - rec.on_hand_percent = None - else: - rec.execution_priority_level = \ - rec.orderpoint_id.execution_priority_level - rec.on_hand_percent = rec.orderpoint_id.on_hand_percent + def _calc_execution_priority(self): + """Technical note: this method cannot be decorated with api.depends, + otherwise it would generate a infinite recursion.""" + prods = self.filtered( + lambda r: r.orderpoint_id and r.state not in ['done', 'cancel']) + for rec in prods: + rec.execution_priority_level = \ + rec.orderpoint_id.execution_priority_level + rec.on_hand_percent = rec.orderpoint_id.on_hand_percent + (self - prods).write({ + 'execution_priority_level': None, + 'on_hand_percent': None, + }) def _search_execution_priority(self, operator, value): """Search on the execution priority by evaluating on all @@ -87,16 +86,15 @@ def _search_execution_priority(self, operator, value): orderpoint_id = fields.Many2one( comodel_name='stock.warehouse.orderpoint', store=True, index=True, string="Reordering rule", compute='_compute_orderpoint_id') - procurement_ids = fields.One2many(comodel_name='procurement.order', - inverse_name='production_id', - string='Procurements') - + procurement_ids = fields.One2many( + comodel_name='procurement.order', + inverse_name='production_id', + string='Procurements', + ) execution_priority_level = fields.Selection( string="Buffer On-Hand Alert Level", - selection=_PRIORITY_LEVEL, - compute="_compute_execution_priority", - search="_search_execution_priority", - store=True) + selection=_PRIORITY_LEVEL, readonly=True, + ) on_hand_percent = fields.Float( - string="On Hand/TOG (%)", compute="_compute_execution_priority", - store=True) + string="On Hand/TOR (%)", + ) diff --git a/ddmrp/models/procurement_order.py b/ddmrp/models/procurement_order.py index a990621b5..fb1e7821c 100644 --- a/ddmrp/models/procurement_order.py +++ b/ddmrp/models/procurement_order.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models +from odoo import api, fields, models class ProcurementOrder(models.Model): @@ -35,3 +35,17 @@ def _procure_orderpoint_confirm(self, use_new_cursor=False, With DDMRP it is in the hands of the planner to manually create procurements, based on the procure recommendations.""" return {} + + @api.multi + def write(self, vals): + """Needed to calculate the execution priority of MOs originated from + a DDMRP buffer just after the creation.""" + res = super(ProcurementOrder, self).write(vals) + mo_id = vals.get('production_id') + if mo_id: + self.env['mrp.production'].browse(mo_id)._calc_execution_priority() + pol_id = vals.get('purchase_line_id') + if pol_id: + self.env['purchase.order.line'].browse( + pol_id)._calc_execution_priority() + return res diff --git a/ddmrp/models/product_adu_calculation_method.py b/ddmrp/models/product_adu_calculation_method.py index b7b05019f..fbbf1231c 100644 --- a/ddmrp/models/product_adu_calculation_method.py +++ b/ddmrp/models/product_adu_calculation_method.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models, _ -from openerp.exceptions import Warning as UserError +from odoo import api, fields, models, _ +from odoo.exceptions import Warning as UserError class ProductAduCalculationMethod(models.Model): diff --git a/ddmrp/models/purchase_order.py b/ddmrp/models/purchase_order.py new file mode 100644 index 000000000..b2f81cf55 --- /dev/null +++ b/ddmrp/models/purchase_order.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from .stock_warehouse_orderpoint import _PRIORITY_LEVEL + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + ddmrp_comment = fields.Text(string="Follow-up Notes") + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + @api.multi + def _get_procurements_domain(self): + self.ensure_one() + return [('purchase_line_id', '=', self.id)] + + @api.model + def _find_orderpoint_from_procurement(self, procurement): + procurement = procurement.move_dest_id.procurement_id + orderpoint = procurement.orderpoint_id + return procurement, orderpoint + + @api.multi + @api.depends("product_id") + def _compute_orderpoint_id(self): + for rec in self: + domain = rec._get_procurements_domain() + procurements = rec.env['procurement.order'].search(domain) + orderpoints = procurements.mapped('orderpoint_id') + if orderpoints: + rec.orderpoint_id = orderpoints[0] + else: + for procurement in procurements: + orderpoint = False + originating_procurement = procurement + while not orderpoint: + originating_procurement, orderpoint = \ + self._find_orderpoint_from_procurement( + originating_procurement) + if orderpoint: + rec.orderpoint_id = orderpoint + if not originating_procurement: + break + + @api.multi + def _calc_execution_priority(self): + prods = self.filtered( + lambda r: r.orderpoint_id and r.state not in ['done', 'cancel']) + for rec in prods: + rec.execution_priority_level = \ + rec.orderpoint_id.execution_priority_level + rec.on_hand_percent = rec.orderpoint_id.on_hand_percent + (self - prods).write({ + 'execution_priority_level': None, + 'on_hand_percent': None, + }) + + orderpoint_id = fields.Many2one( + comodel_name='stock.warehouse.orderpoint', + string="Reordering rule", + compute='_compute_orderpoint_id', store=True, index=True, + ) + execution_priority_level = fields.Selection( + string="Buffer On-Hand Status Level", + selection=_PRIORITY_LEVEL, readonly=True, + ) + on_hand_percent = fields.Float( + string="On Hand/TOR (%)", readonly=True, + ) + ddmrp_comment = fields.Text(related="order_id.ddmrp_comment") diff --git a/ddmrp/models/stock_buffer_profile.py b/ddmrp/models/stock_buffer_profile.py index eec8eb296..d45e12d96 100644 --- a/ddmrp/models/stock_buffer_profile.py +++ b/ddmrp/models/stock_buffer_profile.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models +from odoo import api, fields, models _REPLENISH_METHODS = [ ('replenish', 'Replenished'), - ('replenish_override', 'Replenished override'), + ('replenish_override', 'Replenished Override'), ('min_max', 'Min-max') ] _ITEM_TYPES = [ diff --git a/ddmrp/models/stock_buffer_profile_lead_time.py b/ddmrp/models/stock_buffer_profile_lead_time.py index 9ca290ba5..223b8e632 100644 --- a/ddmrp/models/stock_buffer_profile_lead_time.py +++ b/ddmrp/models/stock_buffer_profile_lead_time.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import fields, models +from odoo import fields, models class StockBufferProfileLeadTime(models.Model): @@ -12,7 +12,7 @@ class StockBufferProfileLeadTime(models.Model): _string = 'Buffer Profile Lead Time Factor' name = fields.Char(string='Name', required=True) - factor = fields.Float(string='Variability Factor', required=True) + factor = fields.Float(string='Lead Time Factor', required=True) company_id = fields.Many2one( 'res.company', 'Company', required=True, default=lambda self: self.env['res.company']._company_default_get( diff --git a/ddmrp/models/stock_buffer_profile_variability.py b/ddmrp/models/stock_buffer_profile_variability.py index 4c568f934..bec4a6ceb 100644 --- a/ddmrp/models/stock_buffer_profile_variability.py +++ b/ddmrp/models/stock_buffer_profile_variability.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import fields, models +from odoo import fields, models class StockBufferProfileVariability(models.Model): diff --git a/ddmrp/models/stock_warehouse_orderpoint.py b/ddmrp/models/stock_warehouse_orderpoint.py index 072525e6a..00c74fd27 100644 --- a/ddmrp/models/stock_warehouse_orderpoint.py +++ b/ddmrp/models/stock_warehouse_orderpoint.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import logging -from openerp import api, fields, models +from odoo import api, fields, models, _ from datetime import timedelta -from openerp.addons import decimal_precision as dp -from openerp.tools import float_compare, float_round +from odoo.addons import decimal_precision as dp +from odoo.tools import float_compare, float_round import operator as py_operator _logger = logging.getLogger(__name__) @@ -48,42 +48,51 @@ class StockWarehouseOrderpoint(models.Model): @api.multi @api.depends("dlt", "adu", "buffer_profile_id.lead_time_id.factor", "buffer_profile_id.variability_id.factor", - "product_uom.rounding") + "product_uom.rounding", "red_override") def _compute_red_zone(self): for rec in self: - rec.red_base_qty = float_round( - rec.dlt * rec.adu * rec.buffer_profile_id.lead_time_id.factor, - precision_rounding=rec.product_uom.rounding) - rec.red_safety_qty = float_round( - rec.red_base_qty * rec.buffer_profile_id.variability_id.factor, - precision_rounding=rec.product_uom.rounding) - rec.red_zone_qty = rec.red_base_qty + rec.red_safety_qty + + if rec.replenish_method in ['replenish', 'min_max']: + factor = rec.buffer_profile_id.lead_time_id.factor + if rec.replenish_method == 'min_max': + factor = factor + 1 + rec.red_base_qty = float_round( + rec.dlt * rec.adu * factor, + precision_rounding=rec.product_uom.rounding) + rec.red_safety_qty = float_round( + rec.red_base_qty * factor, + precision_rounding=rec.product_uom.rounding) + rec.red_zone_qty = rec.red_base_qty + rec.red_safety_qty + else: + rec.red_zone_qty = rec.red_override @api.multi @api.depends("dlt", "adu", "buffer_profile_id.lead_time_id.factor", "order_cycle", "minimum_order_quantity", - "product_uom.rounding") + "product_uom.rounding", "green_override") def _compute_green_zone(self): for rec in self: - # Using imposed or desired minimum order cycle - rec.green_zone_oc = float_round( - rec.order_cycle * rec.adu, - precision_rounding=rec.product_uom.rounding) - # Using lead time factor - rec.green_zone_lt_factor = float_round( - rec.dlt*rec.adu*rec.buffer_profile_id.lead_time_id.factor, - precision_rounding=rec.product_uom.rounding) - # Using minimum order quantity - rec.green_zone_moq = float_round( - rec.minimum_order_quantity, - precision_rounding=rec.product_uom.rounding) - - # The biggest option of the above will be used as the green zone - # value - rec.green_zone_qty = max(rec.green_zone_oc, - rec.green_zone_lt_factor, - rec.green_zone_moq) + if rec.replenish_method in ['replenish', 'min_max']: + # Using imposed or desired minimum order cycle + rec.green_zone_oc = float_round( + rec.order_cycle * rec.adu, + precision_rounding=rec.product_uom.rounding) + # Using lead time factor + rec.green_zone_lt_factor = float_round( + rec.dlt*rec.adu*rec.buffer_profile_id.lead_time_id.factor, + precision_rounding=rec.product_uom.rounding) + # Using minimum order quantity + rec.green_zone_moq = float_round( + rec.minimum_order_quantity, + precision_rounding=rec.product_uom.rounding) + # The biggest option of the above will be used as the green + # zone value + rec.green_zone_qty = max(rec.green_zone_oc, + rec.green_zone_lt_factor, + rec.green_zone_moq) + else: + rec.green_zone_qty = rec.green_override rec.top_of_green = \ rec.green_zone_qty + rec.yellow_zone_qty + rec.red_zone_qty @@ -92,15 +101,17 @@ def _compute_green_zone(self): "buffer_profile_id.variability_id.factor", "buffer_profile_id.replenish_method", "order_cycle", "minimum_order_quantity", - "product_uom.rounding") + "product_uom.rounding", "yellow_override") def _compute_yellow_zone(self): for rec in self: - if rec.buffer_profile_id.replenish_method == 'min_max': + if rec.replenish_method == 'min_max': rec.yellow_zone_qty = 0 - else: + elif rec.replenish_method == 'replenish': rec.yellow_zone_qty = float_round( rec.dlt * rec.adu, precision_rounding=rec.product_uom.rounding) + else: + rec.yellow_zone_qty = rec.yellow_override rec.top_of_yellow = rec.yellow_zone_qty + rec.red_zone_qty @api.multi @@ -122,7 +133,7 @@ def _compute_procure_recommended_date(self): "procurement_ids.product_qty", "procurement_ids.add_to_net_flow_equation") def _compute_procure_recommended_qty(self): - subtract_qty = self.subtract_procurements_from_orderpoints(self.ids) + subtract_qty = self.subtract_procurements_from_orderpoints() for rec in self: procure_recommended_qty = 0.0 if rec.net_flow_position < rec.top_of_yellow: @@ -148,9 +159,8 @@ def _compute_procure_recommended_qty(self): procure_recommended_qty += rec.qty_multiple - reste if rec.procure_uom_id: - product_qty = rec.procure_uom_id._compute_qty( - rec.product_id.uom_id.id, procure_recommended_qty, - rec.procure_uom_id.id) + product_qty = rec.product_id.uom_id._compute_quantity( + procure_recommended_qty, rec.procure_uom_id) else: product_qty = procure_recommended_qty else: @@ -214,10 +224,45 @@ def _compute_order_spike_threshold(self): for rec in self: rec.order_spike_threshold = 0.5 * rec.red_zone_qty + def _get_manufactured_bom(self): + return self.env['mrp.bom'].search( + ['|', + ('product_id', '=', self.product_id.id), + ('product_tmpl_id', '=', self.product_id.product_tmpl_id.id), + '|', + ('location_id', '=', self.location_id.id), + ('location_id', '=', False)], limit=1) + + def _compute_dlt(self): + for rec in self: + if rec.buffer_profile_id.item_type == 'manufactured': + bom = rec._get_manufactured_bom() + rec.dlt = bom.dlt + elif rec.buffer_profile_id.item_type == 'distributed': + rec.dlt = rec.lead_days + else: + rec.dlt = rec.product_id.seller_ids and \ + rec.product_id.seller_ids[0].delay or rec.lead_days + buffer_profile_id = fields.Many2one( comodel_name='stock.buffer.profile', - string="Buffer Profile") - dlt = fields.Float(string="Decoupled Lead Time (days)") + string="Buffer Profile", + ) + replenish_method = fields.Selection( + related="buffer_profile_id.replenish_method", + readonly=True, + ) + green_override = fields.Float( + string="Green Zone (Override)", + ) + yellow_override = fields.Float( + string="Yellow Zone (Override)", + ) + red_override = fields.Float( + string="Red Zone (Override)", + ) + dlt = fields.Float(string="Decoupled Lead Time (days)", + compute="_compute_dlt") adu = fields.Float(string="Average Daily Usage (ADU)", default=0.0, digits=UNIT, readonly=True) adu_calculation_method = fields.Many2one( @@ -268,6 +313,10 @@ def _compute_order_spike_threshold(self): compute="_compute_order_spike_threshold", digits=UNIT, store=True) qualified_demand = fields.Float(string="Qualified demand", digits=UNIT, readonly=True) + incoming_dlt_qty = fields.Float( + string="Incoming (Within DLT)", + readonly=True, + ) net_flow_position = fields.Float(string="Net flow position", digits=UNIT, readonly=True) net_flow_position_percent = fields.Float( @@ -288,6 +337,10 @@ def _compute_order_spike_threshold(self): mrp_production_ids = fields.One2many( string='Manufacturing Orders', comodel_name='mrp.production', inverse_name='orderpoint_id') + purchase_lines_ids = fields.One2many( + string="Purchase Order Lines", comodel_name="purchase.order.line", + inverse_name="orderpoint_id", + ) ddmrp_chart = fields.Text(string='DDMRP Chart', compute=_compute_ddmrp_chart) @@ -330,13 +383,15 @@ def _stock_move_tree_view(self, lines): if form_view: views += [(form_view.id, 'form')] - return {'type': 'ir.actions.act_window', - 'res_model': 'stock.move', - 'view_type': 'form', - 'views': views, - 'view_mode': 'tree,form', - 'domain': str([('id', 'in', lines.ids)]) - } + return { + 'name': _('Non-completed Moves'), + 'type': 'ir.actions.act_window', + 'res_model': 'stock.move', + 'view_type': 'form', + 'views': views, + 'view_mode': 'tree,form', + 'domain': str([('id', 'in', lines.ids)]), + } @api.multi def open_moves(self): @@ -351,14 +406,11 @@ def open_moves(self): def subtract_procurements(self, orderpoint): qty = super(StockWarehouseOrderpoint, self).subtract_procurements( orderpoint) - uom_obj = self.env["product.uom"] for procurement in orderpoint.procurement_ids: if procurement.state not in ('draft', 'cancel') and \ procurement.add_to_net_flow_equation: - qty += uom_obj._compute_qty_obj( - procurement.product_uom, - procurement.product_qty, - procurement.product_id.uom_id) + qty += procurement.product_uom._compute_quantity( + procurement.product_qty, procurement.product_id.uom_id) if qty >= 0.0: return qty else: @@ -368,11 +420,12 @@ def subtract_procurements(self, orderpoint): def _past_demand_estimate_domain(self, date_from, date_to, locations): return [('location_id', 'in', locations.ids), ('product_id', '=', self.product_id.id), - '|', ('period_id.date_from', '>=', date_from), - ('period_id.date_to', '<=', date_to)] + ('date_range_id.date_start', '<=', date_to), + ('date_range_id.date_end', '>=', date_from)] - @api.model + @api.multi def _past_moves_domain(self, date_from, locations): + self.ensure_one() return [('state', '=', 'done'), ('location_id', 'in', locations.ids), ('location_dest_id', 'not in', locations.ids), ('product_id', '=', self.product_id.id), @@ -411,8 +464,8 @@ def _compute_adu_past_demand(self): def _future_demand_estimate_domain(self, date_from, date_to, locations): return [('location_id', 'in', locations.ids), ('product_id', '=', self.product_id.id), - '|', ('period_id.date_from', '>=', date_from), - ('period_id.date_to', '<=', date_to)] + ('date_range_id.date_start', '<=', date_to), + ('date_range_id.date_end', '>=', date_from)] @api.model def _future_moves_domain(self, date_to, locations): @@ -420,7 +473,7 @@ def _future_moves_domain(self, date_to, locations): ('location_id', 'in', locations.ids), ('location_dest_id', 'not in', locations.ids), ('product_id', '=', self.product_id.id), - ('date', '<=', date_to)] + ('date_expected', '<=', date_to)] @api.multi def _compute_adu_future_demand(self): @@ -476,11 +529,28 @@ def _search_stock_moves_qualified_demand_domain(self): locations = self.env['stock.location'].search( [('id', 'child_of', [self.location_id.id])]) return [('product_id', '=', self.product_id.id), - ('state', 'in', ['draft', 'waiting', 'confirmed', - 'assigned']), + ('state', 'in', ['waiting', 'confirmed', 'assigned']), ('location_id', 'in', locations.ids), ('location_dest_id', 'not in', locations.ids), - ('date', '<=', date_to)] + ('date_expected', '<=', date_to)] + + @api.multi + def _search_stock_moves_incoming_domain(self): + self.ensure_one() + horizon = self.dlt + if not horizon: + date_to = fields.Date.to_string(fields.date.today()) + + else: + date_to = fields.Date.to_string(fields.date.today() + timedelta( + days=horizon)) + locations = self.env['stock.location'].search( + [('id', 'child_of', [self.location_id.id])]) + return [('product_id', '=', self.product_id.id), + ('state', 'in', ['waiting', 'confirmed', 'assigned']), + ('location_id', 'not in', locations.ids), + ('location_dest_id', 'in', locations.ids), + ('date_expected', '<=', date_to)] @api.multi def _calc_qualified_demand(self): @@ -491,32 +561,43 @@ def _calc_qualified_demand(self): moves = self.env['stock.move'].search(domain) demand_by_days = {} move_dates = [fields.Datetime.from_string(dt).date() for dt in - moves.mapped('date')] + moves.mapped('date_expected')] for move_date in move_dates: demand_by_days[move_date] = 0.0 for move in moves: date = fields.Datetime.from_string(move.date).date() - demand_by_days[date] += move.product_qty + demand_by_days[date] += \ + move.product_qty - move.reserved_availability for date in demand_by_days.keys(): if demand_by_days[date] >= rec.order_spike_threshold \ - or date == fields.date.today(): + or date <= fields.date.today(): rec.qualified_demand += demand_by_days[date] return True + @api.multi + def _calc_incoming_dlt_qty(self): + for rec in self: + rec.incoming_dlt_qty = 0.0 + domain = rec._search_stock_moves_incoming_domain() + moves = self.env['stock.move'].search(domain) + rec.incoming_dlt_qty = sum(moves.mapped('product_qty')) + return True + @api.multi def _calc_net_flow_position(self): for rec in self: rec.refresh() - rec.net_flow_position = rec.product_location_qty + \ - rec.incoming_location_qty - rec.qualified_demand + rec.net_flow_position = \ + rec.product_location_qty_available_not_res + \ + rec.incoming_dlt_qty - rec.qualified_demand usage = 0.0 if rec.top_of_green: usage = round((rec.net_flow_position / rec.top_of_green*100), 2) rec.net_flow_position_percent = usage - for procurement in rec.procurement_ids: - if procurement.state not in ('draft', 'cancel'): - procurement.add_to_net_flow_equation = False + procurements_to_update = rec.procurement_ids.filtered( + lambda p: p.state not in ('draft', 'cancel')) + procurements_to_update.write({'add_to_net_flow_equation': False}) return True @api.multi @@ -548,20 +629,45 @@ def _calc_execution_priority(self): else: rec.on_hand_percent = 0.0 + @api.model + def cron_ddmrp_adu(self, automatic=False): + """calculate ADU for each DDMRP buffer. Called by cronjob. + """ + _logger.info("Start cron_ddmrp_adu.") + orderpoints = self.search([]) + i = 0 + j = len(orderpoints) + for op in orderpoints: + try: + i += 1 + _logger.debug("ddmrp cron_adu: %s. (%s/%s)" % (op.name, i, j)) + op._calc_adu() + if automatic: + self.env.cr.commit() + except Exception: + if automatic: + self.env.cr.rollback() + _logger.exception( + 'Fail to compute ADU for orderpoint %s', op.name) + else: + raise + _logger.info("End cron_ddmrp_adu.") + return True + @api.multi def cron_actions(self): """This method is meant to be inherited by other modules in order to enhance extensibility.""" self.ensure_one() - self._calc_adu() self._calc_qualified_demand() + self._calc_incoming_dlt_qty() self._calc_net_flow_position() self._calc_planning_priority() self._calc_execution_priority() - self.mrp_production_ids._compute_execution_priority() + self.mrp_production_ids._calc_execution_priority() + self.purchase_lines_ids._calc_execution_priority() return True - @api.model def cron_ddmrp(self, automatic=False): """calculate key DDMRP parameters for each orderpoint @@ -569,7 +675,11 @@ def cron_ddmrp(self, automatic=False): """ _logger.info("Start cron_ddmrp.") orderpoints = self.search([]) + i = 0 + j = len(orderpoints) for op in orderpoints: + i += 1 + _logger.debug("ddmrp cron: %s. (%s/%s)" % (op.name, i, j)) try: op.cron_actions() if automatic: diff --git a/ddmrp/report/__init__.py b/ddmrp/report/__init__.py new file mode 100644 index 000000000..55d26865b --- /dev/null +++ b/ddmrp/report/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import bom_structure diff --git a/ddmrp/report/bom_structure.py b/ddmrp/report/bom_structure.py new file mode 100644 index 000000000..746fcefc2 --- /dev/null +++ b/ddmrp/report/bom_structure.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# © 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from openerp import models + + +class BomStructureReport(models.AbstractModel): + _inherit = 'report.mrp.report_mrpbomstructure' + + def get_children(self, record, level=0): + result = [] + + def _get_rec(record, level, qty=1.0, uom=False): + for l in record: + res = {} + if l.product_id.bom_ids: + lead_time = l.product_id.produce_delay + else: + lead_time = l.product_id.seller_ids and \ + l.product_id.seller_ids[0].delay or 0.0 + res['pname'] = l.product_id.name_get()[0][1] + res['pcode'] = l.product_id.default_code + qty_per_bom = l.bom_id.product_qty + if uom: + if uom != l.bom_id.product_uom_id: + qty = uom._compute_quantity( + qty, l.bom_id.product_uom_id) + res['pqty'] = (l.product_qty * qty) / qty_per_bom + else: + # for the first case, the ponderation is right + res['pqty'] = (l.product_qty * qty) + res['puom'] = l.product_uom_id + res['uname'] = l.product_uom_id.name + res['level'] = level + res['code'] = l.bom_id.code + res['location_name'] = l.location_id.complete_name or '' + res['is_buffered'] = l.is_buffered + res['has_mto_rule'] = l.has_mto_rule + res['lead_time'] = lead_time or '' + res['dlt'] = l.dlt + result.append(res) + if l.child_line_ids: + if level < 6: + level += 1 + _get_rec( + l.child_line_ids, level, + qty=res['pqty'], uom=res['puom']) + if 0 < level < 6: + level -= 1 + return result + + children = _get_rec(record, level) + + return children diff --git a/ddmrp/security/stock_security.xml b/ddmrp/security/stock_security.xml index 0b7a63d0e..c13b7e5a9 100644 --- a/ddmrp/security/stock_security.xml +++ b/ddmrp/security/stock_security.xml @@ -1,7 +1,5 @@ - - - + Buffer Profile Variability multi-company @@ -32,5 +30,4 @@ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - + diff --git a/ddmrp/static/img/demo/101.png b/ddmrp/static/img/demo/101.png new file mode 100644 index 000000000..8356808bb Binary files /dev/null and b/ddmrp/static/img/demo/101.png differ diff --git a/ddmrp/static/img/demo/102.png b/ddmrp/static/img/demo/102.png new file mode 100644 index 000000000..fa24a9d0a Binary files /dev/null and b/ddmrp/static/img/demo/102.png differ diff --git a/ddmrp/static/img/demo/201.png b/ddmrp/static/img/demo/201.png new file mode 100644 index 000000000..ed7d99a1b Binary files /dev/null and b/ddmrp/static/img/demo/201.png differ diff --git a/ddmrp/static/img/demo/202.png b/ddmrp/static/img/demo/202.png new file mode 100644 index 000000000..fb1f6e440 Binary files /dev/null and b/ddmrp/static/img/demo/202.png differ diff --git a/ddmrp/static/img/demo/203.png b/ddmrp/static/img/demo/203.png new file mode 100644 index 000000000..0f36c8def Binary files /dev/null and b/ddmrp/static/img/demo/203.png differ diff --git a/ddmrp/static/img/demo/207.png b/ddmrp/static/img/demo/207.png new file mode 100644 index 000000000..ab159fe17 Binary files /dev/null and b/ddmrp/static/img/demo/207.png differ diff --git a/ddmrp/static/img/demo/301.png b/ddmrp/static/img/demo/301.png new file mode 100644 index 000000000..3c5243668 Binary files /dev/null and b/ddmrp/static/img/demo/301.png differ diff --git a/ddmrp/static/img/demo/302P.png b/ddmrp/static/img/demo/302P.png new file mode 100644 index 000000000..b5746e7ca Binary files /dev/null and b/ddmrp/static/img/demo/302P.png differ diff --git a/ddmrp/static/img/demo/303.png b/ddmrp/static/img/demo/303.png new file mode 100644 index 000000000..244b0f630 Binary files /dev/null and b/ddmrp/static/img/demo/303.png differ diff --git a/ddmrp/static/img/demo/305P.png b/ddmrp/static/img/demo/305P.png new file mode 100644 index 000000000..2e4b521df Binary files /dev/null and b/ddmrp/static/img/demo/305P.png differ diff --git a/ddmrp/static/img/demo/306.png b/ddmrp/static/img/demo/306.png new file mode 100644 index 000000000..8a44bde99 Binary files /dev/null and b/ddmrp/static/img/demo/306.png differ diff --git a/ddmrp/static/img/demo/310.png b/ddmrp/static/img/demo/310.png new file mode 100644 index 000000000..0eb08420d Binary files /dev/null and b/ddmrp/static/img/demo/310.png differ diff --git a/ddmrp/static/img/demo/401P.png b/ddmrp/static/img/demo/401P.png new file mode 100644 index 000000000..60e536c18 Binary files /dev/null and b/ddmrp/static/img/demo/401P.png differ diff --git a/ddmrp/static/img/demo/402P.png b/ddmrp/static/img/demo/402P.png new file mode 100644 index 000000000..f5eff0d9c Binary files /dev/null and b/ddmrp/static/img/demo/402P.png differ diff --git a/ddmrp/static/img/demo/403P.png b/ddmrp/static/img/demo/403P.png new file mode 100644 index 000000000..c53991286 Binary files /dev/null and b/ddmrp/static/img/demo/403P.png differ diff --git a/ddmrp/static/img/demo/404P.png b/ddmrp/static/img/demo/404P.png new file mode 100644 index 000000000..c668e4626 Binary files /dev/null and b/ddmrp/static/img/demo/404P.png differ diff --git a/ddmrp/static/img/demo/410P.png b/ddmrp/static/img/demo/410P.png new file mode 100644 index 000000000..5f38cbbd3 Binary files /dev/null and b/ddmrp/static/img/demo/410P.png differ diff --git a/ddmrp/static/img/demo/411P.png b/ddmrp/static/img/demo/411P.png new file mode 100644 index 000000000..9c0f9b70f Binary files /dev/null and b/ddmrp/static/img/demo/411P.png differ diff --git a/ddmrp/static/img/demo/FPA.png b/ddmrp/static/img/demo/FPA.png new file mode 100644 index 000000000..69bc80ee6 Binary files /dev/null and b/ddmrp/static/img/demo/FPA.png differ diff --git a/ddmrp/static/img/demo/FPB.png b/ddmrp/static/img/demo/FPB.png new file mode 100644 index 000000000..b8e568c64 Binary files /dev/null and b/ddmrp/static/img/demo/FPB.png differ diff --git a/ddmrp/static/img/demo/FPC.png b/ddmrp/static/img/demo/FPC.png new file mode 100644 index 000000000..7b5e8b770 Binary files /dev/null and b/ddmrp/static/img/demo/FPC.png differ diff --git a/ddmrp/static/img/has_mto_rule.png b/ddmrp/static/img/has_mto_rule.png new file mode 100644 index 000000000..f568780bb Binary files /dev/null and b/ddmrp/static/img/has_mto_rule.png differ diff --git a/ddmrp/static/img/is_buffered.png b/ddmrp/static/img/is_buffered.png new file mode 100644 index 000000000..a0b0ed73a Binary files /dev/null and b/ddmrp/static/img/is_buffered.png differ diff --git a/ddmrp/static/img/res_partner_eficent-image.jpg b/ddmrp/static/img/res_partner_eficent-image.jpg new file mode 100644 index 000000000..0cb83687f Binary files /dev/null and b/ddmrp/static/img/res_partner_eficent-image.jpg differ diff --git a/ddmrp/tests/__init__.py b/ddmrp/tests/__init__.py index f8677f290..4990ad14a 100644 --- a/ddmrp/tests/__init__.py +++ b/ddmrp/tests/__init__.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import test_ddmrp diff --git a/ddmrp/tests/test_ddmrp.py b/ddmrp/tests/test_ddmrp.py index 0b391dca3..04b3f5032 100644 --- a/ddmrp/tests/test_ddmrp.py +++ b/ddmrp/tests/test_ddmrp.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -import openerp.tests.common as common -from openerp import fields +import odoo.tests.common as common +from odoo import fields from datetime import datetime, timedelta @@ -220,7 +220,7 @@ def test_adu_calculation_past_120_days(self): def test_adu_calculation_internal_past_120_days(self): """ - Test that internal moves will not affect ADU calculation + Test that internal moves will not affect ADU calculation. """ method = self.env.ref('ddmrp.adu_calculation_method_past_120') orderpointA = self.orderpointModel.create({ @@ -435,7 +435,7 @@ def test_qualified_demand_4(self): self.assertEqual(orderpointA.qualified_demand, expected_result) def test_qualified_demand_5(self): - """Internal moves within the zone designated by the buffer + """Internal moves within the zone designated by the buffer should not be considered demand.""" method = self.env.ref('ddmrp.adu_calculation_method_fixed') orderpointA = self.orderpointModel.create({ diff --git a/ddmrp/views/mrp_bom_view.xml b/ddmrp/views/mrp_bom_view.xml new file mode 100644 index 000000000..95c49f7cc --- /dev/null +++ b/ddmrp/views/mrp_bom_view.xml @@ -0,0 +1,26 @@ + + + + + + mrp.bom.form - mrp_bom_location + mrp.bom + + + + + + + + + + + + + + + + + + diff --git a/ddmrp/views/mrp_production_view.xml b/ddmrp/views/mrp_production_view.xml index 14dba655d..ea760c5dc 100644 --- a/ddmrp/views/mrp_production_view.xml +++ b/ddmrp/views/mrp_production_view.xml @@ -1,45 +1,43 @@ - - - + + - - mrp.production.tree - mrp.production - - - - - - + + mrp.production.tree + mrp.production + + + + + - + + - - mrp.production.select - mrp.production - - - - - - - - - - + + mrp.production.select + mrp.production + + + + + + + + + - + + - - + diff --git a/ddmrp/views/procurement_order_view.xml b/ddmrp/views/procurement_order_view.xml index d9eb2760e..56d4f1ff6 100644 --- a/ddmrp/views/procurement_order_view.xml +++ b/ddmrp/views/procurement_order_view.xml @@ -1,17 +1,15 @@ - - - + + - - procurement.order.select - procurement.order - - - - - + + procurement.order.select + procurement.order + + + + - + + - - + diff --git a/ddmrp/views/product_adu_calculation_method_view.xml b/ddmrp/views/product_adu_calculation_method_view.xml index 0d328a894..fe65e75b5 100644 --- a/ddmrp/views/product_adu_calculation_method_view.xml +++ b/ddmrp/views/product_adu_calculation_method_view.xml @@ -1,67 +1,65 @@ - - - + + - - product.adu.calculation.method.tree - product.adu.calculation.method - - + + product.adu.calculation.method.tree + product.adu.calculation.method + + + + + + + + product.adu.calculation.method.form + product.adu.calculation.method + +
+ - - - - - product.adu.calculation.method.form - product.adu.calculation.method - - - - - - - - - - - - - - - +
+ + + + + + + + +
+
- - product.adu.calculation.method.search - product.adu.calculation.method - - - - - - + + product.adu.calculation.method.search + product.adu.calculation.method + + + + + + - - ADU calculation methods - ir.actions.act_window - product.adu.calculation.method - form - tree,form - - + + ADU calculation methods + ir.actions.act_window + product.adu.calculation.method + form + tree,form + + - + -
-
+ diff --git a/ddmrp/views/purchase_order_line_view.xml b/ddmrp/views/purchase_order_line_view.xml new file mode 100644 index 000000000..9d9c132fa --- /dev/null +++ b/ddmrp/views/purchase_order_line_view.xml @@ -0,0 +1,100 @@ + + + + + + purchase.order.form - ddmrp + purchase.order + + + + + + + + + + + + + + purchase.order.line.tree - ddmrp + purchase.order.line + + + + + + + + + + + + + + + + + purchase.order.line.search - ddmrp + purchase.order.line + + + + + + + + + + + + + + + request.quotation.select - ddmrp + purchase.order + + + + + + + + + + + + PO lines On-Hand Status + purchase.order.line + + [('orderpoint_id','!=',False), ('execution_priority_level','!=',False)] + tree + + + + + diff --git a/ddmrp/views/report_mrpbomstructure.xml b/ddmrp/views/report_mrpbomstructure.xml new file mode 100644 index 000000000..9adfd6400 --- /dev/null +++ b/ddmrp/views/report_mrpbomstructure.xml @@ -0,0 +1,57 @@ + + + + diff --git a/ddmrp/views/stock_buffer_profile_lead_time_view.xml b/ddmrp/views/stock_buffer_profile_lead_time_view.xml index 05b4e52da..256ab3fff 100644 --- a/ddmrp/views/stock_buffer_profile_lead_time_view.xml +++ b/ddmrp/views/stock_buffer_profile_lead_time_view.xml @@ -1,75 +1,77 @@ - - - - - stock.buffer.profile.lead.time.form - stock.buffer.profile.lead.time - -
- -
-
- - - - - - - + + + + + stock.buffer.profile.lead.time.form + stock.buffer.profile.lead.time + + + +
+
+ + + + + + -
- -
-
+
+
+ +
+
- - stock.buffer.profile.lead.time.tree - stock.buffer.profile.lead.time - - - - - - - - + + stock.buffer.profile.lead.time.tree + stock.buffer.profile.lead.time + + + + + + + + - - stock.buffer.profile.lead.time.search - stock.buffer.profile.lead.time - - - - - - - - + + stock.buffer.profile.lead.time.search + stock.buffer.profile.lead.time + + + + + + + + - - Buffer Profile Lead Time Factor - ir.actions.act_window - stock.buffer.profile.lead.time - form - tree,form - - -

- Click to start a new buffer profile lead time factor -

-
-
+ + Buffer Profile Lead Time Factor + ir.actions.act_window + stock.buffer.profile.lead.time + form + tree,form + + +

+ Click to start a new buffer profile lead time factor +

+
+
- + -
-
+ diff --git a/ddmrp/views/stock_buffer_profile_variability_view.xml b/ddmrp/views/stock_buffer_profile_variability_view.xml index c63fbb9c2..21bb670cf 100644 --- a/ddmrp/views/stock_buffer_profile_variability_view.xml +++ b/ddmrp/views/stock_buffer_profile_variability_view.xml @@ -1,75 +1,77 @@ - - - - - stock.buffer.profile.variability.form - stock.buffer.profile.variability - -
- -
-
- - - - - - - + + + + + stock.buffer.profile.variability.form + stock.buffer.profile.variability + + + +
+
+ + + + + + -
- -
-
+
+
+ +
+
- - stock.buffer.profile.variability.tree - stock.buffer.profile.variability - - - - - - - - + + stock.buffer.profile.variability.tree + stock.buffer.profile.variability + + + + + + + + - - stock.buffer.profile.variability.search - stock.buffer.profile.variability - - - - - - - - + + stock.buffer.profile.variability.search + stock.buffer.profile.variability + + + + + + + + - - Buffer Profile Variability Factor - ir.actions.act_window - stock.buffer.profile.variability - form - tree,form - - -

- Click to start a new buffer profile variability factor -

-
-
+ + Buffer Profile Variability Factor + ir.actions.act_window + stock.buffer.profile.variability + form + tree,form + + +

+ Click to start a new buffer profile variability factor +

+
+
- + -
-
+ diff --git a/ddmrp/views/stock_buffer_profile_view.xml b/ddmrp/views/stock_buffer_profile_view.xml index eb0dffd4a..b6b23eea9 100644 --- a/ddmrp/views/stock_buffer_profile_view.xml +++ b/ddmrp/views/stock_buffer_profile_view.xml @@ -1,89 +1,93 @@ - - - - - stock.buffer.profile.form - stock.buffer.profile - -
- -
-
- - - - - - - - - - - - - - - - + + + + + stock.buffer.profile.form + stock.buffer.profile + + + +
+
+ + + + + + + + + -
- -
-
+ + + + + + +
+
+ +
+
+ + + stock.buffer.profile.tree + stock.buffer.profile + + + + + + + + + + - - stock.buffer.profile.tree - stock.buffer.profile - - - - - - - - - - + + stock.buffer.profile.search + stock.buffer.profile + + + + + + + + + + + + - - stock.buffer.profile.search - stock.buffer.profile - - - - - - - - - - - + + Buffer Profiles + ir.actions.act_window + stock.buffer.profile + form + tree,form + + +

+ Click to start a new buffer profile +

+
+
- - Buffer Profiles - ir.actions.act_window - stock.buffer.profile - form - tree,form - - -

- Click to start a new buffer profile -

-
-
+ - + -
-
+ diff --git a/ddmrp/views/stock_warehouse_orderpoint_view.xml b/ddmrp/views/stock_warehouse_orderpoint_view.xml index 83cada540..db0504a8b 100644 --- a/ddmrp/views/stock_warehouse_orderpoint_view.xml +++ b/ddmrp/views/stock_warehouse_orderpoint_view.xml @@ -1,255 +1,264 @@ - - - + + - - stock.warehouse.orderpoint.tree - stock.warehouse.orderpoint - - - + + stock.warehouse.orderpoint.tree + stock.warehouse.orderpoint + + + + + + + + + + + + + + + + + + + + + + + + + stock.warehouse.orderpoint.search + stock.warehouse.orderpoint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stock.warehouse.orderpoint.form + stock.warehouse.orderpoint + + + + + + - - - + + + + + + +
+ +
+
+ + + + + + + + + +
+

+ The green zone determines the average order frequency and the order size. It + is determined as the maximum of the following three factors: Minimum Order Cycle, + Lead Time Factor and Minimum Order Quantity. +

+
+
+ + + + + +
+

+ The yellow zone represents the + stock required to cover a full lead time. +

+
+
+ + + +
+

+ The red zone is the embedded safety in + the buffer. The larger the variability + associated with the product, the larger + the red zone will be. It is composed of + two sub-zones: Red base and red safety. +

+
+
- - - - - - - - - - - stock.warehouse.orderpoint.search - stock.warehouse.orderpoint - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - stock.warehouse.orderpoint.form - stock.warehouse.orderpoint - - - - - - - - - -
- -
-
- - - - - - - - - -
-

- The green zone determines the average order frequency and the order size. It - is determined as the maximum of the following three factors: Minimum Order Cycle, - Lead Time Factor and Minimum Order Quantity. -

-
-
- - - - - - -
-

- The yellow zone represents the - stock required to cover a full lead time. -

-
-
- - - -
-

- The red zone is the embedded safety in - the buffer. The larger the variability - associated with the product, the larger - the red zone will be. It is composed of - two sub-zones: Red base and red safety. -

-
-
- - - - - - - - - - - - - - - - stock.warehouse.orderpoint.planner.tree - stock.warehouse.orderpoint - 50 - - - - - - - - - - - - + + + + diff --git a/ddmrp_adjustment/wizards/__init__.py b/ddmrp_adjustment/wizards/__init__.py new file mode 100644 index 000000000..4c3117ec2 --- /dev/null +++ b/ddmrp_adjustment/wizards/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import ddmrp_adjustment_sheet diff --git a/ddmrp_adjustment/wizards/ddmrp_adjustment_sheet.py b/ddmrp_adjustment/wizards/ddmrp_adjustment_sheet.py new file mode 100644 index 000000000..df3581457 --- /dev/null +++ b/ddmrp_adjustment/wizards/ddmrp_adjustment_sheet.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + +from ..models.ddmrp_adjustment import DAF_string, LTAF_string + + +class DdmrpAdjustmentSheet(models.TransientModel): + _name = 'ddmrp.adjustment.sheet' + _description = 'Buffer Adjustment Sheet' + + def _get_factors(self): + factors = [] + if self.apply_daf: + factors.append(DAF_string) + if self.apply_ltaf: + factors.append(LTAF_string) + return factors + + def _prepare_line(self, period, factor): + vals = { + 'sheet_id': self.id, + 'date_range_id': period.id, + 'factor': factor, + 'value': 0.0, + } + return vals + + @api.multi + def create_lines(self): + self.ensure_one() + periods = self.env['date.range'].search([ + ('type_id', '=', self.date_range_type_id.id), '|', + '&', + ('date_start', '>=', self.date_start), + ('date_start', '<=', self.date_end), + '&', + ('date_end', '>=', self.date_start), + ('date_end', '<=', self.date_end)]) + # TODO: re-check + domain = [('type_id', '=', self.date_range_type_id.id), + ('date_start', '<=', self.date_start), + ('date_end', '>=', self.date_start)] + periods |= self.env['date.range'].search( + domain) + factors = self._get_factors() + items = [] + for period in periods: + for factor in factors: + vals = self._prepare_line(period, factor) + items.append([0, 0, vals]) + return items + + @api.multi + @api.constrains('date_start', 'date_end') + def _check_start_end_dates(self): + for rec in self: + if rec.date_start > rec.date_end: + raise ValidationError(_( + 'The start date cannot be later than the end date.')) + + date_start = fields.Date( + string="Date From", required=True) + date_end = fields.Date( + string="Date To", required=True) + date_range_type_id = fields.Many2one( + string='Date Range Type', comodel_name='date.range.type', + required=True) + buffer_ids = fields.Many2many( + comodel_name="stock.warehouse.orderpoint", string="DDMRP Buffers") + line_ids = fields.Many2many( + string="Adjustments", + comodel_name='ddmrp.adjustment.sheet.line') + apply_daf = fields.Boolean(string="Demand Adjustment Factor") + apply_ltaf = fields.Boolean(string="Lead Time Adjustment Factor") + + # HACK: https://github.com/OCA/server-tools/pull/492#issuecomment-237594285 + @api.multi + def onchange(self, values, field_name, field_onchange): # pragma: no cover + if "line_ids" in field_onchange: + for sub in ("date_range_id", "value", "factor"): + field_onchange.setdefault("line_ids." + sub, u"") + return super(DdmrpAdjustmentSheet, self).onchange( + values, field_name, field_onchange) + + @api.onchange('date_range_type_id', 'date_start', 'date_end', 'apply_daf', + 'apply_ltaf') + def _onchange_sheet(self): + self.line_ids = [(6, 0, [])] + lines = self.create_lines() + self.line_ids = lines + + @api.multi + def button_validate(self): + self.ensure_one() + if not self.buffer_ids: + raise ValidationError(_('You must select at least one buffer.')) + + if not self.line_ids.mapped('factor'): + raise ValidationError(_('You must apply at least one factor')) + + res = [] + for b in self.buffer_ids: + for line in self.line_ids: + data = line._prepare_adjustment_data(b) + estimate = self.env['ddmrp.adjustment'].create( + data) + res.append(estimate.id) + action = { + 'domain': [('id', 'in', res)], + 'name': _('DDMRP Buffer Adjustment'), + 'src_model': 'ddmrp.adjustment.sheet', + 'view_type': 'form', + 'view_mode': 'tree', + 'res_model': 'ddmrp.adjustment', + 'type': 'ir.actions.act_window' + } + return action + + +class DdmrpAdjustmentSheetLine(models.TransientModel): + _name = 'ddmrp.adjustment.sheet.line' + _description = 'Buffer Adjustment Sheet Line' + + sheet_id = fields.Many2one(comodel_name='ddmrp.adjustment.sheet') + date_range_id = fields.Many2one( + comodel_name='date.range', string='Period') + factor = fields.Char(string='Factors') + value = fields.Float(string="Value") + + @api.model + def _prepare_adjustment_data(self, buf): + data = { + 'date_range_id': self.date_range_id.id, + 'buffer_id': buf.id, + } + if self.factor == DAF_string: + data['daf'] = self.value + if self.factor == LTAF_string: + data['ltaf'] = self.value + return data diff --git a/ddmrp_adjustment/wizards/ddmrp_adjustment_wizard_view.xml b/ddmrp_adjustment/wizards/ddmrp_adjustment_wizard_view.xml new file mode 100644 index 000000000..f2696f098 --- /dev/null +++ b/ddmrp_adjustment/wizards/ddmrp_adjustment_wizard_view.xml @@ -0,0 +1,65 @@ + + + + + ddmrp.adjustment.sheet.form1 + ddmrp.adjustment.sheet + +
+ + + + + + + + + + + + + + + + + + +

Fill the factors to apply in each period for all the + selected Buffers.



+ +
+
+
+ +
+
+ + + + + +
diff --git a/ddmrp_exclude_moves_adu_calc/README.rst b/ddmrp_exclude_moves_adu_calc/README.rst index c0db5ee42..c454b3987 100644 --- a/ddmrp_exclude_moves_adu_calc/README.rst +++ b/ddmrp_exclude_moves_adu_calc/README.rst @@ -12,23 +12,22 @@ Average Dailiy Usage on an Orderpoint (Buffer), based on: * Locations * Specific stock moves - Usage ===== -Go to 'Configuration / Reordering Rules' and indicate a Procurement UoM. - - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/153/8.0 +You can exclude specific moves or all moves towards a specific location: +* Go to *Inventory > Reports > Stock Moves* and check the box *Exclude this + move from ADU calculation* for a specific move. +* Go to *Inventory > Configuration > WH Management > Locations* and check the + flag *Exclude this location from ADU calculation* for the desired locations. Bug Tracker =========== Bugs are tracked on `GitHub Issues -`_. In case of trouble, please +`_. In case of +trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. @@ -44,7 +43,7 @@ Contributors ------------ * Jordi Ballester Alomar - +* Lois Rilo Antelo Maintainer ---------- diff --git a/ddmrp_exclude_moves_adu_calc/__init__.py b/ddmrp_exclude_moves_adu_calc/__init__.py index e50f9b656..149f4e4fd 100644 --- a/ddmrp_exclude_moves_adu_calc/__init__.py +++ b/ddmrp_exclude_moves_adu_calc/__init__.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models diff --git a/ddmrp_exclude_moves_adu_calc/__openerp__.py b/ddmrp_exclude_moves_adu_calc/__manifest__.py similarity index 74% rename from ddmrp_exclude_moves_adu_calc/__openerp__.py rename to ddmrp_exclude_moves_adu_calc/__manifest__.py index e0a71ea24..dca1007ac 100644 --- a/ddmrp_exclude_moves_adu_calc/__openerp__.py +++ b/ddmrp_exclude_moves_adu_calc/__manifest__.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - "name": "DDMRP Exclude Moves ADU Calc", + "name": "DDMRP Exclude Moves ADU Calc", "summary": "Define additional rules to exclude certain moves from ADU " "calculation", - "version": "9.0.1.0.0", + "version": "10.0.1.0.0", "author": "Eficent," "Odoo Community Association (OCA)", "website": "https://www.odoo-community.org", @@ -15,7 +15,8 @@ "depends": ["ddmrp"], "data": [ "views/stock_move_view.xml", - "views/stock_location_view.xml"], + "views/stock_location_view.xml", + ], "license": "AGPL-3", 'installable': True, 'application': False, diff --git a/ddmrp_exclude_moves_adu_calc/models/__init__.py b/ddmrp_exclude_moves_adu_calc/models/__init__.py index b719bec35..d5e871fb2 100644 --- a/ddmrp_exclude_moves_adu_calc/models/__init__.py +++ b/ddmrp_exclude_moves_adu_calc/models/__init__.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import stock_location diff --git a/ddmrp_exclude_moves_adu_calc/models/stock_location.py b/ddmrp_exclude_moves_adu_calc/models/stock_location.py index 765a86415..f89754399 100644 --- a/ddmrp_exclude_moves_adu_calc/models/stock_location.py +++ b/ddmrp_exclude_moves_adu_calc/models/stock_location.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import fields, models +from odoo import fields, models class StockLocation(models.Model): @@ -12,4 +12,5 @@ class StockLocation(models.Model): exclude_from_adu = fields.Boolean( string='Exclude this location from ADU calculation', copy=False, help="If this flag is set stock moves into this location will be " - "excluded from ADU calculation in the origin location buffer.") + "excluded from ADU calculation in the origin location buffer.", + ) diff --git a/ddmrp_exclude_moves_adu_calc/models/stock_move.py b/ddmrp_exclude_moves_adu_calc/models/stock_move.py index 00a488aa6..030824eed 100644 --- a/ddmrp_exclude_moves_adu_calc/models/stock_move.py +++ b/ddmrp_exclude_moves_adu_calc/models/stock_move.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import fields, models +from odoo import fields, models class StockMove(models.Model): @@ -12,4 +12,5 @@ class StockMove(models.Model): exclude_from_adu = fields.Boolean( string='Exclude this move from ADU calculation', copy=False, help="If this flag is set this stock move will be excluded from ADU " - "calculation") + "calculation", + ) diff --git a/ddmrp_exclude_moves_adu_calc/models/stock_warehouse_orderpoint.py b/ddmrp_exclude_moves_adu_calc/models/stock_warehouse_orderpoint.py index aab4842fe..4acfd0061 100644 --- a/ddmrp_exclude_moves_adu_calc/models/stock_warehouse_orderpoint.py +++ b/ddmrp_exclude_moves_adu_calc/models/stock_warehouse_orderpoint.py @@ -1,30 +1,25 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, models +from odoo import api, models class StockWarehouseOrderpoint(models.Model): _inherit = "stock.warehouse.orderpoint" @api.model - def _exclude_past_moves_domain(self, locations): + def _exclude_past_moves_domain(self): return [('exclude_from_adu', '=', True), ('state', '=', 'done')] @api.model def _past_moves_domain(self, date_from, locations): - - exclude_locs = self.env['stock.location'].search([( - 'exclude_from_adu', '=', True)]) - new_locs = self.env['stock.location'] - for loc in locations: - if loc not in exclude_locs: - new_locs += loc + new_locs = locations.filtered(lambda l: not l.exclude_from_adu) res = super(StockWarehouseOrderpoint, self)._past_moves_domain(date_from, new_locs) exclude_moves = self.env['stock.move'].search( - self._exclude_past_moves_domain(new_locs)) - res.append(('id', 'not in', exclude_moves.ids)) + self._exclude_past_moves_domain()) + if exclude_moves: + res.append(('id', 'not in', exclude_moves.ids)) return res diff --git a/ddmrp_exclude_moves_adu_calc/static/description/icon.png b/ddmrp_exclude_moves_adu_calc/static/description/icon.png index 3a0328b51..a0b0ed73a 100644 Binary files a/ddmrp_exclude_moves_adu_calc/static/description/icon.png and b/ddmrp_exclude_moves_adu_calc/static/description/icon.png differ diff --git a/ddmrp_exclude_moves_adu_calc/tests/__init__.py b/ddmrp_exclude_moves_adu_calc/tests/__init__.py index f8677f290..4990ad14a 100644 --- a/ddmrp_exclude_moves_adu_calc/tests/__init__.py +++ b/ddmrp_exclude_moves_adu_calc/tests/__init__.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import test_ddmrp diff --git a/ddmrp_exclude_moves_adu_calc/tests/test_ddmrp.py b/ddmrp_exclude_moves_adu_calc/tests/test_ddmrp.py index 7f9076b00..1cfdb9897 100644 --- a/ddmrp_exclude_moves_adu_calc/tests/test_ddmrp.py +++ b/ddmrp_exclude_moves_adu_calc/tests/test_ddmrp.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016-18 Eficent Business and IT Consulting Services S.L. # (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) +# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -import openerp.tests.common as common -from openerp import fields +import odoo.tests.common as common from datetime import datetime, timedelta diff --git a/ddmrp_exclude_moves_adu_calc/views/stock_location_view.xml b/ddmrp_exclude_moves_adu_calc/views/stock_location_view.xml index e15f21411..384b90dc3 100644 --- a/ddmrp_exclude_moves_adu_calc/views/stock_location_view.xml +++ b/ddmrp_exclude_moves_adu_calc/views/stock_location_view.xml @@ -1,17 +1,15 @@ - + - - - stock.location.form - stock.location - - - - - + + stock.location.form + stock.location + + + + - + + - diff --git a/ddmrp_exclude_moves_adu_calc/views/stock_move_view.xml b/ddmrp_exclude_moves_adu_calc/views/stock_move_view.xml index d56858193..3602692ea 100644 --- a/ddmrp_exclude_moves_adu_calc/views/stock_move_view.xml +++ b/ddmrp_exclude_moves_adu_calc/views/stock_move_view.xml @@ -1,17 +1,15 @@ - + - - - stock.move.form - stock.move - - - - - + + stock.move.form + stock.move + + + + - + + - diff --git a/ddmrp_history/README.rst b/ddmrp_history/README.rst index 28af79d41..6ea23b4a6 100644 --- a/ddmrp_history/README.rst +++ b/ddmrp_history/README.rst @@ -5,7 +5,21 @@ DDMRP History ============= -Allow to extend DDMRP App to store history data buffers. +Allow to extend DDMRP App to store historical data of buffers. + +Installation +============ + +You need to install the python pandas library:: + + pip install pandas==0.20.0 + +Usage +===== + +You can access, as a inventory manager, to all the DDMRP historical data +throught *Inventory > Reports > DDMRP Buffer History*. Additionally you can +see a historical evolution chart of any buffer at the bottom of its form view. Bug Tracker =========== diff --git a/ddmrp_history/__openerp__.py b/ddmrp_history/__manifest__.py similarity index 74% rename from ddmrp_history/__openerp__.py rename to ddmrp_history/__manifest__.py index d7f78adab..c2b2e4e53 100644 --- a/ddmrp_history/__openerp__.py +++ b/ddmrp_history/__manifest__.py @@ -6,8 +6,8 @@ { "name": "DDMRP History", "summary": "Allow to store historical data of DDMRP buffers.", - "version": "9.0.1.0.0", - "author": "Eficent", + "version": "10.0.1.0.0", + "author": "Eficent, Odoo Community Association (OCA)", "website": "http://www.eficent.com", "category": "Warehouse Management", "depends": ["ddmrp"], @@ -16,6 +16,12 @@ "views/ddmrp_history_view.xml", "views/stock_warehouse_orderpoint_view.xml", ], + "demo": [ + 'demo/ddmrp.history.csv', + ], + "external_dependencies": { + "python": ['pandas'], + }, "license": "AGPL-3", 'installable': True, } diff --git a/ddmrp_history/demo/ddmrp.history.csv b/ddmrp_history/demo/ddmrp.history.csv new file mode 100644 index 000000000..e72811637 --- /dev/null +++ b/ddmrp_history/demo/ddmrp.history.csv @@ -0,0 +1,12 @@ +id,orderpoint_id/id,date,net_flow_position,on_hand_position,top_of_green,top_of_red,top_of_yellow +ddmrp_history_901p_1,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 00:45:32,400,400,492.82,122.28,400.88 +ddmrp_history_901p_2,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 03:53:30,400,400,492.82,122.28,400.88 +ddmrp_history_901p_3,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 07:19:22,400,400,492.82,122.28,400.88 +ddmrp_history_901p_4,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 10:39:18,400,400,492.82,122.28,400.88 +ddmrp_history_901p_5,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 13:56:05,395,395,496.94,123.3,404.23 +ddmrp_history_901p_6,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 17:15:05,395,395,496.94,123.3,404.23 +ddmrp_history_901p_7,ddmrp.stock_warehouse_orderpoint_901p,2017-09-14 20:37:16,390,390,501.08,124.33,407.6 +ddmrp_history_901p_8,ddmrp.stock_warehouse_orderpoint_901p,2017-09-15 00:05:02,389,389,412.74,102.41,335.74 +ddmrp_history_901p_9,ddmrp.stock_warehouse_orderpoint_901p,2017-09-15 18:55:42,381,381,419.35,104.05,341.12 +ddmrp_history_901p_10,ddmrp.stock_warehouse_orderpoint_901p,2017-09-18 16:33:40,371,371,266.62,66.15,216.88 +ddmrp_history_901p_11,ddmrp.stock_warehouse_orderpoint_901p,2017-09-18 19:59:13,387,371,266.62,66.15,216.88 diff --git a/ddmrp_history/models/ddmrp_history.py b/ddmrp_history/models/ddmrp_history.py index b49f3b845..b218c5223 100644 --- a/ddmrp_history/models/ddmrp_history.py +++ b/ddmrp_history/models/ddmrp_history.py @@ -3,10 +3,10 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import api, fields, models -from openerp.addons import decimal_precision as dp +from odoo import fields, models +from odoo.addons import decimal_precision as dp -UNIT = dp.get_precision('Product Unit of Measure') +UNIT = dp.get_precision("Product Unit of Measure") class DdmrpHistory(models.Model): @@ -14,16 +14,30 @@ class DdmrpHistory(models.Model): orderpoint_id = fields.Many2one( comodel_name="stock.warehouse.orderpoint", string="Buffer", - readonly=True) + readonly=True, ondelete="cascade", + ) date = fields.Datetime( - string="Date", readonly=True) + string="Date", readonly=True, + ) top_of_red = fields.Float( - string="TOR", readonly=True, help="Top of Red") + string="TOR", readonly=True, + help="Top of Red", group_operator="avg", + ) top_of_yellow = fields.Float( - string="TOY", readonly=True, help="Top of Yellow") + string="TOY", readonly=True, + help="Top of Yellow", group_operator="avg", + ) top_of_green = fields.Float( - string="TOG", readonly=True, help="Top of Green") + string="TOG", readonly=True, + help="Top of Green", group_operator="avg", + ) net_flow_position = fields.Float( - string="NFP", digits=UNIT, readonly=True, help="Net flow position") + string="NFP", digits=UNIT, + readonly=True, help="Net flow position", + group_operator="avg", + ) on_hand_position = fields.Float( - string="OHP", digits=UNIT, readonly=True, help="On-Hand Position") + string="OHP", digits=UNIT, + readonly=True, help="On-Hand Position", + group_operator="avg", + ) diff --git a/ddmrp_history/models/stock_warehouse_orderpoint.py b/ddmrp_history/models/stock_warehouse_orderpoint.py index 5c52fc698..333338074 100644 --- a/ddmrp_history/models/stock_warehouse_orderpoint.py +++ b/ddmrp_history/models/stock_warehouse_orderpoint.py @@ -6,9 +6,9 @@ import logging from math import pi -from openerp import api, fields, models, _ +from odoo import api, fields, models, _ from datetime import datetime -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT _logger = logging.getLogger(__name__) try: @@ -24,6 +24,16 @@ class StockWarehouseOrderpoint(models.Model): _inherit = "stock.warehouse.orderpoint" + # TODO: computed through a button? able to select period. + planning_history_chart = fields.Text( + string='Historical Chart', + compute='_compute_history_chart', + ) + execution_history_chart = fields.Text( + string='Execution Historical Chart', + compute='_compute_execution_history_chart', + ) + def _prepare_history_data(self): self.ensure_one() data = { @@ -69,8 +79,10 @@ def stacked(df, categories): r.date, DEFAULT_SERVER_DATETIME_FORMAT) for r in history] data['date'] = dates data[categories[0]] = [r.top_of_red for r in history] - data[categories[1]] = [r.top_of_yellow for r in history] - data[categories[2]] = [r.top_of_green for r in history] + data[categories[1]] = [r.top_of_yellow - + r.top_of_red for r in history] + data[categories[2]] = [r.top_of_green - + r.top_of_yellow for r in history] data['net_flow_position'] = [r.net_flow_position for r in history] data['on_hand_position'] = [r.on_hand_position for r in history] @@ -110,9 +122,113 @@ def stacked(df, categories): line_dash='dotted') script, div = components(p) - rec.history_chart = '%s%s' % (div, script) + rec.planning_history_chart = '%s%s' % (div, script) + + def _compute_execution_history_chart(self): + start_stack = 0 + + def stacked(df, categories): + areas = dict() + last = np.zeros(len(df[categories[0]])) + last += start_stack + for cat in categories: + next = last + df[cat] + areas[cat] = np.hstack((last[::-1], next)) + last = next + return areas + + for rec in self: + history_oh = self.env['ddmrp.history'].search([ + ('orderpoint_id', '=', rec.id)], order='on_hand_position desc', + limit=1) + history_tog = self.env['ddmrp.history'].search([ + ('orderpoint_id', '=', rec.id)], order='top_of_green desc', + limit=1) + finish_stack = max(history_oh.on_hand_position, + history_tog.top_of_green) + + history = self.env['ddmrp.history'].search([ + ('orderpoint_id', '=', rec.id)], order='on_hand_position asc', + limit=1) + start_stack = history.on_hand_position + if start_stack >= 0.0: + start_stack = 0.0 + history = self.env['ddmrp.history'].search([ + ('orderpoint_id', '=', rec.id)], order='date') + if len(history) < 2: + rec.history_chart = _("Not enough data available.") + continue + + N = len(history) + + categories = ['dark_red_low', 'top_of_red_low', + 'top_of_yellow_low', 'top_of_green', + 'top_of_yellow', 'top_of_red', 'dark_red'] + data = {} + + dates = [datetime.strptime( + r.date, DEFAULT_SERVER_DATETIME_FORMAT) for r in history] + data['date'] = dates + data[categories[0]] = [(0 - start_stack) for r in history] + data[categories[1]] = [(r.top_of_red/2) for r in history] + data[categories[2]] = [(r.top_of_red/2) for r in history] + data[categories[3]] = [r.top_of_green - r.top_of_yellow for r in + history] + data[categories[4]] = [r.top_of_yellow - r.top_of_red - ( + r.top_of_green - r.top_of_yellow) for r in history] + data[categories[5]] = [r.top_of_green - r.top_of_yellow for r in + history] + data[categories[6]] = \ + [finish_stack - r.top_of_red - (r.top_of_green - + r.top_of_yellow) - + (r.top_of_yellow - r.top_of_red - (r.top_of_green - + r.top_of_yellow)) - (r.top_of_green - r.top_of_yellow) for r + in history] + + data['net_flow_position'] = [r.net_flow_position for r in history] + data['on_hand_position'] = [r.on_hand_position for r in history] + df = pd.DataFrame(data) + df = df.set_index(['date']) + + areas = stacked(df, categories) + + # FIXME: create a get_colors method for share color from the two + # charst in ddmpr. + colors = ['#bb2f2f', '#ff0000', '#ffff00', '#33cc33', '#ffff00', + '#ff0000', '#bb2f2f'] + + x2 = np.hstack((data['date'][::-1], data['date'])) + + tops = [ + data[categories[0]][i] + + data[categories[1]][i] + + data[categories[2]][i] + + data[categories[3]][i] + + data[categories[4]][i] + + data[categories[5]][i] + + data[categories[6]][i] for i in range(N)] + top_y = max(tops) + p = figure( + x_range=(dates[0], dates[-1]), y_range=(start_stack, top_y), + x_axis_type='datetime') - # TODO: store false and computed through a button? - history_chart = fields.Text( - string='Historical Chart', compute='_compute_history_chart') + p.grid.minor_grid_line_color = '#eeeeee' + p.patches([x2] * len(areas), [areas[cat] for cat in categories], + color=colors, alpha=0.8, line_color=None) + p.xaxis.formatter = DatetimeTickFormatter( + hours=["%d %B %Y"], days=["%d %B %Y"], months=["%d %B %Y"], + years=["%d %B %Y"]) + p.xaxis.major_label_orientation = pi / 4 + + unit = rec.product_uom.name + hover = HoverTool(tooltips=[("qty", "$y %s" % unit)], + point_policy='follow_mouse') + p.add_tools(hover) + + p.line(dates, data['net_flow_position'], line_width=3) + p.line(dates, data['on_hand_position'], line_width=3, + line_dash='dotted') + + script, div = components(p) + rec.execution_history_chart = '%s%s' % (div, script) diff --git a/ddmrp_history/security/ir.model.access.csv b/ddmrp_history/security/ir.model.access.csv index bb43f76da..2e8190832 100644 --- a/ddmrp_history/security/ir.model.access.csv +++ b/ddmrp_history/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -ddmrp_history_access,ddmrp.history.manager,model_ddmrp_history,stock.group_stock_manager,1,0,0,1 +ddmrp_history_user,ddmrp.history.user,model_ddmrp_history,stock.group_stock_user,1,0,0,0 +ddmrp_history_manager,ddmrp.history.manager,model_ddmrp_history,stock.group_stock_manager,1,0,0,1 diff --git a/ddmrp_history/views/stock_warehouse_orderpoint_view.xml b/ddmrp_history/views/stock_warehouse_orderpoint_view.xml index 708a46db9..dda51a6bb 100644 --- a/ddmrp_history/views/stock_warehouse_orderpoint_view.xml +++ b/ddmrp_history/views/stock_warehouse_orderpoint_view.xml @@ -10,9 +10,17 @@ ref="ddmrp.view_warehouse_orderpoint_form"/> - -
- + +
+ +
+
+ +
+
diff --git a/ddmrp_mrp_production_request/README.rst b/ddmrp_mrp_production_request/README.rst index be24759ea..7d0408165 100644 --- a/ddmrp_mrp_production_request/README.rst +++ b/ddmrp_mrp_production_request/README.rst @@ -8,17 +8,24 @@ DDMRP Manufacturing Request Allows to prioritize Manufacturing Requests in a DDMRP strategy. -Configuration -============= - -WIP - Usage ===== -To use this module, you need to: +To use this module go to *Manufacturing > Manufacturing Requests*. Find the +following changes: + +* *Buffer On-Hand Alert Level* and *On Hand/TOR (%)* are shown in the tree + view. +* New filters for the different executions alerts (Red, Yellow, Green) are + available. + +Bug Tracker +=========== -#. WIP +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 smashing it by providing a detailed and welcomed feedback. Credits ======= @@ -26,5 +33,5 @@ Credits Contributors ------------ -* Lois Rilo +* Lois Rilo * Jordi Ballester diff --git a/ddmrp_mrp_production_request/__init__.py b/ddmrp_mrp_production_request/__init__.py index 7201c0805..84830dc8e 100644 --- a/ddmrp_mrp_production_request/__init__.py +++ b/ddmrp_mrp_production_request/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models +from . import wizards diff --git a/ddmrp_mrp_production_request/__openerp__.py b/ddmrp_mrp_production_request/__manifest__.py similarity index 82% rename from ddmrp_mrp_production_request/__openerp__.py rename to ddmrp_mrp_production_request/__manifest__.py index 893fef66f..a7464648f 100644 --- a/ddmrp_mrp_production_request/__openerp__.py +++ b/ddmrp_mrp_production_request/__manifest__.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + { "name": "DDMRP Manufacturing Request", "summary": "Allows to prioritize Manufacturing Requests in a DDMRP " "strategy.", - "version": "9.0.1.0.0", + "version": "10.0.1.0.0", "category": "Manufacturing", "website": "https://github.com/OCA/manufacture", - "author": "Eficent," + "author": "Eficent, " "Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, diff --git a/ddmrp_mrp_production_request/models/__init__.py b/ddmrp_mrp_production_request/models/__init__.py index 68a7ab536..00bd67f01 100644 --- a/ddmrp_mrp_production_request/models/__init__.py +++ b/ddmrp_mrp_production_request/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import mrp_production_request from . import mrp_production +from . import procurement_order +from . import stock_warehouse_orderpoint diff --git a/ddmrp_mrp_production_request/models/mrp_production.py b/ddmrp_mrp_production_request/models/mrp_production.py index 308bfe6e5..8412f15fd 100644 --- a/ddmrp_mrp_production_request/models/mrp_production.py +++ b/ddmrp_mrp_production_request/models/mrp_production.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, fields, models +from odoo import api, models class MrpProduction(models.Model): _inherit = 'mrp.production' @api.multi - @api.depends('move_prod_id', 'mrp_production_request_id') + @api.depends('move_finished_ids', 'mrp_production_request_id') def _compute_orderpoint_id(self): from_request = self.filtered(lambda mo: mo.mrp_production_request_id) super(MrpProduction, self - from_request)._compute_orderpoint_id() diff --git a/ddmrp_mrp_production_request/models/mrp_production_request.py b/ddmrp_mrp_production_request/models/mrp_production_request.py index 5948e96bf..e16f97c75 100644 --- a/ddmrp_mrp_production_request/models/mrp_production_request.py +++ b/ddmrp_mrp_production_request/models/mrp_production_request.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, fields, models +from odoo import api, fields, models _PRIORITY_LEVEL = [ ('1_red', 'Red'), @@ -44,48 +44,26 @@ def _compute_orderpoint_id(self): break @api.multi - @api.depends("orderpoint_id") - def _compute_execution_priority(self): - for rec in self.filtered(lambda r: r.orderpoint_id): - if rec.state in ['done', 'cancel']: - rec.execution_priority_level = None - rec.on_hand_percent = None - else: - rec.execution_priority_level = \ - rec.orderpoint_id.execution_priority_level - rec.on_hand_percent = rec.orderpoint_id.on_hand_percent - - def _search_execution_priority(self, operator, value): - """Search on the execution priority by evaluating on all - open manufacturing orders.""" - all_records = self.search([('state', 'not in', ['done', 'cancel'])]) - if operator == '=': - found_ids = [a.id for a in all_records - if a.execution_priority_level == value] - elif operator == 'in' and isinstance(value, list): - found_ids = [a.id for a in all_records - if a.execution_priority_level in value] - elif operator in ("!=", "<>"): - found_ids = [a.id for a in all_records - if a.execution_priority_level != value] - elif operator == 'not in' and isinstance(value, list): - found_ids = [a.id for a in all_records - if a.execution_priority_level not in value] - else: - raise NotImplementedError( - 'Search operator %s not implemented for value %s' - % (operator, value) - ) - return [('id', 'in', found_ids)] - + def _calc_execution_priority(self): + prods = self.filtered( + lambda r: r.orderpoint_id and r.state not in ['done', 'cancel']) + for rec in prods: + rec.execution_priority_level = \ + rec.orderpoint_id.execution_priority_level + rec.on_hand_percent = rec.orderpoint_id.on_hand_percent + (self - prods).write({ + 'execution_priority_level': None, + 'on_hand_percent': None, + }) orderpoint_id = fields.Many2one( comodel_name='stock.warehouse.orderpoint', store=True, index=True, - string="Reordering rule", compute='_compute_orderpoint_id') + string="Reordering rule", compute='_compute_orderpoint_id', + ) execution_priority_level = fields.Selection( string="Buffer On-Hand Alert Level", selection=_PRIORITY_LEVEL, - compute="_compute_execution_priority", - search="_search_execution_priority", store=True) + readonly=True, + ) on_hand_percent = fields.Float( - string="On Hand/TOG (%)", compute="_compute_execution_priority", - store=True) + string="On Hand/TOR (%)", + ) diff --git a/ddmrp_mrp_production_request/models/procurement_order.py b/ddmrp_mrp_production_request/models/procurement_order.py new file mode 100644 index 000000000..ec82e9e31 --- /dev/null +++ b/ddmrp_mrp_production_request/models/procurement_order.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class ProcurementOrder(models.Model): + _inherit = 'procurement.order' + + @api.multi + def write(self, vals): + """Needed to calculate the execution priority of MOs originated from + a DDMRP buffer just after the creation.""" + res = super(ProcurementOrder, self).write(vals) + mr_id = vals.get('mrp_production_request_id') + if mr_id: + self.env['mrp.production.request'].browse( + mr_id)._calc_execution_priority() + return res diff --git a/ddmrp_mrp_production_request/models/stock_warehouse_orderpoint.py b/ddmrp_mrp_production_request/models/stock_warehouse_orderpoint.py index a47cf1374..ee0175774 100644 --- a/ddmrp_mrp_production_request/models/stock_warehouse_orderpoint.py +++ b/ddmrp_mrp_production_request/models/stock_warehouse_orderpoint.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Eficent Business and IT Consulting Services S.L. -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, fields, models +from odoo import api, fields, models class StockWarehouseOrderpoint(models.Model): @@ -10,10 +10,11 @@ class StockWarehouseOrderpoint(models.Model): mrp_production_request_ids = fields.One2many( string='Manufacturing Requests', comodel_name='mrp.production.request', - inverse_name='orderpoint_id') + inverse_name='orderpoint_id', + ) @api.multi def cron_actions(self): res = super(StockWarehouseOrderpoint, self).cron_actions() - self.mrp_production_request_ids._compute_execution_priority() + self.mrp_production_request_ids._calc_execution_priority() return res diff --git a/ddmrp_mrp_production_request/static/description/icon.png b/ddmrp_mrp_production_request/static/description/icon.png new file mode 100644 index 000000000..a0b0ed73a Binary files /dev/null and b/ddmrp_mrp_production_request/static/description/icon.png differ diff --git a/ddmrp_mrp_production_request/views/mrp_production_request_view.xml b/ddmrp_mrp_production_request/views/mrp_production_request_view.xml index 72a20ef80..79cba7c63 100644 --- a/ddmrp_mrp_production_request/views/mrp_production_request_view.xml +++ b/ddmrp_mrp_production_request/views/mrp_production_request_view.xml @@ -44,5 +44,4 @@
- diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/__init__.py b/ddmrp_mrp_production_request/wizards/__init__.py similarity index 50% rename from stock_warehouse_orderpoint_stock_info_unreserved/__init__.py rename to ddmrp_mrp_production_request/wizards/__init__.py index f3ca9f7be..247bc71ec 100644 --- a/stock_warehouse_orderpoint_stock_info_unreserved/__init__.py +++ b/ddmrp_mrp_production_request/wizards/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import models +from . import mrp_production_request_create_mo diff --git a/ddmrp_mrp_production_request/wizards/mrp_production_request_create_mo.py b/ddmrp_mrp_production_request/wizards/mrp_production_request_create_mo.py new file mode 100644 index 000000000..52bacfe30 --- /dev/null +++ b/ddmrp_mrp_production_request/wizards/mrp_production_request_create_mo.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Eficent Business and IT Consulting Services S.L. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class MrpProductionRequestCreateMo(models.TransientModel): + _inherit = "mrp.production.request.create.mo" + + @api.multi + def _prepare_manufacturing_order(self): + res = super( + MrpProductionRequestCreateMo, self)._prepare_manufacturing_order() + request_id = self.mrp_production_request_id + if request_id.orderpoint_id: + res['orderpoint_id'] = request_id.orderpoint_id.id + res['execution_priority_level'] = \ + request_id.orderpoint_id.execution_priority_level + res['on_hand_percent'] = \ + request_id.orderpoint_id.on_hand_percent + return res diff --git a/ddmrp_product_replace/README.rst b/ddmrp_product_replace/README.rst new file mode 100644 index 000000000..f8ef71dd6 --- /dev/null +++ b/ddmrp_product_replace/README.rst @@ -0,0 +1,41 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :alt: License AGPL-3 + +===================== +DDMRP Product Replace +===================== + +Provides a tool for product replacement. + +Usage +===== + +Go to *Inventory > Configuration > DDMRP > Product Replacement Tool*. + +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 smashing it by providing a detailed and welcomed feedback. + +Roadmap +======= + +* Option to create new buffer and make old ones inactive. +* Consider Demand estimates in the replacement. + +Credits +======= + +Contributors +------------ + +* Lois Rilo +* Jordi Ballester + +Maintainer +---------- + +This module is maintained by the Eficent. diff --git a/mrp_production_service/__init__.py b/ddmrp_product_replace/__init__.py similarity index 54% rename from mrp_production_service/__init__.py rename to ddmrp_product_replace/__init__.py index 08f93b3a4..fbe944470 100644 --- a/mrp_production_service/__init__.py +++ b/ddmrp_product_replace/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from . import models +from . import wizards diff --git a/ddmrp_product_replace/__manifest__.py b/ddmrp_product_replace/__manifest__.py new file mode 100644 index 000000000..153a844ab --- /dev/null +++ b/ddmrp_product_replace/__manifest__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-18 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "DDMRP Product Replace", + "summary": "Provides a assisting tool for product replacement.", + "version": "10.0.1.0.0", + "author": "Eficent", + "website": "http://www.eficent.com", + "category": "Warehouse Management", + "depends": [ + "ddmrp", + "stock_putaway_product", + ], + "data": [ + "wizards/ddmrp_product_replace_view.xml", + ], + "license": "AGPL-3", + 'installable': True, +} diff --git a/ddmrp_product_replace/models/__init__.py b/ddmrp_product_replace/models/__init__.py new file mode 100644 index 000000000..6654fecb0 --- /dev/null +++ b/ddmrp_product_replace/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import stock_warehouse_orderpoint diff --git a/ddmrp_product_replace/models/stock_warehouse_orderpoint.py b/ddmrp_product_replace/models/stock_warehouse_orderpoint.py new file mode 100644 index 000000000..f0a29645e --- /dev/null +++ b/ddmrp_product_replace/models/stock_warehouse_orderpoint.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class StockWarehouseOrderpoint(models.Model): + _inherit = 'stock.warehouse.orderpoint' + + demand_product_ids = fields.Many2many( + comodel_name="product.product", string="Considered As Demand", + help="This field is used for a correct product replacement within a " + "DDMRP buffer.", readonly=True, + ) + + @api.multi + def _past_moves_domain(self, date_from, locations): + if not self.demand_product_ids: + return super(StockWarehouseOrderpoint, self)._past_moves_domain( + date_from, locations) + return [('state', '=', 'done'), ('location_id', 'in', locations.ids), + ('location_dest_id', 'not in', locations.ids), + ('product_id', 'in', self.demand_product_ids.ids), + ('date', '>=', date_from)] diff --git a/ddmrp_product_replace/static/description/icon.png b/ddmrp_product_replace/static/description/icon.png new file mode 100644 index 000000000..a0b0ed73a Binary files /dev/null and b/ddmrp_product_replace/static/description/icon.png differ diff --git a/ddmrp_product_replace/wizards/__init__.py b/ddmrp_product_replace/wizards/__init__.py new file mode 100644 index 000000000..9a50a45a1 --- /dev/null +++ b/ddmrp_product_replace/wizards/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import ddmrp_product_replace diff --git a/ddmrp_product_replace/wizards/ddmrp_product_replace.py b/ddmrp_product_replace/wizards/ddmrp_product_replace.py new file mode 100644 index 000000000..2247b065a --- /dev/null +++ b/ddmrp_product_replace/wizards/ddmrp_product_replace.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models, _ + + +class DdmrpProductReplace(models.TransientModel): + _name = "ddmrp.product.replace" + + @api.multi + @api.depends("old_product_id") + def _compute_orderpoint_ids(self): + for rec in self: + rec.orderpoint_ids = self.env['stock.warehouse.orderpoint'].search( + [('product_id', '=', rec.old_product_id.id)]) + + old_product_id = fields.Many2one( + comodel_name="product.product", string="Replaced Product", + help="Product to be replaced.", required=True, ondelete="cascade", + ) + orderpoint_ids = fields.Many2many( + comodel_name="stock.warehouse.orderpoint", + string="Affected Buffers", + readonly=True, compute="_compute_orderpoint_ids", + ) + new_product_id = fields.Many2one( + comodel_name="product.product", string="Substitute Product", + help="Product that is going to replace the other one.", + ) + use_existing = fields.Selection( + string="Use Existing/New Product", required=True, + selection=[("existing", "Use Existing Product"), + ("new", "Create New Product")], + ) + new_product_name = fields.Char(string="New Product Name") + new_product_default_code = fields.Char(string="New Product Internal Ref.") + copy_route = fields.Boolean(string="Copy Routes") + copy_putaway = fields.Boolean(string="Copy Put Away Strategy") + consider_past_demand = fields.Boolean( + string="Consider Old Product Demand", + help="Consider Old product moves as demand for new product", + default=True, + ) + + @api.multi + def button_validate(self): + self.ensure_one() + if self.use_existing == 'new': + default = dict( + name=self.new_product_name, + default_code=self.new_product_default_code, + ) + if not self.copy_route: + default['route_ids'] = None + if (self.copy_putaway and ( + self.old_product_id.product_putaway_ids or + self.old_product_id.product_tmpl_id.product_putaway_ids)): + default['product_putaway_ids'] = [ + (6, 0, self.old_product_id.product_putaway_ids.ids or + self.old_product_id.product_tmpl_id. + product_putaway_ids.ids)] + self.new_product_id = self.old_product_id.copy( + default=default) + elif self.use_existing == 'existing': + if self.copy_route: + self.new_product_id.write({ + 'route_ids': [(6, 0, self.old_product_id.route_ids.ids)], + }) + if (self.copy_putaway and ( + self.old_product_id.product_putaway_ids or + self.old_product_id.product_tmpl_id.product_putaway_ids)): + self.new_product_id.write({ + 'product_putaway_ids': [ + (6, 0, self.old_product_id.product_putaway_ids.ids or + self.old_product_id.product_tmpl_id. + product_putaway_ids.ids)], + }) + if self.orderpoint_ids: + vals = { + 'product_id': self.new_product_id.id, + } + if self.consider_past_demand: + vals['demand_product_ids'] = [ + (6, 0, (self.old_product_id + self.new_product_id).ids)] + self.orderpoint_ids.write(vals) + return { + 'name': _('Replacing Product'), + 'res_id': self.new_product_id.id, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'product.product', + 'type': 'ir.actions.act_window' + } diff --git a/ddmrp_product_replace/wizards/ddmrp_product_replace_view.xml b/ddmrp_product_replace/wizards/ddmrp_product_replace_view.xml new file mode 100644 index 000000000..6a7d3703e --- /dev/null +++ b/ddmrp_product_replace/wizards/ddmrp_product_replace_view.xml @@ -0,0 +1,58 @@ + + + + + + ddmrp.product.replace.form + ddmrp.product.replace + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + + +
diff --git a/mrp_production_service/README.rst b/mrp_production_service/README.rst deleted file mode 100644 index 3a9d5719f..000000000 --- a/mrp_production_service/README.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -====================== -MRP Production Service -====================== - -Creates procurement orders from manufacturing orders, for services included -in the Bill of Materials. - -This allows users to include additional services that are to be procured as -part of the manufacturing process. - - -Usage -===== - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/129/8.0 - - -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 smashing it by providing a detailed and welcomed feedback. - -Credits -======= - -Images ------- - -* Odoo Community Association: `Icon `_. - -Contributors ------------- - -* Jordi Ballester Alomar - - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -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. - -To contribute to this module, please visit https://odoo-community.org. diff --git a/mrp_production_service/__openerp__.py b/mrp_production_service/__openerp__.py deleted file mode 100644 index ecc34e8b8..000000000 --- a/mrp_production_service/__openerp__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -{ - "name": "MRP Production Service", - "summary": "Creates procurement orders from manufacturing orders, for " - "services included in the Bill of Materials", - "version": "8.0.1.0.0", - "author": "Eficent," - "Odoo Community Association (OCA)", - "website": "https://www.odoo-community.org", - "category": "Warehouse Management", - "depends": ["stock", "mrp", "procurement_service"], - "license": "AGPL-3", - 'installable': True, - 'application': False, -} diff --git a/mrp_production_service/models/__init__.py b/mrp_production_service/models/__init__.py deleted file mode 100644 index 11ef31c2a..000000000 --- a/mrp_production_service/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from . import mrp_production diff --git a/mrp_production_service/models/mrp_production.py b/mrp_production_service/models/mrp_production.py deleted file mode 100644 index 5cb95f82c..000000000 --- a/mrp_production_service/models/mrp_production.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, fields, models, _ - - -class MrpProduction(models.Model): - _inherit = "mrp.production" - - @api.model - def _prepare_service_procurement(self, line): - location = line.production_id.location_src_id - return { - 'name': '%s for %s' % (line.product_id.name, - line.production_id.name), - 'origin': line.production_id.name, - 'company_id': line.production_id.company_id.id, - 'date_planned': line.production_id.date_planned, - 'product_id': line.product_id.id, - 'product_qty': line.product_qty, - 'product_uom': line.product_uom.id, - 'product_uos_qty': line.product_uos_qty, - 'product_uos': line.product_uos.id, - 'location_id': location.id, - 'warehouse_id': location.get_warehouse(location) - } - - @api.model - def _create_service_procurement(self, line): - data = self._prepare_service_procurement(line) - return self.env['procurement.order'].create(data) - - @api.model - def _make_service_procurement(self, line): - """Override standard method""" - if line.product_id.need_procurement(): - proc = self._create_service_procurement(line) - proc.run() diff --git a/mrp_production_service/static/description/icon.png b/mrp_production_service/static/description/icon.png deleted file mode 100644 index 3a0328b51..000000000 Binary files a/mrp_production_service/static/description/icon.png and /dev/null differ diff --git a/oca_dependencies.txt b/oca_dependencies.txt new file mode 100644 index 000000000..90d3b69cc --- /dev/null +++ b/oca_dependencies.txt @@ -0,0 +1,4 @@ +stock-logistics-warehouse +web +manufacture +server-tools diff --git a/procurement_service/models/procurement_order.py b/procurement_service/models/procurement_order.py index 9818c2df6..64ab00aa5 100644 --- a/procurement_service/models/procurement_order.py +++ b/procurement_service/models/procurement_order.py @@ -3,7 +3,7 @@ # Copyright 2015 Tecnativa (http://www.tecnativa.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import models, api +from odoo import models, api class ProcurementOrder(models.Model): diff --git a/procurement_service/models/product.py b/procurement_service/models/product.py index 98b0e3218..e4a4f443d 100644 --- a/procurement_service/models/product.py +++ b/procurement_service/models/product.py @@ -3,7 +3,7 @@ # Copyright 2015 Tecnativa (http://www.tecnativa.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import models, api +from odoo import models, api class Product(models.Model): diff --git a/procurement_service/models/sale_order.py b/procurement_service/models/sale_order.py index 873afee37..6b4869353 100644 --- a/procurement_service/models/sale_order.py +++ b/procurement_service/models/sale_order.py @@ -3,7 +3,7 @@ # Copyright 2015 Tecnativa (http://www.tecnativa.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import models, api +from odoo import models, api class SaleOrder(models.Model): diff --git a/setup/ddmrp/odoo/__init__.py b/setup/ddmrp/odoo/__init__.py new file mode 100644 index 000000000..b0d643371 --- /dev/null +++ b/setup/ddmrp/odoo/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/setup/ddmrp/odoo/addons/__init__.py b/setup/ddmrp/odoo/addons/__init__.py new file mode 100644 index 000000000..b0d643371 --- /dev/null +++ b/setup/ddmrp/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/setup/ddmrp/odoo/addons/ddmrp b/setup/ddmrp/odoo/addons/ddmrp new file mode 120000 index 000000000..77c88a664 --- /dev/null +++ b/setup/ddmrp/odoo/addons/ddmrp @@ -0,0 +1 @@ +../../../../ddmrp \ No newline at end of file diff --git a/setup/ddmrp/setup.py b/setup/ddmrp/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/ddmrp_adjustment/odoo/__init__.py b/setup/ddmrp_adjustment/odoo/__init__.py new file mode 100644 index 000000000..b0d643371 --- /dev/null +++ b/setup/ddmrp_adjustment/odoo/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/setup/ddmrp_adjustment/odoo/addons/__init__.py b/setup/ddmrp_adjustment/odoo/addons/__init__.py new file mode 100644 index 000000000..b0d643371 --- /dev/null +++ b/setup/ddmrp_adjustment/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/setup/ddmrp_adjustment/odoo/addons/ddmrp_adjustment b/setup/ddmrp_adjustment/odoo/addons/ddmrp_adjustment new file mode 120000 index 000000000..df63cf34a --- /dev/null +++ b/setup/ddmrp_adjustment/odoo/addons/ddmrp_adjustment @@ -0,0 +1 @@ +../../../../ddmrp_adjustment/ \ No newline at end of file diff --git a/setup/ddmrp_adjustment/setup.py b/setup/ddmrp_adjustment/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp_adjustment/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/__init__.py b/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/__init__.py new file mode 100644 index 000000000..b0d643371 --- /dev/null +++ b/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) \ No newline at end of file diff --git a/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/ddmrp_exclude_moves_adu_calc b/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/ddmrp_exclude_moves_adu_calc new file mode 120000 index 000000000..4ee1b8482 --- /dev/null +++ b/setup/ddmrp_exclude_moves_adu_calc/odoo/addons/ddmrp_exclude_moves_adu_calc @@ -0,0 +1 @@ +../../../../ddmrp_exclude_moves_adu_calc \ No newline at end of file diff --git a/setup/ddmrp_exclude_moves_adu_calc/setup.py b/setup/ddmrp_exclude_moves_adu_calc/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp_exclude_moves_adu_calc/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/ddmrp_history/odoo/addons/__init__.py b/setup/ddmrp_history/odoo/addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/ddmrp_history/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/ddmrp_history/odoo/addons/ddmrp_history b/setup/ddmrp_history/odoo/addons/ddmrp_history new file mode 120000 index 000000000..f2a2e6a86 --- /dev/null +++ b/setup/ddmrp_history/odoo/addons/ddmrp_history @@ -0,0 +1 @@ +../../../../ddmrp_history/ \ No newline at end of file diff --git a/setup/ddmrp_history/setup.py b/setup/ddmrp_history/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp_history/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/ddmrp_mrp_production_request/odoo/addons/__init__.py b/setup/ddmrp_mrp_production_request/odoo/addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/ddmrp_mrp_production_request/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/ddmrp_mrp_production_request/odoo/addons/ddmrp_mrp_production_request b/setup/ddmrp_mrp_production_request/odoo/addons/ddmrp_mrp_production_request new file mode 120000 index 000000000..95746af7b --- /dev/null +++ b/setup/ddmrp_mrp_production_request/odoo/addons/ddmrp_mrp_production_request @@ -0,0 +1 @@ +../../../../ddmrp_mrp_production_request/ \ No newline at end of file diff --git a/setup/ddmrp_mrp_production_request/setup.py b/setup/ddmrp_mrp_production_request/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp_mrp_production_request/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/ddmrp_product_replace/odoo/addons/__init__.py b/setup/ddmrp_product_replace/odoo/addons/__init__.py new file mode 100644 index 000000000..de40ea7ca --- /dev/null +++ b/setup/ddmrp_product_replace/odoo/addons/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/setup/ddmrp_product_replace/odoo/addons/ddmrp_product_replace b/setup/ddmrp_product_replace/odoo/addons/ddmrp_product_replace new file mode 120000 index 000000000..dd03278f9 --- /dev/null +++ b/setup/ddmrp_product_replace/odoo/addons/ddmrp_product_replace @@ -0,0 +1 @@ +../../../../ddmrp_product_replace/ \ No newline at end of file diff --git a/setup/ddmrp_product_replace/setup.py b/setup/ddmrp_product_replace/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/ddmrp_product_replace/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_demand_estimate/README.rst b/stock_demand_estimate/README.rst deleted file mode 100644 index d3ca4b1c8..000000000 --- a/stock_demand_estimate/README.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -===================== -Stock Demand Estimate -===================== - -This module allows to create demand estimates for a given product and -location, on configurable time periods. - -The module does not provide in itself any specific usage of the estimates. - -Installation -============ - -This module relies on the OCA module '2D matrix for x2many fields', and can -be downloaded from: -* Github: https://github.com/OCA/web/tree/8.0/web_widget_x2many_2d_matrix - - -Usage -===== - -Go to 'Warehouse / Configuration / Demand Estimate Periods' and define your -estimating periods (monthly or weekly). - - -Go to 'Warehouse / Demand Planning / Create Demand Estimates' to create or -update your demand estimates. - -Go to 'Warehouse / Demand Planning / Demand Estimates' to review the -estimates created. - - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/153/8.0 - - -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 smashing it by providing a detailed and welcomed feedback. - -Credits -======= - -Images ------- - -* Odoo Community Association: `Icon `_. - -Contributors ------------- - -* Jordi Ballester Alomar - - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -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. - -To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_demand_estimate/__openerp__.py b/stock_demand_estimate/__openerp__.py deleted file mode 100644 index 3d838b25f..000000000 --- a/stock_demand_estimate/__openerp__.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -{ - "name": "Stock Demand Estimate", - "summary": "Allows to create demand estimates.", - "version": "8.0.1.0.0", - "author": "Eficent Business and IT Consulting Services S.L," - "Odoo Community Association (OCA)", - "website": "https://www.odoo-community.org", - "category": "Warehouse Management", - "depends": ["stock", - "web_widget_x2many_2d_matrix" - ], - "data": ["security/ir.model.access.csv", - "security/stock_security.xml", - "views/stock_demand_estimate_period_view.xml", - "views/stock_demand_estimate_view.xml", - "wizards/stock_demand_estimate_wizard_view.xml", - ], - "license": "AGPL-3", - 'installable': True, - 'application': True, -} diff --git a/stock_demand_estimate/models/__init__.py b/stock_demand_estimate/models/__init__.py deleted file mode 100644 index 7e93e257a..000000000 --- a/stock_demand_estimate/models/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from . import stock_demand_estimate_period -from . import stock_demand_estimate diff --git a/stock_demand_estimate/models/stock_demand_estimate.py b/stock_demand_estimate/models/stock_demand_estimate.py deleted file mode 100644 index d4dc48091..000000000 --- a/stock_demand_estimate/models/stock_demand_estimate.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, fields, models, _ -import openerp.addons.decimal_precision as dp -from openerp.exceptions import Warning as UserError - - -class StockDemandEstimate(models.Model): - _name = 'stock.demand.estimate' - _description = 'Stock Demand Estimate Line' - - @api.multi - @api.depends('product_id', 'product_uom', 'product_uom_qty') - def _compute_product_qty(self): - for rec in self: - if rec.product_uom: - rec.product_qty = rec.product_uom._compute_qty( - rec.product_id.uom_id.id, rec.product_uom_qty, - rec.product_uom.id) - - def _set_product_qty(self): - raise UserError(_('The requested operation cannot be ' - 'processed because of a programming error ' - 'setting the `product_qty` field instead ' - 'of the `product_uom_qty`.')) - - @api.multi - def _compute_daily_qty(self): - for rec in self: - rec.daily_qty = rec.product_qty / rec.period_id.days - - period_id = fields.Many2one( - comodel_name="stock.demand.estimate.period", - string="Estimating Period", - required=True) - product_id = fields.Many2one(comodel_name="product.product", - string="Product", required=True) - product_uom = fields.Many2one(comodel_name="product.uom", - string="Unit of measure") - location_id = fields.Many2one(comodel_name="stock.location", - string="Location", required=True) - product_uom_qty = fields.Float( - string="Quantity", - digits_compute=dp.get_precision('Product Unit of Measure')) - product_qty = fields.Float('Real Quantity', compute='_compute_product_qty', - inverse='_set_product_qty', digits=0, - store=True, - help='Quantity in the default UoM of the ' - 'product', readonly=True) - daily_qty = fields.Float(string='Quantity / Day', - compute='_compute_daily_qty') - company_id = fields.Many2one( - comodel_name='res.company', string='Company', required=True, - default=lambda self: self.env['res.company']._company_default_get( - 'stock.demand.estimate')) - - @api.multi - def name_get(self): - res = [] - for rec in self: - name = "%s - %s - %s" % (rec.period_id.name, rec.product_id.name, - rec.location_id.name) - res.append((rec.id, name)) - return res - - @api.model - def get_quantity_by_date_range(self, date_from, date_to): - # Check if the dates overlap with the period - period_date_from = fields.Date.from_string(self.period_id.date_from) - period_date_to = fields.Date.from_string(self.period_id.date_to) - - if date_from <= period_date_to and period_date_from <= date_to: - overlap_date_from = max(period_date_from, date_from) - overlap_date_to = min(period_date_to, date_to) - days = (abs(overlap_date_to-overlap_date_from)).days + 1 - return days * self.daily_qty - return 0.0 - diff --git a/stock_demand_estimate/models/stock_demand_estimate_period.py b/stock_demand_estimate/models/stock_demand_estimate_period.py deleted file mode 100644 index cb62e6285..000000000 --- a/stock_demand_estimate/models/stock_demand_estimate_period.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, fields, models, _ -from openerp.exceptions import Warning as UserError - - -class StockDemandEstimatePeriod(models.Model): - _name = 'stock.demand.estimate.period' - _description = 'Stock Demand Estimate Period' - _order = 'date_from' - - @api.multi - @api.depends('date_from', 'date_to') - def _compute_days(self): - for rec in self: - if rec.date_from and rec.date_to: - rec.days = (fields.Date.from_string(rec.date_to) - - fields.Date.from_string(rec.date_from)).days + 1 - - name = fields.Char(string="Name", required=True) - date_from = fields.Date(string="Date From", required=True) - date_to = fields.Date(string="Date To", required=True) - days = fields.Float(string="Days between dates", - compute='_compute_days', store=True, readonly=True) - - estimate_ids = fields.One2many( - comodel_name="stock.demand.estimate", - inverse_name="period_id") - - company_id = fields.Many2one( - comodel_name='res.company', string='Company', required=True, - default=lambda self: self.env['res.company']._company_default_get( - 'stock.demand.estimate.period')) - - @api.multi - @api.constrains('name', 'date_from', 'date_to') - def _check_period(self): - for period in self: - self.env.cr.execute('SELECT id, date_from, date_to \ - FROM stock_demand_estimate_period \ - WHERE (date_from <= %s and %s <= date_to) \ - AND id <> %s', (period.date_to, period.date_from, period.id)) - res = self.env.cr.fetchall() - if res: - raise UserError(_('Two periods cannot overlap.')) diff --git a/stock_demand_estimate/security/ir.model.access.csv b/stock_demand_estimate/security/ir.model.access.csv deleted file mode 100644 index 715896e64..000000000 --- a/stock_demand_estimate/security/ir.model.access.csv +++ /dev/null @@ -1,5 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_stock_demand_estimate,stock.orderpoint.demand.estimate,model_stock_demand_estimate,stock.group_stock_user,1,0,0,0 -access_stock_demand_estimate_system,stock.orderpoint.demand.estimate system,model_stock_demand_estimate,stock.group_stock_manager,1,1,1,1 -access_stock_demand_estimate_period,stock.orderpoint.demand.estimate.period,model_stock_demand_estimate_period,stock.group_stock_user,1,0,0,0 -access_stock_demand_estimate_period_system,stock.orderpoint.demand.estimate.period system,model_stock_demand_estimate_period,stock.group_stock_manager,1,1,1,1 diff --git a/stock_demand_estimate/security/stock_security.xml b/stock_demand_estimate/security/stock_security.xml deleted file mode 100644 index c447a085c..000000000 --- a/stock_demand_estimate/security/stock_security.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - Stock demand estimate multi-company - - - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - - - Stock demand estimate multi-company - - - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - - - diff --git a/stock_demand_estimate/static/description/icon.png b/stock_demand_estimate/static/description/icon.png deleted file mode 100644 index 3a0328b51..000000000 Binary files a/stock_demand_estimate/static/description/icon.png and /dev/null differ diff --git a/stock_demand_estimate/views/stock_demand_estimate_period_view.xml b/stock_demand_estimate/views/stock_demand_estimate_period_view.xml deleted file mode 100644 index d32933cee..000000000 --- a/stock_demand_estimate/views/stock_demand_estimate_period_view.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - stock.demand.estimate.period.tree - stock.demand.estimate.period - - - - - - - - - - - - stock.demand.estimate.period.search - stock.demand.estimate.period - - - - - - - - - - - Stock Demand Estimate Periods - ir.actions.act_window - stock.demand.estimate.period - form - tree,form - - - - - - - diff --git a/stock_demand_estimate/views/stock_demand_estimate_view.xml b/stock_demand_estimate/views/stock_demand_estimate_view.xml deleted file mode 100644 index 2b2f0f981..000000000 --- a/stock_demand_estimate/views/stock_demand_estimate_view.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - stock.demand.estimate.tree - stock.demand.estimate - - - - - - - - - - - - - - - stock.demand.estimate.search - stock.demand.estimate - - - - - - - - - - - Stock Demand Estimates - ir.actions.act_window - stock.demand.estimate - form - tree,form - - - - - - - - - diff --git a/stock_demand_estimate/wizards/__init__.py b/stock_demand_estimate/wizards/__init__.py deleted file mode 100644 index 05c5fae45..000000000 --- a/stock_demand_estimate/wizards/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from . import stock_demand_estimate_wizard diff --git a/stock_demand_estimate/wizards/stock_demand_estimate_wizard.py b/stock_demand_estimate/wizards/stock_demand_estimate_wizard.py deleted file mode 100644 index 8d97c95d2..000000000 --- a/stock_demand_estimate/wizards/stock_demand_estimate_wizard.py +++ /dev/null @@ -1,189 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# © 2016 Aleph Objects, Inc. (https://www.alephobjects.com/) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, fields, models, _ -import openerp.addons.decimal_precision as dp -from openerp.exceptions import Warning as UserError - -_PERIOD_SELECTION = [ - ('monthly', 'Monthly'), - ('weekly', 'Weekly') -] - - -class StockDemandEstimateSheet(models.TransientModel): - _name = 'stock.demand.estimate.sheet' - _description = 'Stock Demand Estimate Sheet' - - def _default_date_from(self): - return self.env.context.get('date_from', False) - - def _default_date_to(self): - return self.env.context.get('date_to', False) - - def _default_location_id(self): - location_id = self.env.context.get('location_id', False) - if location_id: - return self.env['stock.location'].browse(location_id) - else: - return False - - def _default_estimate_ids(self): - date_from = self.env.context.get('date_from', False) - date_to = self.env.context.get('date_to', False) - location_id = self.env.context.get('location_id', False) - product_ids = self.env.context.get('product_ids', False) - domain = [('date_from', '>=', date_from), - ('date_to', '<=', date_to)] - periods = self.env['stock.demand.estimate.period'].search( - domain) - products = self.env['product.product'].browse(product_ids) - - lines = [] - for product in products: - name_y = '' - if product.default_code: - name_y += '[%s] ' % product.default_code - name_y += product.name - name_y += ' - %s' % product.uom_id.name - for period in periods: - estimates = self.env['stock.demand.estimate'].search( - [('product_id', '=', product.id), - ('period_id', '=', period.id), - ('location_id', '=', location_id)]) - if estimates: - lines.append((0, 0, { - 'value_x': period.name, - 'value_y': name_y, - 'period_id': period.id, - 'product_id': product.id, - 'product_uom': estimates[0].product_uom.id, - 'location_id': location_id, - 'estimate_id': estimates[0].id, - 'product_uom_qty': estimates[0].product_uom_qty - })) - else: - lines.append((0, 0, { - 'value_x': period.name, - 'value_y': name_y, - 'period_id': period.id, - 'product_id': product.id, - 'product_uom': product.uom_id.id, - 'location_id': location_id, - 'product_uom_qty': 0.0 - })) - return lines - - date_from = fields.Date(string="Date From", readonly=True, - default=_default_date_from) - date_to = fields.Date(string="Date From", readonly=True, - default=_default_date_to) - location_id = fields.Many2one(comodel_name="stock.location", - string="Location", readonly=True, - default=_default_location_id) - line_ids = fields.Many2many( - string="Estimates", - comodel_name='stock.demand.estimate.sheet.line', - rel='stock_demand_estimate_line_rel', - default=_default_estimate_ids) - - @api.model - def _prepare_estimate_data(self, line): - return { - 'period_id': line.period_id.id, - 'product_id': line.product_id.id, - 'location_id': line.location_id.id, - 'product_uom_qty': line.product_uom_qty, - 'product_uom': line.product_id.uom_id.id, - } - - @api.multi - def button_validate(self): - res = [] - for line in self.line_ids: - if line.estimate_id: - line.estimate_id.product_uom_qty = line.product_uom_qty - res.append(line.estimate_id.id) - else: - data = self._prepare_estimate_data(line) - estimate = self.env['stock.demand.estimate'].create( - data) - res.append(estimate.id) - res = { - 'domain': [('id', 'in', res)], - 'name': _('Stock Demand Estimates'), - 'src_model': 'stock.demand.estimate.wizard', - 'view_type': 'form', - 'view_mode': 'tree', - 'res_model': 'stock.demand.estimate', - 'type': 'ir.actions.act_window' - } - return res - - -class StockDemandEstimateSheetLine(models.TransientModel): - _name = 'stock.demand.estimate.sheet.line' - _description = 'Stock Demand Estimate Sheet Line' - - estimate_id = fields.Many2one(comodel_name='stock.demand.estimate') - period_id = fields.Many2one( - comodel_name='stock.demand.estimate.period', - string='Period') - location_id = fields.Many2one(comodel_name='stock.location', - string="Stock Location") - product_id = fields.Many2one(comodel_name='product.product', - string='Product') - value_x = fields.Char(string='Period') - value_y = fields.Char(string='Product') - product_uom_qty = fields.Float( - string="Quantity", digits_compute=dp.get_precision('Product UoM')) - - -class DemandEstimateWizard(models.TransientModel): - _name = 'stock.demand.estimate.wizard' - _description = 'Stock Demand Estimate Wizard' - - date_from = fields.Date(string="Date From", required=True) - date_to = fields.Date(string="Date To", required=True) - location_id = fields.Many2one(comodel_name="stock.location", - string="Location", required=True) - product_ids = fields.Many2many( - comodel_name="product.product", - string="Products") - - @api.multi - def _prepare_demand_estimate_sheet(self): - self.ensure_one() - return { - 'date_from': self.date_from, - 'date_to': self.date_to, - 'location_id': self.location_id.id, - } - - @api.multi - def create_sheet(self): - self.ensure_one() - if not self.product_ids: - raise UserError(_('You must select at lease one product.')) - - context = { - 'date_from': self.date_from, - 'date_to': self.date_to, - 'location_id': self.location_id.id, - 'product_ids': self.product_ids.ids - } - res = { - 'context': context, - 'name': _('Estimate Sheet'), - 'src_model': 'stock.demand.estimate.wizard', - 'view_type': 'form', - 'view_mode': 'form', - 'target': 'new', - 'res_model': 'stock.demand.estimate.sheet', - 'type': 'ir.actions.act_window' - } - - return res diff --git a/stock_demand_estimate/wizards/stock_demand_estimate_wizard_view.xml b/stock_demand_estimate/wizards/stock_demand_estimate_wizard_view.xml deleted file mode 100644 index af5926770..000000000 --- a/stock_demand_estimate/wizards/stock_demand_estimate_wizard_view.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - stock.demand.estimate.sheet.form - stock.demand.estimate.sheet - -
- - - -
- - - - -
- - - -
-
- - - - - - stock.demand.estimate.wizard.form - stock.demand.estimate.wizard - -
- - - -
- - - - -
- - - -
-
- - - - - - - - - - diff --git a/stock_orderpoint_uom/README.rst b/stock_orderpoint_uom/README.rst deleted file mode 100644 index 4bb323ca3..000000000 --- a/stock_orderpoint_uom/README.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -==================== -Stock Orderpoint UoM -==================== - -This module allows users users to define what unit of measure should be used -in procurements created from minimum stock rules. - -A typical use case would be a product that is stocked in centimeters, and -needs to be restocked in meters from another warehouse. When the picking is -created, the quantity to be transferred will be expressed in meters, making -it easier for the people responsible for the transfers to understand the -requirement. - - -Usage -===== - -Go to 'Configuration / Reordering Rules' and indicate a Procurement UoM. - - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/153/8.0 - - -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 smashing it by providing a detailed and welcomed feedback. - -Credits -======= - -Images ------- - -* Odoo Community Association: `Icon `_. - -Contributors ------------- - -* Jordi Ballester Alomar - - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -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. - -To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_orderpoint_uom/__init__.py b/stock_orderpoint_uom/__init__.py deleted file mode 100644 index 08f93b3a4..000000000 --- a/stock_orderpoint_uom/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from . import models diff --git a/stock_orderpoint_uom/__openerp__.py b/stock_orderpoint_uom/__openerp__.py deleted file mode 100644 index 10708378f..000000000 --- a/stock_orderpoint_uom/__openerp__.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -{ - "name": "Stock Orderpoint UoM", - "summary": "Allows to create procurement orders in the UoM indicated in " - "the orderpoint", - "version": "8.0.1.0.0", - "author": "Eficent Business and IT Consulting Services S.L," - "Odoo Community Association (OCA)", - "website": "https://www.odoo-community.org", - "category": "Warehouse Management", - "depends": ["stock"], - "data": ["views/stock_warehouse_orderpoint_view.xml"], - "license": "AGPL-3", - 'installable': True, - 'application': False, -} diff --git a/stock_orderpoint_uom/models/procurement_order.py b/stock_orderpoint_uom/models/procurement_order.py deleted file mode 100644 index 250faf038..000000000 --- a/stock_orderpoint_uom/models/procurement_order.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, models - - -class ProcurementOrder(models.Model): - _inherit = "procurement.order" - - @api.model - def _prepare_orderpoint_procurement(self, orderpoint, product_qty): - res = super(ProcurementOrder, self)._prepare_orderpoint_procurement( - orderpoint, product_qty) - if orderpoint.procure_uom_id: - res['product_qty'] = orderpoint.procure_uom_id._compute_qty( - orderpoint.product_id.uom_id.id, product_qty, - orderpoint.procure_uom_id.id) - res['product_uom'] = orderpoint.procure_uom_id.id - return res diff --git a/stock_orderpoint_uom/models/stock_warehouse_orderpoint.py b/stock_orderpoint_uom/models/stock_warehouse_orderpoint.py deleted file mode 100644 index a4ca61052..000000000 --- a/stock_orderpoint_uom/models/stock_warehouse_orderpoint.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from openerp import api, fields, models, _ -from openerp.exceptions import Warning as UserError - - -class StockWarehouseOrderpoint(models.Model): - _inherit = "stock.warehouse.orderpoint" - - procure_uom_id = fields.Many2one(comodel_name='product.uom', - string="Procurement UoM") - - @api.constrains('product_uom', 'procure_uom_id') - def _check_procure_uom(self): - if any(orderpoint.product_uom and - orderpoint.procure_uom_id and - orderpoint.product_uom.category_id != - orderpoint.procure_uom_id.category_id - for orderpoint in self): - raise UserError( - _('Error: The product default Unit of Measure and ' - 'the procurement Unit of Measure must be in the ' - 'same category.')) - return True diff --git a/stock_orderpoint_uom/static/description/icon.png b/stock_orderpoint_uom/static/description/icon.png deleted file mode 100644 index 3a0328b51..000000000 Binary files a/stock_orderpoint_uom/static/description/icon.png and /dev/null differ diff --git a/stock_orderpoint_uom/tests/__init__.py b/stock_orderpoint_uom/tests/__init__.py deleted file mode 100644 index 8ab7b3f74..000000000 --- a/stock_orderpoint_uom/tests/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -from . import test_stock_orderpoint_procure_uom diff --git a/stock_orderpoint_uom/tests/test_stock_orderpoint_procure_uom.py b/stock_orderpoint_uom/tests/test_stock_orderpoint_procure_uom.py deleted file mode 100644 index b59453679..000000000 --- a/stock_orderpoint_uom/tests/test_stock_orderpoint_procure_uom.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services S.L. -# (http://www.eficent.com) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -import openerp.tests.common as common -from openerp.tools import mute_logger -from openerp.exceptions import ValidationError - - -class TestStockOrderpointProcureUom(common.TransactionCase): - - def setUp(self): - super(TestStockOrderpointProcureUom, self).setUp() - productObj = self.env['product.product'] - self.warehouse = self.env.ref('stock.warehouse0') - self.location_stock = self.env.ref('stock.stock_location_stock') - self.uom_unit = self.env.ref('product.product_uom_unit') - self.uom_dozen = self.env.ref('product.product_uom_dozen') - self.uom_kg = self.env.ref('product.product_uom_kgm') - - self.productA = productObj.create( - {'name': 'product A', - 'standard_price': 1, - 'type': 'product', - 'uom_id': self.uom_unit.id, - 'default_code': 'A', - }) - - def test_stock_orderpoint_procure_uom(self): - - self.env['stock.warehouse.orderpoint'].create({ - 'warehouse_id': self.warehouse.id, - 'location_id': self.location_stock.id, - 'product_id': self.productA.id, - 'product_max_qty': 24, - 'product_min_qty': 12, - 'procure_uom_id': self.uom_dozen.id, - }) - - sched = self.env['procurement.order'] - sched.run_scheduler() - proc = sched.search([('product_id', '=', self.productA.id)]) - self.assertEqual(proc.product_uom, self.uom_dozen) - self.assertEqual(proc.product_qty, 2) - - def test_stock_orderpoint_wrong_uom(self): - - with mute_logger('openerp.sql_db'): - with self.assertRaises(ValidationError): - self.env['stock.warehouse.orderpoint'].create({ - 'warehouse_id': self.warehouse.id, - 'location_id': self.location_stock.id, - 'product_id': self.productA.id, - 'product_max_qty': 24, - 'product_min_qty': 12, - 'procure_uom_id': self.uom_kg.id, - }) diff --git a/stock_orderpoint_uom/views/stock_warehouse_orderpoint_view.xml b/stock_orderpoint_uom/views/stock_warehouse_orderpoint_view.xml deleted file mode 100644 index ae41f7756..000000000 --- a/stock_orderpoint_uom/views/stock_warehouse_orderpoint_view.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - stock.warehouse.orderpoint.tree - stock.warehouse.orderpoint - - - - - - - - - - stock.warehouse.orderpoint.form - stock.warehouse.orderpoint - - - - - - - - - - diff --git a/stock_warehouse_orderpoint_stock_info/README.rst b/stock_warehouse_orderpoint_stock_info/README.rst deleted file mode 100644 index c0895a440..000000000 --- a/stock_warehouse_orderpoint_stock_info/README.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -=========================== -Reordering Rules stock info -=========================== - -This modules allows to know the product availability directly in the reordering -rules. - -Usage -===== - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/153/9.0 - -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 smashing it by providing a detailed and welcomed -feedback. - - -Credits -======= - -Contributors ------------- -* Oihane Crucelaegui -* Pedro M. Baeza -* Ana Juaristi -* Jordi Ballester Alomar -* Serpent Consulting Services Pvt. Ltd. - - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -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. - -To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_warehouse_orderpoint_stock_info/__init__.py b/stock_warehouse_orderpoint_stock_info/__init__.py deleted file mode 100644 index c7f4154ff..000000000 --- a/stock_warehouse_orderpoint_stock_info/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import models diff --git a/stock_warehouse_orderpoint_stock_info/__openerp__.py b/stock_warehouse_orderpoint_stock_info/__openerp__.py deleted file mode 100644 index 0e0ef6a53..000000000 --- a/stock_warehouse_orderpoint_stock_info/__openerp__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - "name": "Reordering rules stock info", - "version": "9.0.1.0.0", - "depends": [ - "stock", - ], - "author": "OdooMRP team, " - "AvanzOSC, " - "Tecnativa, " - "Odoo Community Association (OCA)", - "website": "http://www.odoomrp.com", - "category": "Warehouse", - "license": "AGPL-3", - "data": [ - "views/stock_warehouse_orderpoint_view.xml", - ], - "installable": True, - "auto_install": False, -} diff --git a/stock_warehouse_orderpoint_stock_info/i18n/es.po b/stock_warehouse_orderpoint_stock_info/i18n/es.po deleted file mode 100644 index b88c82083..000000000 --- a/stock_warehouse_orderpoint_stock_info/i18n/es.po +++ /dev/null @@ -1,47 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * stock_warehouse_orderpoint_stock_info -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 8.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-03-04 11:41+0000\n" -"PO-Revision-Date: 2015-03-04 11:41+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: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Availability under minimum" -msgstr "Disponibilidad por debajo del mínimo" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Group By" -msgstr "Agrupar por" - -#. module: stock_warehouse_orderpoint_stock_info -#: field:stock.warehouse.orderpoint,available:0 -msgid "Is enough product available?" -msgstr "Hay producto suficiente?" - -#. module: stock_warehouse_orderpoint_stock_info -#: model:ir.model,name:stock_warehouse_orderpoint_stock_info.model_stock_warehouse_orderpoint -msgid "Minimum Inventory Rule" -msgstr "Regla de inventario mínimo" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Rules" -msgstr "Reglas" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Stock Info" -msgstr "Info. de Stock" - diff --git a/stock_warehouse_orderpoint_stock_info/i18n/pt_BR.po b/stock_warehouse_orderpoint_stock_info/i18n/pt_BR.po deleted file mode 100644 index dad15851b..000000000 --- a/stock_warehouse_orderpoint_stock_info/i18n/pt_BR.po +++ /dev/null @@ -1,49 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * stock_warehouse_orderpoint_stock_info -# -# Translators: -# Armando Vulcano Junior , 2015 -msgid "" -msgstr "" -"Project-Id-Version: odoomrp-utils (8.0)\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-08 08:45+0000\n" -"PO-Revision-Date: 2015-10-09 01:01+0000\n" -"Last-Translator: danimaribeiro \n" -"Language-Team: Portuguese (Brazil) (http://www.transifex.com/oca/odoomrp-utils-8-0/language/pt_BR/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: pt_BR\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Availability under minimum" -msgstr "Disponibilidade menor que mínimo" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Group By" -msgstr "Agrupado por" - -#. module: stock_warehouse_orderpoint_stock_info -#: field:stock.warehouse.orderpoint,available:0 -msgid "Is enough product available?" -msgstr "Produto disponível é suficiente ?" - -#. module: stock_warehouse_orderpoint_stock_info -#: model:ir.model,name:stock_warehouse_orderpoint_stock_info.model_stock_warehouse_orderpoint -msgid "Minimum Inventory Rule" -msgstr "Regra minima de inventário" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Rules" -msgstr "Regras" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Stock Info" -msgstr "Informação de estoque" diff --git a/stock_warehouse_orderpoint_stock_info/i18n/sl.po b/stock_warehouse_orderpoint_stock_info/i18n/sl.po deleted file mode 100644 index 9ffba9caa..000000000 --- a/stock_warehouse_orderpoint_stock_info/i18n/sl.po +++ /dev/null @@ -1,49 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * stock_warehouse_orderpoint_stock_info -# -# Translators: -# Matjaž Mozetič , 2015 -msgid "" -msgstr "" -"Project-Id-Version: odoomrp-utils (8.0)\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-08 08:45+0000\n" -"PO-Revision-Date: 2015-09-28 13:15+0000\n" -"Last-Translator: Matjaž Mozetič \n" -"Language-Team: Slovenian (http://www.transifex.com/oca/odoomrp-utils-8-0/language/sl/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: sl\n" -"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Availability under minimum" -msgstr "Razpoložljivost pod minimumom" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_search_view -msgid "Group By" -msgstr "Združi po..." - -#. module: stock_warehouse_orderpoint_stock_info -#: field:stock.warehouse.orderpoint,available:0 -msgid "Is enough product available?" -msgstr "Je na voljo dovolj proizvodov?" - -#. module: stock_warehouse_orderpoint_stock_info -#: model:ir.model,name:stock_warehouse_orderpoint_stock_info.model_stock_warehouse_orderpoint -msgid "Minimum Inventory Rule" -msgstr "Pravilo minimalne zaloge" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Rules" -msgstr "Pravila" - -#. module: stock_warehouse_orderpoint_stock_info -#: view:stock.warehouse.orderpoint:stock_warehouse_orderpoint_stock_info.stock_warehouse_orderpoint_form_view -msgid "Stock Info" -msgstr "Podatki o zalogi" diff --git a/stock_warehouse_orderpoint_stock_info/models/__init__.py b/stock_warehouse_orderpoint_stock_info/models/__init__.py deleted file mode 100644 index c21daf9b7..000000000 --- a/stock_warehouse_orderpoint_stock_info/models/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import stock_warehouse_orderpoint diff --git a/stock_warehouse_orderpoint_stock_info/models/stock_warehouse_orderpoint.py b/stock_warehouse_orderpoint_stock_info/models/stock_warehouse_orderpoint.py deleted file mode 100644 index c0eaf9a85..000000000 --- a/stock_warehouse_orderpoint_stock_info/models/stock_warehouse_orderpoint.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from openerp import models, fields, api - - -class StockWarehouseOrderpoint(models.Model): - _inherit = 'stock.warehouse.orderpoint' - - @api.multi - def _compute_product_available_qty(self): - for rec in self: - product_available = rec.product_id.with_context( - location=rec.location_id.id - )._product_available()[rec.product_id.id] - rec.product_location_qty = product_available['qty_available'] - rec.incoming_location_qty = product_available['incoming_qty'] - rec.outgoing_location_qty = product_available['outgoing_qty'] - rec.virtual_location_qty = product_available['virtual_available'] - - product_location_qty = fields.Float( - string='Quantity On Location', - compute='_compute_product_available_qty') - incoming_location_qty = fields.Float( - string='Incoming On Location', - compute='_compute_product_available_qty') - outgoing_location_qty = fields.Float( - string='Outgoing On Location', - compute='_compute_product_available_qty') - virtual_location_qty = fields.Float( - string='Forecast On Location', - compute='_compute_product_available_qty') - product_category = fields.Many2one(string='Product Category', - related='product_id.categ_id', - store=True) diff --git a/stock_warehouse_orderpoint_stock_info/tests/__init__.py b/stock_warehouse_orderpoint_stock_info/tests/__init__.py deleted file mode 100644 index a9f167c7a..000000000 --- a/stock_warehouse_orderpoint_stock_info/tests/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import test_stock_warehouse_orderpoint diff --git a/stock_warehouse_orderpoint_stock_info/tests/test_stock_warehouse_orderpoint.py b/stock_warehouse_orderpoint_stock_info/tests/test_stock_warehouse_orderpoint.py deleted file mode 100644 index 6a6880bf3..000000000 --- a/stock_warehouse_orderpoint_stock_info/tests/test_stock_warehouse_orderpoint.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 OdooMRP Team -# Copyright 2016 AvanzOSC -# Copyright 2016 Pedro M. Baeza -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from openerp.tests import common - - -class TestStockWarehouseOrderpoint(common.TransactionCase): - - def setUp(self): - super(TestStockWarehouseOrderpoint, self).setUp() - # Get required Model - self.reordering_rule_model = self.env['stock.warehouse.orderpoint'] - self.stock_move_model = self.env['stock.move'] - self.product_model = self.env['product.product'] - self.product_ctg_model = self.env['product.category'] - - # Get required Model data - self.product_uom = self.env.ref('product.product_uom_unit') - self.dest_location = self.env.ref('stock.stock_location_stock') - self.location = self.env.ref('stock.stock_location_locations_partner') - self.picking = self.env.ref('stock.picking_type_in') - - # Create product category and product - self.product_ctg = self._create_product_category() - self.product = self._create_product() - - # Create Reordering Rule - self.reordering_record = self.create_orderpoint() - - def _create_product_category(self): - """Create a Product Category.""" - product_ctg = self.product_ctg_model.create({ - 'name': 'test_product_ctg', - 'type': 'normal', - }) - return product_ctg - - def _create_product(self): - """Create a Stockable Product.""" - product = self.product_model.create({ - 'name': 'Test Product', - 'categ_id': self.product_ctg.id, - 'type': 'product', - 'uom_id': self.product_uom.id, - }) - return product - - def create_orderpoint(self): - """Create a Reordering rule for the product.""" - record = self.reordering_rule_model.create({ - 'name': 'Reordering Rule', - 'product_id': self.product.id, - 'product_min_qty': '1', - 'product_max_qty': '5', - 'qty_multiple': '1', - 'location_id': self.dest_location.id, - }) - return record - - def create_stock_move(self): - """Create a Stock Move.""" - move = self.stock_move_model.create({ - 'name': 'Reordering Product', - 'product_id': self.product.id, - 'product_uom': self.product_uom.id, - 'product_uom_qty': '10.0', - 'picking_type_id': self.picking.id, - 'location_id': self.location.id, - 'location_dest_id': self.dest_location.id - }) - move.action_confirm() - return move - - def test_product_qty(self): - """Tests the product quantity in the Reordering rules""" - # Create & process moves to test the product quantity - move = self.create_stock_move() - self.reordering_record.refresh() - self.assertEqual(self.reordering_record.incoming_location_qty, - self.product.incoming_qty, - 'Incoming Qty does not match') - self.assertEqual(self.reordering_record.virtual_location_qty, - self.product.virtual_available, - 'Virtual Qty does not match') - move.action_done() - self.reordering_record.refresh() - self.assertEqual(self.reordering_record.product_location_qty, - self.product.qty_available, - 'Available Qty does not match') - self.assertEqual(self.reordering_record.virtual_location_qty, - self.product.virtual_available, - 'Virtual Qty does not match') diff --git a/stock_warehouse_orderpoint_stock_info/views/stock_warehouse_orderpoint_view.xml b/stock_warehouse_orderpoint_stock_info/views/stock_warehouse_orderpoint_view.xml deleted file mode 100644 index e1daa13e0..000000000 --- a/stock_warehouse_orderpoint_stock_info/views/stock_warehouse_orderpoint_view.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - stock.warehouse.orderpoint.tree - stock.warehouse.orderpoint - - - - - - - - - - - - - - - - stock.warehouse.orderpoint.form - stock.warehouse.orderpoint - - - - - - - - - - - - - stock.warehouser.orderpoint.search - stock.warehouse.orderpoint - - - - - - - - - - diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/README.rst b/stock_warehouse_orderpoint_stock_info_unreserved/README.rst deleted file mode 100644 index 8a6e23476..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/README.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - -====================================== -Reordering Rules stock info unreserved -====================================== - -This modules allows to display the availability of stock on hand and -unreserved on a reordering rule. - -Usage -===== - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/153/9.0 - -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 smashing it by providing a detailed and welcomed -feedback. - - -Credits -======= - -Contributors ------------- -* Jordi Ballester Alomar - - - -Maintainer ----------- - -.. image:: https://odoo-community.org/logo.png - :alt: Odoo Community Association - :target: https://odoo-community.org - -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. - -To contribute to this module, please visit https://odoo-community.org. diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/__openerp__.py b/stock_warehouse_orderpoint_stock_info_unreserved/__openerp__.py deleted file mode 100644 index 0e053289f..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/__openerp__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -{ - "name": "Reordering rules stock info unreserved", - "version": "9.0.1.0.0", - "depends": [ - "stock_warehouse_orderpoint_stock_info", - "stock_available_unreserved" - ], - "author": "Eficent, " - "Odoo Community Association (OCA)", - "website": "http://www.eficent.com", - "category": "Warehouse", - "license": "AGPL-3", - "data": [ - "views/stock_warehouse_orderpoint_view.xml", - ], - "installable": True, - "auto_install": False, -} diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/models/__init__.py b/stock_warehouse_orderpoint_stock_info_unreserved/models/__init__.py deleted file mode 100644 index e25cf0d19..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import stock_warehouse_orderpoint diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/models/stock_warehouse_orderpoint.py b/stock_warehouse_orderpoint_stock_info_unreserved/models/stock_warehouse_orderpoint.py deleted file mode 100644 index 6739d8f37..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/models/stock_warehouse_orderpoint.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from openerp import models, fields, api - - -class StockWarehouseOrderpoint(models.Model): - _inherit = 'stock.warehouse.orderpoint' - - @api.multi - def _compute_product_available_qty(self): - super(StockWarehouseOrderpoint, self)._compute_product_available_qty() - for rec in self: - product_available = rec.product_id.with_context( - location=rec.location_id.id - )._product_available()[rec.product_id.id] - rec.product_location_qty_available_not_res = \ - product_available['qty_available_not_res'] - - product_location_qty_available_not_res = fields.Float( - string='Quantity On Location (Unreserved)', - compute='_compute_product_available_qty') diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/tests/__init__.py b/stock_warehouse_orderpoint_stock_info_unreserved/tests/__init__.py deleted file mode 100644 index bf747c324..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/tests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from . import test_stock_warehouse_orderpoint diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/tests/test_stock_warehouse_orderpoint.py b/stock_warehouse_orderpoint_stock_info_unreserved/tests/test_stock_warehouse_orderpoint.py deleted file mode 100644 index d2edc8f95..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/tests/test_stock_warehouse_orderpoint.py +++ /dev/null @@ -1,112 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Eficent Business and IT Consulting Services, S.L. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from openerp.tests import common - - -class TestStockWarehouseOrderpoint(common.TransactionCase): - - def setUp(self): - super(TestStockWarehouseOrderpoint, self).setUp() - # Get required Model - self.reordering_rule_model = self.env['stock.warehouse.orderpoint'] - self.stock_move_model = self.env['stock.move'] - self.product_model = self.env['product.product'] - self.product_ctg_model = self.env['product.category'] - - # Get required Model data - self.product_uom = self.env.ref('product.product_uom_unit') - self.location_stock = self.env.ref('stock.stock_location_stock') - self.location_shelf1 = self.env.ref('stock.stock_location_components') - self.location_customer = self.env.ref('stock.stock_location_customers') - self.location_supplier = self.env.ref('stock.stock_location_suppliers') - - # Create product category and product - self.product_ctg = self._create_product_category() - self.product = self._create_product() - - # Create Reordering Rule - self.reordering_record = self.create_orderpoint() - - def _create_product_category(self): - """Create a Product Category.""" - product_ctg = self.product_ctg_model.create({ - 'name': 'test_product_ctg', - 'type': 'normal', - }) - return product_ctg - - def _create_product(self): - """Create a Stockable Product.""" - product = self.product_model.create({ - 'name': 'Test Product', - 'categ_id': self.product_ctg.id, - 'type': 'product', - 'uom_id': self.product_uom.id, - }) - return product - - def create_orderpoint(self): - """Create a Reordering rule for the product.""" - record = self.reordering_rule_model.create({ - 'name': 'Reordering Rule', - 'product_id': self.product.id, - 'product_min_qty': '1', - 'product_max_qty': '5', - 'qty_multiple': '1', - 'location_id': self.location_stock.id, - }) - return record - - def create_move(self, source_location, destination_location): - move = self.env['stock.move'].create({ - 'name': 'Test move', - 'product_id': self.product.id, - 'product_uom': self.product_uom.id, - 'product_uom_qty': 10, - 'location_id': source_location.id, - 'location_dest_id': destination_location.id} - ) - - move.action_confirm() - return move - - def test_product_qty(self): - """Tests the product quantity in the Reordering rules""" - # Create & process moves to test the product quantity - move_in = self.create_move(self.location_supplier, self.location_stock) - move_out = self.create_move(self.location_stock, - self.location_customer) - self.reordering_record.refresh() - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - 0.0, - 'Quantity On Hand (Unreserved) does not match') - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - self.product.qty_available_not_res, - 'Quantity On Hand (Unreserved) in the orderpoint ' - 'does not match with the product.') - move_in.action_done() - self.reordering_record.refresh() - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - 10.0, - 'Quantity On Hand (Unreserved) does not match') - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - self.product.qty_available_not_res, - 'Quantity On Hand (Unreserved) in the orderpoint ' - 'does not match with the product.') - move_out.action_assign() - self.reordering_record.refresh() - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - 0.0, - 'Quantity On Hand (Unreserved) does not match') - self.assertEqual(self.reordering_record. - product_location_qty_available_not_res, - self.product.qty_available_not_res, - 'Quantity On Hand (Unreserved) in the orderpoint ' - 'does not match with the product.') diff --git a/stock_warehouse_orderpoint_stock_info_unreserved/views/stock_warehouse_orderpoint_view.xml b/stock_warehouse_orderpoint_stock_info_unreserved/views/stock_warehouse_orderpoint_view.xml deleted file mode 100644 index 2b7c27b7c..000000000 --- a/stock_warehouse_orderpoint_stock_info_unreserved/views/stock_warehouse_orderpoint_view.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - stock.warehouse.orderpoint.tree - stock.warehouse.orderpoint - - - - - - - - - - stock.warehouse.orderpoint.form - stock.warehouse.orderpoint - - - - - - - - - -