[IMP] stock_reserve: black, isort, prettier

This commit is contained in:
Carlos Roca
2021-07-23 12:23:01 +02:00
committed by Víctor Martínez
parent 03c1aa983e
commit f7df08dc6a
10 changed files with 407 additions and 384 deletions

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Guewen Baconnier # Author: Guewen Baconnier

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Guewen Baconnier # Author: Guewen Baconnier
@@ -19,25 +18,25 @@
# #
############################################################################## ##############################################################################
{'name': 'Stock Reservation', {
'summary': 'Stock reservations on products', "name": "Stock Reservation",
'version': '10.0.1.0.0', "summary": "Stock reservations on products",
'author': "Camptocamp,Odoo Community Association (OCA)", "version": "10.0.1.0.0",
'category': 'Warehouse', "author": "Camptocamp,Odoo Community Association (OCA)",
'license': 'AGPL-3', "category": "Warehouse",
'complexity': 'normal', "license": "AGPL-3",
'images': [], "complexity": "normal",
'website': "http://www.camptocamp.com", "images": [],
'depends': ['stock', "website": "http://www.camptocamp.com",
], "depends": ["stock",],
'demo': [], "demo": [],
'data': ['view/stock_reserve.xml', "data": [
'view/product.xml', "view/stock_reserve.xml",
'data/stock_data.xml', "view/product.xml",
'security/ir.model.access.csv', "data/stock_data.xml",
], "security/ir.model.access.csv",
'auto_install': False, ],
'test': ['test/stock_reserve.yml', "auto_install": False,
], "test": ["test/stock_reserve.yml",],
'installable': True, "installable": True,
} }

View File

