From 8af40f068d4a9be9ccf613da3f87b171de49eda8 Mon Sep 17 00:00:00 2001
From: Kitti U
Date: Mon, 17 Jun 2019 08:17:04 +0700
Subject: [PATCH 1/5] [12.0][ADD] stock_picking_product_kit_helper
---
stock_picking_product_kit_helper/__init__.py | 4 +
.../__manifest__.py | 20 +++
.../models/__init__.py | 4 +
.../models/stock_picking.py | 133 ++++++++++++++++++
.../readme/CONTRIBUTORS.rst | 1 +
.../readme/DESCRIPTION.rst | 6 +
.../readme/USAGE.rst | 9 ++
.../security/ir.model.access.csv | 2 +
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/icon.svg | 79 +++++++++++
.../tests/__init__.py | 4 +
.../test_stock_picking_product_kit_helper.py | 64 +++++++++
.../views/stock_view.xml | 41 ++++++
13 files changed, 367 insertions(+)
create mode 100644 stock_picking_product_kit_helper/__init__.py
create mode 100644 stock_picking_product_kit_helper/__manifest__.py
create mode 100644 stock_picking_product_kit_helper/models/__init__.py
create mode 100644 stock_picking_product_kit_helper/models/stock_picking.py
create mode 100644 stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
create mode 100644 stock_picking_product_kit_helper/readme/DESCRIPTION.rst
create mode 100644 stock_picking_product_kit_helper/readme/USAGE.rst
create mode 100644 stock_picking_product_kit_helper/security/ir.model.access.csv
create mode 100644 stock_picking_product_kit_helper/static/description/icon.png
create mode 100644 stock_picking_product_kit_helper/static/description/icon.svg
create mode 100644 stock_picking_product_kit_helper/tests/__init__.py
create mode 100644 stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
create mode 100644 stock_picking_product_kit_helper/views/stock_view.xml
diff --git a/stock_picking_product_kit_helper/__init__.py b/stock_picking_product_kit_helper/__init__.py
new file mode 100644
index 000000000..9c982a787
--- /dev/null
+++ b/stock_picking_product_kit_helper/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import models
diff --git a/stock_picking_product_kit_helper/__manifest__.py b/stock_picking_product_kit_helper/__manifest__.py
new file mode 100644
index 000000000..b14f3ff39
--- /dev/null
+++ b/stock_picking_product_kit_helper/__manifest__.py
@@ -0,0 +1,20 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+{
+ 'name': 'Stock Picking Product Kit Helper',
+ 'summary': 'Set quanity in picking line based on product kit quantity',
+ 'version': '12.0.1.0.0',
+ 'category': 'Stock',
+ 'website': 'https://github.com/OCA/manufacture',
+ 'author': 'Ecosoft, Odoo Community Association (OCA)',
+ 'license': 'AGPL-3',
+ 'installable': True,
+ 'depends': [
+ 'sale_mrp',
+ ],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/stock_view.xml',
+ ],
+ 'maintainers': ['kittiu']
+}
diff --git a/stock_picking_product_kit_helper/models/__init__.py b/stock_picking_product_kit_helper/models/__init__.py
new file mode 100644
index 000000000..7945596c7
--- /dev/null
+++ b/stock_picking_product_kit_helper/models/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import stock_picking
diff --git a/stock_picking_product_kit_helper/models/stock_picking.py b/stock_picking_product_kit_helper/models/stock_picking.py
new file mode 100644
index 000000000..1ad27466e
--- /dev/null
+++ b/stock_picking_product_kit_helper/models/stock_picking.py
@@ -0,0 +1,133 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+
+
+class StockPicking(models.Model):
+ _inherit = 'stock.picking'
+
+ product_kit_helper_ids = fields.One2many(
+ comodel_name='stock.picking.product.kit.helper',
+ string='Product Kit Helper Lines',
+ inverse_name='picking_id',
+ readonly=False,
+ states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
+ )
+ has_product_kit = fields.Boolean(
+ string='Has Product Kit',
+ compute='_compute_has_product_kit',
+ help="True if there is at least 1 product kit in the sales order",
+ )
+
+ @api.model
+ def _is_product_kit(self, product, company):
+ BOM = self.env['mrp.bom'].sudo()
+ bom = BOM._bom_find(product=product,
+ company_id=company.id)
+ return bom and bom.type == 'phantom'
+
+ @api.multi
+ def _compute_has_product_kit(self):
+ for picking in self:
+ if any(self._is_product_kit(line.product_id, line.company_id)
+ for line in picking.move_lines.mapped('sale_line_id')):
+ picking.has_product_kit = True
+
+ @api.multi
+ def show_product_kit(self):
+ """Find move_lines with product kit to create helper line."""
+ self.ensure_one()
+ BOM = self.env['mrp.bom'].sudo()
+ helpers = []
+ for sale_line in self.move_lines.mapped('sale_line_id'):
+ bom = BOM._bom_find(product=sale_line.product_id,
+ company_id=sale_line.company_id.id)
+ if bom and bom.type == 'phantom': # Create product kit line
+ helpers.append((0, 0, {'sale_line_id': sale_line.id,
+ 'product_id': sale_line.product_id.id,
+ 'product_uom_qty': 0.0,
+ }))
+ self.product_kit_helper_ids.unlink()
+ self.write({'product_kit_helper_ids': helpers})
+
+ @api.multi
+ def action_product_kit_helper(self):
+ """Assign product kit's quantity to stock move."""
+ self.ensure_one()
+ if self.state in ('done', 'cancel'):
+ raise ValidationError(
+ _('Product Kit Helper is not allowed on current state'))
+ for helper in self.product_kit_helper_ids:
+ helper.action_explode_helper()
+
+
+class StockPickingProductKitHelper(models.Model):
+ _name = 'stock.picking.product.kit.helper'
+ _description = """
+ Product Kit Helper, allow user to specify quantity of product kit,
+ to explode as product quantity in operations tab
+ """
+
+ picking_id = fields.Many2one(
+ comodel_name='stock.picking',
+ string='Picking',
+ required=True,
+ index=True,
+ ondelete='cascade',
+ )
+ sale_line_id = fields.Many2one(
+ comodel_name='sale.order.line',
+ string='Sales Order Line',
+ required=True,
+ )
+ product_id = fields.Many2one(
+ comodel_name='product.product',
+ string='Product',
+ required=True,
+ readonly=True,
+ )
+ product_uom_qty = fields.Float(
+ string='Quantity',
+ )
+ product_uom = fields.Many2one(
+ comodel_name='uom.uom',
+ string='Unit of Measure',
+ related='sale_line_id.product_uom',
+ readonly=True,
+ )
+
+ @api.multi
+ def action_explode_helper(self):
+ """Explodes product kit quantity to detailed product in stock move."""
+ self.ensure_one()
+ # Mock stock.move, in order to resue stock.move's action_explode
+ StockMove = self.env['stock.move']
+ mock_loc = self.env['stock.location'].sudo().search([], limit=1)
+ mock_pt = self.env['stock.picking.type'].sudo().search([], limit=1)
+ mock_stock_move = StockMove.sudo().create({
+ 'name': '/',
+ 'product_id': self.product_id.id,
+ 'product_uom': self.product_uom.id,
+ 'product_uom_qty': self.product_uom_qty,
+ 'picking_type_id': mock_pt.id,
+ 'location_id': mock_loc.id,
+ 'location_dest_id': mock_loc.id,
+ })
+ # Reuse explode function and assign quantity_done in stock.move
+ mock_processed_moves = mock_stock_move.action_explode()
+ for mock_move in mock_processed_moves:
+ stock_move = StockMove.search([
+ ('picking_id', '=', self.picking_id.id),
+ ('sale_line_id', '=', self.sale_line_id.id),
+ ('product_id', '=', mock_move.product_id.id)])
+ if not stock_move:
+ continue
+ if len(stock_move) != 1:
+ raise ValidationError(
+ _('No matching detailed product %s for product kit %s') %
+ (mock_move.product_id.display_name,
+ self.product_id.display_name))
+ stock_move.write({'quantity_done': mock_move.product_uom_qty})
+ mock_processed_moves.sudo().unlink()
diff --git a/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst b/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
new file mode 100644
index 000000000..033e67f43
--- /dev/null
+++ b/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Kitti Upariphutthiphong
diff --git a/stock_picking_product_kit_helper/readme/DESCRIPTION.rst b/stock_picking_product_kit_helper/readme/DESCRIPTION.rst
new file mode 100644
index 000000000..2dfff1bdd
--- /dev/null
+++ b/stock_picking_product_kit_helper/readme/DESCRIPTION.rst
@@ -0,0 +1,6 @@
+When sales order contain product kits (product with BOM of type kit),
+the delivery order (stock.move) created by it will be exploded to multiple product lines.
+
+Normally, to partially deliver, user will calculate manually the quantity of each product lines to delivery.
+
+This module add new tab "Product Kit Help" to help in calculate quantity in product line with ease.
diff --git a/stock_picking_product_kit_helper/readme/USAGE.rst b/stock_picking_product_kit_helper/readme/USAGE.rst
new file mode 100644
index 000000000..843e025c6
--- /dev/null
+++ b/stock_picking_product_kit_helper/readme/USAGE.rst
@@ -0,0 +1,9 @@
+When origin sales order of the underlining delivery order contains at least 1 product kit,
+the tab "Product Kit Helper" will appear.
+
+To use the helper, go to the "Product Kit Helper" tab,
+
+#. Click => Show Product Kit
+#. Edit and fill in required quantity
+#. Click Assign Operation Quantity
+#. Check result in Operations tab
diff --git a/stock_picking_product_kit_helper/security/ir.model.access.csv b/stock_picking_product_kit_helper/security/ir.model.access.csv
new file mode 100644
index 000000000..3fb613bd4
--- /dev/null
+++ b/stock_picking_product_kit_helper/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_stock_picking_product_kit_helper_user,stock.picking.product.kit.helper user,model_stock_picking_product_kit_helper,stock.group_stock_user,1,1,1,1
diff --git a/stock_picking_product_kit_helper/static/description/icon.png b/stock_picking_product_kit_helper/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d
GIT binary patch
literal 9455
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
z
z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp
zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zls4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6#
z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f#
zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC
zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv!
zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG
z-wfS
zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9
z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE#
z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz
zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t
z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN
zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q
ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k
zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG
z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff
z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1
zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO
zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$
zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV(
z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb
zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4
z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{
zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx}
z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov
zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22
zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq
zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t<
z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k
z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp
z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{}
zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N
Xviia!U7SGha1wx#SCgwmn*{w2TRX*I
literal 0
HcmV?d00001
diff --git a/stock_picking_product_kit_helper/static/description/icon.svg b/stock_picking_product_kit_helper/static/description/icon.svg
new file mode 100644
index 000000000..a7a26d093
--- /dev/null
+++ b/stock_picking_product_kit_helper/static/description/icon.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/stock_picking_product_kit_helper/tests/__init__.py b/stock_picking_product_kit_helper/tests/__init__.py
new file mode 100644
index 000000000..aca871d3c
--- /dev/null
+++ b/stock_picking_product_kit_helper/tests/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import test_stock_picking_product_kit_helper
diff --git a/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py b/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
new file mode 100644
index 000000000..cfa50a48d
--- /dev/null
+++ b/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
@@ -0,0 +1,64 @@
+# Copyright 2019 Kitti U. - Ecosoft
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo.tests import common, Form
+from odoo.exceptions import ValidationError
+
+
+class TestStockPickingProductKitHelper(common.TransactionCase):
+
+ def setUp(self):
+ super(TestStockPickingProductKitHelper, self).setUp()
+ self.partner = self.env.ref('base.res_partner_2')
+ self.table_kit = self.env.ref('mrp.product_product_table_kit')
+
+ def test_00_sale_product_kit_helper(self):
+ """Test sale order with product kit, I expect,
+ - Product is exploded on picking
+ - Use helper, will assign quantity to stock.move correctly
+ - After picking is done, do not allow to use helper
+ """
+ # Create sales order of 10 table kit
+ order_form = Form(self.env['sale.order'])
+ order_form.partner_id = self.partner
+ with order_form.order_line.new() as line:
+ line.product_id = self.table_kit
+ line.product_uom_qty = 10
+ order = order_form.save()
+ order.action_confirm()
+ # In the picking, product line is exploded.
+ picking = order.mapped('picking_ids')
+ self.assertEqual(len(picking), 1)
+ stock_moves = picking.move_lines
+ # 1 SO line exploded to 2 moves
+ moves = [{'product': x.product_id.name, 'qty': x.product_uom_qty}
+ for x in stock_moves]
+ self.assertEqual(moves,
+ [{'product': 'Wood Panel', 'qty': 10.0},
+ {'product': 'Bolt', 'qty': 40.0}])
+ self.assertTrue(picking.has_product_kit)
+ self.assertFalse(picking.product_kit_helper_ids) # Not show yet
+ picking.show_product_kit()
+ self.assertEqual(len(picking.product_kit_helper_ids), 1)
+ # Assign product set 4 qty and test that it apply to stock.move
+ picking.product_kit_helper_ids[0].write({'product_uom_qty': 4.0})
+ picking.action_product_kit_helper()
+ moves = [{'product': x.product_id.name, 'qty': x.quantity_done}
+ for x in stock_moves]
+ self.assertEqual(moves,
+ [{'product': 'Wood Panel', 'qty': 4.0},
+ {'product': 'Bolt', 'qty': 16.0}])
+ # Assign again to 10 qty
+ picking.product_kit_helper_ids[0].write({'product_uom_qty': 10.0})
+ picking.action_product_kit_helper()
+ moves = [{'product': x.product_id.name, 'qty': x.quantity_done}
+ for x in stock_moves]
+ self.assertEqual(moves,
+ [{'product': 'Wood Panel', 'qty': 10.0},
+ {'product': 'Bolt', 'qty': 40.0}])
+ # Validate Picking
+ picking.button_validate()
+ self.assertEqual(picking.state, 'done')
+ # After done state, block the helper
+ with self.assertRaises(ValidationError):
+ picking.action_product_kit_helper()
diff --git a/stock_picking_product_kit_helper/views/stock_view.xml b/stock_picking_product_kit_helper/views/stock_view.xml
new file mode 100644
index 000000000..525bdab06
--- /dev/null
+++ b/stock_picking_product_kit_helper/views/stock_view.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ stock.picking.form
+ stock.picking
+
+
+
+
+
+ To deliver partial product kits, you can use this tab to help calculate
+ quantity and auto fill in "Done" column in Operations tab.
+
+
Click => Show Product Kit
+
Edit and fill in required quantity
+
Click Assign Operation Quantity
+
Check result in Operations tab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From e230faf050a958698c3e1d1a0224284314da287f Mon Sep 17 00:00:00 2001
From: oca-travis
Date: Tue, 8 Oct 2019 11:24:17 +0000
Subject: [PATCH 2/5] [UPD] Update stock_picking_product_kit_helper.pot
---
.../i18n/stock_picking_product_kit_helper.pot | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100644 stock_picking_product_kit_helper/i18n/stock_picking_product_kit_helper.pot
diff --git a/stock_picking_product_kit_helper/i18n/stock_picking_product_kit_helper.pot b/stock_picking_product_kit_helper/i18n/stock_picking_product_kit_helper.pot
new file mode 100644
index 000000000..bcf32111e
--- /dev/null
+++ b/stock_picking_product_kit_helper/i18n/stock_picking_product_kit_helper.pot
@@ -0,0 +1,156 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * stock_picking_product_kit_helper
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model,name:stock_picking_product_kit_helper.model_stock_picking_product_kit_helper
+msgid "\n"
+" Product Kit Helper, allow user to specify quantity of product kit,\n"
+" to explode as product quantity in operations tab\n"
+" "
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Assign Operation Quantity"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Check result in Operations tab"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Click => Show Product Kit"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Click Assign Operation Quantity"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Edit and fill in required quantity"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking__has_product_kit
+msgid "Has Product Kit"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__id
+msgid "ID"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "Kit Helper"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: code:addons/stock_picking_product_kit_helper/models/stock_picking.py:129
+#, python-format
+msgid "No matching detailed product %s for product kit %s"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__picking_id
+msgid "Picking"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__product_id
+msgid "Product"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking__product_kit_helper_ids
+msgid "Product Kit Helper Lines"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: code:addons/stock_picking_product_kit_helper/models/stock_picking.py:61
+#, python-format
+msgid "Product Kit Helper is not allowed on current state"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__product_uom_qty
+msgid "Quantity"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__sale_line_id
+msgid "Sales Order Line"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "To deliver partial product kits, you can use this tab to help calculate\n"
+" quantity and auto fill in \"Done\" column in Operations tab. "
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model,name:stock_picking_product_kit_helper.model_stock_picking
+msgid "Transfer"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,help:stock_picking_product_kit_helper.field_stock_picking__has_product_kit
+msgid "True if there is at least 1 product kit in the sales order"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model:ir.model.fields,field_description:stock_picking_product_kit_helper.field_stock_picking_product_kit_helper__product_uom
+msgid "Unit of Measure"
+msgstr ""
+
+#. module: stock_picking_product_kit_helper
+#: model_terms:ir.ui.view,arch_db:stock_picking_product_kit_helper.view_picking_form
+msgid "⇒ Show Product Kit"
+msgstr ""
+
From 0a78e3c9da5cbc6a21546851baee1a83d9b10d0a Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Tue, 8 Oct 2019 11:50:33 +0000
Subject: [PATCH 3/5] [UPD] README.rst
---
stock_picking_product_kit_helper/README.rst | 99 ++++
.../static/description/index.html | 437 ++++++++++++++++++
2 files changed, 536 insertions(+)
create mode 100644 stock_picking_product_kit_helper/README.rst
create mode 100644 stock_picking_product_kit_helper/static/description/index.html
diff --git a/stock_picking_product_kit_helper/README.rst b/stock_picking_product_kit_helper/README.rst
new file mode 100644
index 000000000..59edc52b4
--- /dev/null
+++ b/stock_picking_product_kit_helper/README.rst
@@ -0,0 +1,99 @@
+================================
+Stock Picking Product Kit Helper
+================================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+ :target: https://odoo-community.org/page/development-status
+ :alt: Beta
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
+ :target: https://github.com/OCA/manufacture/tree/12.0/stock_picking_product_kit_helper
+ :alt: OCA/manufacture
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-stock_picking_product_kit_helper
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/129/12.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+When sales order contain product kits (product with BOM of type kit),
+the delivery order (stock.move) created by it will be exploded to multiple product lines.
+
+Normally, to partially deliver, user will calculate manually the quantity of each product lines to delivery.
+
+This module add new tab "Product Kit Help" to help in calculate quantity in product line with ease.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+When origin sales order of the underlining delivery order contains at least 1 product kit,
+the tab "Product Kit Helper" will appear.
+
+To use the helper, go to the "Product Kit Helper" tab,
+
+#. Click => Show Product Kit
+#. Edit and fill in required quantity
+#. Click Assign Operation Quantity
+#. Check result in Operations tab
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us smashing it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Ecosoft
+
+Contributors
+~~~~~~~~~~~~
+
+* Kitti Upariphutthiphong
+
+Maintainers
+~~~~~~~~~~~
+
+This module is maintained by the OCA.
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+.. |maintainer-kittiu| image:: https://github.com/kittiu.png?size=40px
+ :target: https://github.com/kittiu
+ :alt: kittiu
+
+Current `maintainer `__:
+
+|maintainer-kittiu|
+
+This module is part of the `OCA/manufacture `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/stock_picking_product_kit_helper/static/description/index.html b/stock_picking_product_kit_helper/static/description/index.html
new file mode 100644
index 000000000..7501821ce
--- /dev/null
+++ b/stock_picking_product_kit_helper/static/description/index.html
@@ -0,0 +1,437 @@
+
+
+
+
+
+
+Stock Picking Product Kit Helper
+
+
+
+
+
Stock Picking Product Kit Helper
+
+
+
+
When sales order contain product kits (product with BOM of type kit),
+the delivery order (stock.move) created by it will be exploded to multiple product lines.
+
Normally, to partially deliver, user will calculate manually the quantity of each product lines to delivery.
+
This module add new tab “Product Kit Help” to help in calculate quantity in product line with ease.
Bugs are tracked on GitHub Issues.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us smashing it by providing a detailed and welcomed
+feedback.
+
Do not contact contributors directly about support or help with technical issues.
OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+
From 0dd714e9ee0698ba27fb1787fd563fe2d102c18a Mon Sep 17 00:00:00 2001
From: ps-tubtim
Date: Thu, 9 Jan 2020 15:42:43 +0700
Subject: [PATCH 4/5] [IMP] stock_picking_product_kit_helper: black, isort
---
.../__manifest__.py | 27 ++--
.../models/stock_picking.py | 134 ++++++++++--------
.../test_stock_picking_product_kit_helper.py | 54 +++----
3 files changed, 112 insertions(+), 103 deletions(-)
diff --git a/stock_picking_product_kit_helper/__manifest__.py b/stock_picking_product_kit_helper/__manifest__.py
index b14f3ff39..b8ebefed3 100644
--- a/stock_picking_product_kit_helper/__manifest__.py
+++ b/stock_picking_product_kit_helper/__manifest__.py
@@ -1,20 +1,15 @@
# Copyright 2019 Kitti U. - Ecosoft
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
- 'name': 'Stock Picking Product Kit Helper',
- 'summary': 'Set quanity in picking line based on product kit quantity',
- 'version': '12.0.1.0.0',
- 'category': 'Stock',
- 'website': 'https://github.com/OCA/manufacture',
- 'author': 'Ecosoft, Odoo Community Association (OCA)',
- 'license': 'AGPL-3',
- 'installable': True,
- 'depends': [
- 'sale_mrp',
- ],
- 'data': [
- 'security/ir.model.access.csv',
- 'views/stock_view.xml',
- ],
- 'maintainers': ['kittiu']
+ "name": "Stock Picking Product Kit Helper",
+ "summary": "Set quanity in picking line based on product kit quantity",
+ "version": "13.0.1.0.0",
+ "category": "Stock",
+ "website": "https://github.com/OCA/manufacture",
+ "author": "Ecosoft, Odoo Community Association (OCA)",
+ "license": "AGPL-3",
+ "installable": True,
+ "depends": ["sale_mrp"],
+ "data": ["security/ir.model.access.csv", "views/stock_view.xml"],
+ "maintainers": ["kittiu"],
}
diff --git a/stock_picking_product_kit_helper/models/stock_picking.py b/stock_picking_product_kit_helper/models/stock_picking.py
index 1ad27466e..9da66163b 100644
--- a/stock_picking_product_kit_helper/models/stock_picking.py
+++ b/stock_picking_product_kit_helper/models/stock_picking.py
@@ -1,100 +1,103 @@
# Copyright 2019 Kitti U. - Ecosoft
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo import models, fields, api, _
+from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class StockPicking(models.Model):
- _inherit = 'stock.picking'
+ _inherit = "stock.picking"
product_kit_helper_ids = fields.One2many(
- comodel_name='stock.picking.product.kit.helper',
- string='Product Kit Helper Lines',
- inverse_name='picking_id',
+ comodel_name="stock.picking.product.kit.helper",
+ string="Product Kit Helper Lines",
+ inverse_name="picking_id",
readonly=False,
- states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
+ states={"done": [("readonly", True)], "cancel": [("readonly", True)]},
)
has_product_kit = fields.Boolean(
- string='Has Product Kit',
- compute='_compute_has_product_kit',
+ string="Has Product Kit",
+ compute="_compute_has_product_kit",
help="True if there is at least 1 product kit in the sales order",
)
@api.model
def _is_product_kit(self, product, company):
- BOM = self.env['mrp.bom'].sudo()
- bom = BOM._bom_find(product=product,
- company_id=company.id)
- return bom and bom.type == 'phantom'
+ BOM = self.env["mrp.bom"].sudo()
+ bom = BOM._bom_find(product=product, company_id=company.id)
+ return bom and bom.type == "phantom"
@api.multi
def _compute_has_product_kit(self):
for picking in self:
- if any(self._is_product_kit(line.product_id, line.company_id)
- for line in picking.move_lines.mapped('sale_line_id')):
+ if any(
+ self._is_product_kit(line.product_id, line.company_id)
+ for line in picking.move_lines.mapped("sale_line_id")
+ ):
picking.has_product_kit = True
@api.multi
def show_product_kit(self):
"""Find move_lines with product kit to create helper line."""
self.ensure_one()
- BOM = self.env['mrp.bom'].sudo()
+ BOM = self.env["mrp.bom"].sudo()
helpers = []
- for sale_line in self.move_lines.mapped('sale_line_id'):
- bom = BOM._bom_find(product=sale_line.product_id,
- company_id=sale_line.company_id.id)
- if bom and bom.type == 'phantom': # Create product kit line
- helpers.append((0, 0, {'sale_line_id': sale_line.id,
- 'product_id': sale_line.product_id.id,
- 'product_uom_qty': 0.0,
- }))
+ for sale_line in self.move_lines.mapped("sale_line_id"):
+ bom = BOM._bom_find(
+ product=sale_line.product_id, company_id=sale_line.company_id.id
+ )
+ if bom and bom.type == "phantom": # Create product kit line
+ helpers.append(
+ (
+ 0,
+ 0,
+ {
+ "sale_line_id": sale_line.id,
+ "product_id": sale_line.product_id.id,
+ "product_uom_qty": 0.0,
+ },
+ )
+ )
self.product_kit_helper_ids.unlink()
- self.write({'product_kit_helper_ids': helpers})
+ self.write({"product_kit_helper_ids": helpers})
@api.multi
def action_product_kit_helper(self):
"""Assign product kit's quantity to stock move."""
self.ensure_one()
- if self.state in ('done', 'cancel'):
+ if self.state in ("done", "cancel"):
raise ValidationError(
- _('Product Kit Helper is not allowed on current state'))
+ _("Product Kit Helper is not allowed on current state")
+ )
for helper in self.product_kit_helper_ids:
helper.action_explode_helper()
class StockPickingProductKitHelper(models.Model):
- _name = 'stock.picking.product.kit.helper'
+ _name = "stock.picking.product.kit.helper"
_description = """
Product Kit Helper, allow user to specify quantity of product kit,
to explode as product quantity in operations tab
"""
picking_id = fields.Many2one(
- comodel_name='stock.picking',
- string='Picking',
+ comodel_name="stock.picking",
+ string="Picking",
required=True,
index=True,
- ondelete='cascade',
+ ondelete="cascade",
)
sale_line_id = fields.Many2one(
- comodel_name='sale.order.line',
- string='Sales Order Line',
- required=True,
+ comodel_name="sale.order.line", string="Sales Order Line", required=True
)
product_id = fields.Many2one(
- comodel_name='product.product',
- string='Product',
- required=True,
- readonly=True,
- )
- product_uom_qty = fields.Float(
- string='Quantity',
+ comodel_name="product.product", string="Product", required=True, readonly=True
)
+ product_uom_qty = fields.Float(string="Quantity")
product_uom = fields.Many2one(
- comodel_name='uom.uom',
- string='Unit of Measure',
- related='sale_line_id.product_uom',
+ comodel_name="uom.uom",
+ string="Unit of Measure",
+ related="sale_line_id.product_uom",
readonly=True,
)
@@ -103,31 +106,36 @@ class StockPickingProductKitHelper(models.Model):
"""Explodes product kit quantity to detailed product in stock move."""
self.ensure_one()
# Mock stock.move, in order to resue stock.move's action_explode
- StockMove = self.env['stock.move']
- mock_loc = self.env['stock.location'].sudo().search([], limit=1)
- mock_pt = self.env['stock.picking.type'].sudo().search([], limit=1)
- mock_stock_move = StockMove.sudo().create({
- 'name': '/',
- 'product_id': self.product_id.id,
- 'product_uom': self.product_uom.id,
- 'product_uom_qty': self.product_uom_qty,
- 'picking_type_id': mock_pt.id,
- 'location_id': mock_loc.id,
- 'location_dest_id': mock_loc.id,
- })
+ StockMove = self.env["stock.move"]
+ mock_loc = self.env["stock.location"].sudo().search([], limit=1)
+ mock_pt = self.env["stock.picking.type"].sudo().search([], limit=1)
+ mock_stock_move = StockMove.sudo().create(
+ {
+ "name": "/",
+ "product_id": self.product_id.id,
+ "product_uom": self.product_uom.id,
+ "product_uom_qty": self.product_uom_qty,
+ "picking_type_id": mock_pt.id,
+ "location_id": mock_loc.id,
+ "location_dest_id": mock_loc.id,
+ }
+ )
# Reuse explode function and assign quantity_done in stock.move
mock_processed_moves = mock_stock_move.action_explode()
for mock_move in mock_processed_moves:
- stock_move = StockMove.search([
- ('picking_id', '=', self.picking_id.id),
- ('sale_line_id', '=', self.sale_line_id.id),
- ('product_id', '=', mock_move.product_id.id)])
+ stock_move = StockMove.search(
+ [
+ ("picking_id", "=", self.picking_id.id),
+ ("sale_line_id", "=", self.sale_line_id.id),
+ ("product_id", "=", mock_move.product_id.id),
+ ]
+ )
if not stock_move:
continue
if len(stock_move) != 1:
raise ValidationError(
- _('No matching detailed product %s for product kit %s') %
- (mock_move.product_id.display_name,
- self.product_id.display_name))
- stock_move.write({'quantity_done': mock_move.product_uom_qty})
+ _("No matching detailed product %s for product kit %s")
+ % (mock_move.product_id.display_name, self.product_id.display_name)
+ )
+ stock_move.write({"quantity_done": mock_move.product_uom_qty})
mock_processed_moves.sudo().unlink()
diff --git a/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py b/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
index cfa50a48d..8ea8e12c8 100644
--- a/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
+++ b/stock_picking_product_kit_helper/tests/test_stock_picking_product_kit_helper.py
@@ -1,16 +1,15 @@
# Copyright 2019 Kitti U. - Ecosoft
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo.tests import common, Form
from odoo.exceptions import ValidationError
+from odoo.tests import Form, common
class TestStockPickingProductKitHelper(common.TransactionCase):
-
def setUp(self):
super(TestStockPickingProductKitHelper, self).setUp()
- self.partner = self.env.ref('base.res_partner_2')
- self.table_kit = self.env.ref('mrp.product_product_table_kit')
+ self.partner = self.env.ref("base.res_partner_2")
+ self.table_kit = self.env.ref("mrp.product_product_table_kit")
def test_00_sale_product_kit_helper(self):
"""Test sale order with product kit, I expect,
@@ -19,7 +18,7 @@ class TestStockPickingProductKitHelper(common.TransactionCase):
- After picking is done, do not allow to use helper
"""
# Create sales order of 10 table kit
- order_form = Form(self.env['sale.order'])
+ order_form = Form(self.env["sale.order"])
order_form.partner_id = self.partner
with order_form.order_line.new() as line:
line.product_id = self.table_kit
@@ -27,38 +26,45 @@ class TestStockPickingProductKitHelper(common.TransactionCase):
order = order_form.save()
order.action_confirm()
# In the picking, product line is exploded.
- picking = order.mapped('picking_ids')
+ picking = order.mapped("picking_ids")
self.assertEqual(len(picking), 1)
stock_moves = picking.move_lines
# 1 SO line exploded to 2 moves
- moves = [{'product': x.product_id.name, 'qty': x.product_uom_qty}
- for x in stock_moves]
- self.assertEqual(moves,
- [{'product': 'Wood Panel', 'qty': 10.0},
- {'product': 'Bolt', 'qty': 40.0}])
+ moves = [
+ {"product": x.product_id.name, "qty": x.product_uom_qty}
+ for x in stock_moves
+ ]
+ self.assertEqual(
+ moves,
+ [{"product": "Wood Panel", "qty": 10.0}, {"product": "Bolt", "qty": 40.0}],
+ )
self.assertTrue(picking.has_product_kit)
self.assertFalse(picking.product_kit_helper_ids) # Not show yet
picking.show_product_kit()
self.assertEqual(len(picking.product_kit_helper_ids), 1)
# Assign product set 4 qty and test that it apply to stock.move
- picking.product_kit_helper_ids[0].write({'product_uom_qty': 4.0})
+ picking.product_kit_helper_ids[0].write({"product_uom_qty": 4.0})
picking.action_product_kit_helper()
- moves = [{'product': x.product_id.name, 'qty': x.quantity_done}
- for x in stock_moves]
- self.assertEqual(moves,
- [{'product': 'Wood Panel', 'qty': 4.0},
- {'product': 'Bolt', 'qty': 16.0}])
+ moves = [
+ {"product": x.product_id.name, "qty": x.quantity_done} for x in stock_moves
+ ]
+ self.assertEqual(
+ moves,
+ [{"product": "Wood Panel", "qty": 4.0}, {"product": "Bolt", "qty": 16.0}],
+ )
# Assign again to 10 qty
- picking.product_kit_helper_ids[0].write({'product_uom_qty': 10.0})
+ picking.product_kit_helper_ids[0].write({"product_uom_qty": 10.0})
picking.action_product_kit_helper()
- moves = [{'product': x.product_id.name, 'qty': x.quantity_done}
- for x in stock_moves]
- self.assertEqual(moves,
- [{'product': 'Wood Panel', 'qty': 10.0},
- {'product': 'Bolt', 'qty': 40.0}])
+ moves = [
+ {"product": x.product_id.name, "qty": x.quantity_done} for x in stock_moves
+ ]
+ self.assertEqual(
+ moves,
+ [{"product": "Wood Panel", "qty": 10.0}, {"product": "Bolt", "qty": 40.0}],
+ )
# Validate Picking
picking.button_validate()
- self.assertEqual(picking.state, 'done')
+ self.assertEqual(picking.state, "done")
# After done state, block the helper
with self.assertRaises(ValidationError):
picking.action_product_kit_helper()
From 99a3adfb50d35dbebbfa373afc76581fa23cd45a Mon Sep 17 00:00:00 2001
From: ps-tubtim
Date: Thu, 9 Jan 2020 16:10:55 +0700
Subject: [PATCH 5/5] [MIG] stock_picking_product_kit_helper: Migration to 13.0
---
stock_picking_product_kit_helper/README.rst | 28 ++++++++---
.../models/stock_picking.py | 4 --
.../readme/CONTRIBUTORS.rst | 5 +-
.../readme/HISTORY.rst | 9 ++++
.../static/description/index.html | 50 +++++++++++++------
5 files changed, 70 insertions(+), 26 deletions(-)
create mode 100644 stock_picking_product_kit_helper/readme/HISTORY.rst
diff --git a/stock_picking_product_kit_helper/README.rst b/stock_picking_product_kit_helper/README.rst
index 59edc52b4..89dd3ece7 100644
--- a/stock_picking_product_kit_helper/README.rst
+++ b/stock_picking_product_kit_helper/README.rst
@@ -14,13 +14,13 @@ Stock Picking Product Kit Helper
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github
- :target: https://github.com/OCA/manufacture/tree/12.0/stock_picking_product_kit_helper
+ :target: https://github.com/OCA/manufacture/tree/13.0/stock_picking_product_kit_helper
:alt: OCA/manufacture
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/manufacture-12-0/manufacture-12-0-stock_picking_product_kit_helper
+ :target: https://translation.odoo-community.org/projects/manufacture-13-0/manufacture-13-0-stock_picking_product_kit_helper
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
- :target: https://runbot.odoo-community.org/runbot/129/12.0
+ :target: https://runbot.odoo-community.org/runbot/129/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -50,13 +50,26 @@ To use the helper, go to the "Product Kit Helper" tab,
#. Click Assign Operation Quantity
#. Check result in Operations tab
+Changelog
+=========
+
+13.0.1.0.0 (2020-01-09)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Migrated to odoo 13.
+
+12.0.1.0.0 (2019-06-17)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+First version.
+
Bug Tracker
===========
Bugs are tracked on `GitHub Issues `_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -71,7 +84,10 @@ Authors
Contributors
~~~~~~~~~~~~
-* Kitti Upariphutthiphong
+* `Ecosoft `__:
+
+ * Kitti Upariphutthiphong
+ * Pimolnat Suntian
Maintainers
~~~~~~~~~~~
@@ -94,6 +110,6 @@ Current `maintainer `__:
|maintainer-kittiu|
-This module is part of the `OCA/manufacture `_ project on GitHub.
+This module is part of the `OCA/manufacture `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/stock_picking_product_kit_helper/models/stock_picking.py b/stock_picking_product_kit_helper/models/stock_picking.py
index 9da66163b..2ac8b30fd 100644
--- a/stock_picking_product_kit_helper/models/stock_picking.py
+++ b/stock_picking_product_kit_helper/models/stock_picking.py
@@ -27,7 +27,6 @@ class StockPicking(models.Model):
bom = BOM._bom_find(product=product, company_id=company.id)
return bom and bom.type == "phantom"
- @api.multi
def _compute_has_product_kit(self):
for picking in self:
if any(
@@ -36,7 +35,6 @@ class StockPicking(models.Model):
):
picking.has_product_kit = True
- @api.multi
def show_product_kit(self):
"""Find move_lines with product kit to create helper line."""
self.ensure_one()
@@ -61,7 +59,6 @@ class StockPicking(models.Model):
self.product_kit_helper_ids.unlink()
self.write({"product_kit_helper_ids": helpers})
- @api.multi
def action_product_kit_helper(self):
"""Assign product kit's quantity to stock move."""
self.ensure_one()
@@ -101,7 +98,6 @@ class StockPickingProductKitHelper(models.Model):
readonly=True,
)
- @api.multi
def action_explode_helper(self):
"""Explodes product kit quantity to detailed product in stock move."""
self.ensure_one()
diff --git a/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst b/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
index 033e67f43..00cf217c1 100644
--- a/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
+++ b/stock_picking_product_kit_helper/readme/CONTRIBUTORS.rst
@@ -1 +1,4 @@
-* Kitti Upariphutthiphong
+* `Ecosoft `__:
+
+ * Kitti Upariphutthiphong
+ * Pimolnat Suntian
diff --git a/stock_picking_product_kit_helper/readme/HISTORY.rst b/stock_picking_product_kit_helper/readme/HISTORY.rst
new file mode 100644
index 000000000..11e654b98
--- /dev/null
+++ b/stock_picking_product_kit_helper/readme/HISTORY.rst
@@ -0,0 +1,9 @@
+13.0.1.0.0 (2020-01-09)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Migrated to odoo 13.
+
+12.0.1.0.0 (2019-06-17)
+~~~~~~~~~~~~~~~~~~~~~~~
+
+First version.
diff --git a/stock_picking_product_kit_helper/static/description/index.html b/stock_picking_product_kit_helper/static/description/index.html
index 7501821ce..b7d862f1a 100644
--- a/stock_picking_product_kit_helper/static/description/index.html
+++ b/stock_picking_product_kit_helper/static/description/index.html
@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
When sales order contain product kits (product with BOM of type kit),
the delivery order (stock.move) created by it will be exploded to multiple product lines.
Normally, to partially deliver, user will calculate manually the quantity of each product lines to delivery.
@@ -375,18 +375,23 @@ the delivery order (stock.move) created by it will be exploded to multiple produ
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
-feedback.
OCA, or the Odoo Community Association, is a nonprofit organization whose
@@ -428,7 +448,7 @@ mission is to support the collaborative development of Odoo features and
promote its widespread use.