├── internal_stock_quant_package ├── __init__.py ├── pyproject.toml ├── tests │ ├── __init__.py │ ├── test_stock_quant_package.py │ ├── common.py │ └── test_stock_picking.py ├── readme │ ├── CONTRIBUTORS.md │ ├── DESCRIPTION.md │ └── USAGE.md ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── models │ ├── __init__.py │ ├── stock_quant_package.py │ ├── stock_internal_package_config_line.py │ ├── stock_picking_type.py │ └── stock_picking.py ├── security │ └── ir.model.access.csv ├── __manifest__.py ├── views │ ├── stock_quant_package_views.xml │ └── stock_picking_type_views.xml ├── i18n │ ├── internal_stock_quant_package.pot │ ├── fr_BE.po │ └── it.po └── README.rst ├── stock_quant_package_dimension ├── __init__.py ├── models │ ├── __init__.py │ └── stock_quant_package.py ├── tests │ ├── __init__.py │ ├── common.py │ └── test_package_dimension.py ├── readme │ ├── DESCRIPTION.md │ └── CONTRIBUTORS.md ├── pyproject.toml ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── __manifest__.py ├── views │ └── stock_quant_package.xml ├── README.rst └── i18n │ ├── stock_quant_package_dimension.pot │ ├── it.po │ └── es.po ├── stock_quant_package_product_packaging ├── __init__.py ├── pyproject.toml ├── tests │ ├── __init__.py │ ├── common.py │ ├── test_stock_quant_package_product_packaging.py │ └── test_auto_assign_package_type.py ├── static │ └── description │ │ ├── icon.png │ │ └── index.html ├── models │ ├── __init__.py │ ├── stock_quant.py │ ├── product_template.py │ ├── product_product.py │ ├── stock_move_line.py │ └── stock_quant_package.py ├── readme │ ├── CONTRIBUTORS.md │ └── DESCRIPTION.md ├── __manifest__.py ├── views │ └── stock_quant_package.xml ├── i18n │ ├── stock_quant_package_product_packaging.pot │ ├── nl.po │ ├── es.po │ └── it.po └── README.rst ├── checklog-odoo.cfg ├── prettier.config.cjs ├── setup └── _metapackage │ └── pyproject.toml ├── .editorconfig ├── .ruff.toml ├── .copier-answers.yml ├── .gitignore ├── .github └── workflows │ ├── pre-commit.yml │ ├── test.yml │ └── stale.yml ├── README.md ├── .pylintrc-mandatory ├── .pylintrc ├── .pre-commit-config.yaml └── eslint.config.cjs /internal_stock_quant_package/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/__init__.py: -------------------------------------------------------------------------------- 1 | from . import models 2 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import stock_quant_package 2 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_package_dimension 2 | -------------------------------------------------------------------------------- /checklog-odoo.cfg: -------------------------------------------------------------------------------- 1 | [checklog-odoo] 2 | ignore= 3 | WARNING.* 0 failed, 0 error\(s\).* 4 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module adds dimension fields on stock packages. 2 | -------------------------------------------------------------------------------- /internal_stock_quant_package/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["whool"] 3 | build-backend = "whool.buildapi" 4 | -------------------------------------------------------------------------------- /internal_stock_quant_package/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import common 2 | from . import test_stock_picking 3 | from . import test_stock_quant_package 4 | -------------------------------------------------------------------------------- /internal_stock_quant_package/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Hughes Damry \<\> 2 | - Henry Backman \<\> 3 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import test_stock_quant_package_product_packaging 2 | from . import test_auto_assign_package_type 3 | -------------------------------------------------------------------------------- /internal_stock_quant_package/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/stock-logistics-tracking/HEAD/internal_stock_quant_package/static/description/icon.png -------------------------------------------------------------------------------- /stock_quant_package_dimension/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/stock-logistics-tracking/HEAD/stock_quant_package_dimension/static/description/icon.png -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/static/description/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OCA/stock-logistics-tracking/HEAD/stock_quant_package_product_packaging/static/description/icon.png -------------------------------------------------------------------------------- /internal_stock_quant_package/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import stock_internal_package_config_line 2 | from . import stock_picking_type 3 | from . import stock_picking 4 | from . import stock_quant_package 5 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/__init__.py: -------------------------------------------------------------------------------- 1 | from . import product_template 2 | from . import product_product 3 | from . import stock_quant 4 | from . import stock_quant_package 5 | from . import stock_move_line 6 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Akim Juillerat \<\> 2 | - Raphaël Reverdy \<\> 3 | - Fernando La Chica - GreenIce \<\> 4 | - Denis Roussel \<\> 5 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/readme/CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | - Akim Juillerat \<\> 2 | - Fernando La Chica - GreenIce \<\> 3 | - Denis Roussel \<\> 4 | - Alexandre Fayolle \<\> 5 | - Jacques-Etienne Baudoux \<\> 6 | - Guewen Baconnier \<\> 7 | -------------------------------------------------------------------------------- /internal_stock_quant_package/security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 2 | stock_internal_config_line_user_access,stock_internal_config_line_user_access,model_stock_internal_package_config_line,base.group_user,1,0,0,0 3 | stock_internal_config_line_manager_access,stock_internal_config_line_manager_access,model_stock_internal_package_config_line,stock.group_stock_manager,1,1,1,1 4 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | 3 | const config = { 4 | // https://github.com/prettier/prettier/issues/15388#issuecomment-1717746872 5 | plugins: [require.resolve("@prettier/plugin-xml")], 6 | bracketSpacing: false, 7 | printWidth: 88, 8 | proseWrap: "always", 9 | semi: true, 10 | trailingComma: "es5", 11 | xmlWhitespaceSensitivity: "preserve", 12 | }; 13 | 14 | module.exports = config; 15 | -------------------------------------------------------------------------------- /setup/_metapackage/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "odoo-addons-oca-stock-logistics-tracking" 3 | version = "18.0.20250908.0" 4 | dependencies = [ 5 | "odoo-addon-internal_stock_quant_package==18.0.*", 6 | "odoo-addon-stock_quant_package_dimension==18.0.*", 7 | "odoo-addon-stock_quant_package_product_packaging==18.0.*", 8 | ] 9 | classifiers=[ 10 | "Programming Language :: Python", 11 | "Framework :: Odoo", 12 | "Framework :: Odoo :: 18.0", 13 | ] 14 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/stock_quant.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | from odoo import models 4 | 5 | 6 | class StockQuant(models.Model): 7 | _inherit = "stock.quant" 8 | 9 | def move_quants( 10 | self, location_dest_id=False, package_dest_id=False, message=False, unpack=False 11 | ): 12 | packages = self.package_id 13 | res = super().move_quants(location_dest_id, package_dest_id, message, unpack) 14 | if unpack: 15 | packages._reset_empty_package_package_type() 16 | return res 17 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module allows to define on a Product Package (stock.quant.package), 2 | a Packaging Type (product.packaging) that is linked to a product, if 3 | said package only contains Quants (stock.quants) from this product, and 4 | the sum of the quants quantities is equal to the Packaging quantity. 5 | 6 | If such a packaging exists, it will be automatically assigned to a 7 | package after the move is set to done. 8 | 9 | The module also computes the package type of the package when products are put in a package. 10 | The computation is done based on the number of products in the package. -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/product_template.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | from odoo import fields, models 4 | 5 | 6 | class ProductTemplate(models.Model): 7 | _inherit = "product.template" 8 | 9 | package_type_id = fields.Many2one( 10 | "stock.package.type", 11 | string="Package type", 12 | help="Defines a 'default' package type for this product to be " 13 | "applied on packages without product packagings and on put-away " 14 | "computation based on package type for product not in a package", 15 | ) 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Configuration for known file extensions 2 | [*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] 3 | charset = utf-8 4 | end_of_line = lf 5 | indent_size = 4 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{json,yml,yaml,rst,md}] 11 | indent_size = 2 12 | 13 | # Do not configure editor for libs and autogenerated content 14 | [{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] 15 | charset = unset 16 | end_of_line = unset 17 | indent_size = unset 18 | indent_style = unset 19 | insert_final_newline = false 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | { 4 | "name": "Stock Quant Package Product Packaging", 5 | "summary": "Use product packagings on packages", 6 | "version": "18.0.1.3.0", 7 | "development_status": "Beta", 8 | "category": "Warehouse Management", 9 | "website": "https://github.com/OCA/stock-logistics-tracking", 10 | "author": "Camptocamp, BCIM, Odoo Community Association (OCA)", 11 | "license": "AGPL-3", 12 | "installable": True, 13 | "depends": ["stock"], 14 | "data": ["views/stock_quant_package.xml"], 15 | } 16 | -------------------------------------------------------------------------------- /internal_stock_quant_package/models/stock_quant_package.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import api, fields, models 5 | 6 | 7 | class StockQuantPackage(models.Model): 8 | _inherit = "stock.quant.package" 9 | 10 | is_internal = fields.Boolean( 11 | compute="_compute_is_internal", store=True, string="Internal use?" 12 | ) 13 | 14 | @api.depends("package_type_id") 15 | def _compute_is_internal(self): 16 | for record in self: 17 | record.is_internal = ( 18 | record.package_type_id 19 | and not record.package_type_id.package_carrier_type 20 | ) 21 | -------------------------------------------------------------------------------- /internal_stock_quant_package/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | { 5 | "name": "Internal Stock Quant Package", 6 | "summary": "This module allows to declare internal stock quant package", 7 | "version": "18.0.1.1.0", 8 | "license": "AGPL-3", 9 | "author": "ACSONE SA/NV, Odoo Community Association (OCA)", 10 | "website": "https://github.com/OCA/stock-logistics-tracking", 11 | "depends": ["stock", "delivery_procurement_group_carrier"], 12 | "data": [ 13 | "security/ir.model.access.csv", 14 | "views/stock_quant_package_views.xml", 15 | "views/stock_picking_type_views.xml", 16 | ], 17 | "installable": True, 18 | } 19 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | 2 | target-version = "py310" 3 | fix = true 4 | 5 | [lint] 6 | extend-select = [ 7 | "B", 8 | "C90", 9 | "E501", # line too long (default 88) 10 | "I", # isort 11 | "UP", # pyupgrade 12 | ] 13 | extend-safe-fixes = ["UP008"] 14 | exclude = ["setup/*"] 15 | 16 | [format] 17 | exclude = ["setup/*"] 18 | 19 | [lint.per-file-ignores] 20 | "__init__.py" = ["F401", "I001"] # ignore unused and unsorted imports in __init__.py 21 | "__manifest__.py" = ["B018"] # useless expression 22 | 23 | [lint.isort] 24 | section-order = ["future", "standard-library", "third-party", "odoo", "odoo-addons", "first-party", "local-folder"] 25 | 26 | [lint.isort.sections] 27 | "odoo" = ["odoo"] 28 | "odoo-addons" = ["odoo.addons"] 29 | 30 | [lint.mccabe] 31 | max-complexity = 16 32 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/__manifest__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | { 4 | "name": "Stock Quant Package Dimension", 5 | "summary": "Use dimensions on packages", 6 | "version": "18.0.1.0.1", 7 | "development_status": "Beta", 8 | "category": "Warehouse Management", 9 | "website": "https://github.com/OCA/stock-logistics-tracking", 10 | "author": "Camptocamp, Odoo Community Association (OCA)", 11 | "license": "AGPL-3", 12 | "application": False, 13 | "installable": True, 14 | "depends": [ 15 | "stock", 16 | "product_packaging_dimension", 17 | "stock_quant_package_product_packaging", 18 | ], 19 | "data": ["views/stock_quant_package.xml"], 20 | } 21 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/product_product.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | from odoo import models, tools 4 | 5 | 6 | class ProductProduct(models.Model): 7 | _inherit = "product.product" 8 | 9 | def _find_best_packaging(self, quantity): 10 | self.ensure_one() 11 | packagings = self.env["product.packaging"].search( 12 | [("product_id", "=", self.id), ("qty", "<=", quantity)], 13 | order="qty DESC, sequence ASC", 14 | ) 15 | for packaging in packagings: 16 | nb, rem = divmod(quantity, packaging.qty) 17 | if tools.float_is_zero(rem, precision_digits=3): 18 | return packaging 19 | return self.env["product.packaging"] 20 | -------------------------------------------------------------------------------- /internal_stock_quant_package/tests/test_stock_quant_package.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | 5 | from .common import TestStockPickingInternal 6 | 7 | 8 | class TestStockQuantPackage(TestStockPickingInternal): 9 | def test_is_internal(self): 10 | self.assertTrue(self.internal_package.is_internal) 11 | self.assertFalse(self.external_package.is_internal) 12 | # changing the package_type triggers the compute method and updates is_internal 13 | self.internal_package.write({"package_type_id": self.external_package_type.id}) 14 | self.assertFalse(self.internal_package.is_internal) 15 | self.external_package.write({"package_type_id": self.internal_package_type.id}) 16 | self.assertTrue(self.external_package.is_internal) 17 | -------------------------------------------------------------------------------- /internal_stock_quant_package/readme/DESCRIPTION.md: -------------------------------------------------------------------------------- 1 | This module allows to declare internal stock quant package. 2 | 3 | Sometimes, when an operator is picking, he needs to put the product in 4 | internal packages placed on his trolley that will be emptied later. 5 | 6 | Two kinds of operations can lead to the emptying of the internal 7 | packages: 8 | 9 | > - when product from the internal packages will be 'put in pack' at the 10 | > pack station (in a pick / pack / ship scenario) 11 | > - when a carrier will load his truck with the products from the 12 | > internal packages (in a pick / ship scenario) 13 | 14 | This modules extends the stock module to add the concept of internal 15 | stock quant package and therefore allows you to manage this kind of 16 | operational need. It ensures that the internal stock quant packages are 17 | emptied when required depending on the picking type configuration. 18 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/stock_move_line.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Camptocamp SA 2 | # Copyright 2025 Jacques-Etienne Baudoux (BCIM) 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 4 | from odoo import models 5 | 6 | 7 | class StockMoveLine(models.Model): 8 | _inherit = "stock.move.line" 9 | 10 | def _action_done(self): 11 | source_packages = self.package_id 12 | res = super()._action_done() 13 | # _action_done in stock module sometimes delete a move line, we 14 | # have to check if it still exists before reading/writing on it 15 | dest_packages = self.exists().result_package_id 16 | (dest_packages | source_packages).auto_assign_packaging() 17 | if not self.env.context.get("skip_reset_empty_package_package_type"): 18 | source_packages._reset_empty_package_package_type() 19 | return res 20 | -------------------------------------------------------------------------------- /.copier-answers.yml: -------------------------------------------------------------------------------- 1 | # Do NOT update manually; changes here will be overwritten by Copier 2 | _commit: v1.29 3 | _src_path: git+https://github.com/OCA/oca-addons-repo-template 4 | additional_ruff_rules: [] 5 | ci: GitHub 6 | convert_readme_fragments_to_markdown: true 7 | enable_checklog_odoo: true 8 | generate_requirements_txt: true 9 | github_check_license: true 10 | github_ci_extra_env: {} 11 | github_enable_codecov: true 12 | github_enable_makepot: true 13 | github_enable_stale_action: true 14 | github_enforce_dev_status_compatibility: true 15 | include_wkhtmltopdf: false 16 | odoo_test_flavor: Both 17 | odoo_version: 18.0 18 | org_name: Odoo Community Association (OCA) 19 | org_slug: OCA 20 | rebel_module_groups: [] 21 | repo_description: stock-logistics-tracking 22 | repo_name: stock-logistics-tracking 23 | repo_slug: stock-logistics-tracking 24 | repo_website: https://github.com/OCA/stock-logistics-tracking 25 | use_pyproject_toml: true 26 | use_ruff: true 27 | 28 | -------------------------------------------------------------------------------- /internal_stock_quant_package/models/stock_internal_package_config_line.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import fields, models 5 | 6 | 7 | class StockInternalPackageConfigLine(models.Model): 8 | _name = "stock.internal.package.config.line" 9 | _description = "Internal Package Configuration Line" 10 | 11 | empty = fields.Boolean() 12 | delivery_carrier_id = fields.Many2one( 13 | "delivery.carrier", 14 | required=True, 15 | ondelete="cascade", 16 | ) 17 | stock_picking_type_id = fields.Many2one( 18 | "stock.picking.type", 19 | required=True, 20 | readonly=True, 21 | ondelete="cascade", 22 | ) 23 | 24 | def write(self, vals): 25 | res = super().write(vals) 26 | self._invalidate_empty_internal_package_on_transfer_cache() 27 | return res 28 | 29 | def _invalidate_empty_internal_package_on_transfer_cache(self): 30 | self.env.registry.clear_cache() 31 | self.env["stock.picking"].invalidate_model() 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | /.venv 5 | /.pytest_cache 6 | /.ruff_cache 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | bin/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | eggs/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | *.eggs 27 | 28 | # Windows installers 29 | *.msi 30 | 31 | # Debian packages 32 | *.deb 33 | 34 | # Redhat packages 35 | *.rpm 36 | 37 | # MacOS packages 38 | *.dmg 39 | *.pkg 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | 53 | # Translations 54 | *.mo 55 | 56 | # Pycharm 57 | .idea 58 | 59 | # Eclipse 60 | .settings 61 | 62 | # Visual Studio cache/options directory 63 | .vs/ 64 | .vscode 65 | 66 | # OSX Files 67 | .DS_Store 68 | 69 | # Django stuff: 70 | *.log 71 | 72 | # Mr Developer 73 | .mr.developer.cfg 74 | .project 75 | .pydevproject 76 | 77 | # Rope 78 | .ropeproject 79 | 80 | # Sphinx documentation 81 | docs/_build/ 82 | 83 | # Backup files 84 | *~ 85 | *.swp 86 | 87 | # OCA rules 88 | !static/lib/ 89 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/views/stock_quant_package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stock.quant.package.form.inherit 5 | stock.quant.package 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | stock.quant.package.tree.package.reusable 24 | stock.quant.package 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /internal_stock_quant_package/models/stock_picking_type.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import api, fields, models, tools 5 | 6 | 7 | class StockPickingType(models.Model): 8 | _inherit = "stock.picking.type" 9 | 10 | empty_internal_package_on_transfer = fields.Boolean( 11 | help="If set internal packages are emptied after the transfer or " 12 | "when products are put in pack.", 13 | default=True, 14 | ) 15 | stock_internal_package_config_line_ids = fields.One2many( 16 | comodel_name="stock.internal.package.config.line", 17 | inverse_name="stock_picking_type_id", 18 | ) 19 | 20 | @api.model 21 | @tools.ormcache("picking_type_id", "carrier_id") 22 | def _empty_internal_package_on_transfer(self, picking_type_id, carrier_id): 23 | """ 24 | To know if internal packages must be emptied: 25 | 1. We check if the flag is set on the picking type 26 | 2. We lookup if a specific configuration exists for the given partner 27 | 3. If a specific configuration exists we return the value of this configuration 28 | 4. Otherwise we return the value on the picking type. 29 | """ 30 | picking_type = self.browse(picking_type_id) 31 | result = picking_type.empty_internal_package_on_transfer 32 | lines = picking_type.stock_internal_package_config_line_ids 33 | carrier_line = lines.filtered( 34 | lambda cl: cl.delivery_carrier_id.id == carrier_id 35 | ) 36 | if carrier_line: 37 | result = carrier_line.empty 38 | return result 39 | -------------------------------------------------------------------------------- /internal_stock_quant_package/readme/USAGE.md: -------------------------------------------------------------------------------- 1 | As this addon rely on the concept of "internal" packages. If you want to 2 | use packages into your picking operations, you need first to activate 3 | the package functionality in the stock settings (see the "Operations" 4 | section). 5 | 6 | Then, you need to create packages and set them as internal. This is done 7 | by going to Inventory \> Products \> Packages and clicking on the 8 | "Create". (Don't forget to tick the "Internal use" box). 9 | 10 | By default, when you put your products into an internal package when 11 | processing a picking, once the picking is done, the package is 12 | automatically emptied. You can change this behavior at 2 levels: 13 | 14 | 1\. At the picking type level: go to "Inventory \> Configuration \> 15 | Operation Types" and edit the picking type you want to change. Then, 16 | untick the "Empty Internal Package On Transfer" box. (By default 17 | internal packages are always emptied when the picking is done). 2. At 18 | the picking type level for a specific carrier: go to "Inventory \> 19 | Configuration \> Operation Types" and edit the picking type you want to 20 | change. Then, add or remove lines in the "Stock Internal Package Config 21 | Line" table. You can add a line for a specific carrier and tick/untick 22 | the "Empty" box. 23 | 24 | To know if internal packages must be emptied or not for a given picking, 25 | the system will first check if a configuration line exists on the 26 | picking type for the carrier of the picking. If a line exists, the 27 | system will use the value of the "Empty" box. If no line exists, the 28 | system will use the value of the "Empty Internal Package On Transfer" 29 | box of the picking type. 30 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/tests/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) 3 | 4 | from odoo.tests import TransactionCase 5 | 6 | 7 | class TestStockQuantPackageCommon(TransactionCase): 8 | @classmethod 9 | def setUpClass(cls): 10 | super().setUpClass() 11 | cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) 12 | cls.wh = cls.env.ref("stock.warehouse0") 13 | cls.wh.out_type_id.default_location_dest_id = cls.env.ref( 14 | "stock.stock_location_customers" 15 | ) 16 | cls.product = cls.env.ref("product.product_delivery_02") 17 | cls.product.write( 18 | { 19 | "weight": 1, 20 | "packaging_ids": [ 21 | (0, 0, {"name": "Small Box", "qty": "1", "weight": "2"}), 22 | (0, 0, {"name": "Box", "qty": "5", "weight": "7"}), 23 | ], 24 | } 25 | ) 26 | cls.package = cls.env["stock.quant.package"].create({}) 27 | cls.move = cls.env["stock.move"].create( 28 | { 29 | "name": cls.product.name, 30 | "picking_type_id": cls.wh.out_type_id.id, 31 | "product_id": cls.product.id, 32 | "product_uom_qty": 11.0, 33 | "product_uom": cls.product.uom_id.id, 34 | "location_id": cls.wh.out_type_id.default_location_src_id.id, 35 | "location_dest_id": cls.wh.out_type_id.default_location_dest_id.id, 36 | "procure_method": "make_to_stock", 37 | "group_id": cls.env["procurement.group"].create({"name": "Test"}).id, 38 | } 39 | ) 40 | cls.move._assign_picking() 41 | cls.move.picking_id.action_confirm() 42 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | pre-commit: 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.11" 20 | - name: Get python version 21 | run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV 22 | - uses: actions/cache@v4 23 | with: 24 | path: ~/.cache/pre-commit 25 | key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} 26 | - name: Install pre-commit 27 | run: pip install pre-commit 28 | - name: Run pre-commit 29 | run: pre-commit run --all-files --show-diff-on-failure --color=always 30 | env: 31 | # Consider valid a PR that changes README fragments but doesn't 32 | # change the README.rst file itself. It's not really a problem 33 | # because the bot will update it anyway after merge. This way, we 34 | # lower the barrier for functional contributors that want to fix the 35 | # readme fragments, while still letting developers get README 36 | # auto-generated (which also helps functionals when using runboat). 37 | # DOCS https://pre-commit.com/#temporarily-disabling-hooks 38 | SKIP: oca-gen-addon-readme 39 | - name: Check that all files generated by pre-commit are in git 40 | run: | 41 | newfiles="$(git ls-files --others --exclude-from=.gitignore)" 42 | if [ "$newfiles" != "" ] ; then 43 | echo "Please check-in the following files:" 44 | echo "$newfiles" 45 | exit 1 46 | fi 47 | -------------------------------------------------------------------------------- /internal_stock_quant_package/views/stock_quant_package_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | stock.quant.package.form (in internal_stock_quant_package) 9 | stock.quant.package 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | stock.quant.package.search (in internal_stock_quant_package) 22 | stock.quant.package 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | stock.quant.package.tree (in internal_stock_quant_package) 39 | stock.quant.package 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/tests/test_package_dimension.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | 4 | from odoo.tests import Form 5 | 6 | from . import common 7 | 8 | 9 | class TestStockQuantPackageProductPackaging(common.TestStockQuantPackageCommon): 10 | @classmethod 11 | def setUpClass(cls): 12 | super().setUpClass() 13 | cls.product.packaging_ids = cls.packaging = cls.env["product.packaging"].create( 14 | { 15 | "name": "10 pack", 16 | "product_id": cls.product.id, 17 | "qty": 10, 18 | "packaging_length": 12, 19 | "width": 13, 20 | "height": 14, 21 | "weight": 15, 22 | } 23 | ) 24 | cls.uom_cm = cls.env["uom.uom"].search([("name", "=", "cm")], limit=1) 25 | 26 | def test_set_dimensions_on_write(self): 27 | self.package.with_context( 28 | _auto_assign_packaging=True 29 | ).product_packaging_id = self.packaging 30 | self.assertRecordValues( 31 | self.package, 32 | [{"pack_length": 12, "width": 13, "height": 14, "pack_weight": 15}], 33 | ) 34 | 35 | def test_set_dimensions_on_write_no_override(self): 36 | values = {"pack_length": 22, "width": 23, "height": 24, "pack_weight": 25} 37 | self.package.write(values) 38 | self.package.with_context( 39 | _auto_assign_packaging=True 40 | ).product_packaging_id = self.packaging 41 | self.assertRecordValues(self.package, [values]) 42 | 43 | def test_set_dimensions_onchange(self): 44 | values = {"pack_length": 22, "width": 23, "height": 24, "pack_weight": 25} 45 | self.package.write(values) 46 | with Form(self.package) as form: 47 | form.product_packaging_id = self.packaging 48 | form.save() 49 | # onchange overrides values 50 | self.assertRecordValues( 51 | self.package, 52 | [{"pack_length": 12, "width": 13, "height": 14, "pack_weight": 15}], 53 | ) 54 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/views/stock_quant_package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | stock.quant.package.form.inherit 5 | stock.quant.package 6 | 7 | 8 | 9 | 10 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "18.0*" 7 | push: 8 | branches: 9 | - "18.0" 10 | - "18.0-ocabot-*" 11 | 12 | jobs: 13 | unreleased-deps: 14 | runs-on: ubuntu-latest 15 | name: Detect unreleased dependencies 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: | 19 | for reqfile in requirements.txt test-requirements.txt ; do 20 | if [ -f ${reqfile} ] ; then 21 | result=0 22 | # reject non-comment lines that contain a / (i.e. URLs, relative paths) 23 | grep "^[^#].*/" ${reqfile} || result=$? 24 | if [ $result -eq 0 ] ; then 25 | echo "Unreleased dependencies found in ${reqfile}." 26 | exit 1 27 | fi 28 | fi 29 | done 30 | test: 31 | runs-on: ubuntu-22.04 32 | container: ${{ matrix.container }} 33 | name: ${{ matrix.name }} 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | include: 38 | - container: ghcr.io/oca/oca-ci/py3.10-odoo18.0:latest 39 | name: test with Odoo 40 | - container: ghcr.io/oca/oca-ci/py3.10-ocb18.0:latest 41 | name: test with OCB 42 | makepot: "true" 43 | services: 44 | postgres: 45 | image: postgres:12.0 46 | env: 47 | POSTGRES_USER: odoo 48 | POSTGRES_PASSWORD: odoo 49 | POSTGRES_DB: odoo 50 | ports: 51 | - 5432:5432 52 | env: 53 | OCA_ENABLE_CHECKLOG_ODOO: "1" 54 | steps: 55 | - uses: actions/checkout@v4 56 | with: 57 | persist-credentials: false 58 | - name: Install addons and dependencies 59 | run: oca_install_addons 60 | - name: Check licenses 61 | run: manifestoo -d . check-licenses 62 | - name: Check development status 63 | run: manifestoo -d . check-dev-status --default-dev-status=Beta 64 | - name: Initialize test db 65 | run: oca_init_test_database 66 | - name: Run tests 67 | run: oca_run_tests 68 | - uses: codecov/codecov-action@v4 69 | with: 70 | token: ${{ secrets.CODECOV_TOKEN }} 71 | - name: Update .pot files 72 | run: oca_export_and_push_pot https://x-access-token:${{ secrets.GIT_PUSH_TOKEN }}@github.com/${{ github.repository }} 73 | if: ${{ matrix.makepot == 'true' && github.event_name == 'push' && github.repository_owner == 'OCA' }} 74 | -------------------------------------------------------------------------------- /internal_stock_quant_package/models/stock_picking.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo import api, fields, models 5 | 6 | 7 | class StockPicking(models.Model): 8 | _inherit = "stock.picking" 9 | 10 | empty_internal_package_on_transfer = fields.Boolean( 11 | compute="_compute_empty_internal_package_on_transfer", 12 | ) 13 | 14 | @api.depends("picking_type_id.empty_internal_package_on_transfer", "carrier_id") 15 | def _compute_empty_internal_package_on_transfer(self): 16 | for record in self: 17 | carrier = record.carrier_id or record.group_id.carrier_id 18 | carrier_id = carrier.id 19 | picking_type_id = record.picking_type_id.id 20 | value = self.env["stock.picking.type"]._empty_internal_package_on_transfer( 21 | picking_type_id, 22 | carrier_id, 23 | ) 24 | record.empty_internal_package_on_transfer = value 25 | 26 | def action_put_in_pack(self): 27 | self._move_lines_clear_internal_result_packages() 28 | return super().action_put_in_pack() 29 | 30 | def button_validate(self): 31 | self._move_lines_clear_internal_result_packages() 32 | res = super().button_validate() 33 | self._empty_transferred_internal_packages() 34 | return res 35 | 36 | def _empty_transferred_internal_packages(self): 37 | """ 38 | Remove products from internal quant packages on picking done 39 | """ 40 | pickings = self.filtered( 41 | lambda p: p.empty_internal_package_on_transfer and p.state == "done" 42 | ) 43 | packages = pickings.move_line_ids.package_id 44 | internal_packages = packages.filtered("is_internal") 45 | if internal_packages: 46 | internal_packages.unpack() 47 | 48 | def _move_lines_clear_internal_result_packages(self): 49 | """ 50 | Remove links between move lines and stock quant package to ensure 51 | that move lines are put into a non internal stock.quant.package 52 | """ 53 | move_lines = self._get_move_lines_internal_package_used_to_empty() 54 | move_lines.write({"result_package_id": False}) 55 | 56 | def _get_move_lines_internal_package_used_to_empty(self): 57 | pickings = self.filtered("empty_internal_package_on_transfer") 58 | return pickings.move_line_ids.filtered( 59 | lambda line: line.result_package_id.is_internal 60 | ) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Runboat](https://img.shields.io/badge/runboat-Try%20me-875A7B.png)](https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-tracking&target_branch=18.0) 3 | [![Pre-commit Status](https://github.com/OCA/stock-logistics-tracking/actions/workflows/pre-commit.yml/badge.svg?branch=18.0)](https://github.com/OCA/stock-logistics-tracking/actions/workflows/pre-commit.yml?query=branch%3A18.0) 4 | [![Build Status](https://github.com/OCA/stock-logistics-tracking/actions/workflows/test.yml/badge.svg?branch=18.0)](https://github.com/OCA/stock-logistics-tracking/actions/workflows/test.yml?query=branch%3A18.0) 5 | [![codecov](https://codecov.io/gh/OCA/stock-logistics-tracking/branch/18.0/graph/badge.svg)](https://codecov.io/gh/OCA/stock-logistics-tracking) 6 | [![Translation Status](https://translation.odoo-community.org/widgets/stock-logistics-tracking-18-0/-/svg-badge.svg)](https://translation.odoo-community.org/engage/stock-logistics-tracking-18-0/?utm_source=widget) 7 | 8 | 9 | 10 | # Stock Tracking 11 | 12 | Enhance packages (stock.quant.package). 13 | 14 | Are you looking for modules related to logistics? Or would like to contribute 15 | to? There are many repositories with specific purposes. Have a look at this 16 | [README](https://github.com/OCA/wms/blob/18.0/README.md). 17 | 18 | 19 | 20 | 21 | 22 | [//]: # (addons) 23 | 24 | Available addons 25 | ---------------- 26 | addon | version | maintainers | summary 27 | --- | --- | --- | --- 28 | [internal_stock_quant_package](internal_stock_quant_package/) | 18.0.1.1.0 | | This module allows to declare internal stock quant package 29 | [stock_quant_package_dimension](stock_quant_package_dimension/) | 18.0.1.0.1 | | Use dimensions on packages 30 | [stock_quant_package_product_packaging](stock_quant_package_product_packaging/) | 18.0.1.3.0 | | Use product packagings on packages 31 | 32 | [//]: # (end addons) 33 | 34 | 35 | 36 | ## Licenses 37 | 38 | This repository is licensed under [AGPL-3.0](LICENSE). 39 | 40 | However, each module can have a totally different license, as long as they adhere to Odoo Community Association (OCA) 41 | policy. Consult each module's `__manifest__.py` file, which contains a `license` key 42 | that explains its license. 43 | 44 | ---- 45 | OCA, or the [Odoo Community Association](http://odoo-community.org/), is a nonprofit 46 | organization whose mission is to support the collaborative development of Odoo features 47 | and promote its widespread use. 48 | -------------------------------------------------------------------------------- /internal_stock_quant_package/views/stock_picking_type_views.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | stock.internal.package.config.line.form 7 | stock.internal.package.config.line 8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | stock.picking.type.form (in internal_stock_quant_package) 27 | stock.picking.type 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | stock.picking.type.tree (in internal_stock_quant_package) 46 | stock.picking.type 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: "0 12 * * 0" 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Stale PRs and issues policy 12 | uses: actions/stale@v9 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | # General settings. 16 | ascending: true 17 | remove-stale-when-updated: true 18 | # Pull Requests settings. 19 | # 120+30 day stale policy for PRs 20 | # * Except PRs marked as "no stale" 21 | days-before-pr-stale: 120 22 | days-before-pr-close: 30 23 | exempt-pr-labels: "no stale" 24 | stale-pr-label: "stale" 25 | stale-pr-message: > 26 | There hasn't been any activity on this pull request in the past 4 months, so 27 | it has been marked as stale and it will be closed automatically if no 28 | further activity occurs in the next 30 days. 29 | 30 | If you want this PR to never become stale, please ask a PSC member to apply 31 | the "no stale" label. 32 | # Issues settings. 33 | # 180+30 day stale policy for open issues 34 | # * Except Issues marked as "no stale" 35 | days-before-issue-stale: 180 36 | days-before-issue-close: 30 37 | exempt-issue-labels: "no stale,needs more information" 38 | stale-issue-label: "stale" 39 | stale-issue-message: > 40 | There hasn't been any activity on this issue in the past 6 months, so it has 41 | been marked as stale and it will be closed automatically if no further 42 | activity occurs in the next 30 days. 43 | 44 | If you want this issue to never become stale, please ask a PSC member to 45 | apply the "no stale" label. 46 | 47 | # 15+30 day stale policy for issues pending more information 48 | # * Issues that are pending more information 49 | # * Except Issues marked as "no stale" 50 | - name: Needs more information stale issues policy 51 | uses: actions/stale@v9 52 | with: 53 | repo-token: ${{ secrets.GITHUB_TOKEN }} 54 | ascending: true 55 | only-labels: "needs more information" 56 | exempt-issue-labels: "no stale" 57 | days-before-stale: 15 58 | days-before-close: 30 59 | days-before-pr-stale: -1 60 | days-before-pr-close: -1 61 | remove-stale-when-updated: true 62 | stale-issue-label: "stale" 63 | stale-issue-message: > 64 | This issue needs more information and there hasn't been any activity 65 | recently, so it has been marked as stale and it will be closed automatically 66 | if no further activity occurs in the next 30 days. 67 | 68 | If you think this is a mistake, please ask a PSC member to remove the "needs 69 | more information" label. 70 | -------------------------------------------------------------------------------- /.pylintrc-mandatory: -------------------------------------------------------------------------------- 1 | 2 | [MASTER] 3 | load-plugins=pylint_odoo 4 | score=n 5 | 6 | [ODOOLINT] 7 | readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" 8 | manifest-required-authors=Odoo Community Association (OCA) 9 | manifest-required-keys=license 10 | manifest-deprecated-keys=description,active 11 | license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 12 | valid-odoo-versions=18.0 13 | 14 | [MESSAGES CONTROL] 15 | disable=all 16 | 17 | enable=anomalous-backslash-in-string, 18 | api-one-deprecated, 19 | api-one-multi-together, 20 | assignment-from-none, 21 | attribute-deprecated, 22 | class-camelcase, 23 | dangerous-default-value, 24 | dangerous-view-replace-wo-priority, 25 | development-status-allowed, 26 | duplicate-id-csv, 27 | duplicate-key, 28 | duplicate-xml-fields, 29 | duplicate-xml-record-id, 30 | eval-referenced, 31 | eval-used, 32 | incoherent-interpreter-exec-perm, 33 | license-allowed, 34 | manifest-author-string, 35 | manifest-deprecated-key, 36 | manifest-required-author, 37 | manifest-required-key, 38 | manifest-version-format, 39 | method-compute, 40 | method-inverse, 41 | method-required-super, 42 | method-search, 43 | openerp-exception-warning, 44 | pointless-statement, 45 | pointless-string-statement, 46 | print-used, 47 | redundant-keyword-arg, 48 | redundant-modulename-xml, 49 | reimported, 50 | relative-import, 51 | return-in-init, 52 | rst-syntax-error, 53 | sql-injection, 54 | too-few-format-args, 55 | translation-field, 56 | translation-required, 57 | unreachable, 58 | use-vim-comment, 59 | wrong-tabs-instead-of-spaces, 60 | xml-syntax-error, 61 | attribute-string-redundant, 62 | character-not-valid-in-resource-link, 63 | consider-merging-classes-inherited, 64 | context-overridden, 65 | create-user-wo-reset-password, 66 | dangerous-filter-wo-user, 67 | dangerous-qweb-replace-wo-priority, 68 | deprecated-data-xml-node, 69 | deprecated-openerp-xml-node, 70 | duplicate-po-message-definition, 71 | except-pass, 72 | file-not-used, 73 | invalid-commit, 74 | manifest-maintainers-list, 75 | missing-newline-extrafiles, 76 | missing-readme, 77 | missing-return, 78 | odoo-addons-relative-import, 79 | old-api7-method-defined, 80 | po-msgstr-variables, 81 | po-syntax-error, 82 | renamed-field-parameter, 83 | resource-not-exist, 84 | str-format-used, 85 | test-folder-imported, 86 | translation-contains-variable, 87 | translation-positional-used, 88 | unnecessary-utf8-coding-comment, 89 | website-manifest-key-not-valid-uri, 90 | xml-attribute-translatable, 91 | xml-deprecated-qweb-directive, 92 | xml-deprecated-tree-attribute, 93 | external-request-timeout 94 | 95 | [REPORTS] 96 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 97 | output-format=colorized 98 | reports=no 99 | -------------------------------------------------------------------------------- /internal_stock_quant_package/tests/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | 5 | from odoo.tests.common import TransactionCase 6 | 7 | 8 | class TestStockPickingInternal(TransactionCase): 9 | @classmethod 10 | def setUpClass(cls): 11 | super().setUpClass() 12 | cls.env = cls.env( 13 | context=dict( 14 | cls.env.context, tracking_disable=True, test_queue_job_no_delay=True 15 | ) 16 | ) 17 | cls.picking_type_out = cls.env.ref("stock.picking_type_out") 18 | cls.picking_type_out.empty_internal_package_on_transfer = True 19 | cls.internal_package_type = cls.env["stock.package.type"].create( 20 | {"name": "Internal Package Type", "package_carrier_type": False} 21 | ) 22 | cls.env["stock.package.type"]._fields.get("package_carrier_type")._selection[ 23 | "dhl" 24 | ] = "DHL" 25 | cls.external_package_type = cls.env["stock.package.type"].create( 26 | {"name": "External Package Type", "package_carrier_type": "dhl"} 27 | ) 28 | cls.internal_package = cls.env["stock.quant.package"].create( 29 | { 30 | "name": "Internal Package", 31 | "is_internal": True, 32 | "package_type_id": cls.internal_package_type.id, 33 | } 34 | ) 35 | cls.external_package = cls.env["stock.quant.package"].create( 36 | { 37 | "name": "External Package", 38 | "is_internal": False, 39 | "package_type_id": cls.external_package_type.id, 40 | } 41 | ) 42 | cls.product_a = cls.env["product.product"].create( 43 | {"name": "Product A", "type": "consu", "is_storable": True} 44 | ) 45 | cls.customer_location = cls.env.ref("stock.stock_location_customers") 46 | cls.stock_location = cls.env.ref("stock.stock_location_stock") 47 | cls.picking = cls.env["stock.picking"].create( 48 | { 49 | "picking_type_id": cls.picking_type_out.id, 50 | "location_dest_id": cls.customer_location.id, 51 | "location_id": cls.stock_location.id, 52 | } 53 | ) 54 | cls.env["stock.move"].create( 55 | { 56 | "name": cls.product_a.name, 57 | "product_id": cls.product_a.id, 58 | "product_uom_qty": 1, 59 | "product_uom": cls.product_a.uom_id.id, 60 | "picking_id": cls.picking.id, 61 | "location_dest_id": cls.customer_location.id, 62 | "location_id": cls.stock_location.id, 63 | } 64 | ) 65 | wiz = cls.env["stock.change.product.qty"].create( 66 | { 67 | "product_id": cls.product_a.id, 68 | "product_tmpl_id": cls.product_a.product_tmpl_id.id, 69 | "new_quantity": 1, 70 | } 71 | ) 72 | wiz.change_product_qty() 73 | 74 | cls.picking.action_assign() 75 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ============================= 6 | Stock Quant Package Dimension 7 | ============================= 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:b8770cf67abea75f83f1f270fa3ec9d4e9d7273a14713192381c64eafd8ba4a5 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Beta 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--tracking-lightgray.png?logo=github 24 | :target: https://github.com/OCA/stock-logistics-tracking/tree/18.0/stock_quant_package_dimension 25 | :alt: OCA/stock-logistics-tracking 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/stock-logistics-tracking-18-0/stock-logistics-tracking-18-0-stock_quant_package_dimension 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-tracking&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This module adds dimension fields on stock packages. 36 | 37 | **Table of contents** 38 | 39 | .. contents:: 40 | :local: 41 | 42 | Bug Tracker 43 | =========== 44 | 45 | Bugs are tracked on `GitHub Issues `_. 46 | In case of trouble, please check there if your issue has already been reported. 47 | If you spotted it first, help us to smash it by providing a detailed and welcomed 48 | `feedback `_. 49 | 50 | Do not contact contributors directly about support or help with technical issues. 51 | 52 | Credits 53 | ======= 54 | 55 | Authors 56 | ------- 57 | 58 | * Camptocamp 59 | 60 | Contributors 61 | ------------ 62 | 63 | - Akim Juillerat 64 | - Raphaël Reverdy 65 | - Fernando La Chica - GreenIce 66 | - Denis Roussel 67 | 68 | Maintainers 69 | ----------- 70 | 71 | This module is maintained by the OCA. 72 | 73 | .. image:: https://odoo-community.org/logo.png 74 | :alt: Odoo Community Association 75 | :target: https://odoo-community.org 76 | 77 | OCA, or the Odoo Community Association, is a nonprofit organization whose 78 | mission is to support the collaborative development of Odoo features and 79 | promote its widespread use. 80 | 81 | This module is part of the `OCA/stock-logistics-tracking `_ project on GitHub. 82 | 83 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 84 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/i18n/stock_quant_package_product_packaging.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_product_packaging 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: stock_quant_package_product_packaging 17 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_product__package_type_id 18 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_template__package_type_id 19 | msgid "" 20 | "Defines a 'default' package type for this product to be applied on packages " 21 | "without product packagings and on put-away computation based on package type" 22 | " for product not in a package" 23 | msgstr "" 24 | 25 | #. module: stock_quant_package_product_packaging 26 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_product__package_type_id 27 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_template__package_type_id 28 | msgid "Package type" 29 | msgstr "" 30 | 31 | #. module: stock_quant_package_product_packaging 32 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant_package 33 | msgid "Packages" 34 | msgstr "" 35 | 36 | #. module: stock_quant_package_product_packaging 37 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 38 | msgid "" 39 | "Packaging of the product, used for internal logisticstransfers, put-away " 40 | "rules, ..." 41 | msgstr "" 42 | 43 | #. module: stock_quant_package_product_packaging 44 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_template 45 | msgid "Product" 46 | msgstr "" 47 | 48 | #. module: stock_quant_package_product_packaging 49 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_move_line 50 | msgid "Product Moves (Stock Move Line)" 51 | msgstr "" 52 | 53 | #. module: stock_quant_package_product_packaging 54 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 55 | msgid "Product Packaging" 56 | msgstr "" 57 | 58 | #. module: stock_quant_package_product_packaging 59 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_product 60 | msgid "Product Variant" 61 | msgstr "" 62 | 63 | #. module: stock_quant_package_product_packaging 64 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant 65 | msgid "Quants" 66 | msgstr "" 67 | 68 | #. module: stock_quant_package_product_packaging 69 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 70 | msgid "Reset Package Type" 71 | msgstr "" 72 | 73 | #. module: stock_quant_package_product_packaging 74 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_id 75 | msgid "Single Product" 76 | msgstr "" 77 | 78 | #. module: stock_quant_package_product_packaging 79 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_qty 80 | msgid "Single Product Qty" 81 | msgstr "" 82 | 83 | #. module: stock_quant_package_product_packaging 84 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 85 | msgid "When set the package type will be reset on unpacking" 86 | msgstr "" 87 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/tests/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | from odoo.tests import TransactionCase 4 | 5 | 6 | class TestPackageTypeCommon(TransactionCase): 7 | @classmethod 8 | def setUpClass(cls): 9 | super().setUpClass() 10 | cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) 11 | ref = cls.env.ref 12 | cls.warehouse = ref("stock.warehouse0") 13 | # set two steps reception on warehouse 14 | cls.warehouse.reception_steps = "two_steps" 15 | 16 | cls.suppliers_location = ref("stock.stock_location_suppliers") 17 | cls.input_location = ref("stock.stock_location_company") 18 | cls.stock_location = ref("stock.stock_location_stock") 19 | cls.pallet_location = cls.env["stock.location"].create( 20 | {"name": "Pallets", "location_id": cls.stock_location.id} 21 | ) 22 | 23 | cls.env["stock.location"]._parent_store_compute() 24 | 25 | cls.receipts_picking_type = ref("stock.picking_type_in") 26 | cls.internal_picking_type = ref("stock.picking_type_internal") 27 | 28 | cls.product = ref("product.product_product_9") 29 | cls.product_lot = ref("stock.product_cable_management_box") 30 | 31 | cls.package_type_pallets = cls.env["stock.package.type"].create( 32 | {"name": "Pallets"} 33 | ) 34 | cls.package_type_cardboxes = cls.env["stock.package.type"].create( 35 | {"name": "Cardboxes"} 36 | ) 37 | 38 | cls.product_cardbox_product_packaging = cls.env["product.packaging"].create( 39 | { 40 | "name": "4 units cardbox", 41 | "qty": 4, 42 | "product_id": cls.product.id, 43 | "package_type_id": cls.package_type_cardboxes.id, 44 | } 45 | ) 46 | cls.product_single_bag_product_packaging = cls.env["product.packaging"].create( 47 | { 48 | "name": "Single Bag", 49 | "qty": 1, 50 | "product_id": cls.product.id, 51 | } 52 | ) 53 | cls.product_pallet_product_packaging = cls.env["product.packaging"].create( 54 | { 55 | "name": "Pallet", 56 | "qty": 48, 57 | "product_id": cls.product.id, 58 | "package_type_id": cls.package_type_pallets.id, 59 | } 60 | ) 61 | 62 | cls.internal_picking_type.write({"show_entire_packs": True}) 63 | cls.receipts_picking_type.show_entire_packs = True 64 | 65 | @classmethod 66 | def _update_qty_in_location( 67 | cls, location, product, quantity, package=None, lot=None 68 | ): 69 | quants = cls.env["stock.quant"]._gather( 70 | product, location, lot_id=lot, package_id=package, strict=True 71 | ) 72 | # this method adds the quantity to the current quantity, so remove it 73 | quantity -= sum(quants.mapped("quantity")) 74 | cls.env["stock.quant"]._update_available_quantity( 75 | product, location, quantity, package_id=package, lot_id=lot 76 | ) 77 | 78 | @classmethod 79 | def _create_single_move(cls, product, quantity=2.0): 80 | picking_type = cls.warehouse.int_type_id 81 | move_vals = { 82 | "name": product.name, 83 | "picking_type_id": picking_type.id, 84 | "product_id": product.id, 85 | "product_uom_qty": quantity, 86 | "product_uom": product.uom_id.id, 87 | "location_id": cls.input_location.id, 88 | "location_dest_id": picking_type.default_location_dest_id.id, 89 | "state": "confirmed", 90 | "procure_method": "make_to_stock", 91 | } 92 | return cls.env["stock.move"].create(move_vals) 93 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/i18n/nl.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_product_packaging 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2025-04-12 21:24+0000\n" 10 | "Last-Translator: Bosd \n" 11 | "Language-Team: none\n" 12 | "Language: nl\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.10.4\n" 18 | 19 | #. module: stock_quant_package_product_packaging 20 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_product__package_type_id 21 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_template__package_type_id 22 | msgid "" 23 | "Defines a 'default' package type for this product to be applied on packages " 24 | "without product packagings and on put-away computation based on package type " 25 | "for product not in a package" 26 | msgstr "" 27 | 28 | #. module: stock_quant_package_product_packaging 29 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_product__package_type_id 30 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_template__package_type_id 31 | msgid "Package type" 32 | msgstr "" 33 | 34 | #. module: stock_quant_package_product_packaging 35 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant_package 36 | msgid "Packages" 37 | msgstr "Pakketten" 38 | 39 | #. module: stock_quant_package_product_packaging 40 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 41 | msgid "" 42 | "Packaging of the product, used for internal logisticstransfers, put-away " 43 | "rules, ..." 44 | msgstr "" 45 | "Verpakking van het product, gebruikt voor interne logistieke overdrachten, " 46 | "opslagregels, ..." 47 | 48 | #. module: stock_quant_package_product_packaging 49 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_template 50 | msgid "Product" 51 | msgstr "" 52 | 53 | #. module: stock_quant_package_product_packaging 54 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_move_line 55 | msgid "Product Moves (Stock Move Line)" 56 | msgstr "Productbewegingen (Voorraadmutatieregel)" 57 | 58 | #. module: stock_quant_package_product_packaging 59 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 60 | msgid "Product Packaging" 61 | msgstr "Productverpakking" 62 | 63 | #. module: stock_quant_package_product_packaging 64 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_product 65 | msgid "Product Variant" 66 | msgstr "" 67 | 68 | #. module: stock_quant_package_product_packaging 69 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant 70 | msgid "Quants" 71 | msgstr "" 72 | 73 | #. module: stock_quant_package_product_packaging 74 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 75 | msgid "Reset Package Type" 76 | msgstr "" 77 | 78 | #. module: stock_quant_package_product_packaging 79 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_id 80 | msgid "Single Product" 81 | msgstr "Enkel Product" 82 | 83 | #. module: stock_quant_package_product_packaging 84 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_qty 85 | msgid "Single Product Qty" 86 | msgstr "Hoeveelheid Enkel Product" 87 | 88 | #. module: stock_quant_package_product_packaging 89 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 90 | msgid "When set the package type will be reset on unpacking" 91 | msgstr "" 92 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | [MASTER] 4 | load-plugins=pylint_odoo 5 | score=n 6 | 7 | [ODOOLINT] 8 | readme-template-url="https://github.com/OCA/maintainer-tools/blob/master/template/module/README.rst" 9 | manifest-required-authors=Odoo Community Association (OCA) 10 | manifest-required-keys=license 11 | manifest-deprecated-keys=description,active 12 | license-allowed=AGPL-3,GPL-2,GPL-2 or any later version,GPL-3,GPL-3 or any later version,LGPL-3 13 | valid-odoo-versions=18.0 14 | 15 | [MESSAGES CONTROL] 16 | disable=all 17 | 18 | # This .pylintrc contains optional AND mandatory checks and is meant to be 19 | # loaded in an IDE to have it check everything, in the hope this will make 20 | # optional checks more visible to contributors who otherwise never look at a 21 | # green travis to see optional checks that failed. 22 | # .pylintrc-mandatory containing only mandatory checks is used the pre-commit 23 | # config as a blocking check. 24 | 25 | enable=anomalous-backslash-in-string, 26 | api-one-deprecated, 27 | api-one-multi-together, 28 | assignment-from-none, 29 | attribute-deprecated, 30 | class-camelcase, 31 | dangerous-default-value, 32 | dangerous-view-replace-wo-priority, 33 | development-status-allowed, 34 | duplicate-id-csv, 35 | duplicate-key, 36 | duplicate-xml-fields, 37 | duplicate-xml-record-id, 38 | eval-referenced, 39 | eval-used, 40 | incoherent-interpreter-exec-perm, 41 | license-allowed, 42 | manifest-author-string, 43 | manifest-deprecated-key, 44 | manifest-required-author, 45 | manifest-required-key, 46 | manifest-version-format, 47 | method-compute, 48 | method-inverse, 49 | method-required-super, 50 | method-search, 51 | openerp-exception-warning, 52 | pointless-statement, 53 | pointless-string-statement, 54 | print-used, 55 | redundant-keyword-arg, 56 | redundant-modulename-xml, 57 | reimported, 58 | relative-import, 59 | return-in-init, 60 | rst-syntax-error, 61 | sql-injection, 62 | too-few-format-args, 63 | translation-field, 64 | translation-required, 65 | unreachable, 66 | use-vim-comment, 67 | wrong-tabs-instead-of-spaces, 68 | xml-syntax-error, 69 | attribute-string-redundant, 70 | character-not-valid-in-resource-link, 71 | consider-merging-classes-inherited, 72 | context-overridden, 73 | create-user-wo-reset-password, 74 | dangerous-filter-wo-user, 75 | dangerous-qweb-replace-wo-priority, 76 | deprecated-data-xml-node, 77 | deprecated-openerp-xml-node, 78 | duplicate-po-message-definition, 79 | except-pass, 80 | file-not-used, 81 | invalid-commit, 82 | manifest-maintainers-list, 83 | missing-newline-extrafiles, 84 | missing-readme, 85 | missing-return, 86 | odoo-addons-relative-import, 87 | old-api7-method-defined, 88 | po-msgstr-variables, 89 | po-syntax-error, 90 | renamed-field-parameter, 91 | resource-not-exist, 92 | str-format-used, 93 | test-folder-imported, 94 | translation-contains-variable, 95 | translation-positional-used, 96 | unnecessary-utf8-coding-comment, 97 | website-manifest-key-not-valid-uri, 98 | xml-attribute-translatable, 99 | xml-deprecated-qweb-directive, 100 | xml-deprecated-tree-attribute, 101 | external-request-timeout, 102 | # messages that do not cause the lint step to fail 103 | consider-merging-classes-inherited, 104 | create-user-wo-reset-password, 105 | dangerous-filter-wo-user, 106 | deprecated-module, 107 | file-not-used, 108 | invalid-commit, 109 | missing-manifest-dependency, 110 | missing-newline-extrafiles, 111 | missing-readme, 112 | no-utf8-coding-comment, 113 | odoo-addons-relative-import, 114 | old-api7-method-defined, 115 | redefined-builtin, 116 | too-complex, 117 | unnecessary-utf8-coding-comment 118 | 119 | 120 | [REPORTS] 121 | msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} 122 | output-format=colorized 123 | reports=no 124 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_product_packaging 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2023-03-21 12:12+0000\n" 10 | "Last-Translator: FranciscoFactorLibre \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.14.1\n" 18 | 19 | #. module: stock_quant_package_product_packaging 20 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_product__package_type_id 21 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_template__package_type_id 22 | msgid "" 23 | "Defines a 'default' package type for this product to be applied on packages " 24 | "without product packagings and on put-away computation based on package type " 25 | "for product not in a package" 26 | msgstr "" 27 | 28 | #. module: stock_quant_package_product_packaging 29 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_product__package_type_id 30 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_template__package_type_id 31 | msgid "Package type" 32 | msgstr "" 33 | 34 | #. module: stock_quant_package_product_packaging 35 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant_package 36 | msgid "Packages" 37 | msgstr "Paquetes" 38 | 39 | #. module: stock_quant_package_product_packaging 40 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 41 | msgid "" 42 | "Packaging of the product, used for internal logisticstransfers, put-away " 43 | "rules, ..." 44 | msgstr "" 45 | "Empaquetado de producto, usado para trasferencias internas, reglas de " 46 | "traslado, ..." 47 | 48 | #. module: stock_quant_package_product_packaging 49 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_template 50 | msgid "Product" 51 | msgstr "" 52 | 53 | #. module: stock_quant_package_product_packaging 54 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_move_line 55 | msgid "Product Moves (Stock Move Line)" 56 | msgstr "Movimientos de producto (Movimiento de stock)" 57 | 58 | #. module: stock_quant_package_product_packaging 59 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 60 | msgid "Product Packaging" 61 | msgstr "Empaquetado de producto" 62 | 63 | #. module: stock_quant_package_product_packaging 64 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_product 65 | msgid "Product Variant" 66 | msgstr "" 67 | 68 | #. module: stock_quant_package_product_packaging 69 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant 70 | msgid "Quants" 71 | msgstr "" 72 | 73 | #. module: stock_quant_package_product_packaging 74 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 75 | msgid "Reset Package Type" 76 | msgstr "" 77 | 78 | #. module: stock_quant_package_product_packaging 79 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_id 80 | msgid "Single Product" 81 | msgstr "Producto individual" 82 | 83 | #. module: stock_quant_package_product_packaging 84 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_qty 85 | msgid "Single Product Qty" 86 | msgstr "Cantidad de producto individual" 87 | 88 | #. module: stock_quant_package_product_packaging 89 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 90 | msgid "When set the package type will be reset on unpacking" 91 | msgstr "" 92 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ===================================== 6 | Stock Quant Package Product Packaging 7 | ===================================== 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:fd746d0c6307df62e8de261d256704b8710de71b364dbf663198982ac474b5f1 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Beta 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--tracking-lightgray.png?logo=github 24 | :target: https://github.com/OCA/stock-logistics-tracking/tree/18.0/stock_quant_package_product_packaging 25 | :alt: OCA/stock-logistics-tracking 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/stock-logistics-tracking-18-0/stock-logistics-tracking-18-0-stock_quant_package_product_packaging 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-tracking&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This module allows to define on a Product Package (stock.quant.package), 36 | a Packaging Type (product.packaging) that is linked to a product, if 37 | said package only contains Quants (stock.quants) from this product, and 38 | the sum of the quants quantities is equal to the Packaging quantity. 39 | 40 | If such a packaging exists, it will be automatically assigned to a 41 | package after the move is set to done. 42 | 43 | The module also computes the package type of the package when products 44 | are put in a package. The computation is done based on the number of 45 | products in the package. 46 | 47 | **Table of contents** 48 | 49 | .. contents:: 50 | :local: 51 | 52 | Bug Tracker 53 | =========== 54 | 55 | Bugs are tracked on `GitHub Issues `_. 56 | In case of trouble, please check there if your issue has already been reported. 57 | If you spotted it first, help us to smash it by providing a detailed and welcomed 58 | `feedback `_. 59 | 60 | Do not contact contributors directly about support or help with technical issues. 61 | 62 | Credits 63 | ======= 64 | 65 | Authors 66 | ------- 67 | 68 | * Camptocamp 69 | * BCIM 70 | 71 | Contributors 72 | ------------ 73 | 74 | - Akim Juillerat 75 | - Fernando La Chica - GreenIce 76 | - Denis Roussel 77 | - Alexandre Fayolle 78 | - Jacques-Etienne Baudoux 79 | - Guewen Baconnier 80 | 81 | Maintainers 82 | ----------- 83 | 84 | This module is maintained by the OCA. 85 | 86 | .. image:: https://odoo-community.org/logo.png 87 | :alt: Odoo Community Association 88 | :target: https://odoo-community.org 89 | 90 | OCA, or the Odoo Community Association, is a nonprofit organization whose 91 | mission is to support the collaborative development of Odoo features and 92 | promote its widespread use. 93 | 94 | This module is part of the `OCA/stock-logistics-tracking `_ project on GitHub. 95 | 96 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 97 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_product_packaging 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2025-11-25 15:42+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.10.4\n" 18 | 19 | #. module: stock_quant_package_product_packaging 20 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_product__package_type_id 21 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_product_template__package_type_id 22 | msgid "" 23 | "Defines a 'default' package type for this product to be applied on packages " 24 | "without product packagings and on put-away computation based on package type " 25 | "for product not in a package" 26 | msgstr "" 27 | "Definisce un tipo collo 'predefinito' per questo prodotto da utilizzare nei " 28 | "colli senza imballaggio prodotto e nel calcolo del deposito in base al tipo " 29 | "collo per prodotto non in un collo" 30 | 31 | #. module: stock_quant_package_product_packaging 32 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_product__package_type_id 33 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_product_template__package_type_id 34 | msgid "Package type" 35 | msgstr "Tipo collo" 36 | 37 | #. module: stock_quant_package_product_packaging 38 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant_package 39 | msgid "Packages" 40 | msgstr "Colli" 41 | 42 | #. module: stock_quant_package_product_packaging 43 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 44 | msgid "" 45 | "Packaging of the product, used for internal logisticstransfers, put-away " 46 | "rules, ..." 47 | msgstr "" 48 | "Imballo del prodotto, utilizzato per trasferimenti logistici interni, regole " 49 | "di stoccaggio, ..." 50 | 51 | #. module: stock_quant_package_product_packaging 52 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_template 53 | msgid "Product" 54 | msgstr "Prodotto" 55 | 56 | #. module: stock_quant_package_product_packaging 57 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_move_line 58 | msgid "Product Moves (Stock Move Line)" 59 | msgstr "Movimenti prodotto (riga movimento di magazzino)" 60 | 61 | #. module: stock_quant_package_product_packaging 62 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__product_packaging_id 63 | msgid "Product Packaging" 64 | msgstr "Imballaggio prodotto" 65 | 66 | #. module: stock_quant_package_product_packaging 67 | #: model:ir.model,name:stock_quant_package_product_packaging.model_product_product 68 | msgid "Product Variant" 69 | msgstr "Variante prodotto" 70 | 71 | #. module: stock_quant_package_product_packaging 72 | #: model:ir.model,name:stock_quant_package_product_packaging.model_stock_quant 73 | msgid "Quants" 74 | msgstr "Quanti" 75 | 76 | #. module: stock_quant_package_product_packaging 77 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 78 | msgid "Reset Package Type" 79 | msgstr "Resetta tipo collo" 80 | 81 | #. module: stock_quant_package_product_packaging 82 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_id 83 | msgid "Single Product" 84 | msgstr "Prodotto singolo" 85 | 86 | #. module: stock_quant_package_product_packaging 87 | #: model:ir.model.fields,field_description:stock_quant_package_product_packaging.field_stock_quant_package__single_product_qty 88 | msgid "Single Product Qty" 89 | msgstr "Q.tà prodotto singolo" 90 | 91 | #. module: stock_quant_package_product_packaging 92 | #: model:ir.model.fields,help:stock_quant_package_product_packaging.field_stock_quant_package__reset_package_type 93 | msgid "When set the package type will be reset on unpacking" 94 | msgstr "Quando impostata il tipo collo verrà resettato allo spacchettamento" 95 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/models/stock_quant_package.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Camptocamp SA 2 | # Copyright 2025 Jacques-Etienne Baudoux (BCIM) 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 4 | from odoo import api, fields, models 5 | 6 | 7 | class StockQuantPackage(models.Model): 8 | _inherit = "stock.quant.package" 9 | 10 | product_packaging_id = fields.Many2one( 11 | "product.packaging", 12 | "Product Packaging", 13 | index=True, 14 | help="Packaging of the product, used for internal logistics" 15 | "transfers, put-away rules, ...", 16 | ) 17 | single_product_id = fields.Many2one( 18 | "product.product", compute="_compute_single_product" 19 | ) 20 | single_product_qty = fields.Float(compute="_compute_single_product") 21 | reset_package_type = fields.Boolean( 22 | default=True, help="When set the package type will be reset on unpacking" 23 | ) 24 | 25 | @api.model_create_multi 26 | def create(self, vals): 27 | records = super().create(vals) 28 | for rec in records: 29 | rec._sync_package_type_from_packaging(rec.product_packaging_id) 30 | return records 31 | 32 | def write(self, vals): 33 | result = super().write(vals) 34 | if vals.get("product_packaging_id"): 35 | self._sync_package_type_from_packaging(self.product_packaging_id) 36 | return result 37 | 38 | @api.depends("quant_ids", "quant_ids.product_id") 39 | def _compute_single_product(self): 40 | for pack in self: 41 | pack_products = pack.quant_ids.mapped("product_id") 42 | if len(pack_products) == 1: 43 | pack.single_product_id = pack_products.id 44 | # TODO handle uom 45 | pack.single_product_qty = sum(pack.quant_ids.mapped("quantity")) 46 | else: 47 | pack.single_product_id = False 48 | pack.single_product_qty = 0 49 | 50 | def auto_assign_packaging(self): 51 | for pack in self: 52 | if pack.single_product_id and pack.single_product_qty: 53 | pack._assign_packaging(pack.single_product_id, pack.single_product_qty) 54 | elif pack.product_packaging_id and not pack.single_product_id: 55 | pack.product_packaging_id = False 56 | 57 | def _assign_packaging(self, product, quantity): 58 | self.ensure_one() 59 | packaging = product._find_best_packaging(quantity) 60 | if packaging and packaging.qty == quantity: 61 | # the call to write will trigger a call to _sync_package_type_from_packaging 62 | self.product_packaging_id = packaging 63 | elif self.product_packaging_id: 64 | self.product_packaging_id = False 65 | if packaging: 66 | self._sync_package_type_from_packaging(packaging) 67 | else: 68 | self._sync_package_type_from_single_product(product, quantity) 69 | 70 | if not self.package_type_id and product.package_type_id: 71 | self.package_type_id = product.package_type_id 72 | 73 | def _sync_package_type_from_packaging(self, packaging): 74 | for package in self: 75 | if package.package_type_id: 76 | # Do not set package type for delivery packages 77 | # to not trigger constraint like height requirement 78 | # (we are delivering them, not storing them) 79 | continue 80 | package_type = packaging.package_type_id 81 | if not package_type: 82 | continue 83 | package.package_type_id = package_type 84 | 85 | def _sync_package_type_from_single_product(self, product, quantity): 86 | for package in self: 87 | if package.package_type_id: 88 | # Do not set package type for delivery packages 89 | # to not trigger constraint like height requirement 90 | # (we are delivering them, not storing them) 91 | continue 92 | if product.package_type_id: 93 | package.package_type_id = product.package_type_id 94 | 95 | def _reset_empty_package_package_type(self): 96 | empty_packages = self.filtered( 97 | lambda rec: not rec.quant_ids and rec.reset_package_type 98 | ) 99 | empty_packages.package_type_id = False 100 | -------------------------------------------------------------------------------- /internal_stock_quant_package/i18n/internal_stock_quant_package.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * internal_stock_quant_package 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: internal_stock_quant_package 17 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_uid 18 | msgid "Created by" 19 | msgstr "" 20 | 21 | #. module: internal_stock_quant_package 22 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_date 23 | msgid "Created on" 24 | msgstr "" 25 | 26 | #. module: internal_stock_quant_package 27 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__delivery_carrier_id 28 | msgid "Delivery Carrier" 29 | msgstr "" 30 | 31 | #. module: internal_stock_quant_package 32 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__display_name 33 | msgid "Display Name" 34 | msgstr "" 35 | 36 | #. module: internal_stock_quant_package 37 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__empty 38 | msgid "Empty" 39 | msgstr "" 40 | 41 | #. module: internal_stock_quant_package 42 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking__empty_internal_package_on_transfer 43 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 44 | msgid "Empty Internal Package On Transfer" 45 | msgstr "" 46 | 47 | #. module: internal_stock_quant_package 48 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__id 49 | msgid "ID" 50 | msgstr "" 51 | 52 | #. module: internal_stock_quant_package 53 | #: model:ir.model.fields,help:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 54 | msgid "" 55 | "If set internal packages are emptied after the transfer or when products are" 56 | " put in pack." 57 | msgstr "" 58 | 59 | #. module: internal_stock_quant_package 60 | #: model_terms:ir.ui.view,arch_db:internal_stock_quant_package.stock_quant_package_search_view 61 | msgid "Internal" 62 | msgstr "" 63 | 64 | #. module: internal_stock_quant_package 65 | #: model:ir.model,name:internal_stock_quant_package.model_stock_internal_package_config_line 66 | msgid "Internal Package Configuration Line" 67 | msgstr "" 68 | 69 | #. module: internal_stock_quant_package 70 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_quant_package__is_internal 71 | msgid "Internal use?" 72 | msgstr "" 73 | 74 | #. module: internal_stock_quant_package 75 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_uid 76 | msgid "Last Updated by" 77 | msgstr "" 78 | 79 | #. module: internal_stock_quant_package 80 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_date 81 | msgid "Last Updated on" 82 | msgstr "" 83 | 84 | #. module: internal_stock_quant_package 85 | #: model:ir.model,name:internal_stock_quant_package.model_stock_quant_package 86 | msgid "Packages" 87 | msgstr "" 88 | 89 | #. module: internal_stock_quant_package 90 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking_type 91 | msgid "Picking Type" 92 | msgstr "" 93 | 94 | #. module: internal_stock_quant_package 95 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__stock_internal_package_config_line_ids 96 | msgid "Stock Internal Package Config Line" 97 | msgstr "" 98 | 99 | #. module: internal_stock_quant_package 100 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__stock_picking_type_id 101 | msgid "Stock Picking Type" 102 | msgstr "" 103 | 104 | #. module: internal_stock_quant_package 105 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking 106 | msgid "Transfer" 107 | msgstr "" 108 | -------------------------------------------------------------------------------- /internal_stock_quant_package/tests/test_stock_picking.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ACSONE SA/NV 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). 3 | 4 | from odoo.exceptions import UserError 5 | from odoo.fields import Command 6 | 7 | from .common import TestStockPickingInternal 8 | 9 | 10 | class TestStockPickingInternalFlow(TestStockPickingInternal): 11 | def test_internal_result_package_emptied_on_transfer(self): 12 | self.assertEqual(self.picking.state, "assigned") 13 | packop = self.picking.move_line_ids 14 | packop.write( 15 | dict( 16 | result_package_id=self.internal_package.id, 17 | picked=True, 18 | ) 19 | ) 20 | self.picking.button_validate() 21 | self.assertEqual(self.picking.state, "done") 22 | self.assertFalse(self.internal_package.quant_ids) 23 | 24 | def test_internal_result_package_not_emptied_on_transfer(self): 25 | self.picking_type_out.empty_internal_package_on_transfer = False 26 | self.assertEqual(self.picking.state, "assigned") 27 | packop = self.picking.move_line_ids 28 | packop.write( 29 | dict( 30 | result_package_id=self.internal_package.id, 31 | picked=True, 32 | ) 33 | ) 34 | self.picking.button_validate() 35 | self.assertEqual(self.picking.state, "done") 36 | self.assertTrue(self.internal_package.quant_ids) 37 | 38 | def test_external_package_not_emptied_on_transfer(self): 39 | self.assertEqual(self.picking.state, "assigned") 40 | packop = self.picking.move_line_ids 41 | packop.write( 42 | dict( 43 | result_package_id=self.external_package.id, 44 | picked=True, 45 | ) 46 | ) 47 | self.picking.button_validate() 48 | self.assertEqual(self.picking.state, "done") 49 | self.assertTrue(self.external_package.quant_ids) 50 | 51 | def test_internal_package_emptied_on_put_in_pack(self): 52 | self.assertEqual(self.picking.state, "assigned") 53 | packop = self.picking.move_line_ids 54 | packop.write( 55 | dict( 56 | result_package_id=self.internal_package.id, 57 | picked=True, 58 | ) 59 | ) 60 | self.picking.action_put_in_pack() 61 | self.assertNotEqual(packop.result_package_id, self.internal_package) 62 | self.picking.button_validate() 63 | self.assertEqual(self.picking.state, "done") 64 | self.assertFalse(self.internal_package.quant_ids) 65 | 66 | def test_internal_package_not_emptied_on_put_in_pack(self): 67 | self.picking_type_out.empty_internal_package_on_transfer = False 68 | self.assertEqual(self.picking.state, "assigned") 69 | packop = self.picking.move_line_ids 70 | packop.write( 71 | dict( 72 | result_package_id=self.internal_package.id, 73 | picked=True, 74 | ) 75 | ) 76 | msg = "Please add 'Done' quantities to the picking to create a new pack." 77 | with self.assertRaises(UserError, msg=msg): 78 | self.picking.action_put_in_pack() 79 | self.equal = self.assertEqual(packop.result_package_id, self.internal_package) 80 | self.picking.button_validate() 81 | self.assertEqual(self.picking.state, "done") 82 | self.assertTrue(self.internal_package.quant_ids) 83 | 84 | def test_internal_package_emptied_on_transfer_depend_on_carrier(self): 85 | carrier_product = self.env["product.product"].create( 86 | {"name": "Product Carrier", "sale_ok": False, "type": "service"} 87 | ) 88 | carrier_1 = self.env["delivery.carrier"].create( 89 | { 90 | "name": "Carrier1", 91 | "product_id": carrier_product.id, 92 | } 93 | ) 94 | self.picking.carrier_id = carrier_1 95 | vals_line = {"empty": False, "delivery_carrier_id": carrier_1.id} 96 | vals = {"stock_internal_package_config_line_ids": [Command.create(vals_line)]} 97 | self.picking_type_out.write(vals) 98 | 99 | self.assertFalse(self.picking.empty_internal_package_on_transfer) 100 | 101 | line = self.picking_type_out.stock_internal_package_config_line_ids 102 | line.empty = True 103 | 104 | self.assertTrue(self.picking.empty_internal_package_on_transfer) 105 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/i18n/stock_quant_package_dimension.pot: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_dimension 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 18.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: \n" 14 | "Plural-Forms: \n" 15 | 16 | #. module: stock_quant_package_dimension 17 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 18 | msgid "Dimensions" 19 | msgstr "" 20 | 21 | #. module: stock_quant_package_dimension 22 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 23 | msgid "Dimensions Units of Measure" 24 | msgstr "" 25 | 26 | #. module: stock_quant_package_dimension 27 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_name 28 | msgid "Length unit of measure label" 29 | msgstr "" 30 | 31 | #. module: stock_quant_package_dimension 32 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__height 33 | msgid "Pack Height" 34 | msgstr "" 35 | 36 | #. module: stock_quant_package_dimension 37 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_length 38 | msgid "Pack Length" 39 | msgstr "" 40 | 41 | #. module: stock_quant_package_dimension 42 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume 43 | msgid "Pack Volume" 44 | msgstr "" 45 | 46 | #. module: stock_quant_package_dimension 47 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_weight 48 | msgid "Pack Weight" 49 | msgstr "" 50 | 51 | #. module: stock_quant_package_dimension 52 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__width 53 | msgid "Pack Width" 54 | msgstr "" 55 | 56 | #. module: stock_quant_package_dimension 57 | #: model:ir.model,name:stock_quant_package_dimension.model_stock_quant_package 58 | msgid "Packages" 59 | msgstr "" 60 | 61 | #. module: stock_quant_package_dimension 62 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 63 | msgid "Packaging volume unit of measure" 64 | msgstr "" 65 | 66 | #. module: stock_quant_package_dimension 67 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 68 | msgid "Units of Measure" 69 | msgstr "" 70 | 71 | #. module: stock_quant_package_dimension 72 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 73 | msgid "UoM for pack length, height, width (based on lenght UoM)" 74 | msgstr "" 75 | 76 | #. module: stock_quant_package_dimension 77 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_name 78 | msgid "Volume Unit of Measure label" 79 | msgstr "" 80 | 81 | #. module: stock_quant_package_dimension 82 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 83 | msgid "Volume Units of Measure" 84 | msgstr "" 85 | 86 | #. module: stock_quant_package_dimension 87 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 88 | msgid "Weight Unit of Measure" 89 | msgstr "" 90 | 91 | #. module: stock_quant_package_dimension 92 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 93 | msgid "Weight Units of Measure" 94 | msgstr "" 95 | 96 | #. module: stock_quant_package_dimension 97 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_name 98 | msgid "Weight unit of measure label" 99 | msgstr "" 100 | 101 | #. module: stock_quant_package_dimension 102 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__height 103 | msgid "height" 104 | msgstr "" 105 | 106 | #. module: stock_quant_package_dimension 107 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__pack_length 108 | msgid "length" 109 | msgstr "" 110 | 111 | #. module: stock_quant_package_dimension 112 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume 113 | msgid "volume" 114 | msgstr "" 115 | 116 | #. module: stock_quant_package_dimension 117 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__width 118 | msgid "width" 119 | msgstr "" 120 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: | 2 | (?x) 3 | # NOT INSTALLABLE ADDONS 4 | # END NOT INSTALLABLE ADDONS 5 | # Files and folders generated by bots, to avoid loops 6 | ^setup/|/static/description/index\.html$| 7 | # We don't want to mess with tool-generated files 8 | .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| 9 | # Maybe reactivate this when all README files include prettier ignore tags? 10 | ^README\.md$| 11 | # Library files can have extraneous formatting (even minimized) 12 | /static/(src/)?lib/| 13 | # Repos using Sphinx to generate docs don't need prettying 14 | ^docs/_templates/.*\.html$| 15 | # Don't bother non-technical authors with formatting issues in docs 16 | readme/.*\.(rst|md)$| 17 | # Ignore build and dist directories in addons 18 | /build/|/dist/| 19 | # Ignore test files in addons 20 | /tests/samples/.*| 21 | # You don't usually want a bot to modify your legal texts 22 | (LICENSE.*|COPYING.*) 23 | default_language_version: 24 | python: python3 25 | node: "22.9.0" 26 | repos: 27 | - repo: local 28 | hooks: 29 | # These files are most likely copier diff rejection junks; if found, 30 | # review them manually, fix the problem (if needed) and remove them 31 | - id: forbidden-files 32 | name: forbidden files 33 | entry: found forbidden files; remove them 34 | language: fail 35 | files: "\\.rej$" 36 | - id: en-po-files 37 | name: en.po files cannot exist 38 | entry: found a en.po file 39 | language: fail 40 | files: '[a-zA-Z0-9_]*/i18n/en\.po$' 41 | - repo: https://github.com/sbidoul/whool 42 | rev: v1.2 43 | hooks: 44 | - id: whool-init 45 | - repo: https://github.com/oca/maintainer-tools 46 | rev: bf9ecb9938b6a5deca0ff3d870fbd3f33341fded 47 | hooks: 48 | # update the NOT INSTALLABLE ADDONS section above 49 | - id: oca-update-pre-commit-excluded-addons 50 | - id: oca-fix-manifest-website 51 | args: ["https://github.com/OCA/stock-logistics-tracking"] 52 | - id: oca-gen-addon-readme 53 | args: 54 | - --addons-dir=. 55 | - --branch=18.0 56 | - --org-name=OCA 57 | - --repo-name=stock-logistics-tracking 58 | - --if-source-changed 59 | - --keep-source-digest 60 | - --convert-fragments-to-markdown 61 | - id: oca-gen-external-dependencies 62 | - repo: https://github.com/OCA/odoo-pre-commit-hooks 63 | rev: v0.0.33 64 | hooks: 65 | - id: oca-checks-odoo-module 66 | - id: oca-checks-po 67 | args: 68 | - --disable=po-pretty-format 69 | - repo: local 70 | hooks: 71 | - id: prettier 72 | name: prettier (with plugin-xml) 73 | entry: prettier 74 | args: 75 | - --write 76 | - --list-different 77 | - --ignore-unknown 78 | types: [text] 79 | files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ 80 | language: node 81 | additional_dependencies: 82 | - "prettier@3.3.3" 83 | - "@prettier/plugin-xml@3.4.1" 84 | - repo: local 85 | hooks: 86 | - id: eslint 87 | name: eslint 88 | entry: eslint 89 | args: 90 | - --color 91 | - --fix 92 | verbose: true 93 | types: [javascript] 94 | language: node 95 | additional_dependencies: 96 | - "eslint@9.12.0" 97 | - "eslint-plugin-jsdoc@50.3.1" 98 | - repo: https://github.com/pre-commit/pre-commit-hooks 99 | rev: v4.6.0 100 | hooks: 101 | - id: trailing-whitespace 102 | # exclude autogenerated files 103 | exclude: /README\.rst$|\.pot?$ 104 | - id: end-of-file-fixer 105 | # exclude autogenerated files 106 | exclude: /README\.rst$|\.pot?$ 107 | - id: debug-statements 108 | - id: fix-encoding-pragma 109 | args: ["--remove"] 110 | - id: check-case-conflict 111 | - id: check-docstring-first 112 | - id: check-executables-have-shebangs 113 | - id: check-merge-conflict 114 | # exclude files where underlines are not distinguishable from merge conflicts 115 | exclude: /README\.rst$|^docs/.*\.rst$ 116 | - id: check-symlinks 117 | - id: check-xml 118 | - id: mixed-line-ending 119 | args: ["--fix=lf"] 120 | - repo: https://github.com/astral-sh/ruff-pre-commit 121 | rev: v0.6.8 122 | hooks: 123 | - id: ruff 124 | args: [--fix, --exit-non-zero-on-fix] 125 | - id: ruff-format 126 | - repo: https://github.com/OCA/pylint-odoo 127 | rev: v9.1.3 128 | hooks: 129 | - id: pylint_odoo 130 | name: pylint with optional checks 131 | args: 132 | - --rcfile=.pylintrc 133 | - --exit-zero 134 | verbose: true 135 | - id: pylint_odoo 136 | args: 137 | - --rcfile=.pylintrc-mandatory 138 | -------------------------------------------------------------------------------- /internal_stock_quant_package/i18n/fr_BE.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * internal_stock_quant_package 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2023-02-09 12:56+0000\n" 10 | "PO-Revision-Date: 2023-02-09 12:56+0000\n" 11 | "Last-Translator: \n" 12 | "Language-Team: \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: \n" 17 | "Plural-Forms: \n" 18 | 19 | #. module: internal_stock_quant_package 20 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_uid 21 | msgid "Created by" 22 | msgstr "" 23 | 24 | #. module: internal_stock_quant_package 25 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_date 26 | msgid "Created on" 27 | msgstr "" 28 | 29 | #. module: internal_stock_quant_package 30 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__delivery_carrier_id 31 | msgid "Delivery Carrier" 32 | msgstr "" 33 | 34 | #. module: internal_stock_quant_package 35 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__display_name 36 | msgid "Display Name" 37 | msgstr "" 38 | 39 | #. module: internal_stock_quant_package 40 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__empty 41 | msgid "Empty" 42 | msgstr "" 43 | 44 | #. module: internal_stock_quant_package 45 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking__empty_internal_package_on_transfer 46 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 47 | msgid "Empty Internal Package On Transfer" 48 | msgstr "Vider les colis internes lors du transfert?" 49 | 50 | #. module: internal_stock_quant_package 51 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__id 52 | msgid "ID" 53 | msgstr "" 54 | 55 | #. module: internal_stock_quant_package 56 | #: model:ir.model.fields,help:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 57 | msgid "" 58 | "If set internal packages are emptied after the transfer or when products are " 59 | "put in pack." 60 | msgstr "" 61 | "Si coché, les colis internes sont vidés une fois la préparation terminée ou " 62 | "lorsqu'on fait la mise en colis." 63 | 64 | #. module: internal_stock_quant_package 65 | #: model_terms:ir.ui.view,arch_db:internal_stock_quant_package.stock_quant_package_search_view 66 | msgid "Internal" 67 | msgstr "Interne" 68 | 69 | #. module: internal_stock_quant_package 70 | #: model:ir.model,name:internal_stock_quant_package.model_stock_internal_package_config_line 71 | msgid "Internal Package Configuration Line" 72 | msgstr "Vider les colis internes lors du transfert?" 73 | 74 | #. module: internal_stock_quant_package 75 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_quant_package__is_internal 76 | msgid "Internal use?" 77 | msgstr "Usage interne?" 78 | 79 | #. module: internal_stock_quant_package 80 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line____last_update 81 | msgid "Last Modified on" 82 | msgstr "" 83 | 84 | #. module: internal_stock_quant_package 85 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_uid 86 | msgid "Last Updated by" 87 | msgstr "" 88 | 89 | #. module: internal_stock_quant_package 90 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_date 91 | msgid "Last Updated on" 92 | msgstr "" 93 | 94 | #. module: internal_stock_quant_package 95 | #: model:ir.model,name:internal_stock_quant_package.model_stock_quant_package 96 | msgid "Packages" 97 | msgstr "Colis" 98 | 99 | #. module: internal_stock_quant_package 100 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking_type 101 | msgid "Picking Type" 102 | msgstr "" 103 | 104 | #. module: internal_stock_quant_package 105 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__stock_internal_package_config_line_ids 106 | msgid "Stock Internal Package Config Line" 107 | msgstr "Vider les colis internes lors du transfert?" 108 | 109 | #. module: internal_stock_quant_package 110 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__stock_picking_type_id 111 | msgid "Stock Picking Type" 112 | msgstr "" 113 | 114 | #. module: internal_stock_quant_package 115 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking 116 | msgid "Transfer" 117 | msgstr "Transfert" 118 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/models/stock_quant_package.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Camptocamp SA 2 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 3 | from odoo import api, fields, models 4 | 5 | 6 | class StockQuantPackage(models.Model): 7 | _inherit = "stock.quant.package" 8 | 9 | pack_weight = fields.Float() 10 | pack_length = fields.Integer(help="length") 11 | width = fields.Integer("Pack Width", help="width") 12 | height = fields.Integer("Pack Height", help="height") 13 | volume = fields.Float( 14 | "Pack Volume", 15 | digits=(8, 4), 16 | compute="_compute_volume", 17 | readonly=True, 18 | store=False, 19 | help="volume", 20 | ) 21 | 22 | length_uom_id = fields.Many2one( 23 | # Same as product.packing 24 | "uom.uom", 25 | "Dimensions Units of Measure", 26 | domain=lambda self: [ 27 | ("category_id", "=", self.env.ref("uom.uom_categ_length").id) 28 | ], 29 | help="UoM for pack length, height, width (based on lenght UoM)", 30 | default=lambda self: self.env[ 31 | "product.template" 32 | ]._get_length_uom_id_from_ir_config_parameter(), 33 | ) 34 | length_uom_name = fields.Char( 35 | # Same as product.packing 36 | string="Length unit of measure label", 37 | related="length_uom_id.name", 38 | readonly=True, 39 | ) 40 | 41 | weight_uom_id = fields.Many2one( 42 | # Same as product.packing 43 | "uom.uom", 44 | string="Weight Units of Measure", 45 | domain=lambda self: [ 46 | ("category_id", "=", self.env.ref("uom.product_uom_categ_kgm").id) 47 | ], 48 | help="Weight Unit of Measure", 49 | compute=False, 50 | default=lambda self: self.env[ 51 | "product.template" 52 | ]._get_weight_uom_id_from_ir_config_parameter(), 53 | ) 54 | 55 | weight_uom_name = fields.Char( 56 | # Same as product.packing 57 | string="Weight unit of measure label", 58 | compute="_compute_weight_uom_name", 59 | ) 60 | 61 | volume_uom_id = fields.Many2one( 62 | # Same as product.packing 63 | "uom.uom", 64 | string="Volume Units of Measure", 65 | domain=lambda self: [ 66 | ("category_id", "=", self.env.ref("uom.product_uom_categ_vol").id) 67 | ], 68 | help="Packaging volume unit of measure", 69 | default=lambda self: self.env[ 70 | "product.template" 71 | ]._get_volume_uom_id_from_ir_config_parameter(), 72 | ) 73 | 74 | volume_uom_name = fields.Char( 75 | # Same as product.packing 76 | string="Volume Unit of Measure label", 77 | related="volume_uom_id.name", 78 | readonly=True, 79 | ) 80 | 81 | @api.depends("weight_uom_id", "weight_uom_id.name") 82 | def _compute_weight_uom_name(self): 83 | # Don't use a related here as the original default value 84 | # causes warnings (Redundant default on related fields are not wanted) 85 | for package in self: 86 | package.weight_uom_name = package.weight_uom_id.name 87 | 88 | @api.depends("pack_length", "width", "height") 89 | def _compute_volume(self): 90 | Packaging = self.env["product.packaging"] 91 | for pack in self: 92 | pack.volume = Packaging._calculate_volume( 93 | pack.pack_length, 94 | pack.height, 95 | pack.width, 96 | pack.length_uom_id, 97 | pack.volume_uom_id, 98 | ) 99 | 100 | def auto_assign_packaging(self): 101 | self = self.with_context(_auto_assign_packaging=True) 102 | res = super().auto_assign_packaging() 103 | return res 104 | 105 | def write(self, vals): 106 | res = super().write(vals) 107 | if self.env.context.get("_auto_assign_packaging") and vals.get( 108 | "product_packaging_id" 109 | ): 110 | self._update_dimensions_from_packaging(override=False) 111 | return res 112 | 113 | def _update_dimensions_fields(self): 114 | # source: destination 115 | return { 116 | "packaging_length": "pack_length", 117 | "width": "width", 118 | "height": "height", 119 | "weight": "pack_weight", 120 | "length_uom_id": "length_uom_id", 121 | "weight_uom_id": "weight_uom_id", 122 | "volume_uom_id": "volume_uom_id", 123 | } 124 | 125 | def _update_dimensions_from_packaging(self, override=False): 126 | for package in self: 127 | if not package.product_packaging_id: 128 | continue 129 | dimension_fields = self._update_dimensions_fields() 130 | for source, dest in dimension_fields.items(): 131 | if not override and package[dest]: 132 | continue 133 | package[dest] = package.product_packaging_id[source] 134 | 135 | @api.onchange("product_packaging_id") 136 | def onchange_product_packaging_id(self): 137 | self._update_dimensions_from_packaging(override=True) 138 | -------------------------------------------------------------------------------- /internal_stock_quant_package/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * internal_stock_quant_package 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2025-01-22 12:06+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 5.6.2\n" 18 | 19 | #. module: internal_stock_quant_package 20 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_uid 21 | msgid "Created by" 22 | msgstr "Creato da" 23 | 24 | #. module: internal_stock_quant_package 25 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__create_date 26 | msgid "Created on" 27 | msgstr "Creato il" 28 | 29 | #. module: internal_stock_quant_package 30 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__delivery_carrier_id 31 | msgid "Delivery Carrier" 32 | msgstr "Vettore consegna" 33 | 34 | #. module: internal_stock_quant_package 35 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__display_name 36 | msgid "Display Name" 37 | msgstr "Nome visualizzato" 38 | 39 | #. module: internal_stock_quant_package 40 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__empty 41 | msgid "Empty" 42 | msgstr "Vuoto" 43 | 44 | #. module: internal_stock_quant_package 45 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking__empty_internal_package_on_transfer 46 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 47 | msgid "Empty Internal Package On Transfer" 48 | msgstr "Collo interno vuoto nel trasferimento" 49 | 50 | #. module: internal_stock_quant_package 51 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__id 52 | msgid "ID" 53 | msgstr "ID" 54 | 55 | #. module: internal_stock_quant_package 56 | #: model:ir.model.fields,help:internal_stock_quant_package.field_stock_picking_type__empty_internal_package_on_transfer 57 | msgid "" 58 | "If set internal packages are emptied after the transfer or when products are" 59 | " put in pack." 60 | msgstr "" 61 | "Se impostata, i colli interni sono svuotati dopo il trasferimento o quando i " 62 | "prodotti sono inseriti in un pacco." 63 | 64 | #. module: internal_stock_quant_package 65 | #: model_terms:ir.ui.view,arch_db:internal_stock_quant_package.stock_quant_package_search_view 66 | msgid "Internal" 67 | msgstr "Interno" 68 | 69 | #. module: internal_stock_quant_package 70 | #: model:ir.model,name:internal_stock_quant_package.model_stock_internal_package_config_line 71 | msgid "Internal Package Configuration Line" 72 | msgstr "Riga configurazione collo interno" 73 | 74 | #. module: internal_stock_quant_package 75 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_quant_package__is_internal 76 | msgid "Internal use?" 77 | msgstr "Uso interno?" 78 | 79 | #. module: internal_stock_quant_package 80 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line____last_update 81 | msgid "Last Modified on" 82 | msgstr "Ultima modifica il" 83 | 84 | #. module: internal_stock_quant_package 85 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_uid 86 | msgid "Last Updated by" 87 | msgstr "Ultimo aggiornamento di" 88 | 89 | #. module: internal_stock_quant_package 90 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__write_date 91 | msgid "Last Updated on" 92 | msgstr "Ultimo aggiornamento il" 93 | 94 | #. module: internal_stock_quant_package 95 | #: model:ir.model,name:internal_stock_quant_package.model_stock_quant_package 96 | msgid "Packages" 97 | msgstr "Colli" 98 | 99 | #. module: internal_stock_quant_package 100 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking_type 101 | msgid "Picking Type" 102 | msgstr "Tipo prelievo" 103 | 104 | #. module: internal_stock_quant_package 105 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_picking_type__stock_internal_package_config_line_ids 106 | msgid "Stock Internal Package Config Line" 107 | msgstr "Riga configurazione collo interno magazzino" 108 | 109 | #. module: internal_stock_quant_package 110 | #: model:ir.model.fields,field_description:internal_stock_quant_package.field_stock_internal_package_config_line__stock_picking_type_id 111 | msgid "Stock Picking Type" 112 | msgstr "Tipo prelievo di magazzino" 113 | 114 | #. module: internal_stock_quant_package 115 | #: model:ir.model,name:internal_stock_quant_package.model_stock_picking 116 | msgid "Transfer" 117 | msgstr "Trasferimento" 118 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/i18n/it.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_dimension 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2024-06-07 13:39+0000\n" 10 | "Last-Translator: mymage \n" 11 | "Language-Team: none\n" 12 | "Language: it\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: stock_quant_package_dimension 20 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 21 | msgid "Dimensions" 22 | msgstr "Dimensioni" 23 | 24 | #. module: stock_quant_package_dimension 25 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 26 | msgid "Dimensions Units of Measure" 27 | msgstr "Unità di misura per le dimensioni" 28 | 29 | #. module: stock_quant_package_dimension 30 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_name 31 | msgid "Length unit of measure label" 32 | msgstr "Etichetta unità di misura lunghezza" 33 | 34 | #. module: stock_quant_package_dimension 35 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__height 36 | msgid "Pack Height" 37 | msgstr "Altezza collo" 38 | 39 | #. module: stock_quant_package_dimension 40 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_length 41 | msgid "Pack Length" 42 | msgstr "Lunghezza collo" 43 | 44 | #. module: stock_quant_package_dimension 45 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume 46 | msgid "Pack Volume" 47 | msgstr "Volume collo" 48 | 49 | #. module: stock_quant_package_dimension 50 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_weight 51 | msgid "Pack Weight" 52 | msgstr "Peso collo" 53 | 54 | #. module: stock_quant_package_dimension 55 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__width 56 | msgid "Pack Width" 57 | msgstr "Larghezza collo" 58 | 59 | #. module: stock_quant_package_dimension 60 | #: model:ir.model,name:stock_quant_package_dimension.model_stock_quant_package 61 | msgid "Packages" 62 | msgstr "Colli" 63 | 64 | #. module: stock_quant_package_dimension 65 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 66 | msgid "Packaging volume unit of measure" 67 | msgstr "Unità di misura per il volume dell'imballaggio" 68 | 69 | #. module: stock_quant_package_dimension 70 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 71 | msgid "Units of Measure" 72 | msgstr "Unità di misura" 73 | 74 | #. module: stock_quant_package_dimension 75 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 76 | msgid "UoM for pack length, height, width (based on lenght UoM)" 77 | msgstr "" 78 | "UdM per lunghezza, altezza, larghezza (basato sull'UdM della lunghezza) del " 79 | "pacco" 80 | 81 | #. module: stock_quant_package_dimension 82 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_name 83 | msgid "Volume Unit of Measure label" 84 | msgstr "Etichetta unità di misura volume" 85 | 86 | #. module: stock_quant_package_dimension 87 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 88 | msgid "Volume Units of Measure" 89 | msgstr "Unità di misura del volume" 90 | 91 | #. module: stock_quant_package_dimension 92 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 93 | msgid "Weight Unit of Measure" 94 | msgstr "Unità di misura del peso" 95 | 96 | #. module: stock_quant_package_dimension 97 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 98 | msgid "Weight Units of Measure" 99 | msgstr "Unità di misura del peso" 100 | 101 | #. module: stock_quant_package_dimension 102 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_name 103 | msgid "Weight unit of measure label" 104 | msgstr "Etichetta unità di misura del peso" 105 | 106 | #. module: stock_quant_package_dimension 107 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__height 108 | msgid "height" 109 | msgstr "altezza" 110 | 111 | #. module: stock_quant_package_dimension 112 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__pack_length 113 | msgid "length" 114 | msgstr "lunghezza" 115 | 116 | #. module: stock_quant_package_dimension 117 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume 118 | msgid "volume" 119 | msgstr "volume" 120 | 121 | #. module: stock_quant_package_dimension 122 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__width 123 | msgid "width" 124 | msgstr "larghezza" 125 | 126 | #~ msgid "kg" 127 | #~ msgstr "kg" 128 | 129 | #~ msgid "Based on the weight of the product." 130 | #~ msgstr "Basato sul peso del prodotto." 131 | 132 | #~ msgid "Estimated weight (in kg)" 133 | #~ msgstr "Peso stimato (in kg)" 134 | 135 | #~ msgid "Based on the weight of the product packagings." 136 | #~ msgstr "Basato sul peso dell'imballo prodotto." 137 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/tests/test_stock_quant_package_product_packaging.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # Copyright 2025 Jacques-Etienne Baudoux (BCIM) 3 | # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) 4 | from odoo.tests import TransactionCase 5 | 6 | 7 | class TestStockQuantPackageProductPackaging(TransactionCase): 8 | @classmethod 9 | def setUpClass(cls): 10 | super().setUpClass() 11 | cls.receipt_picking_type = cls.env.ref("stock.picking_type_in") 12 | # show_reserved must be set here because it changes the behaviour of 13 | # put_in_pack operation: 14 | # if show_reserved: qty_done must be set on stock.picking.move_line_ids 15 | # if not show_reserved: qty_done must be set on 16 | # stock.picking.move_line_nosuggest_ids 17 | cls.product = cls.env.ref("product.product_delivery_02") 18 | cls.packaging = cls.env["product.packaging"].create( 19 | {"name": "10 pack", "product_id": cls.product.id, "qty": 10} 20 | ) 21 | 22 | def test_auto_assign_packaging(self): 23 | location_dest = self.receipt_picking_type.default_location_dest_id 24 | picking = self.env["stock.picking"].create( 25 | { 26 | "picking_type_id": self.receipt_picking_type.id, 27 | "location_id": self.env.ref("stock.stock_location_suppliers").id, 28 | "location_dest_id": location_dest.id, 29 | } 30 | ) 31 | picking._onchange_picking_type() 32 | picking.write( 33 | { 34 | "move_ids": [ 35 | ( 36 | 0, 37 | 0, 38 | { 39 | "name": "TEST", 40 | "product_id": self.product.id, 41 | "product_uom_qty": 30.0, 42 | "product_uom": self.product.uom_id.id, 43 | "location_id": picking.location_id.id, 44 | "location_dest_id": picking.location_dest_id.id, 45 | }, 46 | ) 47 | ] 48 | } 49 | ) 50 | picking.action_confirm() 51 | picking.move_line_ids.quantity = 10.0 52 | first_package = picking.action_put_in_pack() 53 | picking.action_assign() 54 | picking.move_line_ids.filtered( 55 | lambda ml: not ml.result_package_id 56 | ).quantity = 20.0 57 | second_package = picking.action_put_in_pack() 58 | picking.button_validate() 59 | self.assertEqual(first_package.single_product_id, self.product) 60 | self.assertEqual(first_package.single_product_qty, 10.0) 61 | self.assertEqual(second_package.single_product_id, self.product) 62 | self.assertEqual(second_package.single_product_qty, 20.0) 63 | self.assertEqual(first_package.product_packaging_id, self.packaging) 64 | self.assertFalse(second_package.product_packaging_id) 65 | # Add product to first package 66 | picking = self.env["stock.picking"].create( 67 | { 68 | "picking_type_id": self.receipt_picking_type.id, 69 | "location_id": self.env.ref("stock.stock_location_suppliers").id, 70 | "location_dest_id": location_dest.id, 71 | } 72 | ) 73 | picking._onchange_picking_type() 74 | picking.write( 75 | { 76 | "move_ids": [ 77 | ( 78 | 0, 79 | 0, 80 | { 81 | "name": "TEST", 82 | "product_id": self.product.id, 83 | "product_uom_qty": 5.0, 84 | "product_uom": self.product.uom_id.id, 85 | "location_id": picking.location_id.id, 86 | "location_dest_id": picking.location_dest_id.id, 87 | }, 88 | ) 89 | ] 90 | } 91 | ) 92 | picking.action_confirm() 93 | picking.action_assign() 94 | picking.move_line_ids.result_package_id = first_package 95 | picking.button_validate() 96 | self.assertFalse(first_package.product_packaging_id) 97 | # Remove product from first package 98 | picking = self.env["stock.picking"].create( 99 | { 100 | "picking_type_id": self.receipt_picking_type.id, 101 | "location_id": location_dest.id, 102 | "location_dest_id": self.env.ref("stock.stock_location_suppliers").id, 103 | } 104 | ) 105 | picking._onchange_picking_type() 106 | picking.write( 107 | { 108 | "move_ids": [ 109 | ( 110 | 0, 111 | 0, 112 | { 113 | "name": "TEST", 114 | "product_id": self.product.id, 115 | "product_uom_qty": 5.0, 116 | "product_uom": self.product.uom_id.id, 117 | "location_id": picking.location_dest_id.id, 118 | "location_dest_id": picking.location_id.id, 119 | }, 120 | ) 121 | ] 122 | } 123 | ) 124 | picking.action_confirm() 125 | picking.action_assign() 126 | picking.move_line_ids.package_id = first_package 127 | picking.button_validate() 128 | self.assertEqual(first_package.product_packaging_id, self.packaging) 129 | -------------------------------------------------------------------------------- /internal_stock_quant_package/README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://odoo-community.org/readme-banner-image 2 | :target: https://odoo-community.org/get-involved?utm_source=readme 3 | :alt: Odoo Community Association 4 | 5 | ============================ 6 | Internal Stock Quant Package 7 | ============================ 8 | 9 | .. 10 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 11 | !! This file is generated by oca-gen-addon-readme !! 12 | !! changes will be overwritten. !! 13 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 14 | !! source digest: sha256:1274acfc662f3ab0bab7c9aed3fa1aaaeaf04e4e763fdc3c0cebc533025fa01c 15 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 16 | 17 | .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png 18 | :target: https://odoo-community.org/page/development-status 19 | :alt: Beta 20 | .. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png 21 | :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html 22 | :alt: License: AGPL-3 23 | .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--tracking-lightgray.png?logo=github 24 | :target: https://github.com/OCA/stock-logistics-tracking/tree/18.0/internal_stock_quant_package 25 | :alt: OCA/stock-logistics-tracking 26 | .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png 27 | :target: https://translation.odoo-community.org/projects/stock-logistics-tracking-18-0/stock-logistics-tracking-18-0-internal_stock_quant_package 28 | :alt: Translate me on Weblate 29 | .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png 30 | :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-tracking&target_branch=18.0 31 | :alt: Try me on Runboat 32 | 33 | |badge1| |badge2| |badge3| |badge4| |badge5| 34 | 35 | This module allows to declare internal stock quant package. 36 | 37 | Sometimes, when an operator is picking, he needs to put the product in 38 | internal packages placed on his trolley that will be emptied later. 39 | 40 | Two kinds of operations can lead to the emptying of the internal 41 | packages: 42 | 43 | - when product from the internal packages will be 'put in pack' at 44 | the pack station (in a pick / pack / ship scenario) 45 | - when a carrier will load his truck with the products from the 46 | internal packages (in a pick / ship scenario) 47 | 48 | This modules extends the stock module to add the concept of internal 49 | stock quant package and therefore allows you to manage this kind of 50 | operational need. It ensures that the internal stock quant packages are 51 | emptied when required depending on the picking type configuration. 52 | 53 | **Table of contents** 54 | 55 | .. contents:: 56 | :local: 57 | 58 | Usage 59 | ===== 60 | 61 | As this addon rely on the concept of "internal" packages. If you want to 62 | use packages into your picking operations, you need first to activate 63 | the package functionality in the stock settings (see the "Operations" 64 | section). 65 | 66 | Then, you need to create packages and set them as internal. This is done 67 | by going to Inventory > Products > Packages and clicking on the 68 | "Create". (Don't forget to tick the "Internal use" box). 69 | 70 | By default, when you put your products into an internal package when 71 | processing a picking, once the picking is done, the package is 72 | automatically emptied. You can change this behavior at 2 levels: 73 | 74 | 1. At the picking type level: go to "Inventory > Configuration > 75 | Operation Types" and edit the picking type you want to change. Then, 76 | untick the "Empty Internal Package On Transfer" box. (By default 77 | internal packages are always emptied when the picking is done). 2. At 78 | the picking type level for a specific carrier: go to "Inventory > 79 | Configuration > Operation Types" and edit the picking type you want to 80 | change. Then, add or remove lines in the "Stock Internal Package Config 81 | Line" table. You can add a line for a specific carrier and tick/untick 82 | the "Empty" box. 83 | 84 | To know if internal packages must be emptied or not for a given picking, 85 | the system will first check if a configuration line exists on the 86 | picking type for the carrier of the picking. If a line exists, the 87 | system will use the value of the "Empty" box. If no line exists, the 88 | system will use the value of the "Empty Internal Package On Transfer" 89 | box of the picking type. 90 | 91 | Bug Tracker 92 | =========== 93 | 94 | Bugs are tracked on `GitHub Issues `_. 95 | In case of trouble, please check there if your issue has already been reported. 96 | If you spotted it first, help us to smash it by providing a detailed and welcomed 97 | `feedback `_. 98 | 99 | Do not contact contributors directly about support or help with technical issues. 100 | 101 | Credits 102 | ======= 103 | 104 | Authors 105 | ------- 106 | 107 | * ACSONE SA/NV 108 | 109 | Contributors 110 | ------------ 111 | 112 | - Hughes Damry 113 | - Henry Backman 114 | 115 | Maintainers 116 | ----------- 117 | 118 | This module is maintained by the OCA. 119 | 120 | .. image:: https://odoo-community.org/logo.png 121 | :alt: Odoo Community Association 122 | :target: https://odoo-community.org 123 | 124 | OCA, or the Odoo Community Association, is a nonprofit organization whose 125 | mission is to support the collaborative development of Odoo features and 126 | promote its widespread use. 127 | 128 | This module is part of the `OCA/stock-logistics-tracking `_ project on GitHub. 129 | 130 | You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 131 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/i18n/es.po: -------------------------------------------------------------------------------- 1 | # Translation of Odoo Server. 2 | # This file contains the translation of the following modules: 3 | # * stock_quant_package_dimension 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Odoo Server 16.0\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "PO-Revision-Date: 2023-11-26 21:34+0000\n" 10 | "Last-Translator: Ivorra78 \n" 11 | "Language-Team: none\n" 12 | "Language: es\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: \n" 16 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 17 | "X-Generator: Weblate 4.17\n" 18 | 19 | #. module: stock_quant_package_dimension 20 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 21 | msgid "kg" 22 | msgstr "kg" 23 | 24 | #. module: stock_quant_package_dimension 25 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__estimated_pack_weight_kg 26 | msgid "Based on the weight of the product." 27 | msgstr "En función del peso del producto." 28 | 29 | #. module: stock_quant_package_dimension 30 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 31 | msgid "Dimensions" 32 | msgstr "Dimensiones" 33 | 34 | #. module: stock_quant_package_dimension 35 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 36 | msgid "Dimensions Units of Measure" 37 | msgstr "Dimensiones Unidades de medida" 38 | 39 | #. module: stock_quant_package_dimension 40 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__estimated_pack_weight_kg 41 | msgid "Estimated weight (in kg)" 42 | msgstr "Peso estimado (en kg)" 43 | 44 | #. module: stock_quant_package_dimension 45 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__length_uom_name 46 | msgid "Length unit of measure label" 47 | msgstr "Etiqueta de unidad de medida de longitud" 48 | 49 | #. module: stock_quant_package_dimension 50 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__height 51 | msgid "Pack Height" 52 | msgstr "Altura del paquete" 53 | 54 | #. module: stock_quant_package_dimension 55 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_length 56 | msgid "Pack Length" 57 | msgstr "Longitud del paquete" 58 | 59 | #. module: stock_quant_package_dimension 60 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume 61 | msgid "Pack Volume" 62 | msgstr "Volumen del paquete" 63 | 64 | #. module: stock_quant_package_dimension 65 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__pack_weight 66 | msgid "Pack Weight" 67 | msgstr "Peso del paquete" 68 | 69 | #. module: stock_quant_package_dimension 70 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__width 71 | msgid "Pack Width" 72 | msgstr "Anchura del paquete" 73 | 74 | #. module: stock_quant_package_dimension 75 | #: model:ir.model,name:stock_quant_package_dimension.model_stock_quant_package 76 | msgid "Packages" 77 | msgstr "Paquetes" 78 | 79 | #. module: stock_quant_package_dimension 80 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 81 | msgid "Packaging volume unit of measure" 82 | msgstr "Unidad de medida del volumen del envase" 83 | 84 | #. module: stock_quant_package_dimension 85 | #: model_terms:ir.ui.view,arch_db:stock_quant_package_dimension.view_quant_package_form_inherit 86 | msgid "Units of Measure" 87 | msgstr "Unidades de Medida" 88 | 89 | #. module: stock_quant_package_dimension 90 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__length_uom_id 91 | msgid "UoM for pack length, height, width (based on lenght UoM)" 92 | msgstr "" 93 | "UM de longitud, altura y anchura del envase (basada en la UM de longitud)" 94 | 95 | #. module: stock_quant_package_dimension 96 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_name 97 | msgid "Volume Unit of Measure label" 98 | msgstr "Etiqueta de unidad de Medida de Volumen" 99 | 100 | #. module: stock_quant_package_dimension 101 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__volume_uom_id 102 | msgid "Volume Units of Measure" 103 | msgstr "Unidades de Medida de Volumen" 104 | 105 | #. module: stock_quant_package_dimension 106 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 107 | msgid "Weight Unit of Measure" 108 | msgstr "Unidad de Medida del Peso" 109 | 110 | #. module: stock_quant_package_dimension 111 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_id 112 | msgid "Weight Units of Measure" 113 | msgstr "Unidades de Medida del peso" 114 | 115 | #. module: stock_quant_package_dimension 116 | #: model:ir.model.fields,field_description:stock_quant_package_dimension.field_stock_quant_package__weight_uom_name 117 | msgid "Weight unit of measure label" 118 | msgstr "Etiqueta de unidad de medida de peso" 119 | 120 | #. module: stock_quant_package_dimension 121 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__height 122 | msgid "height" 123 | msgstr "Altura" 124 | 125 | #. module: stock_quant_package_dimension 126 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__pack_length 127 | msgid "length" 128 | msgstr "longitud" 129 | 130 | #. module: stock_quant_package_dimension 131 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__volume 132 | msgid "volume" 133 | msgstr "Volumen" 134 | 135 | #. module: stock_quant_package_dimension 136 | #: model:ir.model.fields,help:stock_quant_package_dimension.field_stock_quant_package__width 137 | msgid "width" 138 | msgstr "Anchura" 139 | 140 | #~ msgid "Based on the weight of the product packagings." 141 | #~ msgstr "Basado en el peso de los envases del producto." 142 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/tests/test_auto_assign_package_type.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Camptocamp SA 2 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) 3 | from .common import TestPackageTypeCommon 4 | 5 | 6 | class TestAutoAssignPackageType(TestPackageTypeCommon): 7 | @classmethod 8 | def setUpClass(cls): 9 | super().setUpClass() 10 | cls.product_packaging = cls.product_pallet_product_packaging 11 | cls.package_type = cls.product_packaging.package_type_id 12 | 13 | # Create a new package type for auto assignation 14 | vals = { 15 | "name": "Auto Assigned Package Type", 16 | } 17 | cls.auto_assigned_package_type = cls.env["stock.package.type"].create(vals) 18 | 19 | def test_auto_assign_package_type_without_packaging_id(self): 20 | """Packages without `packaging_id` are internal packages and they 21 | are intended to be stored in the warehouse. 22 | On such packages, a package type is automatically defined. 23 | """ 24 | package = self.env["stock.quant.package"].create( 25 | {"name": "TEST", "product_packaging_id": self.product_packaging.id} 26 | ) 27 | 28 | self.assertEqual(package.package_type_id, self.package_type) 29 | 30 | def test_unpack_package_reset_package_type(self): 31 | """When the quants are moved out of a package, the package type is reset""" 32 | package = self.env["stock.quant.package"].create( 33 | {"name": "TEST", "product_packaging_id": self.product_packaging.id} 34 | ) 35 | 36 | self._update_qty_in_location( 37 | self.warehouse.lot_stock_id, self.product, 5, package=package 38 | ) 39 | 40 | quants = package.quant_ids 41 | quants.move_quants(location_dest_id=self.warehouse.lot_stock_id, unpack=True) 42 | self.assertFalse(package.package_type_id) 43 | 44 | def test_unpack_package_no_reset_package_type(self): 45 | """Check quants moved out of a package, the package type is NOT reset.""" 46 | package = self.env["stock.quant.package"].create( 47 | { 48 | "name": "TEST", 49 | "product_packaging_id": self.product_packaging.id, 50 | "reset_package_type": False, 51 | } 52 | ) 53 | self._update_qty_in_location( 54 | self.warehouse.lot_stock_id, self.product, 5, package=package 55 | ) 56 | quants = package.quant_ids 57 | quants.move_quants(location_dest_id=self.warehouse.lot_stock_id, unpack=True) 58 | self.assertTrue(package.package_type_id) 59 | 60 | def test_auto_assign_packaging(self): 61 | """ 62 | Test the auto assignation for package type from 63 | the default package type on the product 64 | 65 | - Set the default package type on the product 66 | - Create a move and validate it 67 | - The package type should be set on package 68 | """ 69 | 70 | # Set a default package on the product 71 | self.product.package_type_id = self.auto_assigned_package_type 72 | 73 | confirmed_move = self._create_single_move(self.product) 74 | confirmed_move.location_dest_id = self.pallet_location 75 | confirmed_move._assign_picking() 76 | self._update_qty_in_location( 77 | confirmed_move.location_id, 78 | confirmed_move.product_id, 79 | confirmed_move.product_qty, 80 | ) 81 | confirmed_move._action_assign() 82 | picking = confirmed_move.picking_id 83 | picking.action_confirm() 84 | picking.move_line_ids.picked = True 85 | first_package = picking.action_put_in_pack() 86 | 87 | picking.button_validate() 88 | 89 | self.assertEqual(self.auto_assigned_package_type, first_package.package_type_id) 90 | 91 | def test_auto_assign_no_packaging(self): 92 | """ 93 | Test the non auto assignation for package type from 94 | the default package type on the product 95 | 96 | - Unset the default package type on the product 97 | - Create a move and validate it 98 | - The package type should not be set on package 99 | """ 100 | 101 | # Set a default package on the product 102 | self.product.package_type_id = False 103 | 104 | confirmed_move = self._create_single_move(self.product) 105 | confirmed_move.location_dest_id = self.stock_location 106 | confirmed_move._assign_picking() 107 | self._update_qty_in_location( 108 | confirmed_move.location_id, 109 | confirmed_move.product_id, 110 | confirmed_move.product_qty, 111 | ) 112 | confirmed_move._action_assign() 113 | picking = confirmed_move.picking_id 114 | picking.action_confirm() 115 | picking.move_line_ids.picked = True 116 | first_package = picking.action_put_in_pack() 117 | 118 | picking.button_validate() 119 | 120 | self.assertFalse(first_package.package_type_id) 121 | 122 | def test_auto_assign_packaging_pallet(self): 123 | """ 124 | Test the auto assignation for package type from the quantity packaged 125 | """ 126 | 127 | # Set a default package on the product 128 | self.product.package_type_id = self.auto_assigned_package_type 129 | 130 | confirmed_move = self._create_single_move(self.product, quantity=48) 131 | confirmed_move.location_dest_id = self.pallet_location 132 | confirmed_move._assign_picking() 133 | self._update_qty_in_location( 134 | confirmed_move.location_id, 135 | confirmed_move.product_id, 136 | confirmed_move.product_qty, 137 | ) 138 | confirmed_move._action_assign() 139 | picking = confirmed_move.picking_id 140 | picking.action_confirm() 141 | picking.move_line_ids.picked = True 142 | first_package = picking.action_put_in_pack() 143 | 144 | picking.button_validate() 145 | self.assertEqual( 146 | self.product_pallet_product_packaging.package_type_id, 147 | first_package.package_type_id, 148 | ) 149 | 150 | def test_find_best_packaging_pallet(self): 151 | packaging = self.product._find_best_packaging(48) 152 | self.assertEqual(packaging, self.product_pallet_product_packaging) 153 | 154 | def test_find_best_packaging_cardbox(self): 155 | packaging = self.product._find_best_packaging(4) 156 | self.assertEqual(packaging, self.product_cardbox_product_packaging) 157 | 158 | def test_find_best_packaging_single(self): 159 | packaging = self.product._find_best_packaging(5) 160 | self.assertEqual(packaging, self.product_single_bag_product_packaging) 161 | 162 | def test_find_best_packaging_no_match(self): 163 | packaging = self.product._find_best_packaging(5.5) 164 | self.assertFalse(packaging) 165 | -------------------------------------------------------------------------------- /eslint.config.cjs: -------------------------------------------------------------------------------- 1 | jsdoc = require("eslint-plugin-jsdoc"); 2 | 3 | const config = [{ 4 | plugins: { 5 | jsdoc, 6 | }, 7 | 8 | languageOptions: { 9 | globals: { 10 | _: "readonly", 11 | $: "readonly", 12 | fuzzy: "readonly", 13 | jQuery: "readonly", 14 | moment: "readonly", 15 | odoo: "readonly", 16 | openerp: "readonly", 17 | owl: "readonly", 18 | luxon: "readonly", 19 | }, 20 | 21 | ecmaVersion: 2024, 22 | sourceType: "script", 23 | }, 24 | 25 | rules: { 26 | "accessor-pairs": "warn", 27 | "array-callback-return": "warn", 28 | "callback-return": "warn", 29 | "capitalized-comments": ["warn", "always", { 30 | ignoreConsecutiveComments: true, 31 | ignoreInlineComments: true, 32 | }], 33 | complexity: ["warn", 15], 34 | "constructor-super": "warn", 35 | "dot-notation": "warn", 36 | eqeqeq: "warn", 37 | "global-require": "warn", 38 | "handle-callback-err": "warn", 39 | "id-blacklist": "warn", 40 | "id-match": "warn", 41 | "init-declarations": "error", 42 | "max-depth": "warn", 43 | "max-nested-callbacks": "warn", 44 | "max-statements-per-line": "warn", 45 | "no-alert": "warn", 46 | "no-array-constructor": "warn", 47 | "no-caller": "warn", 48 | "no-case-declarations": "warn", 49 | "no-class-assign": "warn", 50 | "no-cond-assign": "error", 51 | "no-const-assign": "error", 52 | "no-constant-condition": "warn", 53 | "no-control-regex": "warn", 54 | "no-debugger": "error", 55 | "no-delete-var": "warn", 56 | "no-div-regex": "warn", 57 | "no-dupe-args": "error", 58 | "no-dupe-class-members": "error", 59 | "no-dupe-keys": "error", 60 | "no-duplicate-case": "error", 61 | "no-duplicate-imports": "error", 62 | "no-else-return": "warn", 63 | "no-empty-character-class": "warn", 64 | "no-empty-function": "error", 65 | "no-empty-pattern": "error", 66 | "no-empty": "warn", 67 | "no-eq-null": "error", 68 | "no-eval": "error", 69 | "no-ex-assign": "error", 70 | "no-extend-native": "warn", 71 | "no-extra-bind": "warn", 72 | "no-extra-boolean-cast": "warn", 73 | "no-extra-label": "warn", 74 | "no-fallthrough": "warn", 75 | "no-func-assign": "error", 76 | "no-global-assign": "error", 77 | "no-implicit-coercion": ["warn", { 78 | allow: ["~"], 79 | }], 80 | "no-implicit-globals": "warn", 81 | "no-implied-eval": "warn", 82 | "no-inline-comments": "warn", 83 | "no-inner-declarations": "warn", 84 | "no-invalid-regexp": "warn", 85 | "no-irregular-whitespace": "warn", 86 | "no-iterator": "warn", 87 | "no-label-var": "warn", 88 | "no-labels": "warn", 89 | "no-lone-blocks": "warn", 90 | "no-lonely-if": "error", 91 | "no-mixed-requires": "error", 92 | "no-multi-str": "warn", 93 | "no-native-reassign": "error", 94 | "no-negated-condition": "warn", 95 | "no-negated-in-lhs": "error", 96 | "no-new-func": "warn", 97 | "no-new-object": "warn", 98 | "no-new-require": "warn", 99 | "no-new-symbol": "warn", 100 | "no-new-wrappers": "warn", 101 | "no-new": "warn", 102 | "no-obj-calls": "warn", 103 | "no-octal-escape": "warn", 104 | "no-octal": "warn", 105 | "no-param-reassign": "warn", 106 | "no-path-concat": "warn", 107 | "no-process-env": "warn", 108 | "no-process-exit": "warn", 109 | "no-proto": "warn", 110 | "no-prototype-builtins": "warn", 111 | "no-redeclare": "warn", 112 | "no-regex-spaces": "warn", 113 | "no-restricted-globals": "warn", 114 | "no-restricted-imports": "warn", 115 | "no-restricted-modules": "warn", 116 | "no-restricted-syntax": "warn", 117 | "no-return-assign": "error", 118 | "no-script-url": "warn", 119 | "no-self-assign": "warn", 120 | "no-self-compare": "warn", 121 | "no-sequences": "warn", 122 | "no-shadow-restricted-names": "warn", 123 | "no-shadow": "warn", 124 | "no-sparse-arrays": "warn", 125 | "no-sync": "warn", 126 | "no-this-before-super": "warn", 127 | "no-throw-literal": "warn", 128 | "no-undef-init": "warn", 129 | "no-undef": "error", 130 | "no-unmodified-loop-condition": "warn", 131 | "no-unneeded-ternary": "error", 132 | "no-unreachable": "error", 133 | "no-unsafe-finally": "error", 134 | "no-unused-expressions": "error", 135 | "no-unused-labels": "error", 136 | "no-unused-vars": "error", 137 | "no-use-before-define": "error", 138 | "no-useless-call": "warn", 139 | "no-useless-computed-key": "warn", 140 | "no-useless-concat": "warn", 141 | "no-useless-constructor": "warn", 142 | "no-useless-escape": "warn", 143 | "no-useless-rename": "warn", 144 | "no-void": "warn", 145 | "no-with": "warn", 146 | "operator-assignment": ["error", "always"], 147 | "prefer-const": "warn", 148 | radix: "warn", 149 | "require-yield": "warn", 150 | "sort-imports": "warn", 151 | "spaced-comment": ["error", "always"], 152 | strict: ["error", "function"], 153 | "use-isnan": "error", 154 | 155 | "jsdoc/check-tag-names": "warn", 156 | "jsdoc/check-types": "warn", 157 | "jsdoc/require-param-description": "off", 158 | "jsdoc/require-return": "off", 159 | "jsdoc/require-return-description": "off", 160 | "jsdoc/require-return-type": "off", 161 | 162 | "valid-typeof": "warn", 163 | yoda: "warn", 164 | }, 165 | 166 | settings: { 167 | jsdoc: { 168 | tagNamePreference: { 169 | arg: "param", 170 | argument: "param", 171 | augments: "extends", 172 | constructor: "class", 173 | exception: "throws", 174 | func: "function", 175 | method: "function", 176 | prop: "property", 177 | return: "returns", 178 | virtual: "abstract", 179 | yield: "yields", 180 | }, 181 | preferredTypes: { 182 | array: "Array", 183 | bool: "Boolean", 184 | boolean: "Boolean", 185 | number: "Number", 186 | object: "Object", 187 | str: "String", 188 | string: "String", 189 | }, 190 | }, 191 | }, 192 | 193 | }, { 194 | files: ["**/*.esm.js"], 195 | 196 | languageOptions: { 197 | ecmaVersion: 2024, 198 | sourceType: "module", 199 | }, 200 | }]; 201 | 202 | module.exports = config 203 | -------------------------------------------------------------------------------- /stock_quant_package_dimension/static/description/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | README.rst 7 | 361 | 362 | 363 |
364 | 365 | 366 | 367 | Odoo Community Association 368 | 369 |
370 |

Stock Quant Package Dimension

371 | 377 |

Beta License: AGPL-3 OCA/stock-logistics-tracking Translate me on Weblate Try me on Runboat

378 |

This module adds dimension fields on stock packages.

379 |

Table of contents

380 |
381 | 390 |
391 |
392 |

Bug Tracker

393 |

Bugs are tracked on GitHub Issues. 394 | In case of trouble, please check there if your issue has already been reported. 395 | If you spotted it first, help us to smash it by providing a detailed and welcomed 396 | feedback.

397 |

Do not contact contributors directly about support or help with technical issues.

398 |
399 |
400 |

Credits

401 |
402 |

Authors

403 |
    404 |
  • Camptocamp
  • 405 |
406 |
407 |
408 |

Contributors

409 | 415 |
416 |
417 |

Maintainers

418 |

This module is maintained by the OCA.

419 | 420 | Odoo Community Association 421 | 422 |

OCA, or the Odoo Community Association, is a nonprofit organization whose 423 | mission is to support the collaborative development of Odoo features and 424 | promote its widespread use.

425 |

This module is part of the OCA/stock-logistics-tracking project on GitHub.

426 |

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

427 |
428 |
429 |
430 |
431 | 432 | 433 | -------------------------------------------------------------------------------- /stock_quant_package_product_packaging/static/description/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | README.rst 7 | 361 | 362 | 363 |
364 | 365 | 366 | 367 | Odoo Community Association 368 | 369 |
370 |

Stock Quant Package Product Packaging

371 | 377 |

Beta License: AGPL-3 OCA/stock-logistics-tracking Translate me on Weblate Try me on Runboat

378 |

This module allows to define on a Product Package (stock.quant.package), 379 | a Packaging Type (product.packaging) that is linked to a product, if 380 | said package only contains Quants (stock.quants) from this product, and 381 | the sum of the quants quantities is equal to the Packaging quantity.

382 |

If such a packaging exists, it will be automatically assigned to a 383 | package after the move is set to done.

384 |

The module also computes the package type of the package when products 385 | are put in a package. The computation is done based on the number of 386 | products in the package.

387 |

Table of contents

388 |
389 | 398 |
399 |
400 |

Bug Tracker

401 |

Bugs are tracked on GitHub Issues. 402 | In case of trouble, please check there if your issue has already been reported. 403 | If you spotted it first, help us to smash it by providing a detailed and welcomed 404 | feedback.

405 |

Do not contact contributors directly about support or help with technical issues.

406 |
407 |
408 |

Credits

409 |
410 |

Authors

411 |
    412 |
  • Camptocamp
  • 413 |
  • BCIM
  • 414 |
415 |
416 |
417 |

Contributors

418 | 426 |
427 |
428 |

Maintainers

429 |

This module is maintained by the OCA.

430 | 431 | Odoo Community Association 432 | 433 |

OCA, or the Odoo Community Association, is a nonprofit organization whose 434 | mission is to support the collaborative development of Odoo features and 435 | promote its widespread use.

436 |

This module is part of the OCA/stock-logistics-tracking project on GitHub.

437 |

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

438 |
439 |
440 |
441 |
442 | 443 | 444 | -------------------------------------------------------------------------------- /internal_stock_quant_package/static/description/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | README.rst 7 | 361 | 362 | 363 |
364 | 365 | 366 | 367 | Odoo Community Association 368 | 369 |
370 |

Internal Stock Quant Package

371 | 377 |

Beta License: AGPL-3 OCA/stock-logistics-tracking Translate me on Weblate Try me on Runboat

378 |

This module allows to declare internal stock quant package.

379 |

Sometimes, when an operator is picking, he needs to put the product in 380 | internal packages placed on his trolley that will be emptied later.

381 |

Two kinds of operations can lead to the emptying of the internal 382 | packages:

383 |
384 |
    385 |
  • when product from the internal packages will be ‘put in pack’ at 386 | the pack station (in a pick / pack / ship scenario)
  • 387 |
  • when a carrier will load his truck with the products from the 388 | internal packages (in a pick / ship scenario)
  • 389 |
390 |
391 |

This modules extends the stock module to add the concept of internal 392 | stock quant package and therefore allows you to manage this kind of 393 | operational need. It ensures that the internal stock quant packages are 394 | emptied when required depending on the picking type configuration.

395 |

Table of contents

396 |
397 | 407 |
408 |
409 |

Usage

410 |

As this addon rely on the concept of “internal” packages. If you want to 411 | use packages into your picking operations, you need first to activate 412 | the package functionality in the stock settings (see the “Operations” 413 | section).

414 |

Then, you need to create packages and set them as internal. This is done 415 | by going to Inventory > Products > Packages and clicking on the 416 | “Create”. (Don’t forget to tick the “Internal use” box).

417 |

By default, when you put your products into an internal package when 418 | processing a picking, once the picking is done, the package is 419 | automatically emptied. You can change this behavior at 2 levels:

420 |

1. At the picking type level: go to “Inventory > Configuration > 421 | Operation Types” and edit the picking type you want to change. Then, 422 | untick the “Empty Internal Package On Transfer” box. (By default 423 | internal packages are always emptied when the picking is done). 2. At 424 | the picking type level for a specific carrier: go to “Inventory > 425 | Configuration > Operation Types” and edit the picking type you want to 426 | change. Then, add or remove lines in the “Stock Internal Package Config 427 | Line” table. You can add a line for a specific carrier and tick/untick 428 | the “Empty” box.

429 |

To know if internal packages must be emptied or not for a given picking, 430 | the system will first check if a configuration line exists on the 431 | picking type for the carrier of the picking. If a line exists, the 432 | system will use the value of the “Empty” box. If no line exists, the 433 | system will use the value of the “Empty Internal Package On Transfer” 434 | box of the picking type.

435 |
436 |
437 |

Bug Tracker

438 |

Bugs are tracked on GitHub Issues. 439 | In case of trouble, please check there if your issue has already been reported. 440 | If you spotted it first, help us to smash it by providing a detailed and welcomed 441 | feedback.

442 |

Do not contact contributors directly about support or help with technical issues.

443 |
444 |
445 |

Credits

446 |
447 |

Authors

448 |
    449 |
  • ACSONE SA/NV
  • 450 |
451 |
452 |
453 |

Contributors

454 | 458 |
459 |
460 |

Maintainers

461 |

This module is maintained by the OCA.

462 | 463 | Odoo Community Association 464 | 465 |

OCA, or the Odoo Community Association, is a nonprofit organization whose 466 | mission is to support the collaborative development of Odoo features and 467 | promote its widespread use.

468 |

This module is part of the OCA/stock-logistics-tracking project on GitHub.

469 |

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

470 |
471 |
472 |
473 |
474 | 475 | 476 | --------------------------------------------------------------------------------