@@ -1,26 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">
<record id="stock_location_reservation" model="stock.location"> <record id="stock_location_reservation" model="stock.location">
<field name="name">Reservation Stock</field> <field name="name">Reservation Stock</field>
<field name="location_id" ref="stock.stock_location_locations"/> <field name="location_id" ref="stock.stock_location_locations" />
</record> </record>
<!-- Release the stock.reservation when the validity date has
<!-- Release the stock.reservation when the validity date has
passed --> passed -->
<record forcecreate="True" id="ir_cron_release_stock_reservation" model="ir.cron"> <record
<field name="name">Release the stock reservation having a passed validity date</field> forcecreate="True"
<field eval="True" name="active" /> id="ir_cron_release_stock_reservation"
<field name="user_id" ref="base.user_root" /> model="ir.cron"
<field name="interval_number">1</field> >
<field name="interval_type">days</field> <field
<field name="numbercall">-1</field> name="name"
<field eval="False" name="doall" /> >Release the stock reservation having a passed validity date</field>
<field name="model">stock.reservation</field> <field eval="True" name="active" />
<field name="function">release_validity_exceeded</field> <field name="user_id" ref="base.user_root" />
<field name="args">()</field> <field name="interval_number">1</field>
</record> <field name="interval_type">days</field>
<field name="numbercall">-1</field>
</data> <field eval="False" name="doall" />
<field name="model">stock.reservation</field>
<field name="function">release_validity_exceeded</field>
<field name="args">()</field>
</record>
</data>
</odoo> </odoo>

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Author: Leonardo Pistone # Author: Leonardo Pistone
# Copyright 2015 Camptocamp SA # Copyright 2015 Camptocamp SA
# #
@@ -18,8 +17,9 @@
def migrate(cr, installed_version): def migrate(cr, installed_version):
"""Update a wrong location that is no_update in XML.""" """Update a wrong location that is no_update in XML."""
if installed_version == '8.0.0.1': if installed_version == "8.0.0.1":
cr.execute(''' cr.execute(
"""
UPDATE stock_location UPDATE stock_location
SET location_id = ( SET location_id = (
SELECT res_id SELECT res_id
@@ -39,4 +39,5 @@ def migrate(cr, installed_version):
WHERE name = 'stock_location_company' WHERE name = 'stock_location_company'
AND module = 'stock' AND module = 'stock'
); );
''') """
)

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Guewen Baconnier # Author: Guewen Baconnier

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Guewen Baconnier # Author: Guewen Baconnier
@@ -19,59 +18,62 @@
# #
############################################################################## ##############################################################################
from odoo import models, fields, api from odoo import api, fields, models
class ProductTemplate(models.Model): class ProductTemplate(models.Model):
_inherit = 'product.template' _inherit = "product.template"
reservation_count = fields.Float( reservation_count = fields.Float(
compute='_compute_reservation_count', compute="_compute_reservation_count", string="# Sales"
string='# Sales') )
@api.multi @api.multi
def _compute_reservation_count(self): def _compute_reservation_count(self):
for product in self: for product in self:
product.reservation_count = sum( product.reservation_count = sum(
product.product_variant_ids.mapped('reservation_count')) product.product_variant_ids.mapped("reservation_count")
)
@api.multi @api.multi
def action_view_reservations(self): def action_view_reservations(self):
self.ensure_one() self.ensure_one()
ref = 'stock_reserve.action_stock_reservation_tree' ref = "stock_reserve.action_stock_reservation_tree"
product_ids = self.mapped('product_variant_ids.id') product_ids = self.mapped("product_variant_ids.id")
action_dict = self.env.ref(ref).read()[0] action_dict = self.env.ref(ref).read()[0]
action_dict['domain'] = [('product_id', 'in', product_ids)] action_dict["domain"] = [("product_id", "in", product_ids)]
action_dict['context'] = { action_dict["context"] = {
'search_default_draft': 1, "search_default_draft": 1,
'search_default_reserved': 1 "search_default_reserved": 1,
} }
return action_dict return action_dict
class ProductProduct(models.Model): class ProductProduct(models.Model):
_inherit = 'product.product' _inherit = "product.product"
reservation_count = fields.Float( reservation_count = fields.Float(
compute='_compute_reservation_count', compute="_compute_reservation_count", string="# Sales"
string='# Sales') )
@api.multi @api.multi
def _compute_reservation_count(self): def _compute_reservation_count(self):
for product in self: for product in self:
domain = [('product_id', '=', product.id), domain = [
('state', 'in', ['draft', 'assigned'])] ("product_id", "=", product.id),
reservations = self.env['stock.reservation'].search(domain) ("state", "in", ["draft", "assigned"]),
product.reservation_count = sum(reservations.mapped('product_qty')) ]
reservations = self.env["stock.reservation"].search(domain)
product.reservation_count = sum(reservations.mapped("product_qty"))
@api.multi @api.multi
def action_view_reservations(self): def action_view_reservations(self):
self.ensure_one() self.ensure_one()
ref = 'stock_reserve.action_stock_reservation_tree' ref = "stock_reserve.action_stock_reservation_tree"
action_dict = self.env.ref(ref).read()[0] action_dict = self.env.ref(ref).read()[0]
action_dict['domain'] = [('product_id', '=', self.id)] action_dict["domain"] = [("product_id", "=", self.id)]
action_dict['context'] = { action_dict["context"] = {
'search_default_draft': 1, "search_default_draft": 1,
'search_default_reserved': 1 "search_default_reserved": 1,
} }
return action_dict return action_dict

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Guewen Baconnier # Author: Guewen Baconnier
@@ -19,7 +18,7 @@
# #
############################################################################## ##############################################################################
from odoo import models, fields, api from odoo import api, fields, models
from odoo.exceptions import except_orm from odoo.exceptions import except_orm
from odoo.tools.translate import _ from odoo.tools.translate import _
@@ -46,18 +45,20 @@ class StockReservation(models.Model):
* date_validity (once passed, the reservation will be released) * date_validity (once passed, the reservation will be released)
* note * note
""" """
_name = 'stock.reservation'
_description = 'Stock Reservation' _name = "stock.reservation"
_inherits = {'stock.move': 'move_id'} _description = "Stock Reservation"
_inherits = {"stock.move": "move_id"}
move_id = fields.Many2one( move_id = fields.Many2one(
'stock.move', "stock.move",
'Reservation Move', "Reservation Move",
required=True, required=True,
readonly=True, readonly=True,
ondelete='cascade', ondelete="cascade",
index=True) index=True,
date_validity = fields.Date('Validity Date') )
date_validity = fields.Date("Validity Date")
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
@@ -74,14 +75,13 @@ class StockReservation(models.Model):
# if there is 'location_id' field requested, ensure that # if there is 'location_id' field requested, ensure that
# picking_type_id is also requested, because it is required # picking_type_id is also requested, because it is required
# to compute location_id # to compute location_id
if ('location_id' in fields_list and if "location_id" in fields_list and "picking_type_id" not in fields_list:
'picking_type_id' not in fields_list): fields_list = fields_list + ["picking_type_id"]
fields_list = fields_list + ['picking_type_id']
res = super(StockReservation, self).default_get(fields_list) res = super(StockReservation, self).default_get(fields_list)
if 'product_qty' in res: if "product_qty" in res:
del res['product_qty'] del res["product_qty"]
# At this point picking_type_id and location_id # At this point picking_type_id and location_id
# should be computed in default way: # should be computed in default way:
@@ -93,19 +93,20 @@ class StockReservation(models.Model):
# #
# If picking_type_id is present and location_id is not, try to find # If picking_type_id is present and location_id is not, try to find
# default value for location_id # default value for location_id
if not res.get('picking_type_id', None): if not res.get("picking_type_id", None):
res['picking_type_id'] = self._default_picking_type_id() res["picking_type_id"] = self._default_picking_type_id()
picking_type_id = res.get('picking_type_id') picking_type_id = res.get("picking_type_id")
if picking_type_id and not res.get('location_id', False): if picking_type_id and not res.get("location_id", False):
picking = self.env['stock.picking'].new( picking = self.env["stock.picking"].new(
{'picking_type_id': picking_type_id}) {"picking_type_id": picking_type_id}
)
picking.onchange_picking_type() picking.onchange_picking_type()
res['location_id'] = picking.location_id.id res["location_id"] = picking.location_id.id
if 'location_dest_id' in fields_list: if "location_dest_id" in fields_list:
res['location_dest_id'] = self._default_location_dest_id() res["location_dest_id"] = self._default_location_dest_id()
if 'product_uom_qty' in fields_list: if "product_uom_qty" in fields_list:
res['product_uom_qty'] = 1.0 res["product_uom_qty"] = 1.0
return res return res
@api.model @api.model
@@ -115,7 +116,7 @@ class StockReservation(models.Model):
""" """
try: try:
location = self.env.ref(ref, raise_if_not_found=True) location = self.env.ref(ref, raise_if_not_found=True)
location.check_access_rule('read') location.check_access_rule("read")
location_id = location.id location_id = location.id
except (except_orm, ValueError): except (except_orm, ValueError):
location_id = False location_id = False
@@ -123,12 +124,12 @@ class StockReservation(models.Model):
@api.model @api.model
def _default_picking_type_id(self): def _default_picking_type_id(self):
ref = 'stock.picking_type_out' ref = "stock.picking_type_out"
return self.env.ref(ref, raise_if_not_found=False).id return self.env.ref(ref, raise_if_not_found=False).id
@api.model @api.model
def _default_location_dest_id(self): def _default_location_dest_id(self):
ref = 'stock_reserve.stock_location_reservation' ref = "stock_reserve.stock_location_reservation"
return self.get_location_from_ref(ref) return self.get_location_from_ref(ref)
@api.multi @api.multi
@@ -138,9 +139,9 @@ class StockReservation(models.Model):
The reservation is done using the default UOM of the product. The reservation is done using the default UOM of the product.
A date until which the product is reserved can be specified. A date until which the product is reserved can be specified.
""" """
self.write({'date_expected': fields.Datetime.now()}) self.write({"date_expected": fields.Datetime.now()})
self.mapped('move_id').action_confirm() self.mapped("move_id").action_confirm()
self.mapped('move_id.picking_id').action_assign() self.mapped("move_id.picking_id").action_assign()
return True return True
@api.multi @api.multi
@@ -148,16 +149,18 @@ class StockReservation(models.Model):
""" """
Release moves from reservation Release moves from reservation
""" """
self.mapped('move_id').action_cancel() self.mapped("move_id").action_cancel()
return True return True
@api.model @api.model
def release_validity_exceeded(self, ids=None): def release_validity_exceeded(self, ids=None):
""" Release all the reservation having an exceeded validity date """ """ Release all the reservation having an exceeded validity date """
domain = [('date_validity', '<', fields.date.today()), domain = [
('state', '=', 'assigned')] ("date_validity", "<", fields.date.today()),
("state", "=", "assigned"),
]
if ids: if ids:
domain.append(('id', 'in', ids)) domain.append(("id", "in", ids))
self.search(domain).release() self.search(domain).release()
return True return True
@@ -167,18 +170,19 @@ class StockReservation(models.Model):
self.release() self.release()
return super(StockReservation, self).unlink() return super(StockReservation, self).unlink()
@api.onchange('product_id') @api.onchange("product_id")
def _onchange_product_id(self): def _onchange_product_id(self):
""" set product_uom and name from product onchange """ """ set product_uom and name from product onchange """
# save value before reading of self.move_id as this last one erase # save value before reading of self.move_id as this last one erase
# product_id value # product_id value
move = self.move_id or self.env['stock.move'].new( move = self.move_id or self.env["stock.move"].new(
{'product_id': self.product_id}) {"product_id": self.product_id}
)
move.onchange_product_id() move.onchange_product_id()
self.name = move.name self.name = move.name
self.product_uom = move.product_uom self.product_uom = move.product_uom
@api.onchange('product_uom_qty') @api.onchange("product_uom_qty")
def _onchange_quantity(self): def _onchange_quantity(self):
""" On change of product quantity avoid negative quantities """ """ On change of product quantity avoid negative quantities """
if not self.product_id or self.product_uom_qty <= 0.0: if not self.product_id or self.product_uom_qty <= 0.0:
@@ -187,13 +191,12 @@ class StockReservation(models.Model):
@api.multi @api.multi
def open_move(self): def open_move(self):
self.ensure_one() self.ensure_one()
action = self.env.ref('stock.stock_move_action') action = self.env.ref("stock.stock_move_action")
action_dict = action.read()[0] action_dict = action.read()[0]
action_dict['name'] = _('Reservation Move') action_dict["name"] = _("Reservation Move")
# open directly in the form view # open directly in the form view
view_id = self.env.ref('stock.view_move_form').id view_id = self.env.ref("stock.view_move_form").id
action_dict.update( action_dict.update(
views=[(view_id, 'form')], views=[(view_id, "form")], res_id=self.move_id.id,
res_id=self.move_id.id, )
)
return action_dict return action_dict

View File

@@ -1,16 +1,12 @@
- - I force recomputation of stock.location parent left/right
I force recomputation of stock.location parent left/right - ? !python {model: stock.location}
-
!python {model: stock.location}:
# we need this because when running the tests at install time as is done on # we need this because when running the tests at install time as is done on
# Travis, the hook performing this operation for the new stock reservation # Travis, the hook performing this operation for the new stock reservation
# location is run after the test execution. This causes the stock level # location is run after the test execution. This causes the stock level
# computation to be wrong at the time the tests are run. # computation to be wrong at the time the tests are run.
self._parent_store_compute() : self._parent_store_compute()
- - I create a product to test the stock reservation
I create a product to test the stock reservation - !record {model: product.product, id: product_sorbet}:
-
!record {model: product.product, id: product_sorbet}:
default_code: 001SORBET default_code: 001SORBET
name: Sorbet name: Sorbet
type: product type: product
@@ -19,137 +15,96 @@
standard_price: 70.0 standard_price: 70.0
uom_id: product.product_uom_kgm uom_id: product.product_uom_kgm
uom_po_id: product.product_uom_kgm uom_po_id: product.product_uom_kgm
- - I create a stock orderpoint for the product
I create a stock orderpoint for the product - !record {model: stock.warehouse.orderpoint, id: sorbet_orderpoint}:
-
!record {model: stock.warehouse.orderpoint, id: sorbet_orderpoint}:
warehouse_id: stock.warehouse0 warehouse_id: stock.warehouse0
location_id: stock.stock_location_stock location_id: stock.stock_location_stock
product_id: product_sorbet product_id: product_sorbet
product_uom: product.product_uom_kgm product_uom: product.product_uom_kgm
product_min_qty: 4.0 product_min_qty: 4.0
product_max_qty: 15.0 product_max_qty: 15.0
- - I update the current stock of the Sorbet with 10 kgm
I update the current stock of the Sorbet with 10 kgm - !record {model: stock.change.product.qty, id: change_qty}:
-
!record {model: stock.change.product.qty, id: change_qty}:
new_quantity: 10 new_quantity: 10
product_id: product_sorbet product_id: product_sorbet
- - !python {model: stock.change.product.qty}: |
!python {model: stock.change.product.qty}: |
self.browse(ref('change_qty')).change_product_qty() self.browse(ref('change_qty')).change_product_qty()
- - I check Virtual stock of Sorbet after update stock.
I check Virtual stock of Sorbet after update stock. - !python {model: product.product, id: product_sorbet}: |
-
!python {model: product.product, id: product_sorbet}: |
assert self.virtual_available == 10, "Stock is not updated." assert self.virtual_available == 10, "Stock is not updated."
- - I create a stock reservation for 6 kgm
I create a stock reservation for 6 kgm - !record {model: stock.reservation, id: reserv_sorbet1}:
-
!record {model: stock.reservation, id: reserv_sorbet1}:
product_id: product_sorbet product_id: product_sorbet
product_uom_qty: 6.0 product_uom_qty: 6.0
product_uom: product.product_uom_kgm product_uom: product.product_uom_kgm
name: reserve 6 kg of sorbet for test name: reserve 6 kg of sorbet for test
- - I confirm the reservation
I confirm the reservation - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
self.browse(ref('reserv_sorbet1')).reserve() self.browse(ref('reserv_sorbet1')).reserve()
- - I create a stock reservation for 500g
I create a stock reservation for 500g - !record {model: stock.reservation, id: reserv_sorbet2}:
-
!record {model: stock.reservation, id: reserv_sorbet2}:
product_id: product_sorbet product_id: product_sorbet
product_uom_qty: 500 product_uom_qty: 500
product_uom: product.product_uom_gram product_uom: product.product_uom_gram
name: reserve 500g of sorbet for test name: reserve 500g of sorbet for test
- - I confirm the reservation
I confirm the reservation - !python {model: stock.reservation, id: reserv_sorbet2}: |
-
!python {model: stock.reservation, id: reserv_sorbet2}: |
self.reserve() self.reserve()
- - I check the reserved amount of the product and the template
I check the reserved amount of the product and the template - !python {model: product.product, id: product_sorbet}: |
-
!python {model: product.product, id: product_sorbet}: |
assert 6.5 == self.reservation_count assert 6.5 == self.reservation_count
assert 6.5 == self.product_tmpl_id.reservation_count assert 6.5 == self.product_tmpl_id.reservation_count
- - Then the reservation should be assigned and have reserved a quant
Then the reservation should be assigned and have reserved a quant - !python {model: stock.reservation, id: reserv_sorbet2}: |
-
!python {model: stock.reservation, id: reserv_sorbet2}: |
assert 'assigned' == self.state assert 'assigned' == self.state
assert 1 == len(self.reserved_quant_ids) assert 1 == len(self.reserved_quant_ids)
- - I check Virtual stock of Sorbet after update reservation
I check Virtual stock of Sorbet after update reservation - !python {model: product.product, id: product_sorbet}: |
-
!python {model: product.product, id: product_sorbet}: |
assert 3.5 == self.virtual_available assert 3.5 == self.virtual_available
- - I run the scheduler
I run the scheduler - !python {model: procurement.order}: |
-
!python {model: procurement.order}: |
self.run_scheduler() self.run_scheduler()
- - The procurement linked to the orderpoint must be in exception (no routes configured)
The procurement linked to the orderpoint must be in exception (no routes configured) - !python {model: stock.warehouse.orderpoint}: |
-
!python {model: stock.warehouse.orderpoint}: |
orderpoint = self.browse(ref('stock_reserve.sorbet_orderpoint')) orderpoint = self.browse(ref('stock_reserve.sorbet_orderpoint'))
assert orderpoint.procurement_ids[0].state == 'exception', 'procurement must be in exception as there is no rule for procurement' assert orderpoint.procurement_ids[0].state == 'exception', 'procurement must be in exception as there is no rule for procurement'
import math import math
assert orderpoint.procurement_ids[0].product_qty == math.ceil(15 - 3.5), 'wrong product qty ordered' assert orderpoint.procurement_ids[0].product_qty == math.ceil(15 - 3.5), 'wrong product qty ordered'
- - I release the reservation
I release the reservation - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
self.browse(ref('reserv_sorbet1')).release() self.browse(ref('reserv_sorbet1')).release()
- - I check Virtual stock of Sorbet after update reservation
I check Virtual stock of Sorbet after update reservation - !python {model: product.product}: |
-
!python {model: product.product}: |
product = self.browse(ref('stock_reserve.product_sorbet')) product = self.browse(ref('stock_reserve.product_sorbet'))
assert product.virtual_available == 9.5, "Stock is not updated." assert product.virtual_available == 9.5, "Stock is not updated."
- - I set the validity of the second reservation to yesterday
I set the validity of the second reservation to yesterday - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
import datetime import datetime
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
yesterday = datetime.date.today() - datetime.timedelta(days=1) yesterday = datetime.date.today() - datetime.timedelta(days=1)
yesterday = yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT) yesterday = yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
self.browse(ref('reserv_sorbet2')).write({'date_validity': yesterday}) self.browse(ref('reserv_sorbet2')).write({'date_validity': yesterday})
- - I call the function releasing expired reservations
I call the function releasing expired reservations - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
self.release_validity_exceeded() self.release_validity_exceeded()
- - I check Virtual stock of Sorbet after update reservation
I check Virtual stock of Sorbet after update reservation - !python {model: product.product}: |
-
!python {model: product.product}: |
product = self.browse(ref('stock_reserve.product_sorbet')) product = self.browse(ref('stock_reserve.product_sorbet'))
product.refresh() product.refresh()
assert product.virtual_available == 10.0, "Stock is not updated." assert product.virtual_available == 10.0, "Stock is not updated."
- - I create a stock reservation for 3 kgm
I create a stock reservation for 3 kgm - !record {model: stock.reservation, id: reserv_sorbet3}:
-
!record {model: stock.reservation, id: reserv_sorbet3}:
product_id: product_sorbet product_id: product_sorbet
product_uom_qty: 3.0 product_uom_qty: 3.0
product_uom: product.product_uom_kgm product_uom: product.product_uom_kgm
name: reserve 3 kg of sorbet for test (release on unlink) name: reserve 3 kg of sorbet for test (release on unlink)
- - I confirm the reservation
I confirm the reservation - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
self.browse(ref('reserv_sorbet3')).reserve() self.browse(ref('reserv_sorbet3')).reserve()
- - I press the open_move button on reservation and test result
I press the open_move button on reservation and test result - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
reserv = self.browse(ref('reserv_sorbet3')) reserv = self.browse(ref('reserv_sorbet3'))
move = reserv.move_id move = reserv.move_id
action_dict = reserv.open_move() action_dict = reserv.open_move()
@@ -158,10 +113,8 @@
assert action_dict['id'] == ref('stock.stock_move_action'), "action not correct" assert action_dict['id'] == ref('stock.stock_move_action'), "action not correct"
assert action_dict['views'][0][0] == ref('stock.view_move_form'), "action view not correct" assert action_dict['views'][0][0] == ref('stock.view_move_form'), "action view not correct"
- - I press button 'action_view_reservations' on product variant and test result
I press button 'action_view_reservations' on product variant and test result - !python {model: product.product}: |
-
!python {model: product.product}: |
product = self.browse(ref('product_sorbet')) product = self.browse(ref('product_sorbet'))
action_dict = product.action_view_reservations() action_dict = product.action_view_reservations()
assert action_dict['res_model'] == 'stock.reservation', "action model is not 'stock.move'" assert action_dict['res_model'] == 'stock.reservation', "action model is not 'stock.move'"
@@ -171,10 +124,8 @@
assert action_dict['context']['search_default_draft'] == 1, "wrong context" assert action_dict['context']['search_default_draft'] == 1, "wrong context"
assert action_dict['context']['search_default_reserved'] == 1, "wrong context" assert action_dict['context']['search_default_reserved'] == 1, "wrong context"
- - I press button 'action_view_reservations' on product template and test result
I press button 'action_view_reservations' on product template and test result - !python {model: product.template}: |
-
!python {model: product.template}: |
product = self.env['product.product'].browse(ref('product_sorbet')) product = self.env['product.product'].browse(ref('product_sorbet'))
product_tmpl = product.product_tmpl_id product_tmpl = product.product_tmpl_id
product_ids = product_tmpl.mapped('product_variant_ids.id') product_ids = product_tmpl.mapped('product_variant_ids.id')
@@ -188,10 +139,8 @@
assert action_dict['context']['search_default_draft'] == 1, "wrong context" assert action_dict['context']['search_default_draft'] == 1, "wrong context"
assert action_dict['context']['search_default_reserved'] == 1, "wrong context" assert action_dict['context']['search_default_reserved'] == 1, "wrong context"
- - I unlink the reservation
I unlink the reservation - !python {model: stock.reservation}: |
-
!python {model: stock.reservation}: |
reserv = self.browse(ref('reserv_sorbet3')) reserv = self.browse(ref('reserv_sorbet3'))
move = reserv.move_id move = reserv.move_id
reserv.unlink() reserv.unlink()

View File

@@ -1,31 +1,47 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="product_template_form_view_reservation_button">
<record model="ir.ui.view" id="product_template_form_view_reservation_button"> <field name="name">product.template.reservation.button</field>
<field name="name">product.template.reservation.button</field> <field name="model">product.template</field>
<field name="model">product.template</field> <field name="inherit_id" ref="product.product_template_only_form_view" />
<field name="inherit_id" ref="product.product_template_only_form_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_inline oe_stat_button" name="action_view_reservations" <button
type="object" attrs="{'invisible':[('type', '!=', 'product')]}" icon="fa-lock"> class="oe_inline oe_stat_button"
<field string="Stock Reservations" name="reservation_count" widget="statinfo" /> name="action_view_reservations"
</button> type="object"
</xpath> attrs="{'invisible':[('type', '!=', 'product')]}"
</field> icon="fa-lock"
</record> >
<field
<record model="ir.ui.view" id="product_product_form_view_reservation_button"> string="Stock Reservations"
<field name="name">product.template.reservation.button</field> name="reservation_count"
<field name="model">product.product</field> widget="statinfo"
<field name="inherit_id" ref="product.product_normal_form_view"/> />
<field name="arch" type="xml"> </button>
<xpath expr="//div[@name='button_box']" position="inside"> </xpath>
<button class="oe_inline oe_stat_button" name="action_view_reservations" </field>
type="object" attrs="{'invisible':[('type', '!=', 'product')]}" icon="fa-lock"> </record>
<field string="Stock Reservations" name="reservation_count" widget="statinfo" /> <record model="ir.ui.view" id="product_product_form_view_reservation_button">
</button> <field name="name">product.template.reservation.button</field>
</xpath> <field name="model">product.product</field>
</field> <field name="inherit_id" ref="product.product_normal_form_view" />
</record> <field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button
class="oe_inline oe_stat_button"
name="action_view_reservations"
type="object"
attrs="{'invisible':[('type', '!=', 'product')]}"
icon="fa-lock"
>
<field
string="Stock Reservations"
name="reservation_count"
widget="statinfo"
/>
</button>
</xpath>
</field>
</record>
</odoo> </odoo>

View File

@@ -1,140 +1,192 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record id="view_stock_reservation_form" model="ir.ui.view"> <record id="view_stock_reservation_form" model="ir.ui.view">
<field name="name">stock.reservation.form</field> <field name="name">stock.reservation.form</field>
<field name="model">stock.reservation</field> <field name="model">stock.reservation</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Stock Reservations" version="7.0"> <form string="Stock Reservations" version="7.0">
<header> <header>
<button name="reserve" type="object" <button
name="reserve"
type="object"
string="Reserve"
class="oe_highlight"
states="draft"
/>
<button
name="release"
type="object"
string="Release"
class="oe_highlight"
states="assigned,confirmed,done"
/>
<button
name="open_move"
type="object"
string="View Reservation Move"
/>
<field
name="state"
widget="statusbar"
statusbar_visible="draft,assigned"
/>
</header>
<sheet>
<group>
<group name="main_grp" string="Details">
<field name="product_id" />
<label for="product_uom_qty" />
<div>
<field name="product_uom_qty" class="oe_inline" />
<field
name="product_uom"
groups="product.group_uom"
class="oe_inline"
/>
</div>
<field name="name" />
<field name="date_validity" />
<field name="create_date" groups="base.group_no_one" />
<field
name="company_id"
groups="base.group_multi_company"
widget="selection"
/>
<field
name="restrict_partner_id"
groups="stock.group_tracking_owner"
/>
</group>
<group
name="location"
string="Locations"
groups="stock.group_locations"
>
<field name="location_id" />
<field name="location_dest_id" />
</group>
<group name="note" string="Notes">
<field name="note" nolabel="1" />
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_stock_reservation_tree" model="ir.ui.view">
<field name="name">stock.reservation.tree</field>
<field name="model">stock.reservation</field>
<field name="arch" type="xml">
<tree
string="Stock Reservations"
version="7.0"
colors="blue:state == 'draft';grey:state == 'cancel'"
>
<field name="name" />
<field name="product_id" />
<field name="move_id" />
<field name="product_uom_qty" sum="Total" />
<field name="product_uom" />
<field name="date_validity" />
<field name="restrict_partner_id" groups="stock.group_tracking_owner" />
<field name="location_id" />
<field name="state" />
<button
name="reserve"
type="object"
string="Reserve" string="Reserve"
class="oe_highlight" icon="terp-locked"
states="draft"/> states="draft"
<button name="release" type="object" />
<button
name="release"
type="object"
string="Release" string="Release"
class="oe_highlight" icon="gtk-undo"
states="assigned,confirmed,done"/> states="assigned,confirmed,done"
<button name="open_move" type="object" />
string="View Reservation Move"/> </tree>
<field name="state" widget="statusbar" </field>
statusbar_visible="draft,assigned"/> </record>
</header> <record id="view_stock_reservation_search" model="ir.ui.view">
<sheet> <field name="name">stock.reservation.search</field>
<group> <field name="model">stock.reservation</field>
<group name="main_grp" string="Details"> <field name="arch" type="xml">
<field name="product_id" /> <search string="Stock Reservations" version="7.0">
<label for="product_uom_qty" /> <filter
<div> name="draft"
<field name="product_uom_qty" class="oe_inline"/> string="Draft"
<field name="product_uom" domain="[('state', '=', 'draft')]"
groups="product.group_uom" class="oe_inline"/> help="Not already reserved"
</div> />
<field name="name"/> <filter
<field name="date_validity" /> name="reserved"
<field name="create_date" groups="base.group_no_one"/> string="Reserved"
<field name="company_id" domain="[('state', '=', 'assigned')]"
groups="base.group_multi_company" help="Moves are reserved."
widget="selection"/> />
<field name="restrict_partner_id" groups="stock.group_tracking_owner"/> <filter
</group> name="cancel"
<group name="location" string="Locations" string="Released"
groups="stock.group_locations"> domain="[('state', '=', 'cancel')]"
<field name="location_id"/> help="Reservations have been released."
<field name="location_dest_id"/> />
</group> <field name="name" />
<group name="note" string="Notes"> <field name="product_id" />
<field name="note" nolabel="1"/> <field name="move_id" />
</group> <field name="restrict_partner_id" groups="stock.group_tracking_owner" />
<group expand="0" string="Group By...">
<filter
string="Status"
name="groupby_state"
domain="[]"
context="{'group_by': 'state'}"
/>
<filter
string="Product"
domain="[]"
name="groupby_product"
context="{'group_by': 'product_id'}"
/>
<filter
string="Product UoM"
domain="[]"
name="groupby_product_uom"
context="{'group_by': 'product_uom'}"
/>
<filter
string="Source Location"
domain="[]"
name="groupby_location"
context="{'group_by': 'location_id'}"
/>
</group> </group>
</sheet> </search>
</form> </field>
</field> </record>
</record> <record id="action_stock_reservation_tree" model="ir.actions.act_window">
<field name="name">Stock Reservations</field>
<record id="view_stock_reservation_tree" model="ir.ui.view"> <field name="res_model">stock.reservation</field>
<field name="name">stock.reservation.tree</field> <field name="type">ir.actions.act_window</field>
<field name="model">stock.reservation</field> <field name="view_id" ref="view_stock_reservation_tree" />
<field name="arch" type="xml"> <field name="search_view_id" ref="view_stock_reservation_search" />
<tree string="Stock Reservations" version="7.0" <field name="context">{'search_default_draft': 1,
colors="blue:state == 'draft';grey:state == 'cancel'" >
<field name="name" />
<field name="product_id" />
<field name="move_id" />
<field name="product_uom_qty" sum="Total" />
<field name="product_uom" />
<field name="date_validity" />
<field name="restrict_partner_id" groups="stock.group_tracking_owner"/>
<field name="location_id" />
<field name="state"/>
<button name="reserve" type="object"
string="Reserve"
icon="terp-locked"
states="draft"/>
<button name="release" type="object"
string="Release"
icon="gtk-undo"
states="assigned,confirmed,done"/>
</tree>
</field>
</record>
<record id="view_stock_reservation_search" model="ir.ui.view">
<field name="name">stock.reservation.search</field>
<field name="model">stock.reservation</field>
<field name="arch" type="xml">
<search string="Stock Reservations" version="7.0">
<filter name="draft" string="Draft"
domain="[('state', '=', 'draft')]"
help="Not already reserved"/>
<filter name="reserved" string="Reserved"
domain="[('state', '=', 'assigned')]"
help="Moves are reserved."/>
<filter name="cancel" string="Released"
domain="[('state', '=', 'cancel')]"
help="Reservations have been released."/>
<field name="name" />
<field name="product_id" />
<field name="move_id" />
<field name="restrict_partner_id" groups="stock.group_tracking_owner"/>
<group expand="0" string="Group By...">
<filter string="Status"
name="groupby_state"
domain="[]" context="{'group_by': 'state'}"/>
<filter string="Product" domain="[]"
name="groupby_product"
context="{'group_by': 'product_id'}"/>
<filter string="Product UoM" domain="[]"
name="groupby_product_uom"
context="{'group_by': 'product_uom'}"/>
<filter string="Source Location" domain="[]"
name="groupby_location"
context="{'group_by': 'location_id'}"/>
</group>
</search>
</field>
</record>
<record id="action_stock_reservation_tree" model="ir.actions.act_window">
<field name="name">Stock Reservations</field>
<field name="res_model">stock.reservation</field>
<field name="type">ir.actions.act_window</field>
<field name="view_id" ref="view_stock_reservation_tree"/>
<field name="search_view_id" ref="view_stock_reservation_search"/>
<field name="context">{'search_default_draft': 1,
'search_default_reserved': 1, 'search_default_reserved': 1,
'search_default_groupby_product': 1}</field> 'search_default_groupby_product': 1}</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="oe_view_nocontent_create"> <p class="oe_view_nocontent_create">
Click to create a stock reservation. Click to create a stock reservation.
</p><p> </p>
<p>
This menu allow you to prepare and reserve some quantities This menu allow you to prepare and reserve some quantities
of products. of products.
</p> </p>
</field> </field>
</record> </record>
<menuitem
<menuitem action="action_stock_reservation_tree" action="action_stock_reservation_tree"
id="menu_action_stock_reservation" id="menu_action_stock_reservation"
parent="stock.menu_stock_inventory_control" parent="stock.menu_stock_inventory_control"
sequence="30"/> sequence="30"
/>
</odoo> </odoo